#include #include #include "el_runtime.h" el_val_t is_digit(el_val_t ch); el_val_t is_alpha(el_val_t ch); el_val_t is_alnum_or_underscore(el_val_t ch); el_val_t is_whitespace(el_val_t ch); el_val_t make_tok(el_val_t kind, el_val_t value); el_val_t keyword_kind(el_val_t word); el_val_t scan_digits(el_val_t chars, el_val_t start, el_val_t total); el_val_t scan_ident(el_val_t chars, el_val_t start, el_val_t total); el_val_t scan_string(el_val_t chars, el_val_t start, el_val_t total); el_val_t lex(el_val_t source); el_val_t tok_at(el_val_t tokens, el_val_t pos); 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 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 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 c_escape(el_val_t s); el_val_t c_str_lit(el_val_t s); el_val_t el_type_to_c(el_val_t type_str); el_val_t emit_line(el_val_t line); el_val_t emit_blank(void); el_val_t binop_to_c(el_val_t op); el_val_t 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 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_int_call(el_val_t call_expr); el_val_t is_int_expr(el_val_t expr); el_val_t cap_record_violation(el_val_t kind, el_val_t fn_name); el_val_t is_self_formation_call(el_val_t fn_name); 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 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 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 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_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_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_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 compile(el_val_t source); el_val_t compile_js(el_val_t source); el_val_t compile_dispatch(el_val_t tgt, el_val_t source); el_val_t detect_target(el_val_t argv); el_val_t strip_flags(el_val_t argv); el_val_t _argv; el_val_t _target; el_val_t _positional; el_val_t _argc; el_val_t 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 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 (is_digit(ch)) { return 1; } if (is_alpha(ch)) { return 1; } if (str_eq(ch, EL_STR("_"))) { return 1; } return 0; return 0; } el_val_t 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 make_tok(el_val_t kind, el_val_t value) { return el_map_new(2, "kind", kind, "value", 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"); } return EL_STR(""); return 0; } el_val_t scan_digits(el_val_t chars, el_val_t start, el_val_t total) { el_val_t i = start; el_val_t text = EL_STR(""); el_val_t running = 1; while (running) { if (i >= total) { running = 0; } else { el_val_t ch = native_list_get(chars, i); if (is_digit(ch)) { text = el_str_concat(text, ch); i = (i + 1); } else { running = 0; } } } return el_map_new(2, "text", text, "pos", i); return 0; } el_val_t scan_ident(el_val_t chars, el_val_t start, el_val_t total) { el_val_t i = start; el_val_t text = EL_STR(""); el_val_t running = 1; while (running) { if (i >= total) { running = 0; } else { el_val_t ch = native_list_get(chars, i); if (is_alnum_or_underscore(ch)) { text = el_str_concat(text, ch); i = (i + 1); } else { running = 0; } } } return el_map_new(2, "text", text, "pos", i); return 0; } el_val_t scan_string(el_val_t chars, el_val_t start, el_val_t total) { el_val_t i = start; el_val_t text = EL_STR(""); el_val_t running = 1; while (running) { if (i >= total) { running = 0; } else { el_val_t ch = native_list_get(chars, i); if (str_eq(ch, EL_STR("\\"))) { el_val_t next_i = (i + 1); if (next_i < total) { el_val_t next_ch = native_list_get(chars, next_i); if (str_eq(next_ch, EL_STR("\""))) { text = el_str_concat(text, EL_STR("\"")); i = (next_i + 1); } else { if (str_eq(next_ch, EL_STR("n"))) { text = el_str_concat(text, EL_STR("\n")); i = (next_i + 1); } else { if (str_eq(next_ch, EL_STR("t"))) { text = el_str_concat(text, EL_STR("\t")); i = (next_i + 1); } else { if (str_eq(next_ch, EL_STR("r"))) { text = el_str_concat(text, EL_STR("\r")); i = (next_i + 1); } else { if (str_eq(next_ch, EL_STR("\\"))) { text = el_str_concat(text, EL_STR("\\")); i = (next_i + 1); } else { text = el_str_concat(text, next_ch); i = (next_i + 1); } } } } } } else { i = (i + 1); } } else { if (str_eq(ch, EL_STR("\""))) { i = (i + 1); running = 0; } else { text = el_str_concat(text, ch); i = (i + 1); } } } } return el_map_new(2, "text", text, "pos", i); return 0; } el_val_t lex(el_val_t source) { el_val_t chars = native_string_chars(source); el_val_t total = native_list_len(chars); el_val_t tokens = native_list_empty(); el_val_t i = 0; while (i < total) { el_val_t ch = native_list_get(chars, i); if (is_whitespace(ch)) { i = (i + 1); } else { if (str_eq(ch, EL_STR("/"))) { el_val_t next_i = (i + 1); if (next_i < total) { el_val_t next_ch = native_list_get(chars, next_i); if (str_eq(next_ch, EL_STR("/"))) { i = (i + 2); el_val_t running2 = 1; while (running2) { if (i >= total) { running2 = 0; } else { el_val_t lch = native_list_get(chars, i); if (str_eq(lch, EL_STR("\n"))) { running2 = 0; } else { i = (i + 1); } } } } else { tokens = native_list_append(tokens, make_tok(EL_STR("Slash"), EL_STR("/"))); i = (i + 1); } } else { tokens = native_list_append(tokens, make_tok(EL_STR("Slash"), EL_STR("/"))); i = (i + 1); } } else { if (str_eq(ch, EL_STR("\""))) { el_val_t result = scan_string(chars, (i + 1), total); el_val_t str_text = el_get_field(result, EL_STR("text")); el_val_t new_pos = el_get_field(result, EL_STR("pos")); tokens = native_list_append(tokens, make_tok(EL_STR("Str"), str_text)); i = new_pos; } else { if (is_digit(ch)) { el_val_t result = scan_digits(chars, i, total); el_val_t num_text = el_get_field(result, EL_STR("text")); el_val_t new_pos = el_get_field(result, EL_STR("pos")); if (new_pos < total) { el_val_t dot_ch = native_list_get(chars, new_pos); if (str_eq(dot_ch, EL_STR("."))) { el_val_t after_dot = (new_pos + 1); if (after_dot < total) { el_val_t after_dot_ch = native_list_get(chars, after_dot); if (is_digit(after_dot_ch)) { el_val_t frac_result = scan_digits(chars, after_dot, total); el_val_t frac_text = el_get_field(frac_result, EL_STR("text")); el_val_t frac_pos = el_get_field(frac_result, EL_STR("pos")); tokens = native_list_append(tokens, make_tok(EL_STR("Float"), el_str_concat(el_str_concat(num_text, EL_STR(".")), frac_text))); i = frac_pos; } else { tokens = native_list_append(tokens, make_tok(EL_STR("Int"), num_text)); i = new_pos; } } else { tokens = native_list_append(tokens, make_tok(EL_STR("Int"), num_text)); i = new_pos; } } else { tokens = native_list_append(tokens, make_tok(EL_STR("Int"), num_text)); i = new_pos; } } else { tokens = native_list_append(tokens, make_tok(EL_STR("Int"), num_text)); i = new_pos; } } else { if (is_alpha(ch) || str_eq(ch, EL_STR("_"))) { el_val_t result = scan_ident(chars, i, total); el_val_t word = el_get_field(result, EL_STR("text")); el_val_t new_pos = el_get_field(result, EL_STR("pos")); el_val_t kw = keyword_kind(word); if (str_eq(kw, EL_STR(""))) { tokens = native_list_append(tokens, make_tok(EL_STR("Ident"), word)); } else { tokens = native_list_append(tokens, make_tok(kw, word)); } i = new_pos; } else { el_val_t peek_i = (i + 1); el_val_t peek_ch = EL_STR(""); if (peek_i < total) { peek_ch = native_list_get(chars, peek_i); } if (str_eq(ch, EL_STR("="))) { if (str_eq(peek_ch, EL_STR("="))) { tokens = native_list_append(tokens, make_tok(EL_STR("EqEq"), EL_STR("=="))); i = (i + 2); } else { if (str_eq(peek_ch, EL_STR(">"))) { tokens = native_list_append(tokens, make_tok(EL_STR("FatArrow"), EL_STR("=>"))); i = (i + 2); } else { tokens = native_list_append(tokens, make_tok(EL_STR("Eq"), EL_STR("="))); i = (i + 1); } } } else { if (str_eq(ch, EL_STR("!"))) { if (str_eq(peek_ch, EL_STR("="))) { tokens = native_list_append(tokens, make_tok(EL_STR("NotEq"), EL_STR("!="))); i = (i + 2); } else { tokens = native_list_append(tokens, make_tok(EL_STR("Not"), EL_STR("!"))); i = (i + 1); } } else { if (str_eq(ch, EL_STR("<"))) { if (str_eq(peek_ch, EL_STR("="))) { tokens = native_list_append(tokens, make_tok(EL_STR("LtEq"), EL_STR("<="))); i = (i + 2); } else { tokens = native_list_append(tokens, make_tok(EL_STR("Lt"), EL_STR("<"))); i = (i + 1); } } else { if (str_eq(ch, EL_STR(">"))) { if (str_eq(peek_ch, EL_STR("="))) { tokens = native_list_append(tokens, make_tok(EL_STR("GtEq"), EL_STR(">="))); i = (i + 2); } else { tokens = native_list_append(tokens, make_tok(EL_STR("Gt"), EL_STR(">"))); i = (i + 1); } } else { if (str_eq(ch, EL_STR("&"))) { if (str_eq(peek_ch, EL_STR("&"))) { tokens = native_list_append(tokens, make_tok(EL_STR("And"), EL_STR("&&"))); i = (i + 2); } else { i = (i + 1); } } else { if (str_eq(ch, EL_STR("|"))) { if (str_eq(peek_ch, EL_STR("|"))) { tokens = native_list_append(tokens, make_tok(EL_STR("Or"), EL_STR("||"))); i = (i + 2); } else { if (str_eq(peek_ch, EL_STR(">"))) { tokens = native_list_append(tokens, make_tok(EL_STR("PipeOp"), EL_STR("|>"))); i = (i + 2); } else { tokens = native_list_append(tokens, make_tok(EL_STR("Pipe"), EL_STR("|"))); i = (i + 1); } } } else { if (str_eq(ch, EL_STR("-"))) { if (str_eq(peek_ch, EL_STR(">"))) { tokens = native_list_append(tokens, make_tok(EL_STR("Arrow"), EL_STR("->"))); i = (i + 2); } else { tokens = native_list_append(tokens, make_tok(EL_STR("Minus"), EL_STR("-"))); i = (i + 1); } } else { if (str_eq(ch, EL_STR(":"))) { if (str_eq(peek_ch, EL_STR(":"))) { tokens = native_list_append(tokens, make_tok(EL_STR("ColonColon"), EL_STR("::"))); i = (i + 2); } else { tokens = native_list_append(tokens, make_tok(EL_STR("Colon"), EL_STR(":"))); i = (i + 1); } } else { if (str_eq(ch, EL_STR("+"))) { tokens = native_list_append(tokens, make_tok(EL_STR("Plus"), EL_STR("+"))); i = (i + 1); } else { if (str_eq(ch, EL_STR("*"))) { tokens = native_list_append(tokens, make_tok(EL_STR("Star"), EL_STR("*"))); i = (i + 1); } else { if (str_eq(ch, EL_STR("%"))) { tokens = native_list_append(tokens, make_tok(EL_STR("Percent"), EL_STR("%"))); i = (i + 1); } else { if (str_eq(ch, EL_STR("("))) { tokens = native_list_append(tokens, make_tok(EL_STR("LParen"), EL_STR("("))); i = (i + 1); } else { if (str_eq(ch, EL_STR(")"))) { tokens = native_list_append(tokens, make_tok(EL_STR("RParen"), EL_STR(")"))); i = (i + 1); } else { if (str_eq(ch, EL_STR("{"))) { tokens = native_list_append(tokens, make_tok(EL_STR("LBrace"), EL_STR("{"))); i = (i + 1); } else { if (str_eq(ch, EL_STR("}"))) { tokens = native_list_append(tokens, make_tok(EL_STR("RBrace"), EL_STR("}"))); i = (i + 1); } else { if (str_eq(ch, EL_STR("["))) { tokens = native_list_append(tokens, make_tok(EL_STR("LBracket"), EL_STR("["))); i = (i + 1); } else { if (str_eq(ch, EL_STR("]"))) { tokens = native_list_append(tokens, make_tok(EL_STR("RBracket"), EL_STR("]"))); i = (i + 1); } else { if (str_eq(ch, EL_STR(","))) { tokens = native_list_append(tokens, make_tok(EL_STR("Comma"), EL_STR(","))); i = (i + 1); } else { if (str_eq(ch, EL_STR("."))) { tokens = native_list_append(tokens, make_tok(EL_STR("Dot"), EL_STR("."))); i = (i + 1); } else { if (str_eq(ch, EL_STR(";"))) { tokens = native_list_append(tokens, make_tok(EL_STR("Semicolon"), EL_STR(";"))); i = (i + 1); } else { if (str_eq(ch, EL_STR("@"))) { tokens = native_list_append(tokens, make_tok(EL_STR("At"), EL_STR("@"))); i = (i + 1); } else { if (str_eq(ch, EL_STR("?"))) { tokens = native_list_append(tokens, make_tok(EL_STR("QuestionMark"), EL_STR("?"))); i = (i + 1); } else { i = (i + 1); } } } } } } } } } } } } } } } } } } } } } } } } } } } } tokens = native_list_append(tokens, make_tok(EL_STR("Eof"), EL_STR(""))); return tokens; return 0; } el_val_t tok_at(el_val_t tokens, el_val_t pos) { return native_list_get(tokens, pos); return 0; } el_val_t tok_kind(el_val_t tokens, el_val_t pos) { el_val_t t = native_list_get(tokens, pos); return el_get_field(t, EL_STR("kind")); return 0; } el_val_t tok_value(el_val_t tokens, el_val_t pos) { el_val_t t = native_list_get(tokens, pos); return el_get_field(t, EL_STR("value")); 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 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("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")); 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")); 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 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_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); } 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("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")); 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")); 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_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_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")); 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")); 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")); 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")); 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_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)); } 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_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")); 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")); 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 parse_postfix(el_val_t tokens, el_val_t pos) { el_val_t r = parse_primary(tokens, pos); el_val_t node = el_get_field(r, EL_STR("node")); el_val_t p = el_get_field(r, EL_STR("pos")); el_val_t 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")); 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")); 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; } 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; } 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_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")); 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")); 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")); return make_result(el_map_new(2, "stmt", EL_STR("Return"), "value", val), 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_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")); 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); 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_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")); return make_result(el_map_new(3, "stmt", EL_STR("While"), "cond", cond, "body", body), p); } 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 list_expr = el_get_field(r, EL_STR("node")); p = el_get_field(r, EL_STR("pos")); 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")); 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("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); 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); } 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")); 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); 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")); stmts = native_list_append(stmts, stmt); if (new_pos <= pos) { pos = (pos + 1); } else { pos = new_pos; } } } } return stmts; return 0; } el_val_t c_escape(el_val_t s) { el_val_t chars = native_string_chars(s); el_val_t total = native_list_len(chars); el_val_t out = EL_STR(""); el_val_t i = 0; while (i < total) { el_val_t ch = native_list_get(chars, i); if (str_eq(ch, EL_STR("\""))) { out = el_str_concat(out, EL_STR("\\\"")); } else { if (str_eq(ch, EL_STR("\\"))) { out = el_str_concat(out, EL_STR("\\\\")); } else { if (str_eq(ch, EL_STR("\n"))) { out = el_str_concat(out, EL_STR("\\n")); } else { if (str_eq(ch, EL_STR("\r"))) { out = el_str_concat(out, EL_STR("\\r")); } else { if (str_eq(ch, EL_STR("\t"))) { out = el_str_concat(out, EL_STR("\\t")); } else { out = el_str_concat(out, ch); } } } } } i = (i + 1); } return out; 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 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("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 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("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_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 (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 (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 (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_c = EL_STR(""); el_val_t i = 0; while (i < arity) { el_val_t arg = native_list_get(args, i); el_val_t arg_c = cg_expr(arg); if (i > 0) { args_c = el_str_concat(args_c, EL_STR(", ")); } args_c = el_str_concat(args_c, arg_c); i = (i + 1); } 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); 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 = EL_STR(""); el_val_t i = 0; while (i < n) { el_val_t elem = native_list_get(elems, i); el_val_t elem_c = cg_expr(elem); if (i > 0) { items = el_str_concat(items, EL_STR(", ")); } items = el_str_concat(items, elem_c); i = (i + 1); } return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_list_new("), native_int_to_str(n)), EL_STR(", ")), items), EL_STR(")")); } 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 = EL_STR(""); el_val_t i = 0; while (i < n) { el_val_t pair = native_list_get(pairs, i); el_val_t key = el_get_field(pair, EL_STR("key")); el_val_t val = el_get_field(pair, EL_STR("value")); el_val_t val_c = cg_expr(val); if (i > 0) { items = el_str_concat(items, EL_STR(", ")); } items = el_str_concat(el_str_concat(el_str_concat(items, c_str_lit(key)), EL_STR(", ")), val_c); i = (i + 1); } return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("el_map_new("), native_int_to_str(n)), EL_STR(", ")), items), EL_STR(")")); } 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); } return 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 out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("({ el_val_t "), subj_var), EL_STR(" = ")), subj_c), EL_STR("; el_val_t ")), result_var), EL_STR(" = 0; ")); el_val_t 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"))) { out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{ ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); } else { if (str_eq(pkind, EL_STR("Binding"))) { el_val_t bname = el_get_field(pat, EL_STR("name")); out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{ el_val_t ")), bname), EL_STR(" = ")), subj_var), EL_STR("; ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); } else { if (str_eq(pkind, EL_STR("LitInt"))) { el_val_t v = el_get_field(pat, EL_STR("value")); out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (")), subj_var), EL_STR(" == ")), v), EL_STR(") { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); } else { if (str_eq(pkind, EL_STR("LitStr"))) { el_val_t v = el_get_field(pat, EL_STR("value")); out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (str_eq(")), subj_var), EL_STR(", EL_STR(")), c_str_lit(v)), EL_STR("))) { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); } 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"); } out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (")), subj_var), EL_STR(" == ")), bv), EL_STR(") { ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); } else { out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{ ")), result_var), EL_STR(" = (")), body_c), EL_STR("); goto ")), done_label), EL_STR("; } ")); } } } } } i = (i + 1); } out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, done_label), EL_STR(":; ")), result_var), EL_STR("; })")); return out; 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 out = EL_STR(""); el_val_t i = 0; while (i < n) { el_val_t s = native_list_get(stmts, i); el_val_t sk = el_get_field(s, EL_STR("stmt")); el_val_t is_last = 0; if (str_eq(i, (n - 1))) { is_last = 1; } if (str_eq(sk, EL_STR("Let"))) { el_val_t name = el_get_field(s, EL_STR("name")); el_val_t val = el_get_field(s, EL_STR("value")); el_val_t val_c = cg_expr(val); out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("el_val_t ")), name), EL_STR(" = ")), val_c), EL_STR("; ")); } else { if (str_eq(sk, EL_STR("Return"))) { el_val_t val = el_get_field(s, EL_STR("value")); el_val_t val_c = cg_expr(val); out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, result_var), EL_STR(" = (")), val_c), EL_STR("); ")); } else { if (str_eq(sk, EL_STR("Expr"))) { el_val_t val = el_get_field(s, EL_STR("value")); el_val_t val_c = cg_expr(val); if (is_last) { out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, result_var), EL_STR(" = (")), val_c), EL_STR("); ")); } else { out = el_str_concat(el_str_concat(el_str_concat(out, EL_STR("(void)(")), val_c), EL_STR("); ")); } } else { } } } i = (i + 1); } return out; 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); } 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("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; } 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("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("CgiBlock"))) { 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) { el_val_t inner = EL_STR(""); el_val_t j = 1; while (j < (n - 1)) { el_val_t ch = native_list_get(chars, j); inner = el_str_concat(inner, ch); j = (j + 1); } return inner; } } } return 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); decl = cg_stmt(stmt, indent, decl); 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 out = EL_STR(""); el_val_t i = 0; while (i < n) { el_val_t param = native_list_get(params, i); el_val_t decl = param_decl(param, i); if (i > 0) { out = el_str_concat(out, EL_STR(", ")); } out = el_str_concat(out, decl); i = (i + 1); } return out; 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_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("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; } return 0; 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; } return 0; } 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 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("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("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_get_to_file"))) { return 3; } if (str_eq(name, EL_STR("http_post_to_file"))) { return 4; } 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("fs_read"))) { return 1; } if (str_eq(name, EL_STR("fs_write"))) { return 2; } if (str_eq(name, EL_STR("fs_write_bytes"))) { return 3; } 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("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; } 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 build_int_names_for_params(el_val_t params) { state_set(EL_STR("__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"))) { add_int_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); } cg_stmts(body_xformed, EL_STR(" "), 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; } 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("")); emit_line(EL_STR("#include ")); emit_line(EL_STR("#include ")); emit_line(EL_STR("#include \"el_runtime.h\"")); 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(");"))); } } 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(); } i = 0; while (i < n) { el_val_t stmt = native_list_get(stmts, i); if (is_fndef(stmt)) { cg_fn(stmt); } i = (i + 1); } 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); } 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); } } i = (i + 1); } emit_line(EL_STR(" return 0;")); emit_line(EL_STR("}")); emit_blank(); emit_cap_violations(); emit_arity_violations(); 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 out = EL_STR(""); el_val_t i = 0; while (i < total) { el_val_t ch = native_list_get(chars, i); if (str_eq(ch, EL_STR("\""))) { out = el_str_concat(out, EL_STR("\\\"")); } else { if (str_eq(ch, EL_STR("\\"))) { out = el_str_concat(out, EL_STR("\\\\")); } else { if (str_eq(ch, EL_STR("\n"))) { out = el_str_concat(out, EL_STR("\\n")); } else { if (str_eq(ch, EL_STR("\r"))) { out = el_str_concat(out, EL_STR("\\r")); } else { if (str_eq(ch, EL_STR("\t"))) { out = el_str_concat(out, EL_STR("\\t")); } else { out = el_str_concat(out, ch); } } } } } i = (i + 1); } return out; 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_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_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("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_c = EL_STR(""); el_val_t i = 0; while (i < arity) { el_val_t arg = native_list_get(args, i); el_val_t arg_c = js_cg_expr(arg); if (i > 0) { args_c = el_str_concat(args_c, EL_STR(", ")); } args_c = el_str_concat(args_c, arg_c); i = (i + 1); } if (str_eq(func_kind, EL_STR("Ident"))) { el_val_t fn_name = el_get_field(func, EL_STR("name")); return el_str_concat(el_str_concat(el_str_concat(fn_name, EL_STR("(")), args_c), EL_STR(")")); } 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 (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 = 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_c = js_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(", ")), 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")); 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 = EL_STR(""); el_val_t i = 0; while (i < n) { el_val_t elem = native_list_get(elems, i); el_val_t elem_c = js_cg_expr(elem); if (i > 0) { items = el_str_concat(items, EL_STR(", ")); } items = el_str_concat(items, elem_c); i = (i + 1); } return el_str_concat(el_str_concat(EL_STR("["), items), 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 = EL_STR(""); el_val_t i = 0; while (i < n) { el_val_t pair = native_list_get(pairs, i); el_val_t key = el_get_field(pair, EL_STR("key")); el_val_t val = el_get_field(pair, EL_STR("value")); el_val_t val_c = js_cg_expr(val); if (i > 0) { items = el_str_concat(items, EL_STR(", ")); } items = el_str_concat(el_str_concat(el_str_concat(items, js_str_lit(key)), EL_STR(": ")), val_c); i = (i + 1); } return el_str_concat(el_str_concat(EL_STR("{"), items), 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); } 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 out = 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"))) { out = el_str_concat(el_str_concat(el_str_concat(out, EL_STR("return (")), body_c), EL_STR("); ")); } else { if (str_eq(pkind, EL_STR("Binding"))) { el_val_t bname = el_get_field(pat, EL_STR("name")); out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("{ const ")), bname), EL_STR(" = ")), subj_var), EL_STR("; return (")), body_c), EL_STR("); } ")); } else { if (str_eq(pkind, EL_STR("LitInt"))) { el_val_t v = el_get_field(pat, EL_STR("value")); out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (")), subj_var), EL_STR(" === ")), v), EL_STR(") return (")), body_c), EL_STR("); ")); } else { if (str_eq(pkind, EL_STR("LitStr"))) { el_val_t v = el_get_field(pat, EL_STR("value")); out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (str_eq(")), subj_var), EL_STR(", ")), js_str_lit(v)), EL_STR(")) return (")), body_c), EL_STR("); ")); } 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"); } out = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(out, EL_STR("if (")), subj_var), EL_STR(" === ")), bv), EL_STR(") return (")), body_c), EL_STR("); ")); } else { out = el_str_concat(el_str_concat(el_str_concat(out, EL_STR("return (")), body_c), EL_STR("); ")); } } } } } i = (i + 1); } out = el_str_concat(el_str_concat(el_str_concat(out, EL_STR("return null; })(")), subj_c), EL_STR(")")); return out; 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("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("CgiBlock"))) { el_val_t cname = el_get_field(stmt, EL_STR("name")); js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("// cgi block '")), cname), EL_STR("' — no-op in JS target (server-side concept)"))); return declared; } if (str_eq(kind, EL_STR("ServiceBlock"))) { el_val_t sname = el_get_field(stmt, EL_STR("name")); js_emit_line(el_str_concat(el_str_concat(el_str_concat(indent, EL_STR("// service block '")), sname), EL_STR("' — no-op in JS target"))); 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) { el_val_t inner = EL_STR(""); el_val_t j = 1; while (j < (n - 1)) { el_val_t ch = native_list_get(chars, j); inner = el_str_concat(inner, ch); j = (j + 1); } return inner; } } } return 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 out = EL_STR(""); el_val_t i = 0; while (i < n) { el_val_t param = native_list_get(params, i); el_val_t name = el_get_field(param, EL_STR("name")); if (i > 0) { out = el_str_concat(out, EL_STR(", ")); } out = el_str_concat(out, name); i = (i + 1); } return out; 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 params_str = js_params_str(params); js_build_int_names_for_params(params); 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; } return 0; return 0; } el_val_t codegen_js(el_val_t stmts, el_val_t source) { state_set(EL_STR("__js_int_names"), EL_STR("")); state_set(EL_STR("__js_match_counter"), EL_STR("")); js_emit_line(EL_STR("// Generated by elc --target=js")); js_emit_line(EL_STR("// Runtime: foundation/el/el-compiler/runtime/el_runtime.js")); js_emit_line(EL_STR("import \"./el_runtime.js\";")); 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("} = 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); 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();")); } return EL_STR(""); return 0; } el_val_t compile(el_val_t source) { el_val_t tokens = lex(source); el_val_t stmts = parse(tokens); return codegen(stmts, source); return 0; } el_val_t compile_js(el_val_t source) { el_val_t tokens = lex(source); el_val_t stmts = parse(tokens); return codegen_js(stmts, source); 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 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; } int main(int argc, char** argv) { el_runtime_init_args(argc, argv); _argv = args(); _target = detect_target(_argv); _positional = strip_flags(_argv); _argc = native_list_len(_positional); if (_argc >= 1) { el_val_t _src_path = native_list_get(_positional, 0); el_val_t _source = fs_read(_src_path); el_val_t _out = compile_dispatch(_target, _source); if (_argc >= 2) { el_val_t _out_path = native_list_get(_positional, 1); fs_write(_out_path, _out); } } return 0; }