Files
el/dist/platform/elc.c
T
Will Anderson 951b8d574b runtime: HTTP, in-process graph store, LLM, fs_list
Batches 2/3/4 of the runtime extension. The runtime grew from 1620
to 3112 lines (.c) and 247 to 286 lines (.h) — adding 27 new or
real-implementation builtins and replacing every batch-1 stub.

Batch 2 — HTTP / fs (8 builtins)
- http_get, http_post: replaced stubs with real libcurl client.
  Network errors return JSON {"error":"..."} so callers can detect.
- http_post_json: sets Content-Type: application/json.
- http_get_with_headers, http_post_with_headers: ElMap → headers.
- http_post_form_auth: form-urlencoded + Authorization header
  (Stripe-style API calls).
- http_serve: replaced stub with real POSIX-socket server, threaded,
  capped at 64 concurrent connections. Auto-detects content type
  (HTML / JSON / plain). Handler dispatch via named registry.
- fs_list: directory listing via opendir/readdir.

Batch 3 — In-process graph store (14 builtins)
- engram_node, engram_node_full: create node, returns UUID.
- engram_get_node, engram_forget, engram_node_count.
- engram_strengthen: Hebbian potentiation (+0.05, clamp 1.0,
  bumps last_activated).
- engram_search, engram_scan_nodes: text search, paginated scan.
- engram_connect, engram_edge_between, engram_neighbors,
  engram_neighbors_filtered, engram_edge_count.
- engram_activate: real spreading-activation algorithm.
  BFS to depth, max-activation merge across paths, decay 0.7/hop,
  multiplied by node confidence, filtered by epistemic_confidence
  ≥ 0.2 (refresh threshold), sorted desc.
- engram_save, engram_load: JSON snapshot persistence.

Batch 4 — LLM (5 builtins)
- llm_call, llm_call_system: Anthropic /v1/messages via libcurl.
  ANTHROPIC_API_KEY from env. Default model claude-sonnet-4-5.
- llm_vision: adds image content block. URL / base64 / file path
  detected by prefix.
- llm_models: returns the available model list.
- llm_call_agentic: stubbed with TODO (single-turn fallback to
  llm_call_system); full tool_use loop is the next iteration.

Codegen fix: emit Float literals as `el_from_float(<v>)`. Without
the wrapper, C implicit conversion truncates 0.8 to 0 when passed to
a builtin expecting el_val_t. Float helpers moved to el_runtime.h
so generated programs can call them.

Compile-time
- cc -std=c11 -Wall -Wextra -c el_runtime.c → no errors, no warnings.
- Link requires -lcurl -lpthread (documented in header comment).

Verified end-to-end
- engram_node × 2, engram_connect, engram_activate("Hebbian", 2)
  returns 2 activated nodes with correct epistemic confidence.
- http_get("https://httpbin.org/get") returns 259-byte JSON live.
- Self-host closure: stage1 vs stage2 byte-identical against the
  new runtime.
- engram_save → engram_load round-trip preserves graph.

dist/platform/elc rebuilt against the new runtime (147 KB, up from
94 KB due to libcurl link). .prev2 preserves the prior binary.
2026-04-30 13:29:31 -05:00

2318 lines
76 KiB
C

#include <stdint.h>
#include <stdlib.h>
#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 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 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 codegen(el_val_t stmts, el_val_t source);
el_val_t compile(el_val_t source);
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("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 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);
p = (p + 1);
p = expect(tokens, p, EL_STR("Colon"));
el_val_t r = parse_expr(tokens, p);
el_val_t val_node = el_get_field(r, EL_STR("node"));
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, p);
if (str_eq(k3, EL_STR("Comma"))) {
p = (p + 1);
}
}
}
}
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);
}
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 r = parse_expr(tokens, p);
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 r = parse_expr(tokens, p);
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 r = parse_expr(tokens, p);
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"));
p = el_get_field(r, EL_STR("pos"));
stmts = native_list_append(stmts, stmt);
}
}
}
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 r = parse_expr(tokens, p);
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 r = parse_expr(tokens, p);
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);
p = (p + 1);
return parse_stmt(tokens, 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 (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("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)) {
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("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("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"));
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(", ")), 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);
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);
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"))) {
el_val_t cond = el_get_field(expr, EL_STR("cond"));
el_val_t cond_c = cg_expr(cond);
return el_str_concat(el_str_concat(EL_STR("/* if-expr */ (("), cond_c), EL_STR(") ? (el_val_t)1 : (el_val_t)0)"));
}
return EL_STR("EL_NULL");
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(" ")), 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;
}
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(" ")), declared);
if (has_else) {
emit_line(el_str_concat(indent, EL_STR("} else {")));
cg_stmts(else_stmts, el_str_concat(indent, EL_STR(" ")), 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(");")));
cg_stmts(body, el_str_concat(indent, EL_STR(" ")), declared);
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 add_int_name(el_val_t name) {
el_val_t csv = state_get(EL_STR("__int_names"));
if (str_eq(csv, EL_STR(""))) {
csv;
EL_NULL;
EL_STR(",");
}
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);
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;
}
return 0;
return 0;
}
el_val_t codegen(el_val_t stmts, el_val_t source) {
emit_line(EL_STR("#include <stdint.h>"));
emit_line(EL_STR("#include <stdlib.h>"));
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);
el_val_t 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();
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);"));
el_val_t main_decl = native_list_empty();
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();
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;
}
int main(int argc, char** argv) {
el_runtime_init_args(argc, argv);
el_val_t _argv = args();
el_val_t _src_path = native_list_get(_argv, 0);
el_val_t _source = fs_read(_src_path);
compile(_source);
return 0;
}