diff --git a/lang/dist/platform/elb b/lang/dist/platform/elb index 2df99e2..a397f4b 100755 Binary files a/lang/dist/platform/elb and b/lang/dist/platform/elb differ diff --git a/lang/dist/platform/elc-new b/lang/dist/platform/elc-new deleted file mode 100755 index f1cba65..0000000 Binary files a/lang/dist/platform/elc-new and /dev/null differ diff --git a/lang/el-compiler/runtime/el_runtime.c b/lang/el-compiler/runtime/el_runtime.c index 982441a..9697867 100644 --- a/lang/el-compiler/runtime/el_runtime.c +++ b/lang/el-compiler/runtime/el_runtime.c @@ -2242,6 +2242,43 @@ el_val_t url_decode(el_val_t sv) { return el_wrap_str(out); } +/* ── html_raw ──────────────────────────────────────────────────────────────── + * Identity passthrough for raw HTML template interpolation. + * El's {raw(expr)} compiles to html_raw(expr) — the value is output as-is + * without any escaping. The caller is responsible for safety. + */ +el_val_t html_raw(el_val_t s) { + return s; +} + +/* ── html_escape ───────────────────────────────────────────────────────────── + * Escape < > " ' & for safe HTML text interpolation. + * El's {expr} in HTML templates compiles to html_escape(expr). + */ +el_val_t html_escape(el_val_t sv) { + const char* src = EL_CSTR(sv); + if (!src) return EL_STR(""); + size_t len = strlen(src); + /* Worst case: every byte → 6 chars (") */ + char* out = (char*)malloc(len * 6 + 1); + if (!out) return sv; + el_arena_track(out); + char* p = out; + for (size_t i = 0; i < len; i++) { + unsigned char c = (unsigned char)src[i]; + switch (c) { + case '&': memcpy(p, "&", 5); p += 5; break; + case '<': memcpy(p, "<", 4); p += 4; break; + case '>': memcpy(p, ">", 4); p += 4; break; + case '"': memcpy(p, """, 6); p += 6; break; + case '\'': memcpy(p, "'", 5); p += 5; break; + default: *p++ = (char)c; break; + } + } + *p = '\0'; + return el_wrap_str(out); +} + /* ── HTML allowlist sanitizer ──────────────────────────────────────────────── * el_html_sanitize(input, allowlist_json) * diff --git a/lang/el-compiler/runtime/el_runtime.h b/lang/el-compiler/runtime/el_runtime.h index 211c923..2f9583f 100644 --- a/lang/el-compiler/runtime/el_runtime.h +++ b/lang/el-compiler/runtime/el_runtime.h @@ -227,6 +227,8 @@ el_val_t url_decode(el_val_t s); /* '+' → space, %XX → byte */ * {"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); +el_val_t html_raw(el_val_t s); +el_val_t html_escape(el_val_t s); /* ── Filesystem ──────────────────────────────────────────────────────────── */ diff --git a/lang/elb.el b/lang/elb.el index 9dcd731..21a0676 100644 --- a/lang/elb.el +++ b/lang/elb.el @@ -77,6 +77,33 @@ fn parse_manifest_entry(src: String) -> String { return "" } +// parse_manifest_c_sources - collect all `c_source "path"` lines from the +// build block. Returns a flat list of path strings. +fn parse_manifest_c_sources(src: String) -> [String] { + let result: [String] = native_list_empty() + let lines: [String] = str_split(src, "\n") + let n: Int = native_list_len(lines) + let i = 0 + while i < n { + let line: String = native_list_get(lines, i) + let t: String = str_trim(line) + if str_starts_with(t, "c_source ") { + let after: String = str_slice(t, 9, str_len(t)) + let trimmed: String = str_trim(after) + if str_starts_with(trimmed, "\"") { + let inner: String = str_slice(trimmed, 1, str_len(trimmed)) + let q: Int = str_index_of(inner, "\"") + if q >= 0 { + let path: String = str_slice(inner, 0, q) + let result = native_list_append(result, path) + } + } + } + let i = i + 1 + } + return result +} + fn parse_manifest_name(src: String) -> String { let lines: [String] = str_split(src, "\n") let n: Int = native_list_len(lines) @@ -274,7 +301,11 @@ fn link_binary(c_files: [String], out_bin: String, runtime_path: String, out_dir // Detect clang vs gcc: -fbracket-depth is clang-only; silently ignored // if unsupported but gcc rejects it with an error. let bracket_flag: String = "$(cc --version 2>&1 | grep -q clang && printf -- '-fbracket-depth=1024' || true)" - let parts = native_list_append(parts, "cc -O2 " + bracket_flag + " -I " + dirname_of(runtime_path) + " -I " + out_dir) + // On macOS, OpenSSL is not on the default linker path. Detect homebrew + // prefix and add it if present (no-op on Linux where libssl is in /usr/lib). + let ossl_lib_flag: String = "$(brew --prefix openssl 2>/dev/null | xargs -I{} printf -- '-L{}/lib' 2>/dev/null || true)" + let ossl_inc_flag: String = "$(brew --prefix openssl 2>/dev/null | xargs -I{} printf -- '-I{}/include' 2>/dev/null || true)" + let parts = native_list_append(parts, "cc -O2 " + bracket_flag + " " + ossl_inc_flag + " -I " + dirname_of(runtime_path) + " -I " + out_dir) let i = 0 while i < n { let f: String = native_list_get(c_files, i) @@ -282,7 +313,7 @@ fn link_binary(c_files: [String], out_bin: String, runtime_path: String, out_dir let i = i + 1 } let parts = native_list_append(parts, runtime_path) - let parts = native_list_append(parts, "-lcurl -lssl -lcrypto -lpthread -lm") + let parts = native_list_append(parts, ossl_lib_flag + " -lcurl -lssl -lcrypto -lpthread -lm") let parts = native_list_append(parts, "-o " + out_bin) let cmd: String = str_join(parts, " ") println(" link " + out_bin) @@ -315,6 +346,7 @@ fn main() -> Void { let pkg_name: String = parse_manifest_name(manifest_src) let entry: String = parse_manifest_entry(manifest_src) + let extra_c: [String] = parse_manifest_c_sources(manifest_src) if str_eq(entry, "") { println("elb: manifest.el has no 'entry' declaration") exit(1) @@ -393,6 +425,15 @@ fn main() -> Void { exit(1) } + // Append any extra C sources declared in the manifest (e.g. platform stubs) + let ei = 0 + let en: Int = native_list_len(extra_c) + while ei < en { + let ec: String = native_list_get(extra_c, ei) + let c_files = native_list_append(c_files, ec) + let ei = ei + 1 + } + // Link let out_bin: String = out_dir + "/" + pkg_name let linked: Bool = link_binary(c_files, out_bin, runtime_path, out_dir, dry_run) diff --git a/lang/elc.c b/lang/elc.c new file mode 100644 index 0000000..5104d13 --- /dev/null +++ b/lang/elc.c @@ -0,0 +1,10062 @@ +#include +#include +#include "el_runtime.h" + +el_val_t is_digit_code(el_val_t c); +el_val_t is_alpha_code(el_val_t c); +el_val_t is_alnum_or_underscore_code(el_val_t c); +el_val_t is_ws_code(el_val_t c); +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 lex_is_whitespace(el_val_t ch); +el_val_t tok_append(el_val_t tokens, 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 src, el_val_t start, el_val_t total); +el_val_t scan_ident(el_val_t src, el_val_t start, el_val_t total); +el_val_t substr_at(el_val_t src, 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 src, el_val_t start, el_val_t total); +el_val_t scan_interp_brace(el_val_t src, el_val_t start, el_val_t total); +el_val_t interp_tokens_append_all(el_val_t dst, el_val_t src); +el_val_t scan_interp_string(el_val_t src, 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); +el_val_t tok_kind(el_val_t tokens, el_val_t pos); +el_val_t tok_value(el_val_t tokens, el_val_t pos); +el_val_t expect(el_val_t tokens, el_val_t pos, el_val_t kind); +el_val_t make_result(el_val_t node, el_val_t pos); +el_val_t skip_type(el_val_t tokens, el_val_t pos); +el_val_t parse_params(el_val_t tokens, el_val_t pos); +el_val_t is_html_tag_name(el_val_t name); +el_val_t is_void_element(el_val_t name); +el_val_t parse_raw_text_content(el_val_t tokens, el_val_t pos, el_val_t tag_name); +el_val_t parse_html_text_tokens(el_val_t tokens, el_val_t pos); +el_val_t parse_html_attrs(el_val_t tokens, el_val_t pos); +el_val_t parse_html_children(el_val_t tokens, el_val_t pos, el_val_t parent_tag); +el_val_t parse_html_each_body(el_val_t tokens, el_val_t pos); +el_val_t parse_html_element(el_val_t tokens, el_val_t pos); +el_val_t parse_html_template(el_val_t tokens, el_val_t pos); +el_val_t parse_primary(el_val_t tokens, el_val_t pos); +el_val_t parse_if(el_val_t tokens, el_val_t pos); +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); +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 parse_one(el_val_t tokens, el_val_t pos); +el_val_t skip_to_rbrace(el_val_t tokens, el_val_t pos); +el_val_t is_stmt_start_kind(el_val_t k); +el_val_t skip_expr_to_stmt_boundary(el_val_t tokens, el_val_t pos); +el_val_t scan_params_c(el_val_t tokens, el_val_t pos); +el_val_t scan_fn_sigs(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 sanitize_test_name(el_val_t name); +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 next_html_id(void); +el_val_t cg_html_parts(el_val_t children, el_val_t acc_var); +el_val_t cg_html_attrs_str(el_val_t attrs, el_val_t acc_var); +el_val_t cg_html_element_str(el_val_t elem, el_val_t acc_var); +el_val_t cg_html_each(el_val_t node, el_val_t acc_var); +el_val_t cg_html_if(el_val_t node, el_val_t acc_var); +el_val_t cg_html_template(el_val_t expr); +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); +el_val_t cg_match_stmt(el_val_t expr, el_val_t indent, el_val_t declared); +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 cg_if_expr(el_val_t expr); +el_val_t list_contains(el_val_t lst, el_val_t s); +el_val_t cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared); +el_val_t strip_outer_parens(el_val_t s); +el_val_t cg_if_stmt(el_val_t expr, el_val_t indent, el_val_t declared); +el_val_t cg_for_body(el_val_t item, el_val_t list_expr, el_val_t body, el_val_t indent, el_val_t declared); +el_val_t cg_for_stmt(el_val_t expr, el_val_t indent, el_val_t declared); +el_val_t cg_stmts(el_val_t stmts, el_val_t indent, el_val_t declared); +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 is_float_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); +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); +el_val_t is_top_level_decl(el_val_t stmt); +el_val_t cgi_arg(el_val_t value, el_val_t has_value); +el_val_t vbd_is_restricted_name(el_val_t name); +el_val_t vbd_expr_has_restricted_call(el_val_t expr); +el_val_t vbd_has_restricted_call(el_val_t stmts); +el_val_t codegen(el_val_t stmts, el_val_t source); +el_val_t cg_decl_streaming(el_val_t stmt); +el_val_t emit_streaming_preamble(el_val_t sigs, el_val_t source); +el_val_t codegen_streaming(el_val_t tokens, el_val_t sigs, el_val_t source); +el_val_t js_escape(el_val_t s); +el_val_t js_str_lit(el_val_t s); +el_val_t js_emit_line(el_val_t line); +el_val_t js_emit_blank(void); +el_val_t js_binop(el_val_t op); +el_val_t js_is_el_method(el_val_t name); +el_val_t js_is_async_builtin(el_val_t name); +el_val_t js_register_async_fn(el_val_t name); +el_val_t js_is_async_fn(el_val_t name); +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 js_build_int_names_for_params(el_val_t params); +el_val_t js_is_int_call(el_val_t call_expr); +el_val_t js_next_html_id(void); +el_val_t js_cg_html_parts(el_val_t children, el_val_t acc_var); +el_val_t js_cg_html_attrs_str(el_val_t attrs, el_val_t acc_var); +el_val_t js_cg_html_element_str(el_val_t elem, el_val_t acc_var); +el_val_t js_cg_html_each(el_val_t node, el_val_t acc_var); +el_val_t js_cg_html_template(el_val_t expr); +el_val_t js_cg_expr(el_val_t expr); +el_val_t js_next_match_id(void); +el_val_t js_cg_match(el_val_t expr); +el_val_t js_next_lambda_id(void); +el_val_t js_cg_lambda(el_val_t expr); +el_val_t js_list_contains(el_val_t lst, el_val_t s); +el_val_t js_cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared); +el_val_t js_strip_outer_parens(el_val_t s); +el_val_t js_cg_if_stmt(el_val_t expr, el_val_t indent, el_val_t declared); +el_val_t js_cg_for_body(el_val_t item, el_val_t list_expr, el_val_t body, el_val_t indent, el_val_t declared); +el_val_t js_cg_for_stmt(el_val_t expr, el_val_t indent, el_val_t declared); +el_val_t js_cg_stmts(el_val_t stmts, el_val_t indent, el_val_t declared); +el_val_t js_params_str(el_val_t params); +el_val_t js_transform_implicit_return(el_val_t body); +el_val_t js_cg_fn(el_val_t stmt); +el_val_t js_is_fndef(el_val_t stmt); +el_val_t js_is_top_level_decl(el_val_t stmt); +el_val_t codegen_js(el_val_t stmts, el_val_t source); +el_val_t codegen_js_bundle(el_val_t stmts, el_val_t source, el_val_t runtime_content); +el_val_t codegen_js_inner(el_val_t stmts, el_val_t source, el_val_t bundle_mode, el_val_t runtime_content); +el_val_t js_strip_es_exports(el_val_t content); +el_val_t compile(el_val_t source); +el_val_t compile_test(el_val_t source); +el_val_t compile_js(el_val_t source); +el_val_t compile_js_with_bundle(el_val_t source, el_val_t runtime_path); +el_val_t compile_dispatch(el_val_t tgt, el_val_t source); +el_val_t compile_dispatch_bundle(el_val_t tgt, el_val_t source, el_val_t runtime_path); +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 detect_bundle(el_val_t argv); +el_val_t detect_minify(el_val_t argv); +el_val_t detect_obfuscate(el_val_t argv); +el_val_t detect_test(el_val_t argv); +el_val_t make_temp_path(el_val_t suffix); +el_val_t js_reserved_names(void); +el_val_t find_node_tool(el_val_t tool_name, el_val_t src_dir); +el_val_t apply_minify(el_val_t js_path, el_val_t out_path, el_val_t src_dir); +el_val_t apply_obfuscate(el_val_t js_path, el_val_t out_path, el_val_t src_dir); +el_val_t resolve_runtime_path(el_val_t src_path); +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 run_with_postprocess(el_val_t tgt, el_val_t source, el_val_t src_path, el_val_t do_bundle, el_val_t do_obfuscate, el_val_t argc, el_val_t positional); + +el_val_t is_digit_code(el_val_t c) { + if (c >= 48) { + if (c <= 57) { + return 1; + } + } + return 0; + return 0; +} + +el_val_t is_alpha_code(el_val_t c) { + if (c >= 65) { + if (c <= 90) { + return 1; + } + } + if (c >= 97) { + if (c <= 122) { + return 1; + } + } + return 0; + return 0; +} + +el_val_t is_alnum_or_underscore_code(el_val_t c) { + if (is_digit_code(c)) { + return 1; + } + if (is_alpha_code(c)) { + return 1; + } + if (c == 95) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_ws_code(el_val_t c) { + if (c == 32) { + return 1; + } + if (c == 9) { + return 1; + } + if (c == 10) { + return 1; + } + if (c == 13) { + return 1; + } + return 0; + return 0; +} + +el_val_t lex_is_digit(el_val_t ch) { + if (str_eq(ch, EL_STR("0"))) { + return 1; + } + if (str_eq(ch, EL_STR("1"))) { + return 1; + } + if (str_eq(ch, EL_STR("2"))) { + return 1; + } + if (str_eq(ch, EL_STR("3"))) { + return 1; + } + if (str_eq(ch, EL_STR("4"))) { + return 1; + } + if (str_eq(ch, EL_STR("5"))) { + return 1; + } + if (str_eq(ch, EL_STR("6"))) { + return 1; + } + if (str_eq(ch, EL_STR("7"))) { + return 1; + } + if (str_eq(ch, EL_STR("8"))) { + return 1; + } + if (str_eq(ch, EL_STR("9"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t lex_is_alpha(el_val_t ch) { + if (str_eq(ch, EL_STR("a"))) { + return 1; + } + if (str_eq(ch, EL_STR("b"))) { + return 1; + } + if (str_eq(ch, EL_STR("c"))) { + return 1; + } + if (str_eq(ch, EL_STR("d"))) { + return 1; + } + if (str_eq(ch, EL_STR("e"))) { + return 1; + } + if (str_eq(ch, EL_STR("f"))) { + return 1; + } + if (str_eq(ch, EL_STR("g"))) { + return 1; + } + if (str_eq(ch, EL_STR("h"))) { + return 1; + } + if (str_eq(ch, EL_STR("i"))) { + return 1; + } + if (str_eq(ch, EL_STR("j"))) { + return 1; + } + if (str_eq(ch, EL_STR("k"))) { + return 1; + } + if (str_eq(ch, EL_STR("l"))) { + return 1; + } + if (str_eq(ch, EL_STR("m"))) { + return 1; + } + if (str_eq(ch, EL_STR("n"))) { + return 1; + } + if (str_eq(ch, EL_STR("o"))) { + return 1; + } + if (str_eq(ch, EL_STR("p"))) { + return 1; + } + if (str_eq(ch, EL_STR("q"))) { + return 1; + } + if (str_eq(ch, EL_STR("r"))) { + return 1; + } + if (str_eq(ch, EL_STR("s"))) { + return 1; + } + if (str_eq(ch, EL_STR("t"))) { + return 1; + } + if (str_eq(ch, EL_STR("u"))) { + return 1; + } + if (str_eq(ch, EL_STR("v"))) { + return 1; + } + if (str_eq(ch, EL_STR("w"))) { + return 1; + } + if (str_eq(ch, EL_STR("x"))) { + return 1; + } + if (str_eq(ch, EL_STR("y"))) { + return 1; + } + if (str_eq(ch, EL_STR("z"))) { + return 1; + } + if (str_eq(ch, EL_STR("A"))) { + return 1; + } + if (str_eq(ch, EL_STR("B"))) { + return 1; + } + if (str_eq(ch, EL_STR("C"))) { + return 1; + } + if (str_eq(ch, EL_STR("D"))) { + return 1; + } + if (str_eq(ch, EL_STR("E"))) { + return 1; + } + if (str_eq(ch, EL_STR("F"))) { + return 1; + } + if (str_eq(ch, EL_STR("G"))) { + return 1; + } + if (str_eq(ch, EL_STR("H"))) { + return 1; + } + if (str_eq(ch, EL_STR("I"))) { + return 1; + } + if (str_eq(ch, EL_STR("J"))) { + return 1; + } + if (str_eq(ch, EL_STR("K"))) { + return 1; + } + if (str_eq(ch, EL_STR("L"))) { + return 1; + } + if (str_eq(ch, EL_STR("M"))) { + return 1; + } + if (str_eq(ch, EL_STR("N"))) { + return 1; + } + if (str_eq(ch, EL_STR("O"))) { + return 1; + } + if (str_eq(ch, EL_STR("P"))) { + return 1; + } + if (str_eq(ch, EL_STR("Q"))) { + return 1; + } + if (str_eq(ch, EL_STR("R"))) { + return 1; + } + if (str_eq(ch, EL_STR("S"))) { + return 1; + } + if (str_eq(ch, EL_STR("T"))) { + return 1; + } + if (str_eq(ch, EL_STR("U"))) { + return 1; + } + if (str_eq(ch, EL_STR("V"))) { + return 1; + } + if (str_eq(ch, EL_STR("W"))) { + return 1; + } + if (str_eq(ch, EL_STR("X"))) { + return 1; + } + if (str_eq(ch, EL_STR("Y"))) { + return 1; + } + if (str_eq(ch, EL_STR("Z"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_alnum_or_underscore(el_val_t ch) { + if (lex_is_digit(ch)) { + return 1; + } + if (lex_is_alpha(ch)) { + return 1; + } + if (str_eq(ch, EL_STR("_"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t lex_is_whitespace(el_val_t ch) { + if (str_eq(ch, EL_STR(" "))) { + return 1; + } + if (str_eq(ch, EL_STR("\t"))) { + return 1; + } + if (str_eq(ch, EL_STR("\n"))) { + return 1; + } + if (str_eq(ch, EL_STR("\r"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t tok_append(el_val_t tokens, el_val_t kind, el_val_t value) { + tokens = native_list_append(tokens, kind); + return native_list_append(tokens, value); + return 0; +} + +el_val_t keyword_kind(el_val_t word) { + if (str_eq(word, EL_STR("let"))) { + return EL_STR("Let"); + } + if (str_eq(word, EL_STR("fn"))) { + return EL_STR("Fn"); + } + if (str_eq(word, EL_STR("type"))) { + return EL_STR("Type"); + } + if (str_eq(word, EL_STR("enum"))) { + return EL_STR("Enum"); + } + if (str_eq(word, EL_STR("match"))) { + return EL_STR("Match"); + } + if (str_eq(word, EL_STR("return"))) { + return EL_STR("Return"); + } + if (str_eq(word, EL_STR("if"))) { + return EL_STR("If"); + } + if (str_eq(word, EL_STR("else"))) { + return EL_STR("Else"); + } + if (str_eq(word, EL_STR("for"))) { + return EL_STR("For"); + } + if (str_eq(word, EL_STR("in"))) { + return EL_STR("In"); + } + if (str_eq(word, EL_STR("while"))) { + return EL_STR("While"); + } + if (str_eq(word, EL_STR("import"))) { + return EL_STR("Import"); + } + if (str_eq(word, EL_STR("from"))) { + return EL_STR("From"); + } + if (str_eq(word, EL_STR("as"))) { + return EL_STR("As"); + } + if (str_eq(word, EL_STR("with"))) { + return EL_STR("With"); + } + if (str_eq(word, EL_STR("sealed"))) { + return EL_STR("Sealed"); + } + if (str_eq(word, EL_STR("activate"))) { + return EL_STR("Activate"); + } + if (str_eq(word, EL_STR("where"))) { + return EL_STR("Where"); + } + if (str_eq(word, EL_STR("test"))) { + return EL_STR("Test"); + } + if (str_eq(word, EL_STR("seed"))) { + return EL_STR("Seed"); + } + if (str_eq(word, EL_STR("assert"))) { + return EL_STR("Assert"); + } + if (str_eq(word, EL_STR("protocol"))) { + return EL_STR("Protocol"); + } + if (str_eq(word, EL_STR("impl"))) { + return EL_STR("Impl"); + } + if (str_eq(word, EL_STR("retry"))) { + return EL_STR("Retry"); + } + if (str_eq(word, EL_STR("times"))) { + return EL_STR("Times"); + } + if (str_eq(word, EL_STR("fallback"))) { + return EL_STR("Fallback"); + } + if (str_eq(word, EL_STR("reason"))) { + return EL_STR("Reason"); + } + if (str_eq(word, EL_STR("parallel"))) { + return EL_STR("Parallel"); + } + if (str_eq(word, EL_STR("trace"))) { + return EL_STR("Trace"); + } + if (str_eq(word, EL_STR("requires"))) { + return EL_STR("Requires"); + } + if (str_eq(word, EL_STR("deploy"))) { + return EL_STR("Deploy"); + } + if (str_eq(word, EL_STR("to"))) { + return EL_STR("To"); + } + if (str_eq(word, EL_STR("via"))) { + return EL_STR("Via"); + } + if (str_eq(word, EL_STR("target"))) { + return EL_STR("Target"); + } + if (str_eq(word, EL_STR("true"))) { + return EL_STR("Bool"); + } + if (str_eq(word, EL_STR("false"))) { + return EL_STR("Bool"); + } + if (str_eq(word, EL_STR("cgi"))) { + return EL_STR("Cgi"); + } + if (str_eq(word, EL_STR("service"))) { + return EL_STR("Service"); + } + if (str_eq(word, EL_STR("manager"))) { + return EL_STR("Manager"); + } + if (str_eq(word, EL_STR("engine"))) { + return EL_STR("Engine"); + } + if (str_eq(word, EL_STR("accessor"))) { + return EL_STR("Accessor"); + } + if (str_eq(word, EL_STR("vessel"))) { + return EL_STR("Vessel"); + } + if (str_eq(word, EL_STR("extern"))) { + return EL_STR("Extern"); + } + if (str_eq(word, EL_STR("break"))) { + return EL_STR("Break"); + } + if (str_eq(word, EL_STR("continue"))) { + return EL_STR("Continue"); + } + return EL_STR(""); + return 0; +} + +el_val_t scan_digits(el_val_t src, el_val_t start, el_val_t total) { + el_val_t i = start; + el_val_t running = 1; + while (running) { + if (i >= total) { + running = 0; + } else { + el_val_t c = str_char_code(src, i); + if (is_digit_code(c)) { + i = (i + 1); + } else { + running = 0; + } + } + } + return el_map_new(2, "text", str_slice(src, start, i), "pos", i); + return 0; +} + +el_val_t scan_ident(el_val_t src, el_val_t start, el_val_t total) { + el_val_t i = start; + el_val_t running = 1; + while (running) { + if (i >= total) { + running = 0; + } else { + el_val_t c = str_char_code(src, i); + if (is_alnum_or_underscore_code(c)) { + i = (i + 1); + } else { + running = 0; + } + } + } + return el_map_new(2, "text", str_slice(src, start, i), "pos", i); + return 0; +} + +el_val_t substr_at(el_val_t src, el_val_t start, el_val_t total, el_val_t needle) { + el_val_t nlen = str_len(needle); + if ((start + nlen) > total) { + return 0; + } + return str_eq(str_slice(src, start, (start + nlen)), needle); + return 0; +} + +el_val_t str_has(el_val_t s, el_val_t needle) { + return str_contains(s, needle); + 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 = str_char_at(s, 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 = str_char_at(s, i); + if (str_eq(bc, EL_STR("*"))) { + el_val_t after = (i + 1); + if (after < total) { + el_val_t nc2 = str_char_at(s, 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 src, el_val_t start, el_val_t total) { + el_val_t i = start; + el_val_t parts = native_list_empty(); + el_val_t running = 1; + while (running) { + if (i >= total) { + running = 0; + } else { + el_val_t ch = str_char_at(src, i); + if (str_eq(ch, EL_STR("\\"))) { + el_val_t next_i = (i + 1); + if (next_i < total) { + el_val_t next_ch = str_char_at(src, next_i); + if (str_eq(next_ch, EL_STR("\""))) { + parts = native_list_append(parts, EL_STR("\"")); + i = (next_i + 1); + } else { + if (str_eq(next_ch, EL_STR("n"))) { + parts = native_list_append(parts, EL_STR("\n")); + i = (next_i + 1); + } else { + if (str_eq(next_ch, EL_STR("t"))) { + parts = native_list_append(parts, EL_STR("\t")); + i = (next_i + 1); + } else { + if (str_eq(next_ch, EL_STR("r"))) { + parts = native_list_append(parts, EL_STR("\r")); + i = (next_i + 1); + } else { + if (str_eq(next_ch, EL_STR("\\"))) { + parts = native_list_append(parts, EL_STR("\\")); + i = (next_i + 1); + } else { + parts = native_list_append(parts, next_ch); + i = (next_i + 1); + } + } + } + } + } + } else { + i = (i + 1); + } + } else { + if (str_eq(ch, EL_STR("\""))) { + i = (i + 1); + running = 0; + } else { + parts = native_list_append(parts, ch); + i = (i + 1); + } + } + } + } + return el_map_new(2, "text", str_join(parts, EL_STR("")), "pos", i); + return 0; +} + +el_val_t scan_interp_brace(el_val_t src, el_val_t start, el_val_t total) { + el_val_t i = start; + el_val_t depth = 1; + el_val_t running = 1; + while (running) { + if (i >= total) { + running = 0; + } else { + el_val_t ch = str_char_at(src, i); + if (str_eq(ch, EL_STR("{"))) { + depth = (depth + 1); + i = (i + 1); + } else { + if (str_eq(ch, EL_STR("}"))) { + depth = (depth - 1); + if (depth <= 0) { + i = (i + 1); + running = 0; + } else { + i = (i + 1); + } + } else { + i = (i + 1); + } + } + } + } + return el_map_new(2, "text", str_slice(src, start, (i - 1)), "pos", i); + return 0; +} + +el_val_t interp_tokens_append_all(el_val_t dst, el_val_t src) { + el_val_t src_len = native_list_len(src); + el_val_t j = 0; + el_val_t result = dst; + while (j < src_len) { + el_val_t kind = native_list_get(src, j); + if (str_eq(kind, EL_STR("Eof"))) { + j = src_len; + } else { + el_val_t val = native_list_get(src, (j + 1)); + result = native_list_append(result, kind); + result = native_list_append(result, val); + j = (j + 2); + } + } + return result; + return 0; +} + +el_val_t scan_interp_string(el_val_t src, el_val_t start, el_val_t total) { + el_val_t i = start; + el_val_t out_tokens = native_list_empty(); + el_val_t cur_parts = native_list_empty(); + el_val_t clean_start = start; + el_val_t has_interp = 0; + el_val_t need_plus = 0; + el_val_t running = 1; + while (running) { + if (i >= total) { + running = 0; + } else { + el_val_t c = str_char_code(src, i); + if (c == 92) { + if (clean_start < i) { + cur_parts = native_list_append(cur_parts, str_slice(src, clean_start, i)); + } + el_val_t next_i = (i + 1); + if (next_i < total) { + el_val_t nc = str_char_code(src, next_i); + if (nc == 36) { + cur_parts = native_list_append(cur_parts, EL_STR("$")); + clean_start = (next_i + 1); + i = (next_i + 1); + } else { + if (nc == 34) { + cur_parts = native_list_append(cur_parts, EL_STR("\"")); + clean_start = (next_i + 1); + i = (next_i + 1); + } else { + if (nc == 110) { + cur_parts = native_list_append(cur_parts, EL_STR("\n")); + clean_start = (next_i + 1); + i = (next_i + 1); + } else { + if (nc == 116) { + cur_parts = native_list_append(cur_parts, EL_STR("\t")); + clean_start = (next_i + 1); + i = (next_i + 1); + } else { + if (nc == 114) { + cur_parts = native_list_append(cur_parts, EL_STR("\r")); + clean_start = (next_i + 1); + i = (next_i + 1); + } else { + if (nc == 92) { + cur_parts = native_list_append(cur_parts, EL_STR("\\")); + clean_start = (next_i + 1); + i = (next_i + 1); + } else { + cur_parts = native_list_append(cur_parts, str_slice(src, next_i, (next_i + 1))); + clean_start = (next_i + 1); + i = (next_i + 1); + } + } + } + } + } + } + } else { + clean_start = next_i; + i = next_i; + } + } else { + if (c == 34) { + if (clean_start < i) { + cur_parts = native_list_append(cur_parts, str_slice(src, clean_start, i)); + } + i = (i + 1); + clean_start = i; + running = 0; + } else { + if (c == 36) { + el_val_t next_i = (i + 1); + el_val_t is_interp = 0; + if (next_i < total) { + el_val_t nc2 = str_char_code(src, next_i); + if (nc2 == 123) { + is_interp = 1; + } + } + if (is_interp) { + if (clean_start < i) { + cur_parts = native_list_append(cur_parts, str_slice(src, clean_start, i)); + } + el_val_t part_len = native_list_len(cur_parts); + if (part_len > 0) { + el_val_t part_text = str_join(cur_parts, EL_STR("")); + if (need_plus) { + out_tokens = tok_append(out_tokens, EL_STR("Plus"), EL_STR("+")); + } + el_val_t clean_part = part_text; + if (looks_like_code(part_text)) { + clean_part = strip_code_comments(part_text); + } + out_tokens = tok_append(out_tokens, EL_STR("Str"), clean_part); + need_plus = 1; + } + cur_parts = native_list_empty(); + has_interp = 1; + el_val_t brace_result = scan_interp_brace(src, (next_i + 1), total); + el_val_t expr_src = el_get_field(brace_result, EL_STR("text")); + el_val_t new_i = el_get_field(brace_result, EL_STR("pos")); + i = new_i; + clean_start = new_i; + el_val_t inner_toks = lex(expr_src); + el_val_t inner_len = native_list_len(inner_toks); + if (need_plus) { + out_tokens = tok_append(out_tokens, EL_STR("Plus"), EL_STR("+")); + } + if (inner_len <= 2) { + out_tokens = tok_append(out_tokens, EL_STR("Str"), EL_STR("")); + } else { + out_tokens = tok_append(out_tokens, EL_STR("LParen"), EL_STR("(")); + out_tokens = interp_tokens_append_all(out_tokens, inner_toks); + out_tokens = tok_append(out_tokens, EL_STR("RParen"), EL_STR(")")); + } + need_plus = 1; + } else { + i = (i + 1); + } + } else { + i = (i + 1); + } + } + } + } + } + if (clean_start < i) { + cur_parts = native_list_append(cur_parts, str_slice(src, clean_start, i)); + } + el_val_t part_len = native_list_len(cur_parts); + el_val_t part_text = str_join(cur_parts, EL_STR("")); + if (has_interp) { + if (part_len > 0) { + el_val_t clean_part = part_text; + if (looks_like_code(part_text)) { + clean_part = strip_code_comments(part_text); + } + if (need_plus) { + out_tokens = tok_append(out_tokens, EL_STR("Plus"), EL_STR("+")); + } + out_tokens = tok_append(out_tokens, EL_STR("Str"), clean_part); + } + } else { + el_val_t clean_text = part_text; + if (looks_like_code(part_text)) { + clean_text = strip_code_comments(part_text); + } + out_tokens = tok_append(out_tokens, EL_STR("Str"), clean_text); + } + return el_map_new(2, "tokens", out_tokens, "pos", i); + return 0; +} + +el_val_t lex(el_val_t source) { + el_val_t total = str_len(source); + el_val_t tokens = native_list_empty(); + el_val_t i = 0; + while (i < total) { + el_val_t c = str_char_code(source, i); + if (is_ws_code(c)) { + i = (i + 1); + } else { + if (c == 47) { + el_val_t next_i = (i + 1); + if (next_i < total) { + el_val_t nc = str_char_code(source, next_i); + if (nc == 47) { + i = (i + 2); + el_val_t running2 = 1; + while (running2) { + if (i >= total) { + running2 = 0; + } else { + el_val_t lc = str_char_code(source, i); + if (lc == 10) { + running2 = 0; + } else { + i = (i + 1); + } + } + } + } else { + tokens = tok_append(tokens, EL_STR("Slash"), EL_STR("/")); + i = (i + 1); + } + } else { + tokens = tok_append(tokens, EL_STR("Slash"), EL_STR("/")); + i = (i + 1); + } + } else { + if (c == 34) { + el_val_t interp_result = scan_interp_string(source, (i + 1), total); + el_val_t interp_toks = el_get_field(interp_result, EL_STR("tokens")); + el_val_t new_pos = el_get_field(interp_result, EL_STR("pos")); + tokens = interp_tokens_append_all(tokens, interp_toks); + i = new_pos; + } else { + if (is_digit_code(c)) { + el_val_t result = scan_digits(source, 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")); + if (new_pos < total) { + el_val_t dc = str_char_code(source, new_pos); + if (dc == 46) { + el_val_t after_dot = (new_pos + 1); + if (after_dot < total) { + el_val_t adc = str_char_code(source, after_dot); + if (is_digit_code(adc)) { + el_val_t frac_result = scan_digits(source, 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")); + tokens = tok_append(tokens, EL_STR("Float"), el_str_concat(el_str_concat(num_text, EL_STR(".")), frac_text)); + i = frac_pos; + } else { + tokens = tok_append(tokens, EL_STR("Int"), num_text); + i = new_pos; + } + } else { + tokens = tok_append(tokens, EL_STR("Int"), num_text); + i = new_pos; + } + } else { + tokens = tok_append(tokens, EL_STR("Int"), num_text); + i = new_pos; + } + } else { + tokens = tok_append(tokens, EL_STR("Int"), num_text); + i = new_pos; + } + } else { + if (is_alpha_code(c) || (c == 95)) { + el_val_t result = scan_ident(source, 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")); + el_val_t kw = keyword_kind(word); + if (str_eq(kw, EL_STR(""))) { + tokens = tok_append(tokens, EL_STR("Ident"), word); + } else { + tokens = tok_append(tokens, kw, word); + } + i = new_pos; + } else { + el_val_t peek_i = (i + 1); + el_val_t peek_c = (-1); + if (peek_i < total) { + peek_c = str_char_code(source, peek_i); + } + if (c == 61) { + if (peek_c == 61) { + tokens = tok_append(tokens, EL_STR("EqEq"), EL_STR("==")); + i = (i + 2); + } else { + if (peek_c == 62) { + tokens = tok_append(tokens, EL_STR("FatArrow"), EL_STR("=>")); + i = (i + 2); + } else { + tokens = tok_append(tokens, EL_STR("Eq"), EL_STR("=")); + i = (i + 1); + } + } + } else { + if (c == 33) { + if (peek_c == 61) { + tokens = tok_append(tokens, EL_STR("NotEq"), EL_STR("!=")); + i = (i + 2); + } else { + tokens = tok_append(tokens, EL_STR("Not"), EL_STR("!")); + i = (i + 1); + } + } else { + if (c == 60) { + if (peek_c == 61) { + tokens = tok_append(tokens, EL_STR("LtEq"), EL_STR("<=")); + i = (i + 2); + } else { + tokens = tok_append(tokens, EL_STR("Lt"), EL_STR("<")); + i = (i + 1); + } + } else { + if (c == 62) { + if (peek_c == 61) { + tokens = tok_append(tokens, EL_STR("GtEq"), EL_STR(">=")); + i = (i + 2); + } else { + tokens = tok_append(tokens, EL_STR("Gt"), EL_STR(">")); + i = (i + 1); + } + } else { + if (c == 38) { + if (peek_c == 38) { + tokens = tok_append(tokens, EL_STR("And"), EL_STR("&&")); + i = (i + 2); + } else { + i = (i + 1); + } + } else { + if (c == 124) { + if (peek_c == 124) { + tokens = tok_append(tokens, EL_STR("Or"), EL_STR("||")); + i = (i + 2); + } else { + if (peek_c == 62) { + tokens = tok_append(tokens, EL_STR("PipeOp"), EL_STR("|>")); + i = (i + 2); + } else { + tokens = tok_append(tokens, EL_STR("Pipe"), EL_STR("|")); + i = (i + 1); + } + } + } else { + if (c == 45) { + if (peek_c == 62) { + tokens = tok_append(tokens, EL_STR("Arrow"), EL_STR("->")); + i = (i + 2); + } else { + tokens = tok_append(tokens, EL_STR("Minus"), EL_STR("-")); + i = (i + 1); + } + } else { + if (c == 58) { + if (peek_c == 58) { + tokens = tok_append(tokens, EL_STR("ColonColon"), EL_STR("::")); + i = (i + 2); + } else { + tokens = tok_append(tokens, EL_STR("Colon"), EL_STR(":")); + i = (i + 1); + } + } else { + if (c == 43) { + tokens = tok_append(tokens, EL_STR("Plus"), EL_STR("+")); + i = (i + 1); + } else { + if (c == 42) { + tokens = tok_append(tokens, EL_STR("Star"), EL_STR("*")); + i = (i + 1); + } else { + if (c == 37) { + tokens = tok_append(tokens, EL_STR("Percent"), EL_STR("%")); + i = (i + 1); + } else { + if (c == 40) { + tokens = tok_append(tokens, EL_STR("LParen"), EL_STR("(")); + i = (i + 1); + } else { + if (c == 41) { + tokens = tok_append(tokens, EL_STR("RParen"), EL_STR(")")); + i = (i + 1); + } else { + if (c == 123) { + tokens = tok_append(tokens, EL_STR("LBrace"), EL_STR("{")); + i = (i + 1); + } else { + if (c == 125) { + tokens = tok_append(tokens, EL_STR("RBrace"), EL_STR("}")); + i = (i + 1); + } else { + if (c == 91) { + tokens = tok_append(tokens, EL_STR("LBracket"), EL_STR("[")); + i = (i + 1); + } else { + if (c == 93) { + tokens = tok_append(tokens, EL_STR("RBracket"), EL_STR("]")); + i = (i + 1); + } else { + if (c == 44) { + tokens = tok_append(tokens, EL_STR("Comma"), EL_STR(",")); + i = (i + 1); + } else { + if (c == 46) { + el_val_t peek2_i = (i + 2); + el_val_t peek2_c = (-1); + if (peek2_i < total) { + peek2_c = str_char_code(source, peek2_i); + } + if (peek_c == 46) { + if (peek2_c == 61) { + tokens = tok_append(tokens, EL_STR("DotDotEq"), EL_STR("..=")); + i = (i + 3); + } else { + tokens = tok_append(tokens, EL_STR("DotDot"), EL_STR("..")); + i = (i + 2); + } + } else { + tokens = tok_append(tokens, EL_STR("Dot"), EL_STR(".")); + i = (i + 1); + } + } else { + if (c == 59) { + tokens = tok_append(tokens, EL_STR("Semicolon"), EL_STR(";")); + i = (i + 1); + } else { + if (c == 64) { + tokens = tok_append(tokens, EL_STR("At"), EL_STR("@")); + i = (i + 1); + } else { + if (c == 63) { + tokens = tok_append(tokens, EL_STR("QuestionMark"), EL_STR("?")); + i = (i + 1); + } else { + i = (i + 1); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + tokens = tok_append(tokens, EL_STR("Eof"), EL_STR("")); + return tokens; + return 0; +} + +el_val_t tok_at(el_val_t tokens, el_val_t pos) { + el_val_t kind = native_list_get(tokens, (pos * 2)); + el_val_t value = native_list_get(tokens, ((pos * 2) + 1)); + return el_map_new(2, "kind", kind, "value", value); + return 0; +} + +el_val_t tok_kind(el_val_t tokens, el_val_t pos) { + return native_list_get(tokens, (pos * 2)); + return 0; +} + +el_val_t tok_value(el_val_t tokens, el_val_t pos) { + return native_list_get(tokens, ((pos * 2) + 1)); + return 0; +} + +el_val_t expect(el_val_t tokens, el_val_t pos, el_val_t kind) { + el_val_t k = tok_kind(tokens, pos); + if (str_eq(k, kind)) { + return (pos + 1); + } + return (pos + 1); + return 0; +} + +el_val_t make_result(el_val_t node, el_val_t pos) { + return el_map_new(2, "node", node, "pos", pos); + return 0; +} + +el_val_t skip_type(el_val_t tokens, el_val_t pos) { + el_val_t k = tok_kind(tokens, pos); + if (str_eq(k, EL_STR("LBracket"))) { + el_val_t p = (pos + 1); + p = skip_type(tokens, p); + p = expect(tokens, p, EL_STR("RBracket")); + return p; + } + if (str_eq(k, EL_STR("Ident"))) { + el_val_t p = (pos + 1); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Lt"))) { + p = (p + 1); + el_val_t depth = 1; + el_val_t running = 1; + while (running) { + el_val_t kk = tok_kind(tokens, p); + if (str_eq(kk, EL_STR("Eof"))) { + running = 0; + } else { + if (str_eq(kk, EL_STR("Lt"))) { + depth = (depth + 1); + p = (p + 1); + } else { + if (str_eq(kk, EL_STR("Gt"))) { + depth = (depth - 1); + p = (p + 1); + if (depth <= 0) { + running = 0; + } + } else { + p = (p + 1); + } + } + } + } + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("QuestionMark"))) { + p = (p + 1); + } + return p; + } + if (str_eq(k2, EL_STR("QuestionMark"))) { + return (p + 1); + } + return p; + } + return (pos + 1); + return 0; +} + +el_val_t parse_params(el_val_t tokens, el_val_t pos) { + el_val_t p = expect(tokens, pos, EL_STR("LParen")); + el_val_t params = native_list_empty(); + el_val_t running = 1; + while (running) { + el_val_t k = tok_kind(tokens, p); + if (str_eq(k, EL_STR("RParen"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t pname = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("Colon")); + el_val_t ptype = EL_STR(""); + el_val_t kt = tok_kind(tokens, p); + if (str_eq(kt, EL_STR("Ident"))) { + ptype = tok_value(tokens, p); + } + p = skip_type(tokens, p); + el_val_t param = el_map_new(2, "name", pname, "type", ptype); + params = native_list_append(params, param); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RParen")); + return el_map_new(2, "params", params, "pos", p); + return 0; +} + +el_val_t is_html_tag_name(el_val_t name) { + if (str_eq(name, EL_STR("a"))) { + return 1; + } + if (str_eq(name, EL_STR("abbr"))) { + return 1; + } + if (str_eq(name, EL_STR("address"))) { + return 1; + } + if (str_eq(name, EL_STR("area"))) { + return 1; + } + if (str_eq(name, EL_STR("article"))) { + return 1; + } + if (str_eq(name, EL_STR("aside"))) { + return 1; + } + if (str_eq(name, EL_STR("audio"))) { + return 1; + } + if (str_eq(name, EL_STR("b"))) { + return 1; + } + if (str_eq(name, EL_STR("base"))) { + return 1; + } + if (str_eq(name, EL_STR("blockquote"))) { + return 1; + } + if (str_eq(name, EL_STR("body"))) { + return 1; + } + if (str_eq(name, EL_STR("br"))) { + return 1; + } + if (str_eq(name, EL_STR("button"))) { + return 1; + } + if (str_eq(name, EL_STR("canvas"))) { + return 1; + } + if (str_eq(name, EL_STR("caption"))) { + return 1; + } + if (str_eq(name, EL_STR("cite"))) { + return 1; + } + if (str_eq(name, EL_STR("code"))) { + return 1; + } + if (str_eq(name, EL_STR("col"))) { + return 1; + } + if (str_eq(name, EL_STR("colgroup"))) { + return 1; + } + if (str_eq(name, EL_STR("data"))) { + return 1; + } + if (str_eq(name, EL_STR("datalist"))) { + return 1; + } + if (str_eq(name, EL_STR("dd"))) { + return 1; + } + if (str_eq(name, EL_STR("del"))) { + return 1; + } + if (str_eq(name, EL_STR("details"))) { + return 1; + } + if (str_eq(name, EL_STR("dfn"))) { + return 1; + } + if (str_eq(name, EL_STR("dialog"))) { + return 1; + } + if (str_eq(name, EL_STR("div"))) { + return 1; + } + if (str_eq(name, EL_STR("dl"))) { + return 1; + } + if (str_eq(name, EL_STR("dt"))) { + return 1; + } + if (str_eq(name, EL_STR("em"))) { + return 1; + } + if (str_eq(name, EL_STR("embed"))) { + return 1; + } + if (str_eq(name, EL_STR("fieldset"))) { + return 1; + } + if (str_eq(name, EL_STR("figcaption"))) { + return 1; + } + if (str_eq(name, EL_STR("figure"))) { + return 1; + } + if (str_eq(name, EL_STR("footer"))) { + return 1; + } + if (str_eq(name, EL_STR("form"))) { + return 1; + } + if (str_eq(name, EL_STR("h1"))) { + return 1; + } + if (str_eq(name, EL_STR("h2"))) { + return 1; + } + if (str_eq(name, EL_STR("h3"))) { + return 1; + } + if (str_eq(name, EL_STR("h4"))) { + return 1; + } + if (str_eq(name, EL_STR("h5"))) { + return 1; + } + if (str_eq(name, EL_STR("h6"))) { + return 1; + } + if (str_eq(name, EL_STR("head"))) { + return 1; + } + if (str_eq(name, EL_STR("header"))) { + return 1; + } + if (str_eq(name, EL_STR("hr"))) { + return 1; + } + if (str_eq(name, EL_STR("html"))) { + return 1; + } + if (str_eq(name, EL_STR("i"))) { + return 1; + } + if (str_eq(name, EL_STR("iframe"))) { + return 1; + } + if (str_eq(name, EL_STR("img"))) { + return 1; + } + if (str_eq(name, EL_STR("input"))) { + return 1; + } + if (str_eq(name, EL_STR("ins"))) { + return 1; + } + if (str_eq(name, EL_STR("kbd"))) { + return 1; + } + if (str_eq(name, EL_STR("label"))) { + return 1; + } + if (str_eq(name, EL_STR("legend"))) { + return 1; + } + if (str_eq(name, EL_STR("li"))) { + return 1; + } + if (str_eq(name, EL_STR("link"))) { + return 1; + } + if (str_eq(name, EL_STR("main"))) { + return 1; + } + if (str_eq(name, EL_STR("map"))) { + return 1; + } + if (str_eq(name, EL_STR("mark"))) { + return 1; + } + if (str_eq(name, EL_STR("menu"))) { + return 1; + } + if (str_eq(name, EL_STR("meta"))) { + return 1; + } + if (str_eq(name, EL_STR("meter"))) { + return 1; + } + if (str_eq(name, EL_STR("nav"))) { + return 1; + } + if (str_eq(name, EL_STR("noscript"))) { + return 1; + } + if (str_eq(name, EL_STR("object"))) { + return 1; + } + if (str_eq(name, EL_STR("ol"))) { + return 1; + } + if (str_eq(name, EL_STR("optgroup"))) { + return 1; + } + if (str_eq(name, EL_STR("option"))) { + return 1; + } + if (str_eq(name, EL_STR("output"))) { + return 1; + } + if (str_eq(name, EL_STR("p"))) { + return 1; + } + if (str_eq(name, EL_STR("param"))) { + return 1; + } + if (str_eq(name, EL_STR("picture"))) { + return 1; + } + if (str_eq(name, EL_STR("pre"))) { + return 1; + } + if (str_eq(name, EL_STR("progress"))) { + return 1; + } + if (str_eq(name, EL_STR("q"))) { + return 1; + } + if (str_eq(name, EL_STR("rp"))) { + return 1; + } + if (str_eq(name, EL_STR("rt"))) { + return 1; + } + if (str_eq(name, EL_STR("ruby"))) { + return 1; + } + if (str_eq(name, EL_STR("s"))) { + return 1; + } + if (str_eq(name, EL_STR("samp"))) { + return 1; + } + if (str_eq(name, EL_STR("script"))) { + return 1; + } + if (str_eq(name, EL_STR("section"))) { + return 1; + } + if (str_eq(name, EL_STR("select"))) { + return 1; + } + if (str_eq(name, EL_STR("small"))) { + return 1; + } + if (str_eq(name, EL_STR("source"))) { + return 1; + } + if (str_eq(name, EL_STR("span"))) { + return 1; + } + if (str_eq(name, EL_STR("strong"))) { + return 1; + } + if (str_eq(name, EL_STR("style"))) { + return 1; + } + if (str_eq(name, EL_STR("sub"))) { + return 1; + } + if (str_eq(name, EL_STR("summary"))) { + return 1; + } + if (str_eq(name, EL_STR("sup"))) { + return 1; + } + if (str_eq(name, EL_STR("table"))) { + return 1; + } + if (str_eq(name, EL_STR("tbody"))) { + return 1; + } + if (str_eq(name, EL_STR("td"))) { + return 1; + } + if (str_eq(name, EL_STR("template"))) { + return 1; + } + if (str_eq(name, EL_STR("textarea"))) { + return 1; + } + if (str_eq(name, EL_STR("tfoot"))) { + return 1; + } + if (str_eq(name, EL_STR("th"))) { + return 1; + } + if (str_eq(name, EL_STR("thead"))) { + return 1; + } + if (str_eq(name, EL_STR("time"))) { + return 1; + } + if (str_eq(name, EL_STR("title"))) { + return 1; + } + if (str_eq(name, EL_STR("tr"))) { + return 1; + } + if (str_eq(name, EL_STR("track"))) { + return 1; + } + if (str_eq(name, EL_STR("u"))) { + return 1; + } + if (str_eq(name, EL_STR("ul"))) { + return 1; + } + if (str_eq(name, EL_STR("var"))) { + return 1; + } + if (str_eq(name, EL_STR("video"))) { + return 1; + } + if (str_eq(name, EL_STR("wbr"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_void_element(el_val_t name) { + if (str_eq(name, EL_STR("area"))) { + return 1; + } + if (str_eq(name, EL_STR("base"))) { + return 1; + } + if (str_eq(name, EL_STR("br"))) { + return 1; + } + if (str_eq(name, EL_STR("col"))) { + return 1; + } + if (str_eq(name, EL_STR("embed"))) { + return 1; + } + if (str_eq(name, EL_STR("hr"))) { + return 1; + } + if (str_eq(name, EL_STR("img"))) { + return 1; + } + if (str_eq(name, EL_STR("input"))) { + return 1; + } + if (str_eq(name, EL_STR("link"))) { + return 1; + } + if (str_eq(name, EL_STR("meta"))) { + return 1; + } + if (str_eq(name, EL_STR("param"))) { + return 1; + } + if (str_eq(name, EL_STR("source"))) { + return 1; + } + if (str_eq(name, EL_STR("track"))) { + return 1; + } + if (str_eq(name, EL_STR("wbr"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t parse_raw_text_content(el_val_t tokens, el_val_t pos, el_val_t tag_name) { + el_val_t parts = native_list_empty(); + el_val_t p = pos; + el_val_t running = 1; + while (running) { + el_val_t k = tok_kind(tokens, p); + if (str_eq(k, EL_STR("Eof"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("Lt"))) { + el_val_t k2 = tok_kind(tokens, (p + 1)); + if (str_eq(k2, EL_STR("Slash"))) { + el_val_t close_name = tok_value(tokens, (p + 2)); + if (str_eq(close_name, tag_name)) { + p = (p + 3); + p = expect(tokens, p, EL_STR("Gt")); + running = 0; + } else { + el_val_t v = tok_value(tokens, p); + parts = native_list_append(parts, v); + p = (p + 1); + } + } else { + el_val_t v = tok_value(tokens, p); + parts = native_list_append(parts, v); + p = (p + 1); + } + } else { + el_val_t v = tok_value(tokens, p); + parts = native_list_append(parts, v); + p = (p + 1); + } + } + } + return el_map_new(2, "text", str_join(parts, EL_STR(" ")), "pos", p); + return 0; +} + +el_val_t parse_html_text_tokens(el_val_t tokens, el_val_t pos) { + el_val_t parts = native_list_empty(); + el_val_t p = pos; + el_val_t running = 1; + while (running) { + el_val_t k = tok_kind(tokens, p); + if (str_eq(k, EL_STR("Eof"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("Lt"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("LBrace"))) { + running = 0; + } else { + el_val_t v = tok_value(tokens, p); + parts = native_list_append(parts, v); + p = (p + 1); + } + } + } + } + return el_map_new(2, "text", str_join(parts, EL_STR(" ")), "pos", p); + return 0; +} + +el_val_t parse_html_attrs(el_val_t tokens, el_val_t pos) { + el_val_t attrs = native_list_empty(); + el_val_t p = pos; + el_val_t running = 1; + while (running) { + el_val_t k = tok_kind(tokens, p); + if (str_eq(k, EL_STR("Gt"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("Slash"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t attr_name = tok_value(tokens, p); + p = (p + 1); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Eq"))) { + p = (p + 1); + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Str"))) { + el_val_t attr_val = tok_value(tokens, p); + p = (p + 1); + attrs = native_list_append(attrs, el_map_new(3, "name", attr_name, "kind", EL_STR("static"), "value", attr_val)); + } else { + if (str_eq(k3, EL_STR("LBrace"))) { + el_val_t r = parse_expr(tokens, (p + 1)); + el_val_t val_node = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + p = expect(tokens, p, EL_STR("RBrace")); + attrs = native_list_append(attrs, el_map_new(3, "name", attr_name, "kind", EL_STR("dynamic"), "value", val_node)); + } else { + } + } + } else { + attrs = native_list_append(attrs, el_map_new(2, "name", attr_name, "kind", EL_STR("bool"))); + } + } + } + } + } + return el_map_new(2, "attrs", attrs, "pos", p); + return 0; +} + +el_val_t parse_html_children(el_val_t tokens, el_val_t pos, el_val_t parent_tag) { + el_val_t children = native_list_empty(); + el_val_t p = pos; + el_val_t running = 1; + while (running) { + el_val_t k = tok_kind(tokens, p); + if (str_eq(k, EL_STR("Eof"))) { + running = 0; + } else { + /* A bare `}` at this level is the El function-body closing brace — + * e.g. intentionally left open in page_open(), closed by + * page_close(). Stop here so the outer El parser can consume the `}`. */ + if (str_eq(k, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("Lt"))) { + el_val_t k2 = tok_kind(tokens, (p + 1)); + if (str_eq(k2, EL_STR("Slash"))) { + p = (p + 2); + el_val_t close_name = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("Gt")); + running = 0; + } else { + if (str_eq(k2, EL_STR("Not"))) { + el_val_t k3_v = str_lower(tok_value(tokens, (p + 2))); + if (str_eq(k3_v, EL_STR("doctype"))) { + p = (p + 2); + el_val_t scanning = 1; + while (scanning) { + el_val_t ck = tok_kind(tokens, p); + if (str_eq(ck, EL_STR("Eof"))) { + scanning = 0; + } + if (str_eq(ck, EL_STR("Gt"))) { + p = (p + 1); + scanning = 0; + } else { + p = (p + 1); + } + } + children = native_list_append(children, el_map_new(1, "html", EL_STR("Doctype"))); + } else { + p = (p + 1); + } + } else { + el_val_t r = parse_html_element(tokens, p); + el_val_t child = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + children = native_list_append(children, child); + } + } + } else { + if (str_eq(k, EL_STR("LBrace"))) { + el_val_t k2 = tok_kind(tokens, (p + 1)); + if (str_eq(k2, EL_STR("Slash"))) { + p = (p + 2); + p = (p + 1); + p = expect(tokens, p, EL_STR("RBrace")); + running = 0; + } else { + if (str_eq(k2, EL_STR("If"))) { + p = (p + 2); + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r_cond = parse_expr(tokens, p); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t cond_expr = el_get_field(r_cond, EL_STR("node")); + p = el_get_field(r_cond, EL_STR("pos")); + el_release(r_cond); + p = expect(tokens, p, EL_STR("RBrace")); + el_val_t r_then = parse_html_children(tokens, p, EL_STR("__if_then__")); + el_val_t then_children = el_get_field(r_then, EL_STR("children")); + p = el_get_field(r_then, EL_STR("pos")); + el_release(r_then); + el_val_t else_children = native_list_empty(); + el_val_t ck = tok_kind(tokens, p); + if (str_eq(ck, EL_STR("LBrace"))) { + el_val_t ck2 = tok_kind(tokens, (p + 1)); + if (str_eq(ck2, EL_STR("Else"))) { + p = (p + 2); + p = expect(tokens, p, EL_STR("RBrace")); + el_val_t r_else = parse_html_children(tokens, p, EL_STR("__if_else__")); + else_children = el_get_field(r_else, EL_STR("children")); + p = el_get_field(r_else, EL_STR("pos")); + el_release(r_else); + } + } + el_val_t if_node = el_map_new(4, "html", EL_STR("HtmlIf"), "cond", cond_expr, "then", then_children, "else", else_children); + children = native_list_append(children, if_node); + } else { + if (str_eq(k2, EL_STR("Else"))) { + running = 0; + } else { + el_val_t k2_v = tok_value(tokens, (p + 1)); + if (str_eq(k2_v, EL_STR("each"))) { + p = (p + 2); + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r_list = parse_expr(tokens, p); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t list_expr = el_get_field(r_list, EL_STR("node")); + p = el_get_field(r_list, EL_STR("pos")); + el_release(r_list); + p = expect(tokens, p, EL_STR("As")); + el_val_t item_name = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("RBrace")); + el_val_t r_body = parse_html_each_body(tokens, p); + el_val_t body_children = el_get_field(r_body, EL_STR("children")); + p = el_get_field(r_body, EL_STR("pos")); + el_release(r_body); + el_val_t each_node = el_map_new(4, "html", EL_STR("Each"), "list", list_expr, "item", item_name, "body", body_children); + children = native_list_append(children, each_node); + } else { + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r = parse_expr(tokens, (p + 1)); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t interp_val = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + p = expect(tokens, p, EL_STR("RBrace")); + el_val_t is_raw_call = 0; + el_val_t interp_kind = el_get_field(interp_val, EL_STR("expr")); + if (str_eq(interp_kind, EL_STR("Call"))) { + el_val_t fn_node = el_get_field(interp_val, EL_STR("func")); + el_val_t fn_kind = el_get_field(fn_node, EL_STR("expr")); + if (str_eq(fn_kind, EL_STR("Ident"))) { + el_val_t fn_name_v = el_get_field(fn_node, EL_STR("name")); + if (str_eq(fn_name_v, EL_STR("raw"))) { + is_raw_call = 1; + } + } + } + if (is_raw_call) { + el_val_t raw_args = el_get_field(interp_val, EL_STR("args")); + el_val_t raw_inner = native_list_get(raw_args, 0); + children = native_list_append(children, el_map_new(2, "html", EL_STR("Raw"), "value", raw_inner)); + } else { + children = native_list_append(children, el_map_new(2, "html", EL_STR("Interp"), "value", interp_val)); + } + } + } + } + } + } else { + el_val_t r_text = parse_html_text_tokens(tokens, p); + el_val_t text_str = el_get_field(r_text, EL_STR("text")); + p = el_get_field(r_text, EL_STR("pos")); + el_release(r_text); + el_val_t text_trimmed = str_trim(text_str); + if (!str_eq(text_trimmed, EL_STR(""))) { + children = native_list_append(children, el_map_new(2, "html", EL_STR("Text"), "text", text_trimmed)); + } + } + } + } /* closes: else { // not RBrace */ + } + } + return el_map_new(2, "children", children, "pos", p); + return 0; +} + +el_val_t parse_html_each_body(el_val_t tokens, el_val_t pos) { + return parse_html_children(tokens, pos, EL_STR("__each__")); + return 0; +} + +el_val_t parse_html_element(el_val_t tokens, el_val_t pos) { + el_val_t p = pos; + p = expect(tokens, p, EL_STR("Lt")); + el_val_t tag_name = tok_value(tokens, p); + p = (p + 1); + el_val_t r_attrs = parse_html_attrs(tokens, p); + el_val_t attrs = el_get_field(r_attrs, EL_STR("attrs")); + p = el_get_field(r_attrs, EL_STR("pos")); + el_release(r_attrs); + el_val_t k = tok_kind(tokens, p); + el_val_t self_closing = 0; + if (str_eq(k, EL_STR("Slash"))) { + p = (p + 1); + p = expect(tokens, p, EL_STR("Gt")); + self_closing = 1; + return make_result(el_map_new(5, "html", EL_STR("Element"), "tag", tag_name, "attrs", attrs, "children", native_list_empty(), "self_closing", 1), p); + } + p = expect(tokens, p, EL_STR("Gt")); + if (is_void_element(tag_name)) { + return make_result(el_map_new(5, "html", EL_STR("Element"), "tag", tag_name, "attrs", attrs, "children", native_list_empty(), "self_closing", 1), p); + } + if (str_eq(tag_name, EL_STR("style"))) { + el_val_t r_raw = parse_raw_text_content(tokens, p, EL_STR("style")); + el_val_t raw_text = el_get_field(r_raw, EL_STR("text")); + p = el_get_field(r_raw, EL_STR("pos")); + el_release(r_raw); + el_val_t raw_child = el_map_new(2, "html", EL_STR("Text"), "text", raw_text); + el_val_t raw_children = native_list_empty(); + raw_children = native_list_append(raw_children, raw_child); + return make_result(el_map_new(5, "html", EL_STR("Element"), "tag", tag_name, "attrs", attrs, "children", raw_children, "self_closing", 0), p); + } + if (str_eq(tag_name, EL_STR("script"))) { + el_val_t r_raw = parse_raw_text_content(tokens, p, EL_STR("script")); + el_val_t raw_text = el_get_field(r_raw, EL_STR("text")); + p = el_get_field(r_raw, EL_STR("pos")); + el_release(r_raw); + el_val_t raw_child = el_map_new(2, "html", EL_STR("Text"), "text", raw_text); + el_val_t raw_children = native_list_empty(); + raw_children = native_list_append(raw_children, raw_child); + return make_result(el_map_new(5, "html", EL_STR("Element"), "tag", tag_name, "attrs", attrs, "children", raw_children, "self_closing", 0), p); + } + el_val_t r_children = parse_html_children(tokens, p, tag_name); + el_val_t children = el_get_field(r_children, EL_STR("children")); + p = el_get_field(r_children, EL_STR("pos")); + el_release(r_children); + return make_result(el_map_new(5, "html", EL_STR("Element"), "tag", tag_name, "attrs", attrs, "children", children, "self_closing", 0), p); + return 0; +} + +el_val_t parse_html_template(el_val_t tokens, el_val_t pos) { + el_val_t p = pos; + el_val_t doctype = 0; + el_val_t k = tok_kind(tokens, p); + el_val_t k2 = tok_kind(tokens, (p + 1)); + if (str_eq(k, EL_STR("Lt"))) { + if (str_eq(k2, EL_STR("Not"))) { + el_val_t k3_v = str_lower(tok_value(tokens, (p + 2))); + if (str_eq(k3_v, EL_STR("doctype"))) { + doctype = 1; + p = (p + 2); + el_val_t scanning = 1; + while (scanning) { + el_val_t ck = tok_kind(tokens, p); + if (str_eq(ck, EL_STR("Eof"))) { + scanning = 0; + } + if (str_eq(ck, EL_STR("Gt"))) { + p = (p + 1); + scanning = 0; + } else { + p = (p + 1); + } + } + } + } + } + el_val_t r = parse_html_element(tokens, p); + el_val_t root = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t root_with_doctype = root; + if (doctype) { + root_with_doctype = el_map_new(6, "html", el_get_field(root, EL_STR("html")), "tag", el_get_field(root, EL_STR("tag")), "attrs", el_get_field(root, EL_STR("attrs")), "children", el_get_field(root, EL_STR("children")), "self_closing", el_get_field(root, EL_STR("self_closing")), "doctype", 1); + } + return make_result(el_map_new(2, "expr", EL_STR("HtmlTemplate"), "root", root_with_doctype), p); + return 0; +} + +el_val_t parse_primary(el_val_t tokens, el_val_t pos) { + el_val_t k = tok_kind(tokens, pos); + el_val_t v = tok_value(tokens, pos); + if (str_eq(k, EL_STR("Int"))) { + return make_result(el_map_new(2, "expr", EL_STR("Int"), "value", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Float"))) { + return make_result(el_map_new(2, "expr", EL_STR("Float"), "value", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Str"))) { + return make_result(el_map_new(2, "expr", EL_STR("Str"), "value", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Bool"))) { + return make_result(el_map_new(2, "expr", EL_STR("Bool"), "value", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Lt"))) { + el_val_t k2 = tok_kind(tokens, (pos + 1)); + if (str_eq(k2, EL_STR("Not"))) { + return parse_html_template(tokens, pos); + } + if (str_eq(k2, EL_STR("Ident"))) { + el_val_t tag_candidate = tok_value(tokens, (pos + 1)); + if (is_html_tag_name(tag_candidate)) { + return parse_html_template(tokens, pos); + } + } + } + if (str_eq(k, EL_STR("Ident"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("LParen"))) { + el_val_t r = parse_expr(tokens, (pos + 1)); + el_val_t node = el_get_field(r, EL_STR("node")); + el_val_t p = el_get_field(r, EL_STR("pos")); + el_release(r); + p = expect(tokens, p, EL_STR("RParen")); + return make_result(node, p); + } + if (str_eq(k, EL_STR("LBracket"))) { + el_val_t p = (pos + 1); + el_val_t elems = native_list_empty(); + el_val_t running = 1; + while (running) { + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("RBracket"))) { + running = 0; + } else { + if (str_eq(k2, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t r = parse_expr(tokens, p); + el_val_t elem = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + elems = native_list_append(elems, elem); + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RBracket")); + return make_result(el_map_new(2, "expr", EL_STR("Array"), "elems", elems), p); + } + if (str_eq(k, EL_STR("LBrace"))) { + el_val_t no_block = state_get(EL_STR("__no_block_expr")); + if (str_eq(no_block, EL_STR("1"))) { + return make_result(el_map_new(1, "expr", EL_STR("Nil")), pos); + } + el_val_t first_k = tok_kind(tokens, (pos + 1)); + el_val_t second_k = tok_kind(tokens, (pos + 2)); + if (str_eq(first_k, EL_STR("RBrace"))) { + return make_result(el_map_new(2, "expr", EL_STR("Map"), "pairs", native_list_empty()), (pos + 2)); + } + if (str_eq(second_k, EL_STR("Colon"))) { + el_val_t p = (pos + 1); + el_val_t pairs = native_list_empty(); + el_val_t running = 1; + while (running) { + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k2, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t key = tok_value(tokens, p); + el_val_t new_p = (p + 1); + new_p = expect(tokens, new_p, EL_STR("Colon")); + el_val_t r = parse_expr(tokens, new_p); + el_val_t val_node = el_get_field(r, EL_STR("node")); + new_p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t pair = el_map_new(2, "key", key, "value", val_node); + pairs = native_list_append(pairs, pair); + el_val_t k3 = tok_kind(tokens, new_p); + if (str_eq(k3, EL_STR("Comma"))) { + new_p = (new_p + 1); + } + if (new_p <= p) { + p = (p + 1); + } else { + p = new_p; + } + } + } + } + p = expect(tokens, p, EL_STR("RBrace")); + return make_result(el_map_new(2, "expr", EL_STR("Map"), "pairs", pairs), p); + } + el_val_t p = pos; + el_val_t chain_node = el_map_new(1, "expr", EL_STR("Nil")); + el_val_t chain_started = 0; + el_val_t chain_running = 1; + while (chain_running) { + el_val_t ck = tok_kind(tokens, p); + if (str_eq(ck, EL_STR("LBrace"))) { + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r = parse_expr(tokens, (p + 1)); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t part = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + p = expect(tokens, p, EL_STR("RBrace")); + if (!chain_started) { + chain_node = part; + chain_started = 1; + } else { + chain_node = el_map_new(4, "expr", EL_STR("BinOp"), "op", EL_STR("Plus"), "left", chain_node, "right", part); + } + } else { + if (str_eq(ck, EL_STR("Lt"))) { + el_val_t ck2 = tok_kind(tokens, (p + 1)); + if (str_eq(ck2, EL_STR("Not"))) { + el_val_t r = parse_html_template(tokens, p); + el_val_t part = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + if (!chain_started) { + chain_node = part; + chain_started = 1; + } else { + chain_node = el_map_new(4, "expr", EL_STR("BinOp"), "op", EL_STR("Plus"), "left", chain_node, "right", part); + } + } else { + if (str_eq(ck2, EL_STR("Ident"))) { + el_val_t tag_candidate = tok_value(tokens, (p + 1)); + if (is_html_tag_name(tag_candidate)) { + el_val_t r = parse_html_template(tokens, p); + el_val_t part = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + if (!chain_started) { + chain_node = part; + chain_started = 1; + } else { + chain_node = el_map_new(4, "expr", EL_STR("BinOp"), "op", EL_STR("Plus"), "left", chain_node, "right", part); + } + } else { + chain_running = 0; + } + } else { + chain_running = 0; + } + } + } else { + chain_running = 0; + } + } + } + return make_result(chain_node, p); + } + if (str_eq(k, EL_STR("If"))) { + el_val_t r = parse_if(tokens, pos); + return r; + } + if (str_eq(k, EL_STR("Match"))) { + el_val_t r = parse_match(tokens, pos); + return r; + } + if (str_eq(k, EL_STR("For"))) { + el_val_t r = parse_for_expr(tokens, pos); + return r; + } + if (str_eq(k, EL_STR("Fn"))) { + el_val_t p = (pos + 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_release(r); + el_val_t ret_type = EL_STR(""); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, 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); + } + el_val_t r2 = parse_block(tokens, p); + el_val_t body = el_get_field(r2, EL_STR("stmts")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + return make_result(el_map_new(4, "expr", EL_STR("Lambda"), "params", params, "body", body, "ret_type", ret_type), p); + } + if (str_eq(k, EL_STR("Not"))) { + el_val_t r = parse_primary(tokens, (pos + 1)); + el_val_t inner = el_get_field(r, EL_STR("node")); + el_val_t p = el_get_field(r, EL_STR("pos")); + el_release(r); + return make_result(el_map_new(2, "expr", EL_STR("Not"), "inner", inner), p); + } + if (str_eq(k, EL_STR("Minus"))) { + el_val_t r = parse_primary(tokens, (pos + 1)); + el_val_t inner = el_get_field(r, EL_STR("node")); + el_val_t p = el_get_field(r, EL_STR("pos")); + el_release(r); + return make_result(el_map_new(2, "expr", EL_STR("Neg"), "inner", inner), p); + } + if (str_eq(k, EL_STR("Target"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("To"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Via"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Deploy"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Reason"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Times"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Fallback"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Retry"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Parallel"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Trace"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Requires"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Where"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("As"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("With"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Manager"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Engine"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Accessor"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Vessel"))) { + return make_result(el_map_new(2, "expr", EL_STR("Ident"), "name", v), (pos + 1)); + } + return make_result(el_map_new(1, "expr", EL_STR("Nil")), (pos + 1)); + return 0; +} + +el_val_t parse_if(el_val_t tokens, el_val_t pos) { + el_val_t p = expect(tokens, pos, EL_STR("If")); + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r = parse_expr(tokens, p); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t cond = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t r2 = parse_block(tokens, p); + el_val_t then_stmts = el_get_field(r2, EL_STR("stmts")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + el_val_t has_else = 0; + el_val_t else_stmts = native_list_empty(); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Else"))) { + p = (p + 1); + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("If"))) { + el_val_t r3 = parse_if(tokens, p); + el_val_t nested = el_get_field(r3, EL_STR("node")); + p = el_get_field(r3, EL_STR("pos")); + el_release(r3); + else_stmts = native_list_append(else_stmts, el_map_new(2, "stmt", EL_STR("Expr"), "value", nested)); + has_else = 1; + } else { + el_val_t r3 = parse_block(tokens, p); + else_stmts = el_get_field(r3, EL_STR("stmts")); + p = el_get_field(r3, EL_STR("pos")); + el_release(r3); + has_else = 1; + } + } + return make_result(el_map_new(5, "expr", EL_STR("If"), "cond", cond, "then", then_stmts, "else", else_stmts, "has_else", has_else), p); + return 0; +} + +el_val_t parse_match(el_val_t tokens, el_val_t pos) { + el_val_t p = expect(tokens, pos, EL_STR("Match")); + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r = parse_expr(tokens, p); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t subject = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + p = expect(tokens, p, EL_STR("LBrace")); + el_val_t arms = native_list_empty(); + el_val_t running = 1; + while (running) { + el_val_t k = tok_kind(tokens, p); + if (str_eq(k, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t r2 = parse_pattern(tokens, p); + el_val_t pattern = el_get_field(r2, EL_STR("node")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + p = expect(tokens, p, EL_STR("FatArrow")); + el_val_t r3 = parse_expr(tokens, p); + el_val_t body = el_get_field(r3, EL_STR("node")); + p = el_get_field(r3, EL_STR("pos")); + el_release(r3); + el_val_t arm = el_map_new(2, "pattern", pattern, "body", body); + arms = native_list_append(arms, arm); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RBrace")); + return make_result(el_map_new(3, "expr", EL_STR("Match"), "subject", subject, "arms", arms), p); + return 0; +} + +el_val_t parse_pattern(el_val_t tokens, el_val_t pos) { + el_val_t k = tok_kind(tokens, pos); + if (str_eq(k, EL_STR("Ident"))) { + el_val_t v = tok_value(tokens, pos); + if (str_eq(v, EL_STR("_"))) { + return make_result(el_map_new(1, "pattern", EL_STR("Wildcard")), (pos + 1)); + } + el_val_t next_k = tok_kind(tokens, (pos + 1)); + if (str_eq(next_k, EL_STR("ColonColon"))) { + el_val_t variant_name = tok_value(tokens, (pos + 2)); + return make_result(el_map_new(3, "pattern", EL_STR("Variant"), "enum_name", v, "variant", variant_name), (pos + 3)); + } + return make_result(el_map_new(2, "pattern", EL_STR("Binding"), "name", v), (pos + 1)); + } + if (str_eq(k, EL_STR("Int"))) { + return make_result(el_map_new(2, "pattern", EL_STR("LitInt"), "value", tok_value(tokens, pos)), (pos + 1)); + } + if (str_eq(k, EL_STR("Str"))) { + return make_result(el_map_new(2, "pattern", EL_STR("LitStr"), "value", tok_value(tokens, pos)), (pos + 1)); + } + if (str_eq(k, EL_STR("Bool"))) { + return make_result(el_map_new(2, "pattern", EL_STR("LitBool"), "value", tok_value(tokens, pos)), (pos + 1)); + } + return make_result(el_map_new(1, "pattern", EL_STR("Wildcard")), (pos + 1)); + return 0; +} + +el_val_t parse_for_expr(el_val_t tokens, el_val_t pos) { + el_val_t p = expect(tokens, pos, EL_STR("For")); + el_val_t item_name = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("In")); + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r = parse_expr(tokens, p); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t list_expr = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t r2 = parse_block(tokens, p); + el_val_t body = el_get_field(r2, EL_STR("stmts")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + return make_result(el_map_new(4, "expr", EL_STR("For"), "item", item_name, "list", list_expr, "body", body), p); + return 0; +} + +el_val_t parse_block(el_val_t tokens, el_val_t pos) { + el_val_t p = expect(tokens, pos, EL_STR("LBrace")); + el_val_t stmts = native_list_empty(); + el_val_t running = 1; + while (running) { + el_val_t k = tok_kind(tokens, p); + if (str_eq(k, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t r = parse_stmt(tokens, p); + el_val_t stmt = el_get_field(r, EL_STR("node")); + el_val_t new_p = el_get_field(r, EL_STR("pos")); + el_release(r); + stmts = native_list_append(stmts, stmt); + if (new_p <= p) { + p = (p + 1); + } else { + p = new_p; + } + } + } + } + p = expect(tokens, p, EL_STR("RBrace")); + return el_map_new(2, "stmts", stmts, "pos", p); + 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_release(r); + 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); + if (str_eq(k, EL_STR("LParen"))) { + p = (p + 1); + el_val_t args = native_list_empty(); + el_val_t run2 = 1; + while (run2) { + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("RParen"))) { + run2 = 0; + } else { + if (str_eq(k2, EL_STR("Eof"))) { + run2 = 0; + } else { + el_val_t r2 = parse_expr(tokens, p); + el_val_t arg = el_get_field(r2, EL_STR("node")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + args = native_list_append(args, arg); + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RParen")); + node = el_map_new(3, "expr", EL_STR("Call"), "func", node, "args", args); + } else { + if (str_eq(k, EL_STR("Dot"))) { + el_val_t field = tok_value(tokens, (p + 1)); + p = (p + 2); + node = el_map_new(3, "expr", EL_STR("Field"), "object", node, "field", field); + } else { + if (str_eq(k, EL_STR("LBracket"))) { + el_val_t r2 = parse_expr(tokens, (p + 1)); + el_val_t idx = el_get_field(r2, EL_STR("node")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + p = expect(tokens, p, EL_STR("RBracket")); + node = el_map_new(3, "expr", EL_STR("Index"), "object", node, "index", idx); + } else { + if (str_eq(k, EL_STR("QuestionMark"))) { + p = (p + 1); + node = el_map_new(2, "expr", EL_STR("Try"), "inner", node); + } else { + running = 0; + } + } + } + } + } + return make_result(node, p); + return 0; +} + +el_val_t op_precedence(el_val_t kind) { + if (str_eq(kind, EL_STR("Or"))) { + return 1; + } + if (str_eq(kind, EL_STR("And"))) { + return 2; + } + if (str_eq(kind, EL_STR("EqEq"))) { + return 3; + } + if (str_eq(kind, EL_STR("NotEq"))) { + return 3; + } + if (str_eq(kind, EL_STR("Lt"))) { + return 4; + } + if (str_eq(kind, EL_STR("Gt"))) { + return 4; + } + if (str_eq(kind, EL_STR("LtEq"))) { + return 4; + } + if (str_eq(kind, EL_STR("GtEq"))) { + return 4; + } + if (str_eq(kind, EL_STR("Plus"))) { + return 5; + } + if (str_eq(kind, EL_STR("Minus"))) { + return 5; + } + if (str_eq(kind, EL_STR("Star"))) { + return 6; + } + if (str_eq(kind, EL_STR("Slash"))) { + return 6; + } + if (str_eq(kind, EL_STR("Percent"))) { + return 6; + } + return 0; + return 0; +} + +el_val_t is_binop(el_val_t kind) { + if (str_eq(kind, EL_STR("Or"))) { + return 1; + } + if (str_eq(kind, EL_STR("And"))) { + return 1; + } + if (str_eq(kind, EL_STR("EqEq"))) { + return 1; + } + if (str_eq(kind, EL_STR("NotEq"))) { + return 1; + } + if (str_eq(kind, EL_STR("Lt"))) { + return 1; + } + if (str_eq(kind, EL_STR("Gt"))) { + return 1; + } + if (str_eq(kind, EL_STR("LtEq"))) { + return 1; + } + if (str_eq(kind, EL_STR("GtEq"))) { + return 1; + } + if (str_eq(kind, EL_STR("Plus"))) { + return 1; + } + if (str_eq(kind, EL_STR("Minus"))) { + return 1; + } + if (str_eq(kind, EL_STR("Star"))) { + return 1; + } + if (str_eq(kind, EL_STR("Slash"))) { + return 1; + } + if (str_eq(kind, EL_STR("Percent"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t parse_binop(el_val_t tokens, el_val_t pos, el_val_t min_prec) { + el_val_t r = parse_postfix(tokens, pos); + el_val_t left = el_get_field(r, EL_STR("node")); + el_val_t p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t running = 1; + while (running) { + el_val_t k = tok_kind(tokens, p); + el_val_t prec = op_precedence(k); + if (is_binop(k)) { + if (prec >= min_prec) { + el_val_t op = k; + el_val_t r2 = parse_binop(tokens, (p + 1), (prec + 1)); + el_val_t right = el_get_field(r2, EL_STR("node")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + left = el_map_new(4, "expr", EL_STR("BinOp"), "op", op, "left", left, "right", right); + } else { + running = 0; + } + } else { + running = 0; + } + } + return make_result(left, p); + return 0; +} + +el_val_t parse_expr(el_val_t tokens, el_val_t pos) { + return parse_binop(tokens, pos, 1); + return 0; +} + +el_val_t parse_stmt(el_val_t tokens, el_val_t pos) { + el_val_t k = tok_kind(tokens, pos); + if (str_eq(k, EL_STR("Let"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + el_val_t ltype = EL_STR(""); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Colon"))) { + p = (p + 1); + el_val_t kt = tok_kind(tokens, p); + if (str_eq(kt, EL_STR("Ident"))) { + ltype = tok_value(tokens, p); + } + p = skip_type(tokens, p); + } + p = expect(tokens, p, EL_STR("Eq")); + 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")); + el_release(r); + return make_result(el_map_new(4, "stmt", EL_STR("Let"), "name", name, "value", val, "type", ltype), p); + } + if (str_eq(k, EL_STR("Return"))) { + el_val_t p = (pos + 1); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("RBrace"))) { + return make_result(el_map_new(2, "stmt", EL_STR("Return"), "value", el_map_new(1, "expr", EL_STR("Nil"))), p); + } + if (str_eq(k2, EL_STR("Eof"))) { + return make_result(el_map_new(2, "stmt", EL_STR("Return"), "value", el_map_new(1, "expr", EL_STR("Nil"))), p); + } + 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")); + el_release(r); + 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_release(r); + 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); + 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_release(r); + el_val_t ret_type = EL_STR(""); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, 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); + } + el_val_t r2 = parse_block(tokens, p); + el_val_t body = el_get_field(r2, EL_STR("stmts")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + return make_result(el_map_new(5, "stmt", EL_STR("FnDef"), "name", name, "params", params, "body", body, "ret_type", ret_type), p); + } + if (str_eq(k, EL_STR("Type"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + el_val_t pk = tok_kind(tokens, p); + if (str_eq(pk, EL_STR("Eq"))) { + p = (p + 1); + } + p = expect(tokens, p, EL_STR("LBrace")); + el_val_t fields = native_list_empty(); + el_val_t running = 1; + while (running) { + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k2, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t fname = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("Colon")); + p = skip_type(tokens, p); + fields = native_list_append(fields, el_map_new(1, "name", fname)); + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RBrace")); + return make_result(el_map_new(3, "stmt", EL_STR("TypeDef"), "name", name, "fields", fields), p); + } + if (str_eq(k, EL_STR("Enum"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("LBrace")); + el_val_t variants = native_list_empty(); + el_val_t running = 1; + while (running) { + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k2, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t vname = tok_value(tokens, p); + p = (p + 1); + variants = native_list_append(variants, el_map_new(1, "name", vname)); + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RBrace")); + return make_result(el_map_new(3, "stmt", EL_STR("EnumDef"), "name", name, "variants", variants), p); + } + if (str_eq(k, EL_STR("Import"))) { + el_val_t p = (pos + 1); + el_val_t path = tok_value(tokens, p); + p = (p + 1); + return make_result(el_map_new(2, "stmt", EL_STR("Import"), "path", path), p); + } + if (str_eq(k, EL_STR("From"))) { + el_val_t p = (pos + 1); + el_val_t module_name = tok_value(tokens, p); + p = (p + 1); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Import"))) { + p = (p + 1); + } + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("LBrace"))) { + p = (p + 1); + el_val_t running = 1; + while (running) { + el_val_t k4 = tok_kind(tokens, p); + if (str_eq(k4, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k4, EL_STR("Eof"))) { + running = 0; + } else { + p = (p + 1); + el_val_t k5 = tok_kind(tokens, p); + if (str_eq(k5, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RBrace")); + } + return make_result(el_map_new(2, "stmt", EL_STR("Import"), "path", module_name), p); + } + if (str_eq(k, EL_STR("While"))) { + el_val_t p = (pos + 1); + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r = parse_expr(tokens, p); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t cond = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t r2 = parse_block(tokens, p); + el_val_t body = el_get_field(r2, EL_STR("stmts")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + return make_result(el_map_new(3, "stmt", EL_STR("While"), "cond", cond, "body", body), p); + } + if (str_eq(k, EL_STR("Break"))) { + return make_result(el_map_new(1, "stmt", EL_STR("Break")), (pos + 1)); + } + if (str_eq(k, EL_STR("Continue"))) { + return make_result(el_map_new(1, "stmt", EL_STR("Continue")), (pos + 1)); + } + if (str_eq(k, EL_STR("For"))) { + el_val_t p = (pos + 1); + el_val_t item_name = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("In")); + el_val_t prev_no_block = state_get(EL_STR("__no_block_expr")); + state_set(EL_STR("__no_block_expr"), EL_STR("1")); + el_val_t r = parse_expr(tokens, p); + state_set(EL_STR("__no_block_expr"), prev_no_block); + el_val_t start_expr = el_get_field(r, EL_STR("node")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t range_k = tok_kind(tokens, p); + if (str_eq(range_k, EL_STR("DotDot"))) { + p = (p + 1); + el_val_t r2 = parse_expr(tokens, p); + el_val_t end_expr = el_get_field(r2, EL_STR("node")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + el_val_t r3 = parse_block(tokens, p); + el_val_t body = el_get_field(r3, EL_STR("stmts")); + p = el_get_field(r3, EL_STR("pos")); + el_release(r3); + return make_result(el_map_new(6, "stmt", EL_STR("ForRange"), "var", item_name, "start", start_expr, "end", end_expr, "inclusive", 0, "body", body), p); + } + if (str_eq(range_k, EL_STR("DotDotEq"))) { + p = (p + 1); + el_val_t r2 = parse_expr(tokens, p); + el_val_t end_expr = el_get_field(r2, EL_STR("node")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + el_val_t r3 = parse_block(tokens, p); + el_val_t body = el_get_field(r3, EL_STR("stmts")); + p = el_get_field(r3, EL_STR("pos")); + el_release(r3); + return make_result(el_map_new(6, "stmt", EL_STR("ForRange"), "var", item_name, "start", start_expr, "end", end_expr, "inclusive", 1, "body", body), p); + } + el_val_t list_expr = start_expr; + el_val_t r2 = parse_block(tokens, p); + el_val_t body = el_get_field(r2, EL_STR("stmts")); + p = el_get_field(r2, EL_STR("pos")); + el_release(r2); + return make_result(el_map_new(4, "stmt", EL_STR("For"), "item", item_name, "list", list_expr, "body", body), p); + } + if (str_eq(k, EL_STR("Try"))) { + el_val_t p = (pos + 1); + el_val_t r_try = parse_block(tokens, p); + el_val_t try_body = el_get_field(r_try, EL_STR("stmts")); + p = el_get_field(r_try, EL_STR("pos")); + el_release(r_try); + el_val_t catch_name = EL_STR("err"); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Catch"))) { + p = (p + 1); + p = expect(tokens, p, EL_STR("LParen")); + el_val_t kn = tok_kind(tokens, p); + if (str_eq(kn, EL_STR("Ident"))) { + catch_name = tok_value(tokens, p); + p = (p + 1); + } + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Colon"))) { + p = (p + 1); + p = skip_type(tokens, p); + } + p = expect(tokens, p, EL_STR("RParen")); + el_val_t r_catch = parse_block(tokens, p); + el_val_t catch_body = el_get_field(r_catch, EL_STR("stmts")); + p = el_get_field(r_catch, EL_STR("pos")); + el_release(r_catch); + return make_result(el_map_new(4, "stmt", EL_STR("TryCatch"), "try_body", try_body, "catch_name", catch_name, "catch_body", catch_body), p); + } + return make_result(el_map_new(4, "stmt", EL_STR("TryCatch"), "try_body", try_body, "catch_name", catch_name, "catch_body", native_list_empty()), p); + } + if (str_eq(k, EL_STR("At"))) { + el_val_t p = (pos + 1); + el_val_t dec_name = tok_value(tokens, p); + p = (p + 1); + el_val_t r = parse_stmt(tokens, p); + el_val_t inner = el_get_field(r, EL_STR("node")); + el_val_t p2 = el_get_field(r, EL_STR("pos")); + el_val_t inner_kind = el_get_field(inner, EL_STR("stmt")); + if (str_eq(inner_kind, EL_STR("FnDef"))) { + el_val_t with_dec = el_map_new(6, "stmt", EL_STR("FnDef"), "name", el_get_field(inner, EL_STR("name")), "params", el_get_field(inner, EL_STR("params")), "body", el_get_field(inner, EL_STR("body")), "ret_type", el_get_field(inner, EL_STR("ret_type")), "decorator", dec_name); + el_release(r); + return make_result(with_dec, p2); + } + return r; + } + if (str_eq(k, EL_STR("Cgi"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("LBrace")); + el_val_t dharma_id = EL_STR(""); + el_val_t principal = EL_STR(""); + el_val_t network = EL_STR(""); + el_val_t engram = EL_STR(""); + el_val_t has_dharma_id = 0; + el_val_t has_principal = 0; + el_val_t has_network = 0; + el_val_t has_engram = 0; + el_val_t running = 1; + while (running) { + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k2, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t fname = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("Colon")); + el_val_t fval = tok_value(tokens, p); + p = (p + 1); + if (str_eq(fname, EL_STR("dharma_id"))) { + dharma_id = fval; + has_dharma_id = 1; + } + if (str_eq(fname, EL_STR("principal"))) { + principal = fval; + has_principal = 1; + } + if (str_eq(fname, EL_STR("network"))) { + network = fval; + has_network = 1; + } + if (str_eq(fname, EL_STR("engram"))) { + engram = fval; + has_engram = 1; + } + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RBrace")); + return make_result(el_map_new(10, "stmt", EL_STR("CgiBlock"), "name", name, "dharma_id", dharma_id, "principal", principal, "network", network, "engram", engram, "has_dharma_id", has_dharma_id, "has_principal", has_principal, "has_network", has_network, "has_engram", has_engram), p); + } + if (str_eq(k, EL_STR("Service"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("LBrace")); + el_val_t sponsor = EL_STR(""); + el_val_t domain = EL_STR(""); + el_val_t running = 1; + while (running) { + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("RBrace"))) { + running = 0; + } else { + if (str_eq(k2, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t fname = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("Colon")); + el_val_t fval = tok_value(tokens, p); + p = (p + 1); + if (str_eq(fname, EL_STR("sponsor"))) { + sponsor = fval; + } + if (str_eq(fname, EL_STR("domain"))) { + domain = fval; + } + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + 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("Assert"))) { + el_val_t p = (pos + 1); + el_val_t cond_r = parse_expr(tokens, p); + el_val_t cond_node = el_get_field(cond_r, EL_STR("node")); + p = el_get_field(cond_r, EL_STR("pos")); + el_release(cond_r); + el_val_t after_k = tok_kind(tokens, p); + if (str_eq(after_k, EL_STR("Comma"))) { + p = (p + 1); + el_val_t msg_r = parse_expr(tokens, p); + el_val_t msg_node = el_get_field(msg_r, EL_STR("node")); + p = el_get_field(msg_r, EL_STR("pos")); + el_release(msg_r); + return make_result(el_map_new(3, "stmt", EL_STR("Assert"), "cond", cond_node, "msg", msg_node), p); + } + return make_result(el_map_new(3, "stmt", EL_STR("Assert"), "cond", cond_node, "msg", el_map_new(2, "expr", EL_STR("Str"), "value", EL_STR(""))), 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")); + el_release(r); + 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")); + el_release(r); + return make_result(el_map_new(2, "stmt", EL_STR("Expr"), "value", val), p); + return 0; +} + +el_val_t parse(el_val_t tokens) { + el_val_t total = (native_list_len(tokens) / 2); + el_val_t stmts = native_list_empty(); + el_val_t pos = 0; + el_val_t running = 1; + while (running) { + if (pos >= total) { + running = 0; + } else { + el_val_t k = tok_kind(tokens, pos); + if (str_eq(k, EL_STR("Eof"))) { + running = 0; + } else { + el_val_t r = parse_stmt(tokens, pos); + el_val_t stmt = el_get_field(r, EL_STR("node")); + el_val_t new_pos = el_get_field(r, EL_STR("pos")); + el_release(r); + stmts = native_list_append(stmts, stmt); + if (new_pos <= pos) { + pos = (pos + 1); + } else { + pos = new_pos; + } + } + } + } + return stmts; + return 0; +} + +el_val_t parse_one(el_val_t tokens, el_val_t pos) { + return parse_stmt(tokens, pos); + return 0; +} + +el_val_t skip_to_rbrace(el_val_t tokens, el_val_t pos) { + el_val_t total = (native_list_len(tokens) / 2); + el_val_t p = (pos + 1); + el_val_t depth = 1; + el_val_t going = 1; + while (going) { + if (p >= total) { + going = 0; + } else { + el_val_t kk = tok_kind(tokens, p); + if (str_eq(kk, EL_STR("Eof"))) { + going = 0; + } else { + if (str_eq(kk, EL_STR("LBrace"))) { + depth = (depth + 1); + p = (p + 1); + } else { + if (str_eq(kk, EL_STR("RBrace"))) { + depth = (depth - 1); + p = (p + 1); + if (depth <= 0) { + going = 0; + } + } else { + p = (p + 1); + } + } + } + } + } + return p; + return 0; +} + +el_val_t is_stmt_start_kind(el_val_t k) { + if (str_eq(k, EL_STR("Fn"))) { + return 1; + } + if (str_eq(k, EL_STR("Let"))) { + return 1; + } + if (str_eq(k, EL_STR("Extern"))) { + return 1; + } + if (str_eq(k, EL_STR("Cgi"))) { + return 1; + } + if (str_eq(k, EL_STR("Service"))) { + return 1; + } + if (str_eq(k, EL_STR("Type"))) { + return 1; + } + if (str_eq(k, EL_STR("Enum"))) { + return 1; + } + if (str_eq(k, EL_STR("Import"))) { + return 1; + } + if (str_eq(k, EL_STR("From"))) { + return 1; + } + if (str_eq(k, EL_STR("Eof"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t skip_expr_to_stmt_boundary(el_val_t tokens, el_val_t pos) { + el_val_t total = (native_list_len(tokens) / 2); + el_val_t p = pos; + el_val_t depth = 0; + el_val_t going = 1; + while (going) { + if (p >= total) { + going = 0; + } else { + el_val_t kk = tok_kind(tokens, p); + if (str_eq(kk, EL_STR("Eof"))) { + going = 0; + } else { + if (str_eq(kk, EL_STR("LBrace"))) { + depth = (depth + 1); + p = (p + 1); + } else { + if (str_eq(kk, EL_STR("RBrace"))) { + if (depth <= 0) { + going = 0; + } else { + depth = (depth - 1); + p = (p + 1); + } + } else { + if (depth == 0) { + if (is_stmt_start_kind(kk)) { + going = 0; + } else { + p = (p + 1); + } + } else { + p = (p + 1); + } + } + } + } + } + } + return p; + return 0; +} + +el_val_t scan_params_c(el_val_t tokens, el_val_t pos) { + el_val_t p = expect(tokens, pos, EL_STR("LParen")); + el_val_t parts = native_list_empty(); + el_val_t going = 1; + while (going) { + el_val_t kk = tok_kind(tokens, p); + if (str_eq(kk, EL_STR("RParen"))) { + going = 0; + } else { + if (str_eq(kk, EL_STR("Eof"))) { + going = 0; + } else { + el_val_t pname = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("Colon")); + p = skip_type(tokens, p); + parts = native_list_append(parts, el_str_concat(EL_STR("el_val_t "), pname)); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RParen")); + el_val_t c_str = str_join(parts, EL_STR(", ")); + el_release(parts); + if (str_eq(c_str, EL_STR(""))) { + c_str = EL_STR("void"); + } + return el_map_new(2, "c", c_str, "pos", p); + return 0; +} + +el_val_t scan_fn_sigs(el_val_t tokens) { + el_val_t total = (native_list_len(tokens) / 2); + el_val_t sigs = native_list_empty(); + el_val_t pos = 0; + el_val_t going = 1; + while (going) { + if (pos >= total) { + going = 0; + } else { + el_val_t k = tok_kind(tokens, pos); + if (str_eq(k, EL_STR("Eof"))) { + going = 0; + } else { + if (str_eq(k, EL_STR("Fn"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + el_val_t r = scan_params_c(tokens, p); + el_val_t params_c = el_get_field(r, EL_STR("c")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Arrow"))) { + p = (p + 1); + p = skip_type(tokens, p); + } + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("LBrace"))) { + p = skip_to_rbrace(tokens, p); + } + el_val_t is_main = str_eq(name, EL_STR("main")); + sigs = native_list_append(sigs, el_map_new(4, "kind", EL_STR("fn"), "name", name, "params_c", params_c, "is_main", is_main)); + pos = p; + } else { + 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 = scan_params_c(tokens, p); + el_val_t params_c = el_get_field(r, EL_STR("c")); + p = el_get_field(r, EL_STR("pos")); + el_release(r); + el_val_t k3 = tok_kind(tokens, p); + if (str_eq(k3, EL_STR("Arrow"))) { + p = (p + 1); + p = skip_type(tokens, p); + } + sigs = native_list_append(sigs, el_map_new(4, "kind", EL_STR("extern_fn"), "name", name, "params_c", params_c, "is_main", 0)); + pos = p; + } else { + pos = (pos + 1); + } + } else { + if (str_eq(k, EL_STR("Let"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + el_val_t ltype = EL_STR(""); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("Colon"))) { + p = (p + 1); + el_val_t kt = tok_kind(tokens, p); + if (str_eq(kt, EL_STR("Ident"))) { + ltype = tok_value(tokens, p); + } + p = skip_type(tokens, p); + } + p = expect(tokens, p, EL_STR("Eq")); + p = skip_expr_to_stmt_boundary(tokens, p); + sigs = native_list_append(sigs, el_map_new(3, "kind", EL_STR("toplevel_let"), "name", name, "ltype", ltype)); + pos = p; + } else { + if (str_eq(k, EL_STR("Cgi"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("LBrace"))) { + p = skip_to_rbrace(tokens, p); + } + sigs = native_list_append(sigs, el_map_new(2, "kind", EL_STR("cgi_block"), "name", name)); + pos = p; + } else { + if (str_eq(k, EL_STR("Service"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + el_val_t k2 = tok_kind(tokens, p); + if (str_eq(k2, EL_STR("LBrace"))) { + p = skip_to_rbrace(tokens, p); + } + sigs = native_list_append(sigs, el_map_new(2, "kind", EL_STR("service_block"), "name", name)); + pos = p; + } else { + el_val_t p = (pos + 1); + p = skip_expr_to_stmt_boundary(tokens, p); + pos = p; + } + } + } + } + } + } + } + } + return sigs; + 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 total = str_len(s); + el_val_t parts = native_list_empty(); + el_val_t i = 0; + el_val_t clean_start = 0; + el_val_t prev_was_hex_escape = 0; + while (i < total) { + el_val_t bval = str_char_code(s, i); + if (prev_was_hex_escape) { + if (is_hex_digit_byte(bval)) { + if (clean_start < i) { + parts = native_list_append(parts, str_slice(s, clean_start, i)); + } + parts = native_list_append(parts, EL_STR("\"\"")); + clean_start = i; + } + } + prev_was_hex_escape = 0; + if (bval == 34) { + if (clean_start < i) { + parts = native_list_append(parts, str_slice(s, clean_start, i)); + } + parts = native_list_append(parts, EL_STR("\\\"")); + clean_start = (i + 1); + } else { + if (bval == 92) { + if (clean_start < i) { + parts = native_list_append(parts, str_slice(s, clean_start, i)); + } + parts = native_list_append(parts, EL_STR("\\\\")); + clean_start = (i + 1); + } else { + if (bval == 10) { + if (clean_start < i) { + parts = native_list_append(parts, str_slice(s, clean_start, i)); + } + parts = native_list_append(parts, EL_STR("\\n")); + clean_start = (i + 1); + } else { + if (bval == 13) { + if (clean_start < i) { + parts = native_list_append(parts, str_slice(s, clean_start, i)); + } + parts = native_list_append(parts, EL_STR("\\r")); + clean_start = (i + 1); + } else { + if (bval == 9) { + if (clean_start < i) { + parts = native_list_append(parts, str_slice(s, clean_start, i)); + } + parts = native_list_append(parts, EL_STR("\\t")); + clean_start = (i + 1); + } else { + if (bval >= 128) { + if (clean_start < i) { + parts = native_list_append(parts, str_slice(s, clean_start, i)); + } + parts = native_list_append(parts, el_str_concat(EL_STR("\\x"), byte_to_hex2(bval))); + prev_was_hex_escape = 1; + clean_start = (i + 1); + } + } + } + } + } + } + i = (i + 1); + } + if (clean_start < total) { + parts = native_list_append(parts, str_slice(s, clean_start, total)); + } + el_val_t result = str_join(parts, EL_STR("")); + el_release(parts); + return result; + return 0; +} + +el_val_t c_str_lit(el_val_t s) { + return el_str_concat(el_str_concat(EL_STR("\""), c_escape(s)), EL_STR("\"")); + return 0; +} + +el_val_t sanitize_test_name(el_val_t name) { + el_val_t n = str_len(name); + el_val_t i = 0; + el_val_t out = EL_STR(""); + while (i < n) { + el_val_t code = str_char_code(name, i); + if (code >= 97) { + if (code <= 122) { + out = el_str_concat(out, str_char_at(name, i)); + } else { + out = el_str_concat(out, EL_STR("_")); + } + } else { + if (code >= 65) { + if (code <= 90) { + out = el_str_concat(out, str_char_at(name, i)); + } else { + if (code >= 48) { + if (code <= 57) { + out = el_str_concat(out, str_char_at(name, i)); + } else { + out = el_str_concat(out, EL_STR("_")); + } + } else { + out = el_str_concat(out, EL_STR("_")); + } + } + } else { + if (code >= 48) { + if (code <= 57) { + out = el_str_concat(out, str_char_at(name, i)); + } else { + out = el_str_concat(out, EL_STR("_")); + } + } else { + out = el_str_concat(out, EL_STR("_")); + } + } + } + i = (i + 1); + } + return out; + return 0; +} + +el_val_t el_type_to_c(el_val_t type_str) { + if (str_eq(type_str, EL_STR("String"))) { + return EL_STR("const char*"); + } + if (str_eq(type_str, EL_STR("Int"))) { + return EL_STR("int64_t"); + } + if (str_eq(type_str, EL_STR("Bool"))) { + return EL_STR("int"); + } + if (str_eq(type_str, EL_STR("Float"))) { + return EL_STR("double"); + } + if (str_eq(type_str, EL_STR("Void"))) { + return EL_STR("void"); + } + if (str_eq(type_str, EL_STR("void"))) { + return EL_STR("void"); + } + return EL_STR("void*"); + return 0; +} + +el_val_t emit_line(el_val_t line) { + println(line); + return 0; +} + +el_val_t emit_blank(void) { + println(EL_STR("")); + return 0; +} + +el_val_t binop_to_c(el_val_t op) { + if (str_eq(op, EL_STR("Plus"))) { + return EL_STR("+"); + } + if (str_eq(op, EL_STR("Minus"))) { + return EL_STR("-"); + } + if (str_eq(op, EL_STR("Star"))) { + return EL_STR("*"); + } + if (str_eq(op, EL_STR("Slash"))) { + return EL_STR("/"); + } + if (str_eq(op, EL_STR("Percent"))) { + return EL_STR("%"); + } + if (str_eq(op, EL_STR("EqEq"))) { + return EL_STR("=="); + } + if (str_eq(op, EL_STR("NotEq"))) { + return EL_STR("!="); + } + if (str_eq(op, EL_STR("Lt"))) { + return EL_STR("<"); + } + if (str_eq(op, EL_STR("Gt"))) { + return EL_STR(">"); + } + if (str_eq(op, EL_STR("LtEq"))) { + return EL_STR("<="); + } + if (str_eq(op, EL_STR("GtEq"))) { + return EL_STR(">="); + } + if (str_eq(op, EL_STR("And"))) { + return EL_STR("&&"); + } + if (str_eq(op, EL_STR("Or"))) { + return EL_STR("||"); + } + return 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 next_html_id(void) { + el_val_t csv = state_get(EL_STR("__html_counter")); + el_val_t n = 0; + if (!str_eq(csv, EL_STR(""))) { + n = str_to_int(csv); + } + n = (n + 1); + state_set(EL_STR("__html_counter"), native_int_to_str(n)); + return native_int_to_str(n); + return 0; +} + +el_val_t cg_html_parts(el_val_t children, el_val_t acc_var) { + el_val_t n = native_list_len(children); + el_val_t i = 0; + el_val_t parts = native_list_empty(); + while (i < n) { + el_val_t child = native_list_get(children, i); + el_val_t html_kind = el_get_field(child, EL_STR("html")); + if (str_eq(html_kind, EL_STR("Text"))) { + el_val_t text = el_get_field(child, EL_STR("text")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", EL_STR(")), c_str_lit(text)), EL_STR(")); "))); + } + if (str_eq(html_kind, EL_STR("Doctype"))) { + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", EL_STR(\"\")); "))); + } + if (str_eq(html_kind, EL_STR("Interp"))) { + el_val_t val_node = el_get_field(child, EL_STR("value")); + el_val_t val_c = cg_expr(val_node); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", html_escape(")), val_c), EL_STR(")); "))); + } + if (str_eq(html_kind, EL_STR("Raw"))) { + el_val_t val_node = el_get_field(child, EL_STR("value")); + el_val_t val_c = cg_expr(val_node); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", html_raw(")), val_c), EL_STR(")); "))); + } + if (str_eq(html_kind, EL_STR("Element"))) { + el_val_t elem_c = cg_html_element_str(child, acc_var); + parts = native_list_append(parts, elem_c); + } + if (str_eq(html_kind, EL_STR("Each"))) { + el_val_t each_c = cg_html_each(child, acc_var); + parts = native_list_append(parts, each_c); + } + if (str_eq(html_kind, EL_STR("HtmlIf"))) { + el_val_t if_c = cg_html_if(child, acc_var); + parts = native_list_append(parts, if_c); + } + i = (i + 1); + } + return str_join(parts, EL_STR("")); + return 0; +} + +el_val_t cg_html_attrs_str(el_val_t attrs, el_val_t acc_var) { + el_val_t n = native_list_len(attrs); + el_val_t i = 0; + el_val_t parts = native_list_empty(); + el_val_t close_q = el_str_concat(el_str_concat(EL_STR("EL_STR("), c_str_lit(EL_STR("\""))), EL_STR(")")); + while (i < n) { + el_val_t attr = native_list_get(attrs, i); + el_val_t attr_name = el_get_field(attr, EL_STR("name")); + el_val_t kind = el_get_field(attr, EL_STR("kind")); + el_val_t open_val = el_str_concat(el_str_concat(EL_STR(" "), attr_name), EL_STR("=\"")); + el_val_t open_attr = el_str_concat(el_str_concat(EL_STR("EL_STR("), c_str_lit(open_val)), EL_STR(")")); + if (str_eq(kind, EL_STR("static"))) { + el_val_t sv = el_get_field(attr, EL_STR("value")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", ")), open_attr), EL_STR("); "))); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", EL_STR(")), c_str_lit(sv)), EL_STR(")); "))); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", ")), close_q), EL_STR("); "))); + } else { + if (str_eq(kind, EL_STR("dynamic"))) { + el_val_t val_node = el_get_field(attr, EL_STR("value")); + el_val_t val_c = cg_expr(val_node); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", ")), open_attr), EL_STR("); "))); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", html_escape(")), val_c), EL_STR(")); "))); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", ")), close_q), EL_STR("); "))); + } else { + el_val_t bool_attr = el_str_concat(el_str_concat(EL_STR("EL_STR("), c_str_lit(el_str_concat(EL_STR(" "), attr_name))), EL_STR(")")); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", ")), bool_attr), EL_STR("); "))); + } + } + i = (i + 1); + } + return str_join(parts, EL_STR("")); + return 0; +} + +el_val_t cg_html_element_str(el_val_t elem, el_val_t acc_var) { + el_val_t tag = el_get_field(elem, EL_STR("tag")); + el_val_t attrs = el_get_field(elem, EL_STR("attrs")); + el_val_t children = el_get_field(elem, EL_STR("children")); + el_val_t self_closing = el_get_field(elem, EL_STR("self_closing")); + 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(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", EL_STR(\"<")), tag), EL_STR("\")); "))); + parts = native_list_append(parts, cg_html_attrs_str(attrs, acc_var)); + if (self_closing) { + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", EL_STR(\"/>\")); "))); + } else { + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", EL_STR(\">\")); "))); + parts = native_list_append(parts, cg_html_parts(children, acc_var)); + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" = el_str_concat(")), acc_var), EL_STR(", EL_STR(\"\")); "))); + } + return str_join(parts, EL_STR("")); + return 0; +} + +el_val_t cg_html_each(el_val_t node, el_val_t acc_var) { + el_val_t list_expr = el_get_field(node, EL_STR("list")); + el_val_t item_name = el_get_field(node, EL_STR("item")); + el_val_t body_children = el_get_field(node, EL_STR("body")); + el_val_t id = next_html_id(); + el_val_t list_var = el_str_concat(EL_STR("_html_list_"), id); + el_val_t len_var = el_str_concat(EL_STR("_html_len_"), id); + el_val_t idx_var = el_str_concat(EL_STR("_html_i_"), id); + el_val_t list_c = cg_expr(list_expr); + el_val_t inner_c = cg_html_parts(body_children, acc_var); + return 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(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(el_str_concat(el_str_concat(EL_STR("{ el_val_t "), list_var), EL_STR(" = (")), list_c), EL_STR("); el_val_t ")), len_var), EL_STR(" = el_list_len(")), list_var), EL_STR("); for (el_val_t ")), idx_var), EL_STR(" = 0; ")), idx_var), EL_STR(" < ")), len_var), EL_STR("; ")), idx_var), EL_STR("++) { el_val_t ")), item_name), EL_STR(" = el_list_get(")), list_var), EL_STR(", ")), idx_var), EL_STR("); ")), inner_c), EL_STR("} } ")); + return 0; +} + +el_val_t cg_html_if(el_val_t node, el_val_t acc_var) { + el_val_t cond_expr = el_get_field(node, EL_STR("cond")); + el_val_t then_children = el_get_field(node, EL_STR("then")); + el_val_t else_children = el_get_field(node, EL_STR("else")); + el_val_t cond_c = cg_expr(cond_expr); + el_val_t then_c = cg_html_parts(then_children, acc_var); + el_val_t else_c = cg_html_parts(else_children, acc_var); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("if ("), cond_c), EL_STR(") { ")), then_c), EL_STR(" } else { ")), else_c), EL_STR(" } ")); + return 0; +} + +el_val_t cg_html_template(el_val_t expr) { + el_val_t root = el_get_field(expr, EL_STR("root")); + el_val_t id = next_html_id(); + el_val_t acc = el_str_concat(EL_STR("_html_"), id); + el_val_t doctype_flag = el_get_field(root, EL_STR("doctype")); + el_val_t doctype_prefix = EL_STR(""); + if (doctype_flag) { + doctype_prefix = el_str_concat(el_str_concat(el_str_concat(acc, EL_STR(" = el_str_concat(")), acc), EL_STR(", EL_STR(\"\")); ")); + } + el_val_t body = cg_html_element_str(root, acc); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("({ el_val_t "), acc), EL_STR(" = EL_STR(\"\"); ")), doctype_prefix), body), acc), EL_STR("; })")); + 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(")")); + } + if (str_eq(kind, EL_STR("Str"))) { + el_val_t v = el_get_field(expr, EL_STR("value")); + return el_str_concat(el_str_concat(EL_STR("EL_STR("), c_str_lit(v)), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Bool"))) { + el_val_t v = el_get_field(expr, EL_STR("value")); + if (str_eq(v, EL_STR("true"))) { + return EL_STR("1"); + } + return EL_STR("0"); + } + if (str_eq(kind, EL_STR("Nil"))) { + return EL_STR("EL_NULL"); + } + if (str_eq(kind, EL_STR("Ident"))) { + el_val_t name = el_get_field(expr, EL_STR("name")); + return name; + } + if (str_eq(kind, EL_STR("Not"))) { + el_val_t inner = el_get_field(expr, EL_STR("inner")); + el_val_t inner_c = cg_expr(inner); + return el_str_concat(EL_STR("!"), inner_c); + } + if (str_eq(kind, EL_STR("Neg"))) { + el_val_t inner = el_get_field(expr, EL_STR("inner")); + el_val_t inner_kind = el_get_field(inner, EL_STR("expr")); + if (str_eq(inner_kind, EL_STR("Float"))) { + el_val_t fval = el_get_field(inner, EL_STR("value")); + return el_str_concat(el_str_concat(EL_STR("el_from_float(-"), fval), EL_STR(")")); + } + el_val_t inner_c = cg_expr(inner); + return el_str_concat(el_str_concat(EL_STR("(-"), inner_c), EL_STR(")")); + } + if (str_eq(kind, EL_STR("BinOp"))) { + el_val_t op = el_get_field(expr, EL_STR("op")); + el_val_t left = el_get_field(expr, EL_STR("left")); + el_val_t right = el_get_field(expr, EL_STR("right")); + el_val_t left_c = cg_expr(left); + 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(")")); + } + 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 (is_int_expr(left)) { + if (is_int_expr(right)) { + el_val_t op_c = binop_to_c(op); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")")); + } + } + if (str_eq(left_kind, EL_STR("Int"))) { + el_val_t op_c = binop_to_c(op); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")")); + } + if (str_eq(right_kind, EL_STR("Int"))) { + el_val_t op_c = binop_to_c(op); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")")); + } + if (str_eq(left_kind, EL_STR("Call"))) { + 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("Call"))) { + 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(left_kind, EL_STR("BinOp"))) { + el_val_t left_op = el_get_field(left, EL_STR("op")); + if (str_eq(left_op, EL_STR("Plus"))) { + 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("BinOp"))) { + el_val_t right_op = el_get_field(right, EL_STR("op")); + if (str_eq(right_op, EL_STR("Plus"))) { + 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(left_kind, EL_STR("Ident"))) { + 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("Ident"))) { + 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("Int"))) { + 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(right_kind, EL_STR("Int"))) { + 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("Bool"))) { + 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(right_kind, EL_STR("Bool"))) { + 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("Ident"))) { + if (str_eq(right_kind, EL_STR("Ident"))) { + el_val_t lname = el_get_field(left, EL_STR("name")); + el_val_t rname = el_get_field(right, EL_STR("name")); + if (is_int_name(lname)) { + if (is_int_name(rname)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" == ")), right_c), EL_STR(")")); + } + } + } + } + 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 (is_float_expr(left)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" == ")), right_c), EL_STR(")")); + } + if (is_float_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(")")); + } + 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(left_kind, EL_STR("Ident"))) { + 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("Ident"))) { + 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(left_kind, EL_STR("Call"))) { + 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("Call"))) { + 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("Int"))) { + 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(right_kind, EL_STR("Int"))) { + 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("Bool"))) { + 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(right_kind, EL_STR("Bool"))) { + 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("Ident"))) { + if (str_eq(right_kind, EL_STR("Ident"))) { + el_val_t lname = el_get_field(left, EL_STR("name")); + el_val_t rname = el_get_field(right, EL_STR("name")); + if (is_int_name(lname)) { + if (is_int_name(rname)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" != ")), right_c), EL_STR(")")); + } + } + } + } + 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 (is_float_expr(left)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" != ")), right_c), EL_STR(")")); + } + if (is_float_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(")")); + } + 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(left_kind, EL_STR("Ident"))) { + 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("Ident"))) { + 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(left_kind, EL_STR("Call"))) { + 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("Call"))) { + 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 op_c = binop_to_c(op); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Call"))) { + el_val_t func = el_get_field(expr, EL_STR("func")); + 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_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); + args_parts = native_list_append(args_parts, arg_c); + i = (i + 1); + } + el_val_t args_c = str_join(args_parts, EL_STR(", ")); + el_release(args_parts); + 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"))) { + el_val_t obj = el_get_field(func, EL_STR("object")); + el_val_t field = el_get_field(func, EL_STR("field")); + el_val_t obj_c = cg_expr(obj); + if (arity > 0) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(field, EL_STR("(")), obj_c), EL_STR(", ")), args_c), EL_STR(")")); + } + return el_str_concat(el_str_concat(el_str_concat(field, EL_STR("(")), obj_c), EL_STR(")")); + } + el_val_t fn_c = cg_expr(func); + return el_str_concat(el_str_concat(el_str_concat(fn_c, EL_STR("(")), args_c), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Field"))) { + el_val_t obj = el_get_field(expr, EL_STR("object")); + el_val_t field = el_get_field(expr, EL_STR("field")); + el_val_t obj_c = cg_expr(obj); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_get_field("), obj_c), EL_STR(", EL_STR(")), c_str_lit(field)), EL_STR("))")); + } + if (str_eq(kind, EL_STR("Index"))) { + el_val_t obj = el_get_field(expr, EL_STR("object")); + el_val_t idx = el_get_field(expr, EL_STR("index")); + el_val_t obj_c = cg_expr(obj); + el_val_t idx_c = cg_expr(idx); + el_val_t idx_kind = el_get_field(idx, EL_STR("expr")); + if (str_eq(idx_kind, EL_STR("Str"))) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_get_field("), obj_c), EL_STR(", ")), idx_c), EL_STR(")")); + } + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_list_get("), obj_c), EL_STR(", ")), idx_c), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Array"))) { + el_val_t elems = el_get_field(expr, EL_STR("elems")); + el_val_t n = native_list_len(elems); + if (n == 0) { + return EL_STR("el_list_empty()"); + } + 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); + items_parts = native_list_append(items_parts, elem_c); + i = (i + 1); + } + el_val_t items_joined = str_join(items_parts, EL_STR(", ")); + el_release(items_parts); + 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_joined), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Map"))) { + el_val_t pairs = el_get_field(expr, EL_STR("pairs")); + el_val_t n = native_list_len(pairs); + if (n == 0) { + return EL_STR("el_map_new(0)"); + } + 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); + items_parts = native_list_append(items_parts, el_str_concat(el_str_concat(c_str_lit(key), EL_STR(", ")), val_c)); + i = (i + 1); + } + el_val_t items_joined = str_join(items_parts, EL_STR(", ")); + el_release(items_parts); + 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_joined), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Try"))) { + el_val_t inner = el_get_field(expr, EL_STR("inner")); + return cg_expr(inner); + } + if (str_eq(kind, EL_STR("If"))) { + return cg_if_expr(expr); + } + if (str_eq(kind, EL_STR("Match"))) { + return cg_match(expr); + } + if (str_eq(kind, EL_STR("HtmlTemplate"))) { + return cg_html_template(expr); + } + if (str_eq(kind, EL_STR("Lambda"))) { + EL_STR("EL_NULL"); + } else { + EL_STR("EL_NULL"); + } + return 0; +} + +el_val_t next_match_id(void) { + el_val_t csv = state_get(EL_STR("__match_counter")); + el_val_t n = 0; + if (!str_eq(csv, EL_STR(""))) { + n = str_to_int(csv); + } + n = (n + 1); + state_set(EL_STR("__match_counter"), native_int_to_str(n)); + return native_int_to_str(n); + return 0; +} + +el_val_t cg_match(el_val_t expr) { + el_val_t subject = el_get_field(expr, EL_STR("subject")); + el_val_t arms = el_get_field(expr, EL_STR("arms")); + el_val_t subj_c = cg_expr(subject); + el_val_t id = next_match_id(); + 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 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) { + el_val_t arm = native_list_get(arms, i); + el_val_t pat = el_get_field(arm, EL_STR("pattern")); + el_val_t body = el_get_field(arm, EL_STR("body")); + 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"))) { + 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")); + 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")); + 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")); + 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")); + el_val_t bv = EL_STR("0"); + if (str_eq(v, EL_STR("true"))) { + bv = EL_STR("1"); + } + 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 { + if (str_eq(pkind, EL_STR("Variant"))) { + el_val_t variant = el_get_field(pat, EL_STR("variant")); + 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(variant)), EL_STR("))) { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } "))); + } else { + 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("; } "))); + } + } + } + } + } + } + i = (i + 1); + } + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(done_label, EL_STR(":; ")), result_var), EL_STR("; })"))); + el_val_t result = str_join(parts, EL_STR("")); + el_release(parts); + return result; + return 0; +} + +el_val_t cg_match_stmt(el_val_t expr, el_val_t indent, el_val_t declared) { + el_val_t subject = el_get_field(expr, EL_STR("subject")); + el_val_t arms = el_get_field(expr, EL_STR("arms")); + el_val_t subj_c = cg_expr(subject); + el_val_t id = next_match_id(); + el_val_t subj_var = el_str_concat(EL_STR("_match_subj_"), id); + el_val_t inner = el_str_concat(indent, EL_STR(" ")); + emit_line(el_str_concat(indent, EL_STR("{"))); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR("el_val_t ")), subj_var), EL_STR(" = ")), subj_c), EL_STR(";"))); + el_val_t n = native_list_len(arms); + el_val_t i = 0; + el_val_t first_cond = 1; + while (i < n) { + el_val_t arm = native_list_get(arms, i); + el_val_t pat = el_get_field(arm, EL_STR("pattern")); + el_val_t body = el_get_field(arm, EL_STR("body")); + 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("LitStr"))) { + el_val_t v = el_get_field(pat, EL_STR("value")); + el_val_t cond_str = el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("str_eq("), subj_var), EL_STR(", EL_STR(")), c_str_lit(v)), EL_STR("))")); + if (first_cond) { + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR("if (")), cond_str), EL_STR(") {"))); + first_cond = 0; + } else { + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR("} else if (")), cond_str), EL_STR(") {"))); + } + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR(" ")), body_c), EL_STR(";"))); + } else { + if (str_eq(pkind, EL_STR("LitInt"))) { + el_val_t v = el_get_field(pat, EL_STR("value")); + el_val_t cond_str = el_str_concat(el_str_concat(subj_var, EL_STR(" == ")), v); + if (first_cond) { + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR("if (")), cond_str), EL_STR(") {"))); + first_cond = 0; + } else { + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR("} else if (")), cond_str), EL_STR(") {"))); + } + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR(" ")), body_c), EL_STR(";"))); + } else { + if (str_eq(pkind, EL_STR("LitBool"))) { + el_val_t v = el_get_field(pat, EL_STR("value")); + el_val_t bv = EL_STR("0"); + if (str_eq(v, EL_STR("true"))) { + bv = EL_STR("1"); + } + el_val_t cond_str = el_str_concat(el_str_concat(subj_var, EL_STR(" == ")), bv); + if (first_cond) { + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR("if (")), cond_str), EL_STR(") {"))); + first_cond = 0; + } else { + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR("} else if (")), cond_str), EL_STR(") {"))); + } + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR(" ")), body_c), EL_STR(";"))); + } else { + if (first_cond) { + emit_line(el_str_concat(inner, EL_STR("{"))); + } else { + emit_line(el_str_concat(inner, EL_STR("} else {"))); + } + if (str_eq(pkind, EL_STR("Binding"))) { + el_val_t bname = el_get_field(pat, EL_STR("name")); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR(" el_val_t ")), bname), EL_STR(" = ")), subj_var), EL_STR(";"))); + } + emit_line(el_str_concat(el_str_concat(el_str_concat(inner, EL_STR(" ")), body_c), EL_STR(";"))); + emit_line(el_str_concat(inner, EL_STR("}"))); + first_cond = 1; + } + } + } + i = (i + 1); + } + if (!first_cond) { + emit_line(el_str_concat(inner, EL_STR("}"))); + } + emit_line(el_str_concat(indent, EL_STR("}"))); + return 0; +} + +el_val_t next_if_id(void) { + el_val_t csv = state_get(EL_STR("__if_expr_counter")); + el_val_t n = 0; + if (!str_eq(csv, EL_STR(""))) { + n = str_to_int(csv); + } + n = (n + 1); + state_set(EL_STR("__if_expr_counter"), native_int_to_str(n)); + return native_int_to_str(n); + return 0; +} + +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 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 (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); + 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); + 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) { + parts = native_list_append(parts, el_str_concat(el_str_concat(el_str_concat(result_var, EL_STR(" = (")), val_c), EL_STR("); "))); + } else { + 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); + } + el_val_t result = str_join(parts, EL_STR("")); + el_release(parts); + return result; + return 0; +} + +el_val_t cg_if_expr(el_val_t expr) { + el_val_t cond = el_get_field(expr, EL_STR("cond")); + el_val_t then_stmts = el_get_field(expr, EL_STR("then")); + el_val_t else_stmts = el_get_field(expr, EL_STR("else")); + el_val_t has_else = el_get_field(expr, EL_STR("has_else")); + el_val_t cond_c = cg_expr(cond); + el_val_t id = next_if_id(); + el_val_t result_var = el_str_concat(EL_STR("_if_result_"), id); + el_val_t then_c = cg_if_expr_arm(then_stmts, result_var); + el_val_t else_c = EL_STR(""); + if (has_else) { + else_c = cg_if_expr_arm(else_stmts, result_var); + } + el_val_t 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("({ el_val_t "), result_var), EL_STR(" = 0; if (")), cond_c), EL_STR(") { ")), then_c), EL_STR("} else { ")), else_c), EL_STR("} ")), result_var), EL_STR("; })")); + return out; + return 0; +} + +el_val_t list_contains(el_val_t lst, el_val_t s) { + el_val_t n = native_list_len(lst); + el_val_t i = 0; + while (i < n) { + el_val_t item = native_list_get(lst, i); + if (str_eq(item, s)) { + return 1; + } + i = (i + 1); + } + return 0; + return 0; +} + +el_val_t cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) { + el_val_t kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("Let"))) { + 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); + el_val_t ltype = el_get_field(stmt, EL_STR("type")); + 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); + } + if (list_contains(declared, name)) { + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, name), EL_STR(" = ")), val_c), EL_STR(";"))); + return declared; + } else { + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("el_val_t ")), name), EL_STR(" = ")), val_c), EL_STR(";"))); + return native_list_append(declared, name); + } + } + if (str_eq(kind, EL_STR("Return"))) { + el_val_t val = el_get_field(stmt, EL_STR("value")); + el_val_t val_kind = el_get_field(val, EL_STR("expr")); + if (str_eq(val_kind, EL_STR("Nil"))) { + emit_line(el_str_concat(indent, EL_STR("return 0;"))); + } else { + el_val_t val_c = cg_expr(val); + emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("return ")), val_c), EL_STR(";"))); + } + return declared; + } + if (str_eq(kind, EL_STR("Break"))) { + emit_line(el_str_concat(indent, EL_STR("break;"))); + return declared; + } + if (str_eq(kind, EL_STR("Continue"))) { + emit_line(el_str_concat(indent, EL_STR("continue;"))); + 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")); + if (str_eq(val_kind, EL_STR("If"))) { + cg_if_stmt(val, indent, declared); + return declared; + } + if (str_eq(val_kind, EL_STR("For"))) { + cg_for_stmt(val, indent, declared); + return declared; + } + if (str_eq(val_kind, EL_STR("Match"))) { + cg_match_stmt(val, indent, declared); + return declared; + } + el_val_t val_c = cg_expr(val); + emit_line(el_str_concat(el_str_concat(indent, val_c), EL_STR(";"))); + return declared; + } + if (str_eq(kind, EL_STR("While"))) { + el_val_t cond = el_get_field(stmt, EL_STR("cond")); + el_val_t body = el_get_field(stmt, EL_STR("body")); + el_val_t cond_c = cg_expr(cond); + cond_c = strip_outer_parens(cond_c); + emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("while (")), cond_c), EL_STR(") {"))); + cg_stmts(body, el_str_concat(indent, EL_STR(" ")), native_list_clone(declared)); + emit_line(el_str_concat(indent, EL_STR("}"))); + return declared; + } + if (str_eq(kind, EL_STR("For"))) { + el_val_t item = el_get_field(stmt, EL_STR("item")); + el_val_t list_expr = el_get_field(stmt, EL_STR("list")); + el_val_t body = el_get_field(stmt, EL_STR("body")); + cg_for_body(item, list_expr, body, indent, declared); + return declared; + } + if (str_eq(kind, EL_STR("ForRange"))) { + el_val_t var_name = el_get_field(stmt, EL_STR("var")); + el_val_t start_expr = el_get_field(stmt, EL_STR("start")); + el_val_t end_expr = el_get_field(stmt, EL_STR("end")); + el_val_t inclusive = el_get_field(stmt, EL_STR("inclusive")); + el_val_t body = el_get_field(stmt, EL_STR("body")); + el_val_t start_c = cg_expr(start_expr); + el_val_t end_c = cg_expr(end_expr); + el_val_t body_decl = native_list_clone(declared); + body_decl = native_list_append(body_decl, var_name); + if (inclusive) { + emit_line(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(indent, EL_STR("for (el_val_t ")), var_name), EL_STR(" = ")), start_c), EL_STR("; ")), var_name), EL_STR(" <= ")), end_c), EL_STR("; ")), var_name), EL_STR("++) {"))); + } else { + emit_line(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(indent, EL_STR("for (el_val_t ")), var_name), EL_STR(" = ")), start_c), EL_STR("; ")), var_name), EL_STR(" < ")), end_c), EL_STR("; ")), var_name), EL_STR("++) {"))); + } + cg_stmts(body, el_str_concat(indent, EL_STR(" ")), body_decl); + emit_line(el_str_concat(indent, EL_STR("}"))); + return declared; + } + if (str_eq(kind, EL_STR("FnDef"))) { + return declared; + } + if (str_eq(kind, EL_STR("TypeDef"))) { + return declared; + } + if (str_eq(kind, EL_STR("EnumDef"))) { + return 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; + } + if (str_eq(kind, EL_STR("TryCatch"))) { + el_val_t try_body = el_get_field(stmt, EL_STR("try_body")); + emit_line(el_str_concat(indent, EL_STR("/* try (C target: exception handling not supported) */"))); + cg_stmts(try_body, indent, native_list_clone(declared)); + return declared; + } + if (str_eq(kind, EL_STR("Assert"))) { + el_val_t cond_node = el_get_field(stmt, EL_STR("cond")); + el_val_t msg_node = el_get_field(stmt, EL_STR("msg")); + el_val_t c_cond = cg_expr(cond_node); + el_val_t c_msg = EL_STR(""); + el_val_t msg_kind = el_get_field(msg_node, EL_STR("expr")); + if (str_eq(msg_kind, EL_STR("Str"))) { + el_val_t raw_msg = el_get_field(msg_node, EL_STR("value")); + c_msg = el_str_concat(el_str_concat(EL_STR("\""), c_escape(raw_msg)), EL_STR("\"")); + } else { + c_msg = el_str_concat(el_str_concat(EL_STR("EL_STR_PTR("), cg_expr(msg_node)), EL_STR(")")); + } + emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("if (!(")), c_cond), EL_STR(")) {"))); + emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR(" __el_test_fail(__el_cur_test, ")), c_msg), EL_STR("); __el_fail++;"))); + emit_line(el_str_concat(indent, EL_STR("} else { __el_pass++; }"))); + return declared; + } + return declared; + return 0; +} + +el_val_t strip_outer_parens(el_val_t s) { + el_val_t chars = native_string_chars(s); + el_val_t n = native_list_len(chars); + if (n < 2) { + return s; + } + el_val_t first = native_list_get(chars, 0); + el_val_t last = native_list_get(chars, (n - 1)); + if (str_eq(first, EL_STR("("))) { + if (str_eq(last, EL_STR(")"))) { + el_val_t depth = 1; + el_val_t i = 1; + el_val_t balanced = 1; + while (i < (n - 1)) { + el_val_t ch = native_list_get(chars, i); + if (str_eq(ch, EL_STR("("))) { + depth = (depth + 1); + } + if (str_eq(ch, EL_STR(")"))) { + depth = (depth - 1); + if (depth == 0) { + balanced = 0; + i = n; + } + } + i = (i + 1); + } + if (balanced) { + return str_slice(s, 1, (n - 1)); + } + } + } + return s; + return 0; +} + +el_val_t cg_if_stmt(el_val_t expr, el_val_t indent, el_val_t declared) { + el_val_t cond = el_get_field(expr, EL_STR("cond")); + el_val_t then_stmts = el_get_field(expr, EL_STR("then")); + el_val_t else_stmts = el_get_field(expr, EL_STR("else")); + el_val_t has_else = el_get_field(expr, EL_STR("has_else")); + el_val_t cond_c = cg_expr(cond); + cond_c = strip_outer_parens(cond_c); + emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("if (")), cond_c), EL_STR(") {"))); + cg_stmts(then_stmts, el_str_concat(indent, EL_STR(" ")), native_list_clone(declared)); + if (has_else) { + emit_line(el_str_concat(indent, EL_STR("} else {"))); + cg_stmts(else_stmts, el_str_concat(indent, EL_STR(" ")), native_list_clone(declared)); + } + emit_line(el_str_concat(indent, EL_STR("}"))); + return 0; +} + +el_val_t cg_for_body(el_val_t item, el_val_t list_expr, el_val_t body, el_val_t indent, el_val_t declared) { + el_val_t list_c = cg_expr(list_expr); + el_val_t idx = EL_STR("_el_i"); + el_val_t list_tmp = EL_STR("_el_lst"); + el_val_t len_tmp = EL_STR("_el_len"); + emit_line(el_str_concat(indent, EL_STR("{"))); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR(" el_val_t ")), list_tmp), EL_STR(" = ")), list_c), EL_STR(";"))); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR(" el_val_t ")), len_tmp), EL_STR(" = el_list_len(")), list_tmp), EL_STR(");"))); + emit_line(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(indent, EL_STR(" for (el_val_t ")), idx), EL_STR(" = 0; ")), idx), EL_STR(" < ")), len_tmp), EL_STR("; ")), idx), EL_STR("++) {"))); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR(" el_val_t ")), item), EL_STR(" = el_list_get(")), list_tmp), EL_STR(", ")), idx), EL_STR(");"))); + el_val_t body_decl = native_list_clone(declared); + body_decl = native_list_append(body_decl, item); + cg_stmts(body, el_str_concat(indent, EL_STR(" ")), body_decl); + emit_line(el_str_concat(indent, EL_STR(" }"))); + emit_line(el_str_concat(indent, EL_STR("}"))); + return 0; +} + +el_val_t cg_for_stmt(el_val_t expr, el_val_t indent, el_val_t declared) { + el_val_t item = el_get_field(expr, EL_STR("item")); + el_val_t list_expr = el_get_field(expr, EL_STR("list")); + el_val_t body = el_get_field(expr, EL_STR("body")); + cg_for_body(item, list_expr, body, indent, declared); + return 0; +} + +el_val_t cg_stmts(el_val_t stmts, el_val_t indent, el_val_t declared) { + el_val_t n = native_list_len(stmts); + el_val_t i = 0; + el_val_t decl = declared; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + el_val_t s_mark = el_arena_push(); + decl = cg_stmt(stmt, indent, decl); + el_arena_pop(s_mark); + i = (i + 1); + } + return decl; + return 0; +} + +el_val_t param_decl(el_val_t param, el_val_t idx) { + el_val_t name = el_get_field(param, EL_STR("name")); + return el_str_concat(EL_STR("el_val_t "), name); + return 0; +} + +el_val_t params_to_c(el_val_t params) { + el_val_t n = native_list_len(params); + if (n == 0) { + return EL_STR("void"); + } + 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); + parts = native_list_append(parts, decl); + i = (i + 1); + } + el_val_t result = str_join(parts, EL_STR(", ")); + el_release(parts); + return result; + return 0; +} + +el_val_t transform_implicit_return(el_val_t body) { + el_val_t n = native_list_len(body); + if (n == 0) { + return body; + } + el_val_t last = native_list_get(body, (n - 1)); + el_val_t last_kind = el_get_field(last, EL_STR("stmt")); + if (str_eq(last_kind, EL_STR("Expr"))) { + el_val_t val = el_get_field(last, EL_STR("value")); + el_val_t val_kind = el_get_field(val, EL_STR("expr")); + if (str_eq(val_kind, EL_STR("If"))) { + return body; + } + if (str_eq(val_kind, EL_STR("For"))) { + return body; + } + el_val_t new_body = native_list_empty(); + el_val_t i = 0; + while (i < (n - 1)) { + new_body = native_list_append(new_body, native_list_get(body, i)); + i = (i + 1); + } + el_val_t return_stmt = el_map_new(2, "stmt", EL_STR("Return"), "value", val); + new_body = native_list_append(new_body, return_stmt); + return new_body; + } + return body; + return 0; +} + +el_val_t is_int_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__int_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_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")); + 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("str_len"))) { + return 1; + } + if (str_eq(name, EL_STR("str_index_of"))) { + return 1; + } + if (str_eq(name, EL_STR("str_to_int"))) { + return 1; + } + 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; + } + if (str_eq(name, EL_STR("el_list_len"))) { + return 1; + } + if (str_eq(name, EL_STR("len"))) { + return 1; + } + if (str_eq(name, EL_STR("json_get_int"))) { + return 1; + } + if (str_eq(name, EL_STR("json_array_len"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_node_count"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_edge_count"))) { + return 1; + } + if (str_eq(name, EL_STR("time_now"))) { + return 1; + } + if (str_eq(name, EL_STR("time_now_utc"))) { + return 1; + } + if (str_eq(name, EL_STR("time_diff"))) { + return 1; + } + if (str_eq(name, EL_STR("time_add"))) { + return 1; + } + if (str_eq(name, EL_STR("time_from_parts"))) { + return 1; + } + if (str_eq(name, EL_STR("el_abs"))) { + return 1; + } + if (str_eq(name, EL_STR("el_max"))) { + return 1; + } + if (str_eq(name, EL_STR("el_min"))) { + return 1; + } + 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"))) { + return 1; + } + if (str_eq(k, EL_STR("Ident"))) { + el_val_t name = el_get_field(expr, EL_STR("name")); + return is_int_name(name); + } + if (str_eq(k, EL_STR("Call"))) { + return is_int_call(expr); + } + if (str_eq(k, EL_STR("Neg"))) { + return is_int_expr(el_get_field(expr, EL_STR("inner"))); + } + if (str_eq(k, EL_STR("Not"))) { + return 1; + } + if (str_eq(k, EL_STR("BinOp"))) { + el_val_t op = el_get_field(expr, EL_STR("op")); + if (str_eq(op, EL_STR("EqEq"))) { + return 1; + } + if (str_eq(op, EL_STR("NotEq"))) { + return 1; + } + if (str_eq(op, EL_STR("Lt"))) { + return 1; + } + if (str_eq(op, EL_STR("Gt"))) { + return 1; + } + if (str_eq(op, EL_STR("LtEq"))) { + return 1; + } + if (str_eq(op, EL_STR("GtEq"))) { + return 1; + } + if (str_eq(op, EL_STR("And"))) { + return 1; + } + if (str_eq(op, EL_STR("Or"))) { + return 1; + } + if (str_eq(op, EL_STR("Plus"))) { + if (is_int_expr(el_get_field(expr, EL_STR("left")))) { + if (is_int_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + if (str_eq(op, EL_STR("Minus"))) { + if (is_int_expr(el_get_field(expr, EL_STR("left")))) { + if (is_int_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + if (str_eq(op, EL_STR("Star"))) { + if (is_int_expr(el_get_field(expr, EL_STR("left")))) { + if (is_int_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + if (str_eq(op, EL_STR("Slash"))) { + if (is_int_expr(el_get_field(expr, EL_STR("left")))) { + if (is_int_expr(el_get_field(expr, EL_STR("right")))) { + return 1; + } + } + return 0; + } + if (str_eq(op, EL_STR("Percent"))) { + if (is_int_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 is_float_expr(el_val_t expr) { + el_val_t k = el_get_field(expr, EL_STR("expr")); + if (str_eq(k, EL_STR("Float"))) { + return 1; + } + if (str_eq(k, EL_STR("Neg"))) { + el_val_t inner = el_get_field(expr, EL_STR("inner")); + el_val_t ik = el_get_field(inner, EL_STR("expr")); + if (str_eq(ik, EL_STR("Float"))) { + return 1; + } + } + return 0; + return 0; +} + +el_val_t cap_record_violation(el_val_t kind, el_val_t fn_name) { + el_val_t csv = state_get(EL_STR("__cap_violations")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t entry = el_str_concat(el_str_concat(kind, EL_STR(":")), fn_name); + 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("__cap_violations"), el_str_concat(el_str_concat(csv, entry), EL_STR(","))); + return 1; + return 0; +} + +el_val_t is_self_formation_call(el_val_t fn_name) { + if (str_eq(fn_name, EL_STR("llm_call_agentic"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("llm_register_tool"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_emit"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_field"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_dharma_call(el_val_t fn_name) { + if (str_eq(fn_name, EL_STR("dharma_connect"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_send"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_activate"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_emit"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_field"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_strengthen"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_relationship"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("dharma_peers"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_llm_call(el_val_t fn_name) { + if (str_eq(fn_name, EL_STR("llm_call"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("llm_call_system"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("llm_call_agentic"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("llm_vision"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("llm_register_tool"))) { + return 1; + } + if (str_eq(fn_name, EL_STR("llm_models"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t cap_check_call(el_val_t fn_name) { + el_val_t kind = state_get(EL_STR("__program_kind")); + if (str_eq(kind, EL_STR("cgi"))) { + return 1; + } + if (str_eq(kind, EL_STR("service"))) { + if (is_self_formation_call(fn_name)) { + cap_record_violation(EL_STR("service"), fn_name); + return 0; + } + return 1; + } + if (is_dharma_call(fn_name)) { + cap_record_violation(EL_STR("utility"), fn_name); + return 0; + } + if (is_llm_call(fn_name)) { + cap_record_violation(EL_STR("utility"), fn_name); + return 0; + } + return 1; + return 0; +} + +el_val_t emit_cap_violations(void) { + el_val_t csv = state_get(EL_STR("__cap_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 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)\""))); + } + 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); + } + return 0; +} + +el_val_t builtin_arity(el_val_t name) { + if (str_eq(name, EL_STR("println"))) { + return 1; + } + if (str_eq(name, EL_STR("print"))) { + return 1; + } + if (str_eq(name, EL_STR("readline"))) { + return 0; + } + if (str_eq(name, EL_STR("__read_n"))) { + return 1; + } + if (str_eq(name, EL_STR("__print_raw"))) { + return 1; + } + if (str_eq(name, EL_STR("el_str_concat"))) { + return 2; + } + if (str_eq(name, EL_STR("str_eq"))) { + return 2; + } + if (str_eq(name, EL_STR("str_starts_with"))) { + return 2; + } + if (str_eq(name, EL_STR("str_ends_with"))) { + return 2; + } + if (str_eq(name, EL_STR("str_len"))) { + return 1; + } + if (str_eq(name, EL_STR("str_concat"))) { + return 2; + } + if (str_eq(name, EL_STR("int_to_str"))) { + return 1; + } + if (str_eq(name, EL_STR("str_to_int"))) { + return 1; + } + if (str_eq(name, EL_STR("str_slice"))) { + return 3; + } + if (str_eq(name, EL_STR("str_contains"))) { + return 2; + } + if (str_eq(name, EL_STR("str_replace"))) { + return 3; + } + if (str_eq(name, EL_STR("str_to_upper"))) { + return 1; + } + if (str_eq(name, EL_STR("str_to_lower"))) { + return 1; + } + if (str_eq(name, EL_STR("str_trim"))) { + return 1; + } + if (str_eq(name, EL_STR("str_index_of"))) { + return 2; + } + if (str_eq(name, EL_STR("str_split"))) { + return 2; + } + if (str_eq(name, EL_STR("str_char_at"))) { + return 2; + } + if (str_eq(name, EL_STR("str_char_code"))) { + return 2; + } + if (str_eq(name, EL_STR("str_pad_left"))) { + return 3; + } + if (str_eq(name, EL_STR("str_pad_right"))) { + return 3; + } + if (str_eq(name, EL_STR("str_format"))) { + return 2; + } + if (str_eq(name, EL_STR("str_lower"))) { + return 1; + } + 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; + } + if (str_eq(name, EL_STR("el_max"))) { + return 2; + } + if (str_eq(name, EL_STR("el_min"))) { + return 2; + } + if (str_eq(name, EL_STR("el_list_len"))) { + return 1; + } + if (str_eq(name, EL_STR("el_list_get"))) { + return 2; + } + if (str_eq(name, EL_STR("el_list_append"))) { + return 2; + } + if (str_eq(name, EL_STR("el_list_empty"))) { + return 0; + } + if (str_eq(name, EL_STR("el_list_clone"))) { + return 1; + } + if (str_eq(name, EL_STR("list_push"))) { + return 2; + } + if (str_eq(name, EL_STR("list_push_front"))) { + return 2; + } + if (str_eq(name, EL_STR("list_join"))) { + return 2; + } + if (str_eq(name, EL_STR("list_range"))) { + return 2; + } + if (str_eq(name, EL_STR("el_get_field"))) { + return 2; + } + if (str_eq(name, EL_STR("el_map_get"))) { + return 2; + } + if (str_eq(name, EL_STR("el_map_set"))) { + return 3; + } + if (str_eq(name, EL_STR("http_get"))) { + return 1; + } + if (str_eq(name, EL_STR("http_post"))) { + return 2; + } + if (str_eq(name, EL_STR("http_post_json"))) { + return 2; + } + if (str_eq(name, EL_STR("http_get_with_headers"))) { + return 2; + } + if (str_eq(name, EL_STR("http_post_with_headers"))) { + return 3; + } + if (str_eq(name, EL_STR("http_post_form_auth"))) { + return 3; + } + if (str_eq(name, EL_STR("http_serve"))) { + return 2; + } + if (str_eq(name, EL_STR("http_set_handler"))) { + return 1; + } + if (str_eq(name, EL_STR("__str_len"))) { + return 1; + } + if (str_eq(name, EL_STR("__str_char_at"))) { + return 2; + } + if (str_eq(name, EL_STR("__str_alloc"))) { + return 1; + } + if (str_eq(name, EL_STR("__str_set_char"))) { + return 3; + } + if (str_eq(name, EL_STR("__str_cmp"))) { + return 2; + } + if (str_eq(name, EL_STR("__str_ncmp"))) { + return 3; + } + if (str_eq(name, EL_STR("__str_concat_raw"))) { + return 2; + } + if (str_eq(name, EL_STR("__str_slice_raw"))) { + return 3; + } + if (str_eq(name, EL_STR("__int_to_str"))) { + return 1; + } + if (str_eq(name, EL_STR("__str_to_int"))) { + return 1; + } + if (str_eq(name, EL_STR("__float_to_str"))) { + return 1; + } + if (str_eq(name, EL_STR("__str_to_float"))) { + return 1; + } + if (str_eq(name, EL_STR("__println"))) { + return 1; + } + if (str_eq(name, EL_STR("__print"))) { + return 1; + } + if (str_eq(name, EL_STR("__readline"))) { + return 0; + } + if (str_eq(name, EL_STR("__fs_read"))) { + return 1; + } + if (str_eq(name, EL_STR("__fs_write"))) { + return 2; + } + if (str_eq(name, EL_STR("__fs_exists"))) { + return 1; + } + if (str_eq(name, EL_STR("__fs_list_raw"))) { + return 1; + } + if (str_eq(name, EL_STR("__fs_mkdir"))) { + return 1; + } + if (str_eq(name, EL_STR("__fs_write_bytes"))) { + return 3; + } + if (str_eq(name, EL_STR("__http_do"))) { + return 5; + } + if (str_eq(name, EL_STR("__http_do_map"))) { + return 5; + } + if (str_eq(name, EL_STR("__http_do_to_file"))) { + return 5; + } + if (str_eq(name, EL_STR("__http_serve"))) { + return 2; + } + if (str_eq(name, EL_STR("__http_serve_v2"))) { + return 2; + } + if (str_eq(name, EL_STR("__http_response"))) { + return 3; + } + if (str_eq(name, EL_STR("__thread_create"))) { + return 2; + } + if (str_eq(name, EL_STR("__thread_join"))) { + return 1; + } + if (str_eq(name, EL_STR("__mutex_new"))) { + return 0; + } + if (str_eq(name, EL_STR("__mutex_lock"))) { + return 1; + } + if (str_eq(name, EL_STR("__mutex_unlock"))) { + 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("__env_get"))) { + return 1; + } + if (str_eq(name, EL_STR("__args_json"))) { + return 0; + } + if (str_eq(name, EL_STR("__exit_program"))) { + return 1; + } + if (str_eq(name, EL_STR("__time_now_ns"))) { + return 0; + } + if (str_eq(name, EL_STR("__sleep_ms"))) { + return 1; + } + if (str_eq(name, EL_STR("__uuid_v4"))) { + return 0; + } + if (str_eq(name, EL_STR("__sqrt_f"))) { + return 1; + } + if (str_eq(name, EL_STR("__log_f"))) { + return 1; + } + if (str_eq(name, EL_STR("__ln_f"))) { + return 1; + } + if (str_eq(name, EL_STR("__sin_f"))) { + return 1; + } + if (str_eq(name, EL_STR("__cos_f"))) { + return 1; + } + if (str_eq(name, EL_STR("__pi_f"))) { + return 0; + } + if (str_eq(name, EL_STR("__state_set"))) { + return 2; + } + if (str_eq(name, EL_STR("__state_get"))) { + return 1; + } + if (str_eq(name, EL_STR("__state_del"))) { + return 1; + } + if (str_eq(name, EL_STR("__state_keys"))) { + return 0; + } + if (str_eq(name, EL_STR("__html_sanitize"))) { + return 2; + } + if (str_eq(name, EL_STR("__url_encode"))) { + return 1; + } + if (str_eq(name, EL_STR("__url_decode"))) { + return 1; + } + if (str_eq(name, EL_STR("__json_get"))) { + return 2; + } + if (str_eq(name, EL_STR("__json_get_raw"))) { + return 2; + } + if (str_eq(name, EL_STR("__json_parse_map"))) { + return 1; + } + if (str_eq(name, EL_STR("__json_stringify_val"))) { + return 1; + } + if (str_eq(name, EL_STR("__json_array_len"))) { + return 1; + } + if (str_eq(name, EL_STR("__json_array_get"))) { + return 2; + } + if (str_eq(name, EL_STR("__json_array_get_string"))) { + return 2; + } + if (str_eq(name, EL_STR("__json_set"))) { + return 3; + } + if (str_eq(name, EL_STR("__engram_node"))) { + return 3; + } + if (str_eq(name, EL_STR("__engram_node_full"))) { + return 8; + } + if (str_eq(name, EL_STR("__engram_get_node"))) { + return 1; + } + if (str_eq(name, EL_STR("__engram_strengthen"))) { + return 1; + } + if (str_eq(name, EL_STR("__engram_forget"))) { + return 1; + } + if (str_eq(name, EL_STR("__engram_node_count"))) { + return 0; + } + if (str_eq(name, EL_STR("__engram_search"))) { + return 2; + } + if (str_eq(name, EL_STR("__engram_scan_nodes"))) { + return 2; + } + if (str_eq(name, EL_STR("__engram_connect"))) { + return 4; + } + if (str_eq(name, EL_STR("__engram_edge_between"))) { + return 2; + } + if (str_eq(name, EL_STR("__engram_neighbors"))) { + return 1; + } + if (str_eq(name, EL_STR("__engram_neighbors_filtered"))) { + return 3; + } + if (str_eq(name, EL_STR("__engram_activate"))) { + return 2; + } + if (str_eq(name, EL_STR("__engram_activate_json"))) { + return 2; + } + if (str_eq(name, EL_STR("__engram_scan_nodes_json"))) { + return 2; + } + if (str_eq(name, EL_STR("__generate"))) { + return 1; + } + if (str_eq(name, EL_STR("fs_read"))) { + return 1; + } + if (str_eq(name, EL_STR("fs_write"))) { + return 2; + } + if (str_eq(name, EL_STR("fs_list"))) { + return 1; + } + if (str_eq(name, EL_STR("json_get"))) { + return 2; + } + if (str_eq(name, EL_STR("json_parse"))) { + return 1; + } + if (str_eq(name, EL_STR("json_stringify"))) { + return 1; + } + if (str_eq(name, EL_STR("json_get_string"))) { + return 2; + } + if (str_eq(name, EL_STR("json_get_int"))) { + return 2; + } + if (str_eq(name, EL_STR("json_get_float"))) { + return 2; + } + if (str_eq(name, EL_STR("json_get_bool"))) { + return 2; + } + if (str_eq(name, EL_STR("json_get_raw"))) { + return 2; + } + if (str_eq(name, EL_STR("json_set"))) { + return 3; + } + if (str_eq(name, EL_STR("json_array_len"))) { + return 1; + } + if (str_eq(name, EL_STR("time_now"))) { + return 0; + } + if (str_eq(name, EL_STR("time_now_utc"))) { + return 0; + } + if (str_eq(name, EL_STR("sleep_secs"))) { + return 1; + } + if (str_eq(name, EL_STR("sleep_ms"))) { + return 1; + } + if (str_eq(name, EL_STR("time_format"))) { + return 2; + } + if (str_eq(name, EL_STR("time_to_parts"))) { + return 1; + } + if (str_eq(name, EL_STR("time_from_parts"))) { + return 3; + } + if (str_eq(name, EL_STR("time_add"))) { + return 3; + } + if (str_eq(name, EL_STR("time_diff"))) { + return 3; + } + if (str_eq(name, EL_STR("uuid_new"))) { + return 0; + } + if (str_eq(name, EL_STR("uuid_v4"))) { + return 0; + } + if (str_eq(name, EL_STR("env"))) { + return 1; + } + if (str_eq(name, EL_STR("state_set"))) { + return 2; + } + if (str_eq(name, EL_STR("state_get"))) { + return 1; + } + if (str_eq(name, EL_STR("state_del"))) { + return 1; + } + if (str_eq(name, EL_STR("state_keys"))) { + return 0; + } + if (str_eq(name, EL_STR("float_to_str"))) { + return 1; + } + if (str_eq(name, EL_STR("int_to_float"))) { + return 1; + } + if (str_eq(name, EL_STR("float_to_int"))) { + return 1; + } + if (str_eq(name, EL_STR("format_float"))) { + return 2; + } + if (str_eq(name, EL_STR("decimal_round"))) { + return 2; + } + if (str_eq(name, EL_STR("str_to_float"))) { + return 1; + } + if (str_eq(name, EL_STR("math_sqrt"))) { + return 1; + } + if (str_eq(name, EL_STR("math_log"))) { + return 1; + } + if (str_eq(name, EL_STR("math_ln"))) { + return 1; + } + if (str_eq(name, EL_STR("math_sin"))) { + return 1; + } + if (str_eq(name, EL_STR("math_cos"))) { + return 1; + } + if (str_eq(name, EL_STR("math_pi"))) { + return 0; + } + if (str_eq(name, EL_STR("bool_to_str"))) { + return 1; + } + if (str_eq(name, EL_STR("exit_program"))) { + return 1; + } + if (str_eq(name, EL_STR("getpid_now"))) { + return 0; + } + if (str_eq(name, EL_STR("stdout_to_file"))) { + return 1; + } + if (str_eq(name, EL_STR("stdout_restore"))) { + return 0; + } + 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; + } + if (str_eq(name, EL_STR("dharma_send"))) { + return 2; + } + if (str_eq(name, EL_STR("dharma_activate"))) { + return 1; + } + if (str_eq(name, EL_STR("dharma_emit"))) { + return 2; + } + if (str_eq(name, EL_STR("dharma_field"))) { + return 1; + } + if (str_eq(name, EL_STR("dharma_strengthen"))) { + return 2; + } + if (str_eq(name, EL_STR("dharma_relationship"))) { + return 1; + } + if (str_eq(name, EL_STR("dharma_peers"))) { + return 0; + } + if (str_eq(name, EL_STR("engram_node"))) { + return 3; + } + if (str_eq(name, EL_STR("engram_node_full"))) { + return 8; + } + if (str_eq(name, EL_STR("engram_get_node"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_strengthen"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_forget"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_node_count"))) { + return 0; + } + if (str_eq(name, EL_STR("engram_search"))) { + return 2; + } + if (str_eq(name, EL_STR("engram_scan_nodes"))) { + return 2; + } + if (str_eq(name, EL_STR("engram_connect"))) { + return 4; + } + if (str_eq(name, EL_STR("engram_edge_between"))) { + return 2; + } + if (str_eq(name, EL_STR("engram_neighbors"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_neighbors_filtered"))) { + return 3; + } + if (str_eq(name, EL_STR("engram_edge_count"))) { + return 0; + } + if (str_eq(name, EL_STR("engram_activate"))) { + return 2; + } + if (str_eq(name, EL_STR("engram_save"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_load"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_get_node_json"))) { + return 1; + } + if (str_eq(name, EL_STR("engram_search_json"))) { + return 2; + } + if (str_eq(name, EL_STR("engram_scan_nodes_json"))) { + return 2; + } + if (str_eq(name, EL_STR("engram_neighbors_json"))) { + return 3; + } + if (str_eq(name, EL_STR("engram_activate_json"))) { + return 2; + } + if (str_eq(name, EL_STR("engram_stats_json"))) { + return 0; + } + if (str_eq(name, EL_STR("llm_call"))) { + return 2; + } + if (str_eq(name, EL_STR("llm_call_system"))) { + return 3; + } + if (str_eq(name, EL_STR("llm_call_agentic"))) { + return 4; + } + if (str_eq(name, EL_STR("llm_vision"))) { + return 4; + } + if (str_eq(name, EL_STR("llm_models"))) { + return 0; + } + if (str_eq(name, EL_STR("llm_register_tool"))) { + return 2; + } + if (str_eq(name, EL_STR("sha256_hex"))) { + return 1; + } + if (str_eq(name, EL_STR("sha256_bytes"))) { + return 1; + } + if (str_eq(name, EL_STR("hmac_sha256_hex"))) { + return 2; + } + if (str_eq(name, EL_STR("hmac_sha256_bytes"))) { + return 2; + } + if (str_eq(name, EL_STR("base64_encode"))) { + return 1; + } + if (str_eq(name, EL_STR("base64_decode"))) { + return 1; + } + if (str_eq(name, EL_STR("base64url_encode"))) { + return 1; + } + if (str_eq(name, EL_STR("base64url_decode"))) { + return 1; + } + if (str_eq(name, EL_STR("native_list_get"))) { + return 2; + } + if (str_eq(name, EL_STR("native_list_len"))) { + return 1; + } + if (str_eq(name, EL_STR("native_list_append"))) { + return 2; + } + if (str_eq(name, EL_STR("native_list_empty"))) { + return 0; + } + if (str_eq(name, EL_STR("native_list_clone"))) { + return 1; + } + if (str_eq(name, EL_STR("native_string_chars"))) { + return 1; + } + if (str_eq(name, EL_STR("native_int_to_str"))) { + return 1; + } + if (str_eq(name, EL_STR("append"))) { + return 2; + } + if (str_eq(name, EL_STR("len"))) { + return 1; + } + if (str_eq(name, EL_STR("get"))) { + return 2; + } + if (str_eq(name, EL_STR("map_get"))) { + return 2; + } + if (str_eq(name, EL_STR("map_set"))) { + return 3; + } + if (str_eq(name, EL_STR("__thread_create"))) { + return 2; + } + if (str_eq(name, EL_STR("__thread_join"))) { + return 1; + } + if (str_eq(name, EL_STR("__mutex_new"))) { + return 0; + } + if (str_eq(name, EL_STR("__mutex_lock"))) { + return 1; + } + if (str_eq(name, EL_STR("__mutex_unlock"))) { + return 1; + } + if (str_eq(name, EL_STR("__channel_new"))) { + return 1; + } + if (str_eq(name, EL_STR("__channel_send"))) { + return 2; + } + if (str_eq(name, EL_STR("__channel_recv"))) { + return 1; + } + if (str_eq(name, EL_STR("__channel_try_recv"))) { + return 1; + } + if (str_eq(name, EL_STR("__channel_close"))) { + return 1; + } + if (str_eq(name, EL_STR("el_arena_push"))) { + return 0; + } + if (str_eq(name, EL_STR("el_arena_pop"))) { + return 1; + } + return (-1); + return 0; +} + +el_val_t arity_record_violation(el_val_t fn_name, el_val_t expected, el_val_t actual) { + el_val_t csv = state_get(EL_STR("__arity_violations")); + if (str_eq(csv, EL_STR(""))) { + csv = EL_STR(","); + } + el_val_t entry = el_str_concat(el_str_concat(el_str_concat(el_str_concat(fn_name, EL_STR("|")), native_int_to_str(expected)), EL_STR("|")), native_int_to_str(actual)); + 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("__arity_violations"), el_str_concat(el_str_concat(csv, entry), EL_STR(","))); + return 1; + return 0; +} + +el_val_t arity_check_call(el_val_t fn_name, el_val_t actual) { + el_val_t expected = builtin_arity(fn_name); + if (expected < 0) { + return 1; + } + if (expected == actual) { + return 1; + } + arity_record_violation(fn_name, expected, actual); + return 1; + return 0; +} + +el_val_t emit_arity_violations(void) { + el_val_t csv = state_get(EL_STR("__arity_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 p1 = str_index_of(entry, EL_STR("|")); + if (p1 > 0) { + el_val_t fn_name = str_slice(entry, 0, p1); + el_val_t rest = str_slice(entry, (p1 + 1), str_len(entry)); + el_val_t p2 = str_index_of(rest, EL_STR("|")); + if (p2 > 0) { + el_val_t exp_s = str_slice(rest, 0, p2); + el_val_t act_s = str_slice(rest, (p2 + 1), str_len(rest)); + emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("#error \"arity error: '"), fn_name), EL_STR("' takes ")), exp_s), EL_STR(" arguments, but called with ")), act_s), EL_STR("\""))); + } + } + i = ((i + next_comma) + 1); + } + return 0; +} + +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_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("__int_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + 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) { + 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("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; + return 0; +} + +el_val_t cg_fn(el_val_t stmt) { + el_val_t fn_name = el_get_field(stmt, EL_STR("name")); + if (str_eq(fn_name, EL_STR("main"))) { + return 0; + } + el_val_t params = el_get_field(stmt, EL_STR("params")); + el_val_t body = el_get_field(stmt, EL_STR("body")); + el_val_t ret_type = el_get_field(stmt, EL_STR("ret_type")); + el_val_t params_c = params_to_c(params); + el_val_t decorator = el_get_field(stmt, EL_STR("decorator")); + if (vbd_has_restricted_call(body)) { + if (!str_eq(decorator, EL_STR("manager"))) { + emit_line(el_str_concat(el_str_concat(EL_STR("#error \"VBD violation: dharma_emit/dharma_field called from non-@manager fn '"), fn_name), EL_STR("'\""))); + } + } + build_int_names_for_params(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(") {"))); + el_val_t decl = native_list_empty(); + el_val_t np = native_list_len(params); + el_val_t pi = 0; + while (pi < np) { + el_val_t param = native_list_get(params, pi); + el_val_t pname = el_get_field(param, EL_STR("name")); + decl = native_list_append(decl, pname); + pi = (pi + 1); + } + el_val_t body_xformed = body; + if (!str_eq(ret_type, EL_STR("Void"))) { + body_xformed = transform_implicit_return(body); + } + el_val_t final_decl = cg_stmts(body_xformed, EL_STR(" "), decl); + el_release(final_decl); + emit_line(EL_STR(" return 0;")); + emit_line(EL_STR("}")); + emit_blank(); + return 0; +} + +el_val_t is_fndef(el_val_t stmt) { + el_val_t kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("FnDef"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t is_top_level_decl(el_val_t stmt) { + el_val_t kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("TypeDef"))) { + return 1; + } + if (str_eq(kind, EL_STR("EnumDef"))) { + return 1; + } + if (str_eq(kind, EL_STR("Import"))) { + return 1; + } + if (str_eq(kind, EL_STR("CgiBlock"))) { + return 1; + } + if (str_eq(kind, EL_STR("ExternFn"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t cgi_arg(el_val_t value, el_val_t has_value) { + if (has_value) { + return el_str_concat(el_str_concat(EL_STR("EL_STR("), c_str_lit(value)), EL_STR(")")); + } + return EL_STR("EL_NULL"); + return 0; +} + +el_val_t vbd_is_restricted_name(el_val_t name) { + if (str_eq(name, EL_STR("dharma_emit"))) { + return 1; + } + if (str_eq(name, EL_STR("dharma_field"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t vbd_expr_has_restricted_call(el_val_t expr) { + el_val_t kind = el_get_field(expr, EL_STR("expr")); + if (str_eq(kind, EL_STR("Call"))) { + el_val_t func = el_get_field(expr, EL_STR("func")); + el_val_t fk = el_get_field(func, EL_STR("expr")); + if (str_eq(fk, EL_STR("Ident"))) { + el_val_t fname = el_get_field(func, EL_STR("name")); + if (vbd_is_restricted_name(fname)) { + return 1; + } + } + if (vbd_expr_has_restricted_call(func)) { + return 1; + } + el_val_t args = el_get_field(expr, EL_STR("args")); + el_val_t an = native_list_len(args); + el_val_t ai = 0; + while (ai < an) { + el_val_t a = native_list_get(args, ai); + if (vbd_expr_has_restricted_call(a)) { + return 1; + } + ai = (ai + 1); + } + return 0; + } + if (str_eq(kind, EL_STR("BinOp"))) { + el_val_t l = el_get_field(expr, EL_STR("left")); + el_val_t r = el_get_field(expr, EL_STR("right")); + if (vbd_expr_has_restricted_call(l)) { + return 1; + } + if (vbd_expr_has_restricted_call(r)) { + return 1; + } + return 0; + } + if (str_eq(kind, EL_STR("Not"))) { + return vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("inner"))); + } + if (str_eq(kind, EL_STR("Neg"))) { + return vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("inner"))); + } + if (str_eq(kind, EL_STR("Field"))) { + return vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("object"))); + } + if (str_eq(kind, EL_STR("Index"))) { + if (vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("object")))) { + return 1; + } + if (vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("index")))) { + return 1; + } + return 0; + } + if (str_eq(kind, EL_STR("Try"))) { + return vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("inner"))); + } + if (str_eq(kind, EL_STR("Array"))) { + el_val_t elems = el_get_field(expr, EL_STR("elems")); + el_val_t n = native_list_len(elems); + el_val_t i = 0; + while (i < n) { + el_val_t e = native_list_get(elems, i); + if (vbd_expr_has_restricted_call(e)) { + return 1; + } + i = (i + 1); + } + return 0; + } + if (str_eq(kind, EL_STR("Map"))) { + el_val_t pairs = el_get_field(expr, EL_STR("pairs")); + el_val_t n = native_list_len(pairs); + el_val_t i = 0; + while (i < n) { + el_val_t pair = native_list_get(pairs, i); + el_val_t v = el_get_field(pair, EL_STR("value")); + if (vbd_expr_has_restricted_call(v)) { + return 1; + } + i = (i + 1); + } + return 0; + } + if (str_eq(kind, EL_STR("If"))) { + if (vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("cond")))) { + return 1; + } + if (vbd_has_restricted_call(el_get_field(expr, EL_STR("then")))) { + return 1; + } + if (vbd_has_restricted_call(el_get_field(expr, EL_STR("else")))) { + return 1; + } + return 0; + } + if (str_eq(kind, EL_STR("For"))) { + if (vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("list")))) { + return 1; + } + if (vbd_has_restricted_call(el_get_field(expr, EL_STR("body")))) { + return 1; + } + return 0; + } + if (str_eq(kind, EL_STR("Match"))) { + if (vbd_expr_has_restricted_call(el_get_field(expr, EL_STR("subject")))) { + return 1; + } + el_val_t arms = el_get_field(expr, EL_STR("arms")); + el_val_t n = native_list_len(arms); + el_val_t i = 0; + while (i < n) { + el_val_t arm = native_list_get(arms, i); + el_val_t body = el_get_field(arm, EL_STR("body")); + if (vbd_expr_has_restricted_call(body)) { + return 1; + } + i = (i + 1); + } + return 0; + } + return 0; + return 0; +} + +el_val_t vbd_has_restricted_call(el_val_t stmts) { + el_val_t n = native_list_len(stmts); + 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")); + if (str_eq(sk, EL_STR("Let"))) { + if (vbd_expr_has_restricted_call(el_get_field(s, EL_STR("value")))) { + return 1; + } + } + if (str_eq(sk, EL_STR("Return"))) { + if (vbd_expr_has_restricted_call(el_get_field(s, EL_STR("value")))) { + return 1; + } + } + if (str_eq(sk, EL_STR("Expr"))) { + if (vbd_expr_has_restricted_call(el_get_field(s, EL_STR("value")))) { + return 1; + } + } + if (str_eq(sk, EL_STR("While"))) { + if (vbd_expr_has_restricted_call(el_get_field(s, EL_STR("cond")))) { + return 1; + } + if (vbd_has_restricted_call(el_get_field(s, EL_STR("body")))) { + return 1; + } + } + if (str_eq(sk, EL_STR("For"))) { + if (vbd_expr_has_restricted_call(el_get_field(s, EL_STR("list")))) { + return 1; + } + if (vbd_has_restricted_call(el_get_field(s, EL_STR("body")))) { + return 1; + } + } + i = (i + 1); + } + return 0; + return 0; +} + +el_val_t codegen(el_val_t stmts, el_val_t source) { + el_val_t n_top = native_list_len(stmts); + el_val_t cgi_count = 0; + el_val_t cgi_block = el_map_new(1, "stmt", EL_STR("None")); + el_val_t svc_count = 0; + el_val_t svc_block = el_map_new(1, "stmt", EL_STR("None")); + el_val_t ti = 0; + while (ti < n_top) { + el_val_t s = native_list_get(stmts, ti); + el_val_t sk = el_get_field(s, EL_STR("stmt")); + if (str_eq(sk, EL_STR("CgiBlock"))) { + cgi_count = (cgi_count + 1); + if (cgi_count == 1) { + cgi_block = s; + } + } + if (str_eq(sk, EL_STR("ServiceBlock"))) { + svc_count = (svc_count + 1); + if (svc_count == 1) { + svc_block = s; + } + } + ti = (ti + 1); + } + if (cgi_count > 1) { + emit_line(EL_STR("#error \"El: multiple cgi blocks in program (only one allowed)\"")); + } + if (svc_count > 1) { + emit_line(EL_STR("#error \"El: multiple service blocks in program (only one allowed)\"")); + } + 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)\"")); + } + } + el_val_t kind = EL_STR("utility"); + if (cgi_count >= 1) { + kind = EL_STR("cgi"); + } + if (svc_count >= 1) { + kind = EL_STR("service"); + } + 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; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("FnDef"))) { + el_val_t fn_name = el_get_field(stmt, EL_STR("name")); + if (!str_eq(fn_name, EL_STR("main"))) { + 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(");"))); + } + } + 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(); + el_val_t has_toplevel_lets = 0; + i = 0; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("Let"))) { + el_val_t name = el_get_field(stmt, EL_STR("name")); + el_val_t ltype = el_get_field(stmt, EL_STR("type")); + if (str_eq(ltype, EL_STR("Int"))) { + add_int_name(name); + } + el_val_t val = el_get_field(stmt, EL_STR("value")); + el_val_t vk = el_get_field(val, EL_STR("expr")); + if (str_eq(vk, EL_STR("Int"))) { + add_int_name(name); + } + emit_line(el_str_concat(el_str_concat(EL_STR("el_val_t "), name), EL_STR(";"))); + has_toplevel_lets = 1; + } + i = (i + 1); + } + 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); + if (is_fndef(stmt)) { + cg_fn(stmt); + } + i = (i + 1); + } + 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")); + el_val_t cprin = el_get_field(cgi_block, EL_STR("principal")); + el_val_t cnet = el_get_field(cgi_block, EL_STR("network")); + el_val_t ceng = el_get_field(cgi_block, EL_STR("engram")); + el_val_t has_did = el_get_field(cgi_block, EL_STR("has_dharma_id")); + el_val_t has_prin = el_get_field(cgi_block, EL_STR("has_principal")); + el_val_t has_net = el_get_field(cgi_block, EL_STR("has_network")); + el_val_t has_eng = el_get_field(cgi_block, EL_STR("has_engram")); + el_val_t arg_name = el_str_concat(el_str_concat(EL_STR("EL_STR("), c_str_lit(cname)), EL_STR(")")); + el_val_t arg_did = cgi_arg(cdid, has_did); + el_val_t arg_prin = cgi_arg(cprin, has_prin); + el_val_t arg_net = cgi_arg(cnet, has_net); + el_val_t arg_eng = cgi_arg(ceng, has_eng); + emit_line(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_cgi_init("), arg_name), EL_STR(", ")), arg_did), EL_STR(", ")), arg_prin), EL_STR(", ")), arg_net), EL_STR(", ")), arg_eng), EL_STR(");"))); + } + el_val_t main_decl = native_list_empty(); + i = 0; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("Let"))) { + el_val_t name = el_get_field(stmt, EL_STR("name")); + main_decl = native_list_append(main_decl, name); + } + 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); + if (is_fndef(stmt)) { + } else { + if (is_top_level_decl(stmt)) { + } else { + main_decl = cg_stmt(stmt, EL_STR(" "), main_decl); + } + } + 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; +} + +el_val_t cg_decl_streaming(el_val_t stmt) { + el_val_t sk = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(sk, EL_STR("FnDef"))) { + cg_fn(stmt); + return 0; + } + if (!str_eq(sk, EL_STR("FnDef"))) { + if (!is_top_level_decl(stmt)) { + if (!str_eq(sk, EL_STR("Let"))) { + state_set(EL_STR("__streaming_has_toplevel_stmts"), EL_STR("1")); + } + } + } + return 0; +} + +el_val_t emit_streaming_preamble(el_val_t sigs, el_val_t source) { + el_val_t n = native_list_len(sigs); + el_val_t cgi_count = 0; + el_val_t svc_count = 0; + el_val_t i = 0; + while (i < n) { + el_val_t sig = native_list_get(sigs, i); + el_val_t sk = el_get_field(sig, EL_STR("kind")); + if (str_eq(sk, EL_STR("cgi_block"))) { + cgi_count = (cgi_count + 1); + } + if (str_eq(sk, EL_STR("service_block"))) { + svc_count = (svc_count + 1); + } + i = (i + 1); + } + if (cgi_count > 1) { + emit_line(EL_STR("#error \"El: multiple cgi blocks in program (only one allowed)\"")); + } + if (svc_count > 1) { + emit_line(EL_STR("#error \"El: multiple service blocks in program (only one allowed)\"")); + } + 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)\"")); + } + } + el_val_t kind = EL_STR("utility"); + if (cgi_count >= 1) { + kind = EL_STR("cgi"); + } + if (svc_count >= 1) { + kind = EL_STR("service"); + } + 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\"")); + emit_blank(); + i = 0; + while (i < n) { + el_val_t sig = native_list_get(sigs, i); + el_val_t sk = el_get_field(sig, EL_STR("kind")); + if (str_eq(sk, EL_STR("fn"))) { + el_val_t fn_name = el_get_field(sig, EL_STR("name")); + if (!str_eq(fn_name, EL_STR("main"))) { + el_val_t params_c = el_get_field(sig, EL_STR("params_c")); + 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(sk, EL_STR("extern_fn"))) { + el_val_t fn_name = el_get_field(sig, EL_STR("name")); + el_val_t params_c = el_get_field(sig, EL_STR("params_c")); + 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(); + el_val_t has_toplevel_lets = 0; + i = 0; + while (i < n) { + el_val_t sig = native_list_get(sigs, i); + el_val_t sk = el_get_field(sig, EL_STR("kind")); + if (str_eq(sk, EL_STR("toplevel_let"))) { + el_val_t name = el_get_field(sig, EL_STR("name")); + el_val_t ltype = el_get_field(sig, EL_STR("ltype")); + if (str_eq(ltype, EL_STR("Int"))) { + add_int_name(name); + } + emit_line(el_str_concat(el_str_concat(EL_STR("el_val_t "), name), EL_STR(";"))); + has_toplevel_lets = 1; + } + i = (i + 1); + } + if (has_toplevel_lets) { + emit_blank(); + } + return 0; +} + +el_val_t codegen_streaming(el_val_t tokens, el_val_t sigs, el_val_t source) { + el_val_t total_tokens = (native_list_len(tokens) / 2); + el_val_t preamble_mark = el_arena_push(); + emit_streaming_preamble(sigs, source); + el_arena_pop(preamble_mark); + el_val_t has_el_main = 0; + el_val_t ns = native_list_len(sigs); + el_val_t si = 0; + while (si < ns) { + el_val_t sig = native_list_get(sigs, si); + el_val_t sk2 = el_get_field(sig, EL_STR("kind")); + if (str_eq(sk2, EL_STR("fn"))) { + el_val_t fn_name_chk = el_get_field(sig, EL_STR("name")); + if (str_eq(fn_name_chk, EL_STR("main"))) { + has_el_main = 1; + } + } + si = (si + 1); + } + el_val_t toplevel_let_names = native_list_empty(); + si = 0; + while (si < ns) { + el_val_t sig = native_list_get(sigs, si); + el_val_t sk2 = el_get_field(sig, EL_STR("kind")); + if (str_eq(sk2, EL_STR("toplevel_let"))) { + el_val_t tname = el_get_field(sig, EL_STR("name")); + toplevel_let_names = native_list_append(toplevel_let_names, tname); + } + si = (si + 1); + } + el_val_t test_is_mode = 0; + el_val_t tmode_str = state_get(EL_STR("__test_mode")); + if (str_eq(tmode_str, EL_STR("1"))) { + test_is_mode = 1; + } + el_val_t test_names = native_list_empty(); + el_val_t test_c_names = native_list_empty(); + if (test_is_mode) { + emit_line(EL_STR("#include ")); + emit_blank(); + emit_line(EL_STR("static int __el_pass = 0, __el_fail = 0;")); + emit_line(EL_STR("static const char *__el_cur_test = \"(none)\";")); + emit_line(EL_STR("static void __el_test_fail(const char *test, const char *msg) {")); + emit_line(EL_STR(" fprintf(stderr, \"FAIL %-40s %s\\n\", test, msg);")); + emit_line(EL_STR("}")); + emit_blank(); + } + el_val_t pos = 0; + el_val_t el_main_body = native_list_empty(); + el_val_t toplevel_exec_stmts = native_list_empty(); + el_val_t has_toplevel_exec = 0; + el_val_t stream_running = 1; + while (stream_running) { + if (pos >= total_tokens) { + stream_running = 0; + } else { + el_val_t k = tok_kind(tokens, pos); + if (str_eq(k, EL_STR("Eof"))) { + stream_running = 0; + } else { + if (str_eq(k, EL_STR("Test"))) { + if (test_is_mode) { + el_val_t p = (pos + 1); + el_val_t test_name = EL_STR("unnamed"); + if (str_eq(tok_kind(tokens, p), EL_STR("Str"))) { + test_name = tok_value(tokens, p); + p = (p + 1); + } + el_val_t fn_c_name = el_str_concat(EL_STR("__el_test_"), sanitize_test_name(test_name)); + test_names = native_list_append(test_names, test_name); + test_c_names = native_list_append(test_c_names, fn_c_name); + emit_line(el_str_concat(el_str_concat(EL_STR("static void "), fn_c_name), EL_STR("(void) {"))); + emit_line(el_str_concat(el_str_concat(EL_STR(" __el_cur_test = \""), c_escape(test_name)), EL_STR("\";"))); + if (str_eq(tok_kind(tokens, p), EL_STR("LBrace"))) { + p = (p + 1); + } + el_val_t body_decl = native_list_empty(); + el_val_t body_done = 0; + while (!body_done) { + el_val_t bk = tok_kind(tokens, p); + if (str_eq(bk, EL_STR("RBrace"))) { + body_done = 1; + } else { + if (str_eq(bk, EL_STR("Eof"))) { + body_done = 1; + } else { + el_val_t br = parse_one(tokens, p); + el_val_t bstmt = el_get_field(br, EL_STR("node")); + el_val_t np = el_get_field(br, EL_STR("pos")); + el_release(br); + if (np > p) { + el_val_t body_arena = el_arena_push(); + body_decl = cg_stmt(bstmt, EL_STR(" "), body_decl); + el_arena_pop(body_arena); + el_release(bstmt); + p = np; + } else { + p = (p + 1); + } + } + } + } + if (str_eq(tok_kind(tokens, p), EL_STR("RBrace"))) { + p = (p + 1); + } + el_release(body_decl); + emit_line(EL_STR("}")); + emit_blank(); + pos = p; + } else { + el_val_t p = (pos + 1); + el_val_t k_name = tok_kind(tokens, p); + if (str_eq(k_name, EL_STR("Str"))) { + p = (p + 1); + } + el_val_t k_body = tok_kind(tokens, p); + if (str_eq(k_body, EL_STR("LBrace"))) { + p = skip_to_rbrace(tokens, p); + } + pos = p; + } + } else { + el_val_t r = parse_one(tokens, pos); + el_val_t stmt = el_get_field(r, EL_STR("node")); + el_val_t new_pos = el_get_field(r, EL_STR("pos")); + el_release(r); + if (new_pos <= pos) { + el_release(stmt); + pos = (pos + 1); + } else { + el_val_t sk = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(sk, EL_STR("FnDef"))) { + el_val_t fn_name2 = el_get_field(stmt, EL_STR("name")); + if (str_eq(fn_name2, 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); + } + el_release(stmt); + } else { + el_val_t fn_arena_mark = el_arena_push(); + cg_fn(stmt); + el_release(stmt); + el_arena_pop(fn_arena_mark); + } + } else { + if (is_top_level_decl(stmt)) { + el_release(stmt); + } else { + if (str_eq(sk, EL_STR("Let"))) { + toplevel_exec_stmts = native_list_append(toplevel_exec_stmts, stmt); + has_toplevel_exec = 1; + } else { + toplevel_exec_stmts = native_list_append(toplevel_exec_stmts, stmt); + has_toplevel_exec = 1; + } + } + } + pos = new_pos; + } + } + } + } + } + el_release(tokens); + if (test_is_mode) { + el_release(el_main_body); + el_release(toplevel_exec_stmts); + el_release(toplevel_let_names); + el_release(sigs); + el_val_t test_arena_mark = el_arena_push(); + emit_line(EL_STR("int main(int _argc, char **_argv) {")); + emit_line(EL_STR(" el_runtime_init_args(_argc, _argv);")); + el_val_t ti = 0; + el_val_t tn = native_list_len(test_c_names); + while (ti < tn) { + el_val_t tc_name = native_list_get(test_c_names, ti); + emit_line(el_str_concat(el_str_concat(EL_STR(" "), tc_name), EL_STR("();"))); + ti = (ti + 1); + } + emit_line(EL_STR(" printf(\"%d passed, %d failed\\n\", __el_pass, __el_fail);")); + emit_line(EL_STR(" return __el_fail;")); + emit_line(EL_STR("}")); + el_arena_pop(test_arena_mark); + el_release(test_names); + el_release(test_c_names); + return EL_STR(""); + } + el_release(test_names); + el_release(test_c_names); + el_val_t is_library = 0; + if (!has_el_main) { + if (!has_toplevel_exec) { + is_library = 1; + } + } + if (is_library) { + return EL_STR(""); + } + el_val_t main_arena_mark = el_arena_push(); + el_val_t kind2 = state_get(EL_STR("__program_kind")); + emit_line(EL_STR("int main(int _argc, char** _argv) {")); + emit_line(EL_STR(" el_runtime_init_args(_argc, _argv);")); + el_val_t ns2 = native_list_len(sigs); + el_val_t si2 = 0; + while (si2 < ns2) { + el_val_t sig2 = native_list_get(sigs, si2); + el_val_t sk3 = el_get_field(sig2, EL_STR("kind")); + if (str_eq(sk3, EL_STR("cgi_block"))) { + el_val_t tes_n = native_list_len(toplevel_exec_stmts); + el_val_t tes_i = 0; + while (tes_i < tes_n) { + el_val_t tes = native_list_get(toplevel_exec_stmts, tes_i); + el_val_t tes_k = el_get_field(tes, EL_STR("stmt")); + if (str_eq(tes_k, EL_STR("CgiBlock"))) { + el_val_t cname2 = el_get_field(tes, EL_STR("name")); + el_val_t cdid2 = el_get_field(tes, EL_STR("dharma_id")); + el_val_t cprin2 = el_get_field(tes, EL_STR("principal")); + el_val_t cnet2 = el_get_field(tes, EL_STR("network")); + el_val_t ceng2 = el_get_field(tes, EL_STR("engram")); + el_val_t has_did2 = el_get_field(tes, EL_STR("has_dharma_id")); + el_val_t has_prin2 = el_get_field(tes, EL_STR("has_principal")); + el_val_t has_net2 = el_get_field(tes, EL_STR("has_network")); + el_val_t has_eng2 = el_get_field(tes, EL_STR("has_engram")); + el_val_t arg_name2 = el_str_concat(el_str_concat(EL_STR("EL_STR("), c_str_lit(cname2)), EL_STR(")")); + el_val_t arg_did2 = cgi_arg(cdid2, has_did2); + el_val_t arg_prin2 = cgi_arg(cprin2, has_prin2); + el_val_t arg_net2 = cgi_arg(cnet2, has_net2); + el_val_t arg_eng2 = cgi_arg(ceng2, has_eng2); + emit_line(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_cgi_init("), arg_name2), EL_STR(", ")), arg_did2), EL_STR(", ")), arg_prin2), EL_STR(", ")), arg_net2), EL_STR(", ")), arg_eng2), EL_STR(");"))); + } + tes_i = (tes_i + 1); + } + } + si2 = (si2 + 1); + } + el_release(sigs); + el_val_t main_decl2 = native_list_empty(); + el_val_t tln = native_list_len(toplevel_let_names); + el_val_t tli = 0; + while (tli < tln) { + main_decl2 = native_list_append(main_decl2, native_list_get(toplevel_let_names, tli)); + tli = (tli + 1); + } + el_release(toplevel_let_names); + el_val_t tes_n2 = native_list_len(toplevel_exec_stmts); + el_val_t tes_i2 = 0; + while (tes_i2 < tes_n2) { + el_val_t tes2 = native_list_get(toplevel_exec_stmts, tes_i2); + el_val_t tes_k2 = el_get_field(tes2, EL_STR("stmt")); + if (!str_eq(tes_k2, EL_STR("CgiBlock"))) { + if (!str_eq(tes_k2, EL_STR("ServiceBlock"))) { + el_val_t tes_mark = el_arena_push(); + main_decl2 = cg_stmt(tes2, EL_STR(" "), main_decl2); + el_arena_pop(tes_mark); + } + } + tes_i2 = (tes_i2 + 1); + } + el_release(toplevel_exec_stmts); + 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); + el_val_t stmt_mark = el_arena_push(); + main_decl2 = cg_stmt(mstmt, EL_STR(" "), main_decl2); + el_arena_pop(stmt_mark); + mi = (mi + 1); + } + el_release(el_main_body); + el_release(main_decl2); + emit_line(EL_STR(" return 0;")); + emit_line(EL_STR("}")); + emit_blank(); + emit_cap_violations(); + emit_arity_violations(); + emit_time_violations(); + el_arena_pop(main_arena_mark); + return EL_STR(""); + return 0; +} + +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 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("\""))) { + parts = native_list_append(parts, EL_STR("\\\"")); + } else { + if (str_eq(ch, EL_STR("\\"))) { + parts = native_list_append(parts, EL_STR("\\\\")); + } else { + if (str_eq(ch, EL_STR("\n"))) { + parts = native_list_append(parts, EL_STR("\\n")); + } else { + if (str_eq(ch, EL_STR("\r"))) { + parts = native_list_append(parts, EL_STR("\\r")); + } else { + if (str_eq(ch, EL_STR("\t"))) { + parts = native_list_append(parts, EL_STR("\\t")); + } else { + parts = native_list_append(parts, ch); + } + } + } + } + } + i = (i + 1); + } + return str_join(parts, EL_STR("")); + return 0; +} + +el_val_t js_str_lit(el_val_t s) { + return el_str_concat(el_str_concat(EL_STR("\""), js_escape(s)), EL_STR("\"")); + return 0; +} + +el_val_t js_emit_line(el_val_t line) { + println(line); + return 0; +} + +el_val_t js_emit_blank(void) { + println(EL_STR("")); + return 0; +} + +el_val_t js_binop(el_val_t op) { + if (str_eq(op, EL_STR("Plus"))) { + return EL_STR("+"); + } + if (str_eq(op, EL_STR("Minus"))) { + return EL_STR("-"); + } + if (str_eq(op, EL_STR("Star"))) { + return EL_STR("*"); + } + if (str_eq(op, EL_STR("Slash"))) { + return EL_STR("/"); + } + if (str_eq(op, EL_STR("Percent"))) { + return EL_STR("%"); + } + if (str_eq(op, EL_STR("EqEq"))) { + return EL_STR("==="); + } + if (str_eq(op, EL_STR("NotEq"))) { + return EL_STR("!=="); + } + if (str_eq(op, EL_STR("Lt"))) { + return EL_STR("<"); + } + if (str_eq(op, EL_STR("Gt"))) { + return EL_STR(">"); + } + if (str_eq(op, EL_STR("LtEq"))) { + return EL_STR("<="); + } + if (str_eq(op, EL_STR("GtEq"))) { + return EL_STR(">="); + } + if (str_eq(op, EL_STR("And"))) { + return EL_STR("&&"); + } + if (str_eq(op, EL_STR("Or"))) { + return EL_STR("||"); + } + return op; + return 0; +} + +el_val_t js_is_el_method(el_val_t name) { + if (str_eq(name, EL_STR("append"))) { + return 1; + } + if (str_eq(name, EL_STR("len"))) { + return 1; + } + if (str_eq(name, EL_STR("get"))) { + return 1; + } + if (str_eq(name, EL_STR("map_get"))) { + return 1; + } + if (str_eq(name, EL_STR("map_set"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t js_is_async_builtin(el_val_t name) { + if (str_eq(name, EL_STR("http_get"))) { + return 1; + } + if (str_eq(name, EL_STR("http_post"))) { + return 1; + } + if (str_eq(name, EL_STR("http_post_json"))) { + return 1; + } + if (str_eq(name, EL_STR("http_get_with_headers"))) { + return 1; + } + if (str_eq(name, EL_STR("http_post_with_headers"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t js_register_async_fn(el_val_t name) { + el_val_t csv = state_get(EL_STR("__js_async_fns")); + 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("__js_async_fns"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t js_is_async_fn(el_val_t name) { + el_val_t csv = state_get(EL_STR("__js_async_fns")); + 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 js_is_int_name(el_val_t name) { + el_val_t csv = state_get(EL_STR("__js_int_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 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_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("__js_int_names"), el_str_concat(el_str_concat(csv, name), EL_STR(","))); + return 1; + return 0; +} + +el_val_t js_build_int_names_for_params(el_val_t params) { + state_set(EL_STR("__js_int_names"), EL_STR(",")); + el_val_t np = native_list_len(params); + el_val_t pi = 0; + 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("Int"))) { + js_add_int_name(pname); + } + pi = (pi + 1); + } + return 1; + return 0; +} + +el_val_t js_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")); + 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("str_len"))) { + return 1; + } + if (str_eq(name, EL_STR("str_index_of"))) { + return 1; + } + if (str_eq(name, EL_STR("str_to_int"))) { + return 1; + } + if (str_eq(name, EL_STR("str_char_code"))) { + return 1; + } + if (str_eq(name, EL_STR("native_list_len"))) { + return 1; + } + if (str_eq(name, EL_STR("el_list_len"))) { + return 1; + } + if (str_eq(name, EL_STR("len"))) { + return 1; + } + if (str_eq(name, EL_STR("json_get_int"))) { + return 1; + } + if (str_eq(name, EL_STR("time_now"))) { + return 1; + } + if (str_eq(name, EL_STR("time_now_utc"))) { + return 1; + } + if (str_eq(name, EL_STR("el_abs"))) { + return 1; + } + if (str_eq(name, EL_STR("el_max"))) { + return 1; + } + if (str_eq(name, EL_STR("el_min"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t js_next_html_id(void) { + el_val_t csv = state_get(EL_STR("__js_html_counter")); + el_val_t n = 0; + if (!str_eq(csv, EL_STR(""))) { + n = str_to_int(csv); + } + n = (n + 1); + state_set(EL_STR("__js_html_counter"), native_int_to_str(n)); + return native_int_to_str(n); + return 0; +} + +el_val_t js_cg_html_parts(el_val_t children, el_val_t acc_var) { + el_val_t n = native_list_len(children); + el_val_t i = 0; + el_val_t out = EL_STR(""); + while (i < n) { + el_val_t child = native_list_get(children, i); + el_val_t html_kind = el_get_field(child, EL_STR("html")); + if (str_eq(html_kind, EL_STR("Text"))) { + el_val_t text = el_get_field(child, EL_STR("text")); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += ")), js_str_lit(text)), EL_STR("; ")); + } + if (str_eq(html_kind, EL_STR("Doctype"))) { + out = el_str_concat(el_str_concat(out, acc_var), EL_STR(" += \"\"; ")); + } + if (str_eq(html_kind, EL_STR("Interp"))) { + el_val_t val_node = el_get_field(child, EL_STR("value")); + el_val_t val_c = js_cg_expr(val_node); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += html_escape(")), val_c), EL_STR("); ")); + } + if (str_eq(html_kind, EL_STR("Raw"))) { + el_val_t val_node = el_get_field(child, EL_STR("value")); + el_val_t val_c = js_cg_expr(val_node); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += html_raw(")), val_c), EL_STR("); ")); + } + if (str_eq(html_kind, EL_STR("Element"))) { + el_val_t elem_c = js_cg_html_element_str(child, acc_var); + out = el_str_concat(out, elem_c); + } + if (str_eq(html_kind, EL_STR("Each"))) { + el_val_t each_c = js_cg_html_each(child, acc_var); + out = el_str_concat(out, each_c); + } + i = (i + 1); + } + return out; + return 0; +} + +el_val_t js_cg_html_attrs_str(el_val_t attrs, el_val_t acc_var) { + el_val_t n = native_list_len(attrs); + el_val_t i = 0; + el_val_t out = EL_STR(""); + while (i < n) { + el_val_t attr = native_list_get(attrs, i); + el_val_t attr_name = el_get_field(attr, EL_STR("name")); + el_val_t kind = el_get_field(attr, EL_STR("kind")); + el_val_t open_val = el_str_concat(el_str_concat(EL_STR(" "), attr_name), EL_STR("=\"")); + if (str_eq(kind, EL_STR("static"))) { + el_val_t sv = el_get_field(attr, EL_STR("value")); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += ")), js_str_lit(open_val)), EL_STR("; ")); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += ")), js_str_lit(sv)), EL_STR("; ")); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += ")), js_str_lit(EL_STR("\""))), EL_STR("; ")); + } else { + if (str_eq(kind, EL_STR("dynamic"))) { + el_val_t val_node = el_get_field(attr, EL_STR("value")); + el_val_t val_c = js_cg_expr(val_node); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += ")), js_str_lit(open_val)), EL_STR("; ")); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += html_escape(")), val_c), EL_STR("); ")); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += ")), js_str_lit(EL_STR("\""))), EL_STR("; ")); + } else { + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += ")), js_str_lit(el_str_concat(EL_STR(" "), attr_name))), EL_STR("; ")); + } + } + i = (i + 1); + } + return out; + return 0; +} + +el_val_t js_cg_html_element_str(el_val_t elem, el_val_t acc_var) { + el_val_t tag = el_get_field(elem, EL_STR("tag")); + el_val_t attrs = el_get_field(elem, EL_STR("attrs")); + el_val_t children = el_get_field(elem, EL_STR("children")); + el_val_t self_closing = el_get_field(elem, EL_STR("self_closing")); + el_val_t out = el_str_concat(el_str_concat(el_str_concat(acc_var, EL_STR(" += ")), js_str_lit(el_str_concat(EL_STR("<"), tag))), EL_STR("; ")); + out = el_str_concat(out, js_cg_html_attrs_str(attrs, acc_var)); + if (self_closing) { + out = el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += \"/>\"")), EL_STR("; ")); + } else { + out = el_str_concat(el_str_concat(out, acc_var), EL_STR(" += \">\"; ")); + out = el_str_concat(out, js_cg_html_parts(children, acc_var)); + out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, acc_var), EL_STR(" += ")), js_str_lit(el_str_concat(el_str_concat(EL_STR("")))), EL_STR("; ")); + } + return out; + return 0; +} + +el_val_t js_cg_html_each(el_val_t node, el_val_t acc_var) { + el_val_t list_expr = el_get_field(node, EL_STR("list")); + el_val_t item_name = el_get_field(node, EL_STR("item")); + el_val_t body_children = el_get_field(node, EL_STR("body")); + el_val_t id = js_next_html_id(); + el_val_t list_var = el_str_concat(EL_STR("_html_list_"), id); + el_val_t len_var = el_str_concat(EL_STR("_html_len_"), id); + el_val_t idx_var = el_str_concat(EL_STR("_html_i_"), id); + el_val_t list_c = js_cg_expr(list_expr); + el_val_t inner_c = js_cg_html_parts(body_children, acc_var); + return 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(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(el_str_concat(el_str_concat(EL_STR("{ const "), list_var), EL_STR(" = ")), list_c), EL_STR("; const ")), len_var), EL_STR(" = el_list_len(")), list_var), EL_STR("); for (let ")), idx_var), EL_STR(" = 0; ")), idx_var), EL_STR(" < ")), len_var), EL_STR("; ")), idx_var), EL_STR("++) { const ")), item_name), EL_STR(" = el_list_get(")), list_var), EL_STR(", ")), idx_var), EL_STR("); ")), inner_c), EL_STR("} } ")); + return 0; +} + +el_val_t js_cg_html_template(el_val_t expr) { + el_val_t root = el_get_field(expr, EL_STR("root")); + el_val_t id = js_next_html_id(); + el_val_t acc = el_str_concat(EL_STR("_html_"), id); + el_val_t doctype_flag = el_get_field(root, EL_STR("doctype")); + el_val_t doctype_prefix = EL_STR(""); + if (doctype_flag) { + doctype_prefix = el_str_concat(acc, EL_STR(" += \"\"; ")); + } + el_val_t body = js_cg_html_element_str(root, acc); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("(() => { let "), acc), EL_STR(" = \"\"; ")), doctype_prefix), body), EL_STR("return ")), acc), EL_STR("; })()")); + return 0; +} + +el_val_t js_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_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; + } + if (str_eq(kind, EL_STR("Str"))) { + el_val_t v = el_get_field(expr, EL_STR("value")); + return js_str_lit(v); + } + if (str_eq(kind, EL_STR("Bool"))) { + el_val_t v = el_get_field(expr, EL_STR("value")); + if (str_eq(v, EL_STR("true"))) { + return EL_STR("true"); + } + return EL_STR("false"); + } + if (str_eq(kind, EL_STR("Nil"))) { + return EL_STR("null"); + } + if (str_eq(kind, EL_STR("Ident"))) { + el_val_t name = el_get_field(expr, EL_STR("name")); + return name; + } + if (str_eq(kind, EL_STR("Not"))) { + el_val_t inner = el_get_field(expr, EL_STR("inner")); + el_val_t inner_c = js_cg_expr(inner); + return el_str_concat(EL_STR("!"), inner_c); + } + if (str_eq(kind, EL_STR("Neg"))) { + el_val_t inner = el_get_field(expr, EL_STR("inner")); + el_val_t inner_c = js_cg_expr(inner); + return el_str_concat(el_str_concat(EL_STR("(-"), inner_c), EL_STR(")")); + } + if (str_eq(kind, EL_STR("BinOp"))) { + el_val_t op = el_get_field(expr, EL_STR("op")); + el_val_t left = el_get_field(expr, EL_STR("left")); + el_val_t right = el_get_field(expr, EL_STR("right")); + el_val_t left_c = js_cg_expr(left); + el_val_t right_c = js_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(left_kind, EL_STR("Int"))) { + 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(right_kind, EL_STR("Int"))) { + 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("Ident"))) { + if (str_eq(right_kind, EL_STR("Ident"))) { + el_val_t lname = el_get_field(left, EL_STR("name")); + el_val_t rname = el_get_field(right, EL_STR("name")); + if (js_is_int_name(lname)) { + if (js_is_int_name(rname)) { + 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("Ident"))) { + if (str_eq(right_kind, EL_STR("Call"))) { + el_val_t lname = el_get_field(left, EL_STR("name")); + if (js_is_int_name(lname)) { + if (js_is_int_call(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(right_kind, EL_STR("Ident"))) { + if (str_eq(left_kind, EL_STR("Call"))) { + el_val_t rname = el_get_field(right, EL_STR("name")); + if (js_is_int_name(rname)) { + if (js_is_int_call(left)) { + 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("Call"))) { + if (str_eq(right_kind, EL_STR("Call"))) { + if (js_is_int_call(left)) { + if (js_is_int_call(right)) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" + ")), right_c), EL_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("Call"))) { + 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(left_kind, EL_STR("Ident"))) { + 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("Ident"))) { + 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("Int"))) { + 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(right_kind, EL_STR("Int"))) { + 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("Bool"))) { + 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(right_kind, EL_STR("Bool"))) { + 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("Nil"))) { + 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(right_kind, EL_STR("Nil"))) { + 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("Ident"))) { + if (str_eq(right_kind, EL_STR("Ident"))) { + el_val_t lname = el_get_field(left, EL_STR("name")); + el_val_t rname = el_get_field(right, EL_STR("name")); + if (js_is_int_name(lname)) { + if (js_is_int_name(rname)) { + 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(")")); + } + 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(")")); + } + 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(op, EL_STR("NotEq"))) { + if (str_eq(left_kind, EL_STR("Int"))) { + 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(right_kind, EL_STR("Int"))) { + 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("Bool"))) { + 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(right_kind, EL_STR("Bool"))) { + 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("Nil"))) { + 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(right_kind, EL_STR("Nil"))) { + 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("Ident"))) { + if (str_eq(right_kind, EL_STR("Ident"))) { + el_val_t lname = el_get_field(left, EL_STR("name")); + el_val_t rname = el_get_field(right, EL_STR("name")); + if (js_is_int_name(lname)) { + if (js_is_int_name(rname)) { + 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(")")); + } + 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(")")); + } + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" !== ")), right_c), EL_STR(")")); + } + el_val_t op_c = js_binop(op); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), left_c), EL_STR(" ")), op_c), EL_STR(" ")), right_c), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Call"))) { + el_val_t func = el_get_field(expr, EL_STR("func")); + 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_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); + 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")); + el_val_t call_expr = el_str_concat(el_str_concat(el_str_concat(fn_name, EL_STR("(")), args_c), EL_STR(")")); + if (js_is_async_builtin(fn_name)) { + return el_str_concat(EL_STR("await "), call_expr); + } + if (js_is_async_fn(fn_name)) { + return el_str_concat(EL_STR("await "), call_expr); + } + return call_expr; + } + if (str_eq(func_kind, EL_STR("Field"))) { + el_val_t obj = el_get_field(func, EL_STR("object")); + el_val_t field = el_get_field(func, EL_STR("field")); + el_val_t obj_c = js_cg_expr(obj); + if (js_is_el_method(field)) { + if (arity > 0) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(field, EL_STR("(")), obj_c), EL_STR(", ")), args_c), EL_STR(")")); + } + return el_str_concat(el_str_concat(el_str_concat(field, EL_STR("(")), obj_c), EL_STR(")")); + } + if (arity > 0) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(obj_c, EL_STR(".")), field), EL_STR("(")), args_c), EL_STR(")")); + } + return el_str_concat(el_str_concat(el_str_concat(obj_c, EL_STR(".")), field), EL_STR("()")); + } + el_val_t fn_c = js_cg_expr(func); + return el_str_concat(el_str_concat(el_str_concat(fn_c, EL_STR("(")), args_c), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Field"))) { + el_val_t obj = el_get_field(expr, EL_STR("object")); + el_val_t field = el_get_field(expr, EL_STR("field")); + el_val_t obj_kind = el_get_field(obj, EL_STR("expr")); + if (str_eq(obj_kind, EL_STR("Try"))) { + el_val_t inner = el_get_field(obj, EL_STR("inner")); + el_val_t inner_c = js_cg_expr(inner); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), inner_c), EL_STR(")?.[")), js_str_lit(field)), EL_STR("] ?? null")); + } + el_val_t obj_c = js_cg_expr(obj); + return el_str_concat(el_str_concat(el_str_concat(obj_c, EL_STR("[")), js_str_lit(field)), EL_STR("]")); + } + if (str_eq(kind, EL_STR("Index"))) { + el_val_t obj = el_get_field(expr, EL_STR("object")); + el_val_t idx = el_get_field(expr, EL_STR("index")); + el_val_t obj_c = js_cg_expr(obj); + el_val_t idx_c = js_cg_expr(idx); + el_val_t idx_kind = el_get_field(idx, EL_STR("expr")); + el_val_t obj_kind = el_get_field(obj, EL_STR("expr")); + if (str_eq(obj_kind, EL_STR("Try"))) { + el_val_t inner = el_get_field(obj, EL_STR("inner")); + el_val_t inner_c = js_cg_expr(inner); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("("), inner_c), EL_STR(")?.[")), idx_c), EL_STR("] ?? null")); + } + if (str_eq(idx_kind, EL_STR("Str"))) { + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_get_field("), obj_c), EL_STR(", ")), idx_c), EL_STR(")")); + } + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_list_get("), obj_c), EL_STR(", ")), idx_c), EL_STR(")")); + } + if (str_eq(kind, EL_STR("Array"))) { + el_val_t elems = el_get_field(expr, EL_STR("elems")); + el_val_t n = native_list_len(elems); + if (n == 0) { + return 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); + items_parts = native_list_append(items_parts, elem_c); + i = (i + 1); + } + 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")); + el_val_t n = native_list_len(pairs); + if (n == 0) { + return 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); + 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("{"), 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")); + return js_cg_expr(inner); + } + if (str_eq(kind, EL_STR("If"))) { + el_val_t cond = el_get_field(expr, EL_STR("cond")); + el_val_t cond_c = js_cg_expr(cond); + return el_str_concat(el_str_concat(EL_STR("("), cond_c), EL_STR(" ? 1 : 0)")); + } + if (str_eq(kind, EL_STR("Match"))) { + return js_cg_match(expr); + } + if (str_eq(kind, EL_STR("Lambda"))) { + return js_cg_lambda(expr); + } + if (str_eq(kind, EL_STR("HtmlTemplate"))) { + return js_cg_html_template(expr); + } + return EL_STR("null"); + return 0; +} + +el_val_t js_next_match_id(void) { + el_val_t csv = state_get(EL_STR("__js_match_counter")); + el_val_t n = 0; + if (!str_eq(csv, EL_STR(""))) { + n = str_to_int(csv); + } + n = (n + 1); + state_set(EL_STR("__js_match_counter"), native_int_to_str(n)); + return native_int_to_str(n); + return 0; +} + +el_val_t js_cg_match(el_val_t expr) { + el_val_t subject = el_get_field(expr, EL_STR("subject")); + el_val_t arms = el_get_field(expr, EL_STR("arms")); + 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 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) { + el_val_t arm = native_list_get(arms, i); + el_val_t pat = el_get_field(arm, EL_STR("pattern")); + el_val_t body = el_get_field(arm, EL_STR("body")); + 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"))) { + 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")); + 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")); + 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")); + 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")); + el_val_t bv = EL_STR("false"); + if (str_eq(v, EL_STR("true"))) { + bv = EL_STR("true"); + } + 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 { + if (str_eq(pkind, EL_STR("Variant"))) { + el_val_t variant = el_get_field(pat, EL_STR("variant")); + 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(variant)), EL_STR(")) return (")), body_c), EL_STR("); "))); + } else { + parts = native_list_append(parts, el_str_concat(el_str_concat(EL_STR("return ("), body_c), EL_STR("); "))); + } + } + } + } + } + } + i = (i + 1); + } + 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; +} + +el_val_t js_next_lambda_id(void) { + el_val_t csv = state_get(EL_STR("__js_lambda_counter")); + el_val_t n = 0; + if (!str_eq(csv, EL_STR(""))) { + n = str_to_int(csv); + } + n = (n + 1); + state_set(EL_STR("__js_lambda_counter"), native_int_to_str(n)); + return native_int_to_str(n); + return 0; +} + +el_val_t js_cg_lambda(el_val_t expr) { + el_val_t params = el_get_field(expr, EL_STR("params")); + el_val_t body = el_get_field(expr, EL_STR("body")); + el_val_t ret_type = el_get_field(expr, EL_STR("ret_type")); + el_val_t id = js_next_lambda_id(); + el_val_t lambda_name = el_str_concat(EL_STR("__lambda_"), id); + el_val_t params_str = js_params_str(params); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("function "), lambda_name), EL_STR("(")), params_str), EL_STR(") {"))); + el_val_t decl = native_list_empty(); + el_val_t np = native_list_len(params); + el_val_t pi = 0; + while (pi < np) { + el_val_t param = native_list_get(params, pi); + el_val_t pname = el_get_field(param, EL_STR("name")); + decl = native_list_append(decl, pname); + pi = (pi + 1); + } + el_val_t body_xformed = body; + if (!str_eq(ret_type, EL_STR("Void"))) { + body_xformed = js_transform_implicit_return(body); + } + js_build_int_names_for_params(params); + js_cg_stmts(body_xformed, EL_STR(" "), decl); + js_emit_line(EL_STR("}")); + js_emit_blank(); + return lambda_name; + return 0; +} + +el_val_t js_list_contains(el_val_t lst, el_val_t s) { + el_val_t n = native_list_len(lst); + el_val_t i = 0; + while (i < n) { + el_val_t item = native_list_get(lst, i); + if (str_eq(item, s)) { + return 1; + } + i = (i + 1); + } + return 0; + return 0; +} + +el_val_t js_cg_stmt(el_val_t stmt, el_val_t indent, el_val_t declared) { + el_val_t kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("Let"))) { + 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); + el_val_t ltype = el_get_field(stmt, EL_STR("type")); + if (str_eq(ltype, EL_STR("Int"))) { + js_add_int_name(name); + } + el_val_t vk = el_get_field(val, EL_STR("expr")); + if (str_eq(vk, EL_STR("Int"))) { + js_add_int_name(name); + } + if (js_list_contains(declared, name)) { + 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; + } else { + js_emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("let ")), name), EL_STR(" = ")), val_c), EL_STR(";"))); + return native_list_append(declared, name); + } + } + if (str_eq(kind, EL_STR("Return"))) { + el_val_t val = el_get_field(stmt, EL_STR("value")); + el_val_t val_kind = el_get_field(val, EL_STR("expr")); + if (str_eq(val_kind, EL_STR("Nil"))) { + js_emit_line(el_str_concat(indent, EL_STR("return null;"))); + } else { + el_val_t val_c = js_cg_expr(val); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("return ")), val_c), EL_STR(";"))); + } + 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")); + if (str_eq(val_kind, EL_STR("If"))) { + js_cg_if_stmt(val, indent, declared); + return declared; + } + if (str_eq(val_kind, EL_STR("For"))) { + js_cg_for_stmt(val, indent, declared); + return declared; + } + el_val_t val_c = js_cg_expr(val); + js_emit_line(el_str_concat(el_str_concat(indent, val_c), EL_STR(";"))); + return declared; + } + if (str_eq(kind, EL_STR("While"))) { + el_val_t cond = el_get_field(stmt, EL_STR("cond")); + el_val_t body = el_get_field(stmt, EL_STR("body")); + el_val_t cond_c = js_cg_expr(cond); + cond_c = js_strip_outer_parens(cond_c); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("while (")), cond_c), EL_STR(") {"))); + js_cg_stmts(body, el_str_concat(indent, EL_STR(" ")), native_list_clone(declared)); + js_emit_line(el_str_concat(indent, EL_STR("}"))); + return declared; + } + if (str_eq(kind, EL_STR("For"))) { + el_val_t item = el_get_field(stmt, EL_STR("item")); + el_val_t list_expr = el_get_field(stmt, EL_STR("list")); + el_val_t body = el_get_field(stmt, EL_STR("body")); + js_cg_for_body(item, list_expr, body, indent, declared); + return declared; + } + if (str_eq(kind, EL_STR("FnDef"))) { + return declared; + } + if (str_eq(kind, EL_STR("TypeDef"))) { + return declared; + } + if (str_eq(kind, EL_STR("EnumDef"))) { + return declared; + } + if (str_eq(kind, EL_STR("Import"))) { + return declared; + } + if (str_eq(kind, EL_STR("TryCatch"))) { + el_val_t try_body = el_get_field(stmt, EL_STR("try_body")); + el_val_t catch_name = el_get_field(stmt, EL_STR("catch_name")); + el_val_t catch_body = el_get_field(stmt, EL_STR("catch_body")); + js_emit_line(el_str_concat(indent, EL_STR("try {"))); + js_cg_stmts(try_body, el_str_concat(indent, EL_STR(" ")), native_list_clone(declared)); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("} catch (")), catch_name), EL_STR(") {"))); + js_cg_stmts(catch_body, el_str_concat(indent, EL_STR(" ")), native_list_clone(declared)); + js_emit_line(el_str_concat(indent, EL_STR("}"))); + return declared; + } + if (str_eq(kind, EL_STR("ExternFn"))) { + el_val_t ename = el_get_field(stmt, EL_STR("name")); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("// extern fn ")), ename), EL_STR(" \xe2\x80\x94 provided by the JS environment"))); + return 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("' \xe2\x80\x94 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("' \xe2\x80\x94 no-op in JS target"))); + return declared; + } + return declared; + return 0; +} + +el_val_t js_strip_outer_parens(el_val_t s) { + el_val_t chars = native_string_chars(s); + el_val_t n = native_list_len(chars); + if (n < 2) { + return s; + } + el_val_t first = native_list_get(chars, 0); + el_val_t last = native_list_get(chars, (n - 1)); + if (str_eq(first, EL_STR("("))) { + if (str_eq(last, EL_STR(")"))) { + el_val_t depth = 1; + el_val_t i = 1; + el_val_t balanced = 1; + while (i < (n - 1)) { + el_val_t ch = native_list_get(chars, i); + if (str_eq(ch, EL_STR("("))) { + depth = (depth + 1); + } + if (str_eq(ch, EL_STR(")"))) { + depth = (depth - 1); + if (depth == 0) { + balanced = 0; + i = n; + } + } + i = (i + 1); + } + if (balanced) { + return str_slice(s, 1, (n - 1)); + } + } + } + return s; + return 0; +} + +el_val_t js_cg_if_stmt(el_val_t expr, el_val_t indent, el_val_t declared) { + el_val_t cond = el_get_field(expr, EL_STR("cond")); + el_val_t then_stmts = el_get_field(expr, EL_STR("then")); + el_val_t else_stmts = el_get_field(expr, EL_STR("else")); + el_val_t has_else = el_get_field(expr, EL_STR("has_else")); + el_val_t cond_c = js_cg_expr(cond); + cond_c = js_strip_outer_parens(cond_c); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("if (")), cond_c), EL_STR(") {"))); + js_cg_stmts(then_stmts, el_str_concat(indent, EL_STR(" ")), native_list_clone(declared)); + if (has_else) { + js_emit_line(el_str_concat(indent, EL_STR("} else {"))); + js_cg_stmts(else_stmts, el_str_concat(indent, EL_STR(" ")), native_list_clone(declared)); + } + js_emit_line(el_str_concat(indent, EL_STR("}"))); + return 0; +} + +el_val_t js_cg_for_body(el_val_t item, el_val_t list_expr, el_val_t body, el_val_t indent, el_val_t declared) { + el_val_t list_c = js_cg_expr(list_expr); + js_emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("for (const ")), item), EL_STR(" of ")), list_c), EL_STR(") {"))); + el_val_t body_decl = native_list_clone(declared); + body_decl = native_list_append(body_decl, item); + js_cg_stmts(body, el_str_concat(indent, EL_STR(" ")), body_decl); + js_emit_line(el_str_concat(indent, EL_STR("}"))); + return 0; +} + +el_val_t js_cg_for_stmt(el_val_t expr, el_val_t indent, el_val_t declared) { + el_val_t item = el_get_field(expr, EL_STR("item")); + el_val_t list_expr = el_get_field(expr, EL_STR("list")); + el_val_t body = el_get_field(expr, EL_STR("body")); + js_cg_for_body(item, list_expr, body, indent, declared); + return 0; +} + +el_val_t js_cg_stmts(el_val_t stmts, el_val_t indent, el_val_t declared) { + el_val_t n = native_list_len(stmts); + el_val_t i = 0; + el_val_t decl = declared; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + decl = js_cg_stmt(stmt, indent, decl); + i = (i + 1); + } + return decl; + return 0; +} + +el_val_t js_params_str(el_val_t params) { + el_val_t n = native_list_len(params); + if (n == 0) { + return 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")); + parts = native_list_append(parts, name); + i = (i + 1); + } + return str_join(parts, EL_STR(", ")); + return 0; +} + +el_val_t js_transform_implicit_return(el_val_t body) { + el_val_t n = native_list_len(body); + if (n == 0) { + return body; + } + el_val_t last = native_list_get(body, (n - 1)); + el_val_t last_kind = el_get_field(last, EL_STR("stmt")); + if (str_eq(last_kind, EL_STR("Expr"))) { + el_val_t val = el_get_field(last, EL_STR("value")); + el_val_t val_kind = el_get_field(val, EL_STR("expr")); + if (str_eq(val_kind, EL_STR("If"))) { + return body; + } + if (str_eq(val_kind, EL_STR("For"))) { + return body; + } + el_val_t new_body = native_list_empty(); + el_val_t i = 0; + while (i < (n - 1)) { + new_body = native_list_append(new_body, native_list_get(body, i)); + i = (i + 1); + } + el_val_t return_stmt = el_map_new(2, "stmt", EL_STR("Return"), "value", val); + new_body = native_list_append(new_body, return_stmt); + return new_body; + } + return body; + return 0; +} + +el_val_t js_cg_fn(el_val_t stmt) { + 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 body = el_get_field(stmt, EL_STR("body")); + el_val_t ret_type = el_get_field(stmt, EL_STR("ret_type")); + el_val_t decorator = el_get_field(stmt, EL_STR("decorator")); + el_val_t params_str = js_params_str(params); + js_build_int_names_for_params(params); + if (str_eq(decorator, EL_STR("async"))) { + js_register_async_fn(fn_name); + if (str_eq(fn_name, EL_STR("main"))) { + js_emit_line(el_str_concat(el_str_concat(EL_STR("async function main("), params_str), EL_STR(") {"))); + } else { + js_emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("async function "), fn_name), EL_STR("(")), params_str), EL_STR(") {"))); + } + } else { + if (str_eq(fn_name, EL_STR("main"))) { + js_emit_line(el_str_concat(el_str_concat(EL_STR("function main("), params_str), EL_STR(") {"))); + } else { + js_emit_line(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("function "), fn_name), EL_STR("(")), params_str), EL_STR(") {"))); + } + } + el_val_t decl = native_list_empty(); + el_val_t np = native_list_len(params); + el_val_t pi = 0; + while (pi < np) { + el_val_t param = native_list_get(params, pi); + el_val_t pname = el_get_field(param, EL_STR("name")); + decl = native_list_append(decl, pname); + pi = (pi + 1); + } + el_val_t body_xformed = body; + if (!str_eq(ret_type, EL_STR("Void"))) { + body_xformed = js_transform_implicit_return(body); + } + js_cg_stmts(body_xformed, EL_STR(" "), decl); + js_emit_line(EL_STR("}")); + js_emit_blank(); + return 0; +} + +el_val_t js_is_fndef(el_val_t stmt) { + el_val_t kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("FnDef"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t js_is_top_level_decl(el_val_t stmt) { + el_val_t kind = el_get_field(stmt, EL_STR("stmt")); + if (str_eq(kind, EL_STR("TypeDef"))) { + return 1; + } + if (str_eq(kind, EL_STR("EnumDef"))) { + return 1; + } + if (str_eq(kind, EL_STR("Import"))) { + return 1; + } + if (str_eq(kind, EL_STR("CgiBlock"))) { + return 1; + } + if (str_eq(kind, EL_STR("ServiceBlock"))) { + return 1; + } + if (str_eq(kind, EL_STR("ExternFn"))) { + return 1; + } + return 0; + return 0; +} + +el_val_t codegen_js(el_val_t stmts, el_val_t source) { + return codegen_js_inner(stmts, source, 0, EL_STR("")); + return 0; +} + +el_val_t codegen_js_bundle(el_val_t stmts, el_val_t source, el_val_t runtime_content) { + return codegen_js_inner(stmts, source, 1, runtime_content); + return 0; +} + +el_val_t codegen_js_inner(el_val_t stmts, el_val_t source, el_val_t bundle_mode, el_val_t runtime_content) { + state_set(EL_STR("__js_int_names"), EL_STR("")); + state_set(EL_STR("__js_match_counter"), EL_STR("")); + state_set(EL_STR("__js_async_fns"), EL_STR("")); + state_set(EL_STR("__js_lambda_counter"), EL_STR("")); + js_emit_line(EL_STR("// Generated by elc --target=js")); + if (bundle_mode) { + js_emit_line(EL_STR("// Bundle mode: runtime inlined, no import statement needed.")); + js_emit_line(EL_STR("")); + js_emit_line(EL_STR(";(function() {")); + js_emit_line(EL_STR("\"use strict\";")); + js_emit_line(js_strip_es_exports(runtime_content)); + js_emit_line(EL_STR("")); + } else { + js_emit_line(EL_STR("// Runtime: foundation/el/el-compiler/runtime/el_runtime.js")); + js_emit_line(EL_STR("import \"./el_runtime.js\";")); + } + if (!bundle_mode) { + js_emit_line(EL_STR("const {")); + js_emit_line(EL_STR(" println, print, el_str_concat, str_concat, str_eq, str_starts_with, str_ends_with,")); + js_emit_line(EL_STR(" str_len, int_to_str, str_to_int, str_slice, str_contains, str_replace,")); + js_emit_line(EL_STR(" str_to_upper, str_to_lower, str_trim, str_index_of, str_split, str_char_at,")); + js_emit_line(EL_STR(" str_char_code, str_lower, str_upper, el_abs, el_max, el_min,")); + js_emit_line(EL_STR(" el_list_new, el_list_len, el_list_get, el_list_append, el_list_empty, el_list_clone,")); + js_emit_line(EL_STR(" list_push, list_join, list_range,")); + js_emit_line(EL_STR(" el_map_new, el_get_field, el_map_get, el_map_set,")); + js_emit_line(EL_STR(" http_get, http_post, http_post_json,")); + js_emit_line(EL_STR(" fs_read, fs_write, fs_list,")); + js_emit_line(EL_STR(" json_parse, json_stringify, json_get, json_get_string, json_get_int,")); + js_emit_line(EL_STR(" time_now, time_now_utc, sleep_ms, bool_to_str, exit_program,")); + js_emit_line(EL_STR(" el_retain, el_release,")); + js_emit_line(EL_STR(" append, len, get, map_get, map_set,")); + js_emit_line(EL_STR(" native_list_get, native_list_len, native_list_append, native_list_empty,")); + js_emit_line(EL_STR(" native_list_clone, native_string_chars, native_int_to_str,")); + js_emit_line(EL_STR(" args, state_set, state_get, state_del, state_keys, env,")); + js_emit_line(EL_STR(" dharma_connect, dharma_send, dharma_emit, dharma_field, dharma_activate,")); + js_emit_line(EL_STR(" engram_node, engram_search, engram_activate,")); + js_emit_line(EL_STR(" llm_call, llm_call_system,")); + js_emit_line(EL_STR(" dom_get_element, dom_get_value, dom_set_value, dom_get_text, dom_set_text,")); + js_emit_line(EL_STR(" dom_set_prop, dom_get_prop, dom_set_style, dom_add_class, dom_remove_class,")); + js_emit_line(EL_STR(" dom_show, dom_hide, dom_listen, dom_query, dom_query_all, dom_create,")); + js_emit_line(EL_STR(" dom_append, dom_remove, dom_is_null,")); + js_emit_line(EL_STR(" dom_set_attr, dom_get_attr, dom_remove_attr, dom_set_html, dom_get_html,")); + js_emit_line(EL_STR(" dom_get_parent, dom_contains_class, dom_get_checked, dom_set_checked,")); + js_emit_line(EL_STR(" set_timeout, set_interval, clear_interval,")); + js_emit_line(EL_STR(" local_storage_get, local_storage_set, local_storage_remove,")); + js_emit_line(EL_STR(" window_location, window_redirect, window_on_load,")); + js_emit_line(EL_STR(" console_log,")); + js_emit_line(EL_STR(" window_set, window_get, native_js, native_js_call,")); + js_emit_line(EL_STR(" promise_then, promise_catch, promise_resolve, promise_reject,")); + js_emit_line(EL_STR(" object_assign, object_keys, object_values, json_deep_clone,")); + js_emit_line(EL_STR(" array_from, type_of, instanceof_check,")); + js_emit_line(EL_STR("} = globalThis.__el;")); + js_emit_blank(); + } + el_val_t n = native_list_len(stmts); + el_val_t 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("Import"))) { + el_val_t ipath = el_get_field(stmt, EL_STR("path")); + el_val_t is_url = str_starts_with(ipath, EL_STR("http://")); + is_url = (is_url || str_starts_with(ipath, EL_STR("https://"))); + el_val_t is_js = !str_ends_with(ipath, EL_STR(".el")); + if (is_url || is_js) { + if (bundle_mode) { + js_emit_line(el_str_concat(EL_STR("// external: "), ipath)); + } else { + js_emit_line(el_str_concat(el_str_concat(EL_STR("import "), js_str_lit(ipath)), EL_STR(";"))); + } + } + } + i = (i + 1); + } + js_emit_blank(); + n = native_list_len(stmts); + 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 dec = el_get_field(stmt, EL_STR("decorator")); + if (str_eq(dec, EL_STR("async"))) { + el_val_t aname = el_get_field(stmt, EL_STR("name")); + js_register_async_fn(aname); + } + } + i = (i + 1); + } + i = 0; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + if (js_is_fndef(stmt)) { + js_cg_fn(stmt); + } + i = (i + 1); + } + el_val_t has_main = 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 = el_get_field(stmt, EL_STR("name")); + if (str_eq(fn_name, EL_STR("main"))) { + has_main = 1; + } + } + i = (i + 1); + } + el_val_t main_decl = native_list_empty(); + i = 0; + while (i < n) { + el_val_t stmt = native_list_get(stmts, i); + if (js_is_fndef(stmt)) { + } else { + if (js_is_top_level_decl(stmt)) { + } else { + main_decl = js_cg_stmt(stmt, EL_STR(""), main_decl); + } + } + i = (i + 1); + } + if (has_main) { + js_emit_blank(); + js_emit_line(EL_STR("main();")); + } + if (bundle_mode) { + js_emit_line(EL_STR("")); + js_emit_line(EL_STR("})();")); + } + return EL_STR(""); + return 0; +} + +el_val_t js_strip_es_exports(el_val_t content) { + el_val_t lines = str_split(content, EL_STR("\n")); + el_val_t n = native_list_len(lines); + el_val_t out = 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); + if (str_starts_with(trimmed, EL_STR("export {"))) { + i = n; + } else { + if (str_starts_with(trimmed, EL_STR("export default"))) { + i = n; + } else { + out = native_list_append(out, line); + } + } + i = (i + 1); + } + return str_join(out, EL_STR("\n")); + return 0; +} + +el_val_t compile(el_val_t source) { + el_val_t top_mark = el_arena_push(); + el_val_t tokens = lex(source); + el_val_t sigs = scan_fn_sigs(tokens); + codegen_streaming(tokens, sigs, source); + el_arena_pop(top_mark); + return EL_STR(""); + return 0; +} + +el_val_t compile_test(el_val_t source) { + state_set(EL_STR("__test_mode"), EL_STR("1")); + el_val_t top_mark = el_arena_push(); + el_val_t tokens = lex(source); + el_val_t sigs = scan_fn_sigs(tokens); + codegen_streaming(tokens, sigs, source); + el_arena_pop(top_mark); + state_set(EL_STR("__test_mode"), EL_STR("")); + return EL_STR(""); + return 0; +} + +el_val_t compile_js(el_val_t source) { + el_val_t tokens = lex(source); + el_val_t stmts = parse(tokens); + el_release(tokens); + return codegen_js(stmts, source); + return 0; +} + +el_val_t compile_js_with_bundle(el_val_t source, el_val_t runtime_path) { + el_val_t tokens = lex(source); + el_val_t stmts = parse(tokens); + el_release(tokens); + el_val_t runtime_content = fs_read(runtime_path); + if (str_eq(runtime_content, EL_STR(""))) { + println(el_str_concat(EL_STR("el-compiler: warning: --bundle: could not read runtime at "), runtime_path)); + println(EL_STR("el-compiler: warning: bundle output will be incomplete")); + } + return codegen_js_bundle(stmts, source, runtime_content); + return 0; +} + +el_val_t compile_dispatch(el_val_t tgt, el_val_t source) { + if (str_eq(tgt, EL_STR("js"))) { + return compile_js(source); + } + return compile(source); + return 0; +} + +el_val_t compile_dispatch_bundle(el_val_t tgt, el_val_t source, el_val_t runtime_path) { + if (str_eq(tgt, EL_STR("js"))) { + return compile_js_with_bundle(source, runtime_path); + } + return compile(source); + return 0; +} + +el_val_t detect_target(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_starts_with(a, EL_STR("--target="))) { + el_val_t v = str_slice(a, 9, str_len(a)); + return v; + } + i = (i + 1); + } + return EL_STR("c"); + return 0; +} + +el_val_t strip_flags(el_val_t argv) { + el_val_t out = native_list_empty(); + 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_starts_with(a, EL_STR("--"))) { + out = native_list_append(out, a); + } + i = (i + 1); + } + return out; + return 0; +} + +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 detect_bundle(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("--bundle"))) { + return 1; + } + i = (i + 1); + } + return 0; + return 0; +} + +el_val_t detect_minify(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("--minify"))) { + return 1; + } + i = (i + 1); + } + return 0; + return 0; +} + +el_val_t detect_obfuscate(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("--obfuscate"))) { + return 1; + } + i = (i + 1); + } + return 0; + return 0; +} + +el_val_t detect_test(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("--test"))) { + return 1; + } + i = (i + 1); + } + return 0; + return 0; +} + +el_val_t make_temp_path(el_val_t suffix) { + el_val_t pid = getpid_now(); + el_val_t ts = time_now(); + return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("/tmp/elc-"), native_int_to_str(pid)), EL_STR("-")), native_int_to_str(ts)), EL_STR(".")), suffix); + return 0; +} + +el_val_t js_reserved_names(void) { + return EL_STR("neuronDemoToggle,neuronDemoSend,neuronDemoReset,signInWith,signInWithEmail,signUpWithEmail,sendMagicLink,signOut,resetPassword,sendResetEmail,updatePassword,showSignIn,showSignUp,hideReset,setSort,addFamilyMember,removeFamilyMember,copyForPlatform,entHeadcountChange,NEURON_CFG"); + return 0; +} + +el_val_t find_node_tool(el_val_t tool_name, el_val_t src_dir) { + el_val_t cand1 = el_str_concat(el_str_concat(src_dir, EL_STR("/node_modules/.bin/")), tool_name); + el_val_t check1 = str_trim(exec_capture(el_str_concat(el_str_concat(EL_STR("test -x "), cand1), EL_STR(" && echo yes 2>/dev/null")))); + if (str_eq(check1, EL_STR("yes"))) { + return cand1; + } + el_val_t parent_dir = dirname_of(src_dir); + el_val_t cand2 = el_str_concat(el_str_concat(parent_dir, EL_STR("/node_modules/.bin/")), tool_name); + el_val_t check2 = str_trim(exec_capture(el_str_concat(el_str_concat(EL_STR("test -x "), cand2), EL_STR(" && echo yes 2>/dev/null")))); + if (str_eq(check2, EL_STR("yes"))) { + return cand2; + } + el_val_t npx_path = str_trim(exec_capture(EL_STR("which npx 2>/dev/null"))); + if (!str_eq(npx_path, EL_STR(""))) { + return el_str_concat(EL_STR("npx --yes "), tool_name); + } + return EL_STR(""); + return 0; +} + +el_val_t apply_minify(el_val_t js_path, el_val_t out_path, el_val_t src_dir) { + el_val_t terser = find_node_tool(EL_STR("terser"), src_dir); + if (str_eq(terser, EL_STR(""))) { + println(EL_STR("el-compiler: error: terser not found. Run 'npm install terser' in your project directory.")); + return 0; + } + el_val_t names = js_reserved_names(); + el_val_t compress_opts = EL_STR("passes=2,drop_console=false,drop_debugger=true"); + el_val_t mangle_reserved = el_str_concat(el_str_concat(EL_STR("'reserved=["), names), EL_STR("]'")); + el_val_t cmd = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(terser, EL_STR(" ")), js_path), EL_STR(" --compress ")), compress_opts), EL_STR(" --mangle ")), mangle_reserved), EL_STR(" --output ")), out_path); + el_val_t ret = exec_command(cmd); + if (ret == 0) { + return 1; + } + println(el_str_concat(el_str_concat(EL_STR("el-compiler: error: terser failed (exit "), native_int_to_str(ret)), EL_STR(")"))); + return 0; + return 0; +} + +el_val_t apply_obfuscate(el_val_t js_path, el_val_t out_path, el_val_t src_dir) { + el_val_t obfuscator = find_node_tool(EL_STR("javascript-obfuscator"), src_dir); + if (str_eq(obfuscator, EL_STR(""))) { + println(EL_STR("el-compiler: error: javascript-obfuscator not found. Run 'npm install javascript-obfuscator' in your project directory.")); + return 0; + } + el_val_t names = js_reserved_names(); + el_val_t cmd = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(obfuscator, EL_STR(" ")), js_path), EL_STR(" --output ")), out_path), EL_STR(" --compact true --simplify true --string-array true --string-array-encoding base64 --string-array-threshold 0.75 --identifier-names-generator hexadecimal --rename-globals false --self-defending false --reserved-names ")), names); + el_val_t ret = exec_command(cmd); + if (ret == 0) { + return 1; + } + println(el_str_concat(el_str_concat(EL_STR("el-compiler: error: javascript-obfuscator failed (exit "), native_int_to_str(ret)), EL_STR(")"))); + return 0; + return 0; +} + +el_val_t resolve_runtime_path(el_val_t src_path) { + el_val_t src_dir = dirname_of(src_path); + el_val_t candidate = el_str_concat(src_dir, EL_STR("/el_runtime.js")); + el_val_t existing = fs_read(candidate); + if (!str_eq(existing, EL_STR(""))) { + return candidate; + } + return EL_STR(""); + 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; +} + +/* ── emit_header_from_tokens ───────────────────────────────────────────────── + * Token-based header emitter. Scans the token stream for `fn` definitions + * using brace-depth counting (same strategy as scan_fn_sigs) so large + * HTML-template functions like page_open() don't cause the parser to consume + * subsequent function definitions. + * + * Replaces the parse()-based emit_header for the --emit-header code path. + */ +el_val_t emit_header_from_tokens(el_val_t tokens, el_val_t hdr_path) { + el_val_t total = (native_list_len(tokens) / 2); + el_val_t pos = 0; + el_val_t parts = native_list_empty(); + parts = native_list_append(parts, EL_STR("// auto-generated by elc --emit-header \xe2\x80\x94 do not edit\n")); + while (pos < total) { + el_val_t k = tok_kind(tokens, pos); + if (str_eq(k, EL_STR("Eof"))) { + pos = total; + } else { + if (str_eq(k, EL_STR("Fn"))) { + el_val_t p = (pos + 1); + el_val_t name = tok_value(tokens, p); + p = (p + 1); + /* Scan El-format params: collect "name: Type" pairs */ + el_val_t param_parts = native_list_empty(); + p = expect(tokens, p, EL_STR("LParen")); + el_val_t going = 1; + while (going) { + el_val_t pk = tok_kind(tokens, p); + if (str_eq(pk, EL_STR("RParen"))) { + going = 0; + } else { + if (str_eq(pk, EL_STR("Eof"))) { + going = 0; + } else { + el_val_t pname = tok_value(tokens, p); + p = (p + 1); + p = expect(tokens, p, EL_STR("Colon")); + el_val_t type_name = EL_STR("Any"); + el_val_t type_k = tok_kind(tokens, p); + if (str_eq(type_k, EL_STR("Ident"))) { + type_name = tok_value(tokens, p); + } + p = skip_type(tokens, p); + param_parts = native_list_append(param_parts, el_str_concat(el_str_concat(pname, EL_STR(": ")), type_name)); + el_val_t ck = tok_kind(tokens, p); + if (str_eq(ck, EL_STR("Comma"))) { + p = (p + 1); + } + } + } + } + p = expect(tokens, p, EL_STR("RParen")); + el_val_t params_el = str_join(param_parts, EL_STR(", ")); + el_release(param_parts); + /* Capture return type */ + el_val_t ret_str = EL_STR("Any"); + el_val_t rk = tok_kind(tokens, p); + if (str_eq(rk, EL_STR("Arrow"))) { + p = (p + 1); + el_val_t rtk = tok_kind(tokens, p); + if (str_eq(rtk, EL_STR("Ident"))) { + ret_str = tok_value(tokens, p); + } + p = skip_type(tokens, p); + } + /* Skip function body using brace depth (same as scan_fn_sigs) */ + el_val_t bk = tok_kind(tokens, p); + if (str_eq(bk, EL_STR("LBrace"))) { + p = skip_to_rbrace(tokens, p); + } + if (!str_eq(name, EL_STR("main"))) { + 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_el), EL_STR(") -> ")), ret_str); + parts = native_list_append(parts, el_str_concat(sig, EL_STR("\n"))); + } + pos = p; + } else { + pos = (pos + 1); + } + } + } + el_val_t content = str_join(parts, EL_STR("")); + el_release(parts); + el_val_t ok = fs_write(hdr_path, content); + 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 \xe2\x80\x94 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; +} + +el_val_t run_with_postprocess(el_val_t tgt, el_val_t source, el_val_t src_path, el_val_t do_bundle, el_val_t do_obfuscate, el_val_t argc, el_val_t positional) { + el_val_t src_dir = dirname_of(src_path); + el_val_t tmp_gen = make_temp_path(EL_STR("js")); + el_val_t tmp_min = make_temp_path(EL_STR("min.js")); + stdout_to_file(tmp_gen); + if (do_bundle) { + el_val_t runtime_path = resolve_runtime_path(src_path); + compile_dispatch_bundle(tgt, source, runtime_path); + } else { + compile_dispatch(tgt, source); + } + stdout_restore(); + el_val_t ok_min = apply_minify(tmp_gen, tmp_min, src_dir); + if (!ok_min) { + exec_command(el_str_concat(el_str_concat(el_str_concat(EL_STR("rm -f "), tmp_gen), EL_STR(" ")), tmp_min)); + exit(1); + } + state_set(EL_STR("__elc_final_js"), tmp_min); + if (do_obfuscate) { + el_val_t tmp_obf = make_temp_path(EL_STR("obf.js")); + el_val_t ok_obf = apply_obfuscate(tmp_min, tmp_obf, src_dir); + if (!ok_obf) { + exec_command(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("rm -f "), tmp_gen), EL_STR(" ")), tmp_min), EL_STR(" ")), tmp_obf)); + exit(1); + } + state_set(EL_STR("__elc_final_js"), tmp_obf); + } + el_val_t final_path = state_get(EL_STR("__elc_final_js")); + el_val_t final_js = fs_read(final_path); + exec_command(el_str_concat(el_str_concat(el_str_concat(EL_STR("rm -f "), tmp_gen), EL_STR(" ")), tmp_min)); + if (do_obfuscate) { + exec_command(el_str_concat(EL_STR("rm -f "), final_path)); + } + if (argc >= 2) { + el_val_t out_path = native_list_get(positional, 1); + el_val_t ok = fs_write(out_path, final_js); + if (ok) { + return 0; + } else { + println(EL_STR("el-compiler: failed to write output")); + exit(1); + } + } + print(final_js); + 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 do_bundle = detect_bundle(argv); + el_val_t do_minify = detect_minify(argv); + el_val_t do_obfuscate = detect_obfuscate(argv); + el_val_t do_test = detect_test(argv); + if (do_obfuscate) { + do_minify = 1; + } + 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] [--bundle] [--minify] [--obfuscate] [--emit-header] [--test] []")); + exit(1); + } + if (do_minify) { + if (!str_eq(tgt, EL_STR("js"))) { + println(EL_STR("el-compiler: error: --minify and --obfuscate require --target=js")); + 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_path = el_str_concat(str_slice(src_path, 0, (str_len(src_path) - 3)), EL_STR(".elh")); + emit_header_from_tokens(hdr_tokens, hdr_path); + el_release(hdr_tokens); + } + el_val_t source = resolve_imports(src_path); + if (do_minify) { + run_with_postprocess(tgt, source, src_path, do_bundle, do_obfuscate, argc, positional); + exit(0); + } + if (do_test) { + compile_test(source); + exit(0); + } + el_val_t out = EL_STR(""); + if (do_bundle) { + el_val_t runtime_path = resolve_runtime_path(src_path); + out = compile_dispatch_bundle(tgt, source, runtime_path); + } else { + 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; +} +