// elc-combined.el // lexer.el — el self-hosting lexer // // Tokenises an el source string into a list of token maps. // Each token is a Map with keys: // "kind" -> String (e.g. "Int", "Ident", "Plus") // "value" -> String (the raw text of the token) // // Entry point: fn lex(source: String) -> [Map] // // Uses native_string_chars to split the source into a chars list, // then indexes it with native_list_get — avoids O(N²) string cloning. // ── Character helpers ───────────────────────────────────────────────────────── fn is_digit(ch: String) -> Bool { if ch == "0" { return true } if ch == "1" { return true } if ch == "2" { return true } if ch == "3" { return true } if ch == "4" { return true } if ch == "5" { return true } if ch == "6" { return true } if ch == "7" { return true } if ch == "8" { return true } if ch == "9" { return true } false } fn is_alpha(ch: String) -> Bool { if ch == "a" { return true } if ch == "b" { return true } if ch == "c" { return true } if ch == "d" { return true } if ch == "e" { return true } if ch == "f" { return true } if ch == "g" { return true } if ch == "h" { return true } if ch == "i" { return true } if ch == "j" { return true } if ch == "k" { return true } if ch == "l" { return true } if ch == "m" { return true } if ch == "n" { return true } if ch == "o" { return true } if ch == "p" { return true } if ch == "q" { return true } if ch == "r" { return true } if ch == "s" { return true } if ch == "t" { return true } if ch == "u" { return true } if ch == "v" { return true } if ch == "w" { return true } if ch == "x" { return true } if ch == "y" { return true } if ch == "z" { return true } if ch == "A" { return true } if ch == "B" { return true } if ch == "C" { return true } if ch == "D" { return true } if ch == "E" { return true } if ch == "F" { return true } if ch == "G" { return true } if ch == "H" { return true } if ch == "I" { return true } if ch == "J" { return true } if ch == "K" { return true } if ch == "L" { return true } if ch == "M" { return true } if ch == "N" { return true } if ch == "O" { return true } if ch == "P" { return true } if ch == "Q" { return true } if ch == "R" { return true } if ch == "S" { return true } if ch == "T" { return true } if ch == "U" { return true } if ch == "V" { return true } if ch == "W" { return true } if ch == "X" { return true } if ch == "Y" { return true } if ch == "Z" { return true } false } fn is_alnum_or_underscore(ch: String) -> Bool { if is_digit(ch) { return true } if is_alpha(ch) { return true } if ch == "_" { return true } false } fn is_whitespace(ch: String) -> Bool { if ch == " " { return true } if ch == "\t" { return true } if ch == "\n" { return true } if ch == "\r" { return true } false } fn make_tok(kind: String, value: String) -> Map { { "kind": kind, "value": value } } // ── Keyword lookup ──────────────────────────────────────────────────────────── fn keyword_kind(word: String) -> String { if word == "let" { return "Let" } if word == "fn" { return "Fn" } if word == "type" { return "Type" } if word == "enum" { return "Enum" } if word == "match" { return "Match" } if word == "return" { return "Return" } if word == "if" { return "If" } if word == "else" { return "Else" } if word == "for" { return "For" } if word == "in" { return "In" } if word == "while" { return "While" } if word == "import" { return "Import" } if word == "from" { return "From" } if word == "as" { return "As" } if word == "with" { return "With" } if word == "sealed" { return "Sealed" } if word == "activate" { return "Activate" } if word == "where" { return "Where" } if word == "test" { return "Test" } if word == "seed" { return "Seed" } if word == "assert" { return "Assert" } if word == "protocol" { return "Protocol" } if word == "impl" { return "Impl" } if word == "retry" { return "Retry" } if word == "times" { return "Times" } if word == "fallback" { return "Fallback" } if word == "reason" { return "Reason" } if word == "parallel" { return "Parallel" } if word == "trace" { return "Trace" } if word == "requires" { return "Requires" } if word == "deploy" { return "Deploy" } if word == "to" { return "To" } if word == "via" { return "Via" } if word == "target" { return "Target" } if word == "true" { return "Bool" } if word == "false" { return "Bool" } if word == "cgi" { return "Cgi" } if word == "service" { return "Service" } if word == "manager" { return "Manager" } if word == "engine" { return "Engine" } if word == "accessor" { return "Accessor" } if word == "vessel" { return "Vessel" } "" } // ── Scan helpers ────────────────────────────────────────────────────────────── // All scan helpers receive the chars list and total length. // scan_digits — advance i while chars[i] is a digit // Returns { "text": ..., "pos": i } fn scan_digits(chars: [String], start: Int, total: Int) -> Map { let i = start let text = "" let running = true while running { if i >= total { let running = false } else { let ch: String = native_list_get(chars, i) if is_digit(ch) { let text = text + ch let i = i + 1 } else { let running = false } } } { "text": text, "pos": i } } // scan_ident — advance i while chars[i] is alphanumeric or underscore fn scan_ident(chars: [String], start: Int, total: Int) -> Map { let i = start let text = "" let running = true while running { if i >= total { let running = false } else { let ch: String = native_list_get(chars, i) if is_alnum_or_underscore(ch) { let text = text + ch let i = i + 1 } else { let running = false } } } { "text": text, "pos": i } } // scan_string — scan a quoted string literal, handling \" escapes. // Starts AFTER the opening quote. Returns { "text": content, "pos": i_after_close } fn scan_string(chars: [String], start: Int, total: Int) -> Map { let i = start let text = "" let running = true while running { if i >= total { let running = false } else { let ch: String = native_list_get(chars, i) if ch == "\\" { // escape: peek next char let next_i = i + 1 if next_i < total { let next_ch: String = native_list_get(chars, next_i) if next_ch == "\"" { let text = text + "\"" let i = next_i + 1 } else { if next_ch == "n" { let text = text + "\n" let i = next_i + 1 } else { if next_ch == "t" { let text = text + "\t" let i = next_i + 1 } else { if next_ch == "r" { let text = text + "\r" let i = next_i + 1 } else { if next_ch == "\\" { let text = text + "\\" let i = next_i + 1 } else { let text = text + next_ch let i = next_i + 1 } } } } } } else { let i = i + 1 } } else { if ch == "\"" { let i = i + 1 let running = false } else { let text = text + ch let i = i + 1 } } } } { "text": text, "pos": i } } // ── Main lexer ──────────────────────────────────────────────────────────────── fn lex(source: String) -> [Map] { let chars: [String] = native_string_chars(source) let total: Int = native_list_len(chars) let tokens: [Map] = native_list_empty() let i: Int = 0 while i < total { let ch: String = native_list_get(chars, i) // Skip whitespace if is_whitespace(ch) { let i = i + 1 } else { // Line comments: // if ch == "/" { let next_i = i + 1 if next_i < total { let next_ch: String = native_list_get(chars, next_i) if next_ch == "/" { // skip to end of line let i = i + 2 let running2 = true while running2 { if i >= total { let running2 = false } else { let lch: String = native_list_get(chars, i) if lch == "\n" { let running2 = false } else { let i = i + 1 } } } } else { let tokens = native_list_append(tokens, make_tok("Slash", "/")) let i = i + 1 } } else { let tokens = native_list_append(tokens, make_tok("Slash", "/")) let i = i + 1 } } else { // String literal if ch == "\"" { let result = scan_string(chars, i + 1, total) let str_text: String = result["text"] let new_pos: Int = result["pos"] let tokens = native_list_append(tokens, make_tok("Str", str_text)) let i = new_pos } else { // Number literal if is_digit(ch) { let result = scan_digits(chars, i, total) let num_text: String = result["text"] let new_pos: Int = result["pos"] // check for float (dot followed by digit) if new_pos < total { let dot_ch: String = native_list_get(chars, new_pos) if dot_ch == "." { let after_dot = new_pos + 1 if after_dot < total { let after_dot_ch: String = native_list_get(chars, after_dot) if is_digit(after_dot_ch) { let frac_result = scan_digits(chars, after_dot, total) let frac_text: String = frac_result["text"] let frac_pos: Int = frac_result["pos"] let tokens = native_list_append(tokens, make_tok("Float", num_text + "." + frac_text)) let i = frac_pos } else { let tokens = native_list_append(tokens, make_tok("Int", num_text)) let i = new_pos } } else { let tokens = native_list_append(tokens, make_tok("Int", num_text)) let i = new_pos } } else { let tokens = native_list_append(tokens, make_tok("Int", num_text)) let i = new_pos } } else { let tokens = native_list_append(tokens, make_tok("Int", num_text)) let i = new_pos } } else { // Identifier or keyword if is_alpha(ch) || ch == "_" { let result = scan_ident(chars, i, total) let word: String = result["text"] let new_pos: Int = result["pos"] let kw = keyword_kind(word) if kw == "" { let tokens = native_list_append(tokens, make_tok("Ident", word)) } else { let tokens = native_list_append(tokens, make_tok(kw, word)) } let i = new_pos } else { // Multi-char and single-char operators/delimiters let peek_i = i + 1 let peek_ch = "" if peek_i < total { let peek_ch: String = native_list_get(chars, peek_i) } if ch == "=" { if peek_ch == "=" { let tokens = native_list_append(tokens, make_tok("EqEq", "==")) let i = i + 2 } else { if peek_ch == ">" { let tokens = native_list_append(tokens, make_tok("FatArrow", "=>")) let i = i + 2 } else { let tokens = native_list_append(tokens, make_tok("Eq", "=")) let i = i + 1 } } } else { if ch == "!" { if peek_ch == "=" { let tokens = native_list_append(tokens, make_tok("NotEq", "!=")) let i = i + 2 } else { let tokens = native_list_append(tokens, make_tok("Not", "!")) let i = i + 1 } } else { if ch == "<" { if peek_ch == "=" { let tokens = native_list_append(tokens, make_tok("LtEq", "<=")) let i = i + 2 } else { let tokens = native_list_append(tokens, make_tok("Lt", "<")) let i = i + 1 } } else { if ch == ">" { if peek_ch == "=" { let tokens = native_list_append(tokens, make_tok("GtEq", ">=")) let i = i + 2 } else { let tokens = native_list_append(tokens, make_tok("Gt", ">")) let i = i + 1 } } else { if ch == "&" { if peek_ch == "&" { let tokens = native_list_append(tokens, make_tok("And", "&&")) let i = i + 2 } else { let i = i + 1 } } else { if ch == "|" { if peek_ch == "|" { let tokens = native_list_append(tokens, make_tok("Or", "||")) let i = i + 2 } else { if peek_ch == ">" { let tokens = native_list_append(tokens, make_tok("PipeOp", "|>")) let i = i + 2 } else { let tokens = native_list_append(tokens, make_tok("Pipe", "|")) let i = i + 1 } } } else { if ch == "-" { if peek_ch == ">" { let tokens = native_list_append(tokens, make_tok("Arrow", "->")) let i = i + 2 } else { let tokens = native_list_append(tokens, make_tok("Minus", "-")) let i = i + 1 } } else { if ch == ":" { if peek_ch == ":" { let tokens = native_list_append(tokens, make_tok("ColonColon", "::")) let i = i + 2 } else { let tokens = native_list_append(tokens, make_tok("Colon", ":")) let i = i + 1 } } else { if ch == "+" { let tokens = native_list_append(tokens, make_tok("Plus", "+")) let i = i + 1 } else { if ch == "*" { let tokens = native_list_append(tokens, make_tok("Star", "*")) let i = i + 1 } else { if ch == "%" { let tokens = native_list_append(tokens, make_tok("Percent", "%")) let i = i + 1 } else { if ch == "(" { let tokens = native_list_append(tokens, make_tok("LParen", "(")) let i = i + 1 } else { if ch == ")" { let tokens = native_list_append(tokens, make_tok("RParen", ")")) let i = i + 1 } else { if ch == "{" { let tokens = native_list_append(tokens, make_tok("LBrace", "{")) let i = i + 1 } else { if ch == "}" { let tokens = native_list_append(tokens, make_tok("RBrace", "}")) let i = i + 1 } else { if ch == "[" { let tokens = native_list_append(tokens, make_tok("LBracket", "[")) let i = i + 1 } else { if ch == "]" { let tokens = native_list_append(tokens, make_tok("RBracket", "]")) let i = i + 1 } else { if ch == "," { let tokens = native_list_append(tokens, make_tok("Comma", ",")) let i = i + 1 } else { if ch == "." { let tokens = native_list_append(tokens, make_tok("Dot", ".")) let i = i + 1 } else { if ch == ";" { let tokens = native_list_append(tokens, make_tok("Semicolon", ";")) let i = i + 1 } else { if ch == "@" { let tokens = native_list_append(tokens, make_tok("At", "@")) let i = i + 1 } else { if ch == "?" { let tokens = native_list_append(tokens, make_tok("QuestionMark", "?")) let i = i + 1 } else { // unknown char — skip let i = i + 1 } } } } } } } } } } } } } } } } } } } } } } } } } } } } let tokens = native_list_append(tokens, make_tok("Eof", "")) tokens } // parser.el — el self-hosting recursive descent parser // // Consumes the token list produced by lexer.el and builds a list of AST // statement maps. Each statement and expression is a Map. // // The cursor (integer position into the token list) is threaded through every // parse function. Functions return { "node": , "pos": }. // // The token list is passed as a parameter to all parse functions. // native_list_get is used to index into it without cloning. // // Entry point: fn parse(tokens: [Map]) -> [Map] // ── Token access helpers ────────────────────────────────────────────────────── fn tok_at(tokens: [Map], pos: Int) -> Map { native_list_get(tokens, pos) } fn tok_kind(tokens: [Map], pos: Int) -> String { let t = native_list_get(tokens, pos) t["kind"] } fn tok_value(tokens: [Map], pos: Int) -> String { let t = native_list_get(tokens, pos) t["value"] } fn expect(tokens: [Map], pos: Int, kind: String) -> Int { let k = tok_kind(tokens, pos) if k == kind { return pos + 1 } // On mismatch just advance; error recovery is best-effort pos + 1 } // ── Result helpers ──────────────────────────────────────────────────────────── fn make_result(node: Map, pos: Int) -> Map { { "node": node, "pos": pos } } // ── Type annotation parser ──────────────────────────────────────────────────── // Skips over a type annotation, returning the new position. // Types can be: Ident, [Type], Map, Type?, Type fn skip_type(tokens: [Map], pos: Int) -> Int { let k = tok_kind(tokens, pos) // Array type: [Type] if k == "LBracket" { let p = pos + 1 let p = skip_type(tokens, p) let p = expect(tokens, p, "RBracket") return p } // Named type (possibly generic) if k == "Ident" { let p = pos + 1 let k2 = tok_kind(tokens, p) if k2 == "Lt" { // Generic params: skip until matching > let p = p + 1 let depth = 1 let running = true while running { let kk = tok_kind(tokens, p) if kk == "Eof" { let running = false } else { if kk == "Lt" { let depth = depth + 1 let p = p + 1 } else { if kk == "Gt" { let depth = depth - 1 let p = p + 1 if depth <= 0 { let running = false } } else { let p = p + 1 } } } } let k3 = tok_kind(tokens, p) if k3 == "QuestionMark" { let p = p + 1 } return p } // Optional marker if k2 == "QuestionMark" { return p + 1 } return p } pos + 1 } // ── Parameter list ──────────────────────────────────────────────────────────── // Parses (name: Type, name: Type, ...) — returns { "params": [...], "pos": ... } fn parse_params(tokens: [Map], pos: Int) -> Map { let p = expect(tokens, pos, "LParen") let params: [Map] = native_list_empty() let running = true while running { let k = tok_kind(tokens, p) if k == "RParen" { let running = false } else { if k == "Eof" { let running = false } else { // param name let pname = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "Colon") // Capture the leading type identifier so codegen can dispatch // arithmetic vs string-concat on `+` based on declared types. let ptype = "" let kt = tok_kind(tokens, p) if kt == "Ident" { let ptype = tok_value(tokens, p) } let p = skip_type(tokens, p) let param = { "name": pname, "type": ptype } let params = native_list_append(params, param) let k2 = tok_kind(tokens, p) if k2 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RParen") { "params": params, "pos": p } } // ── Expression parsing ──────────────────────────────────────────────────────── fn parse_primary(tokens: [Map], pos: Int) -> Map { let k = tok_kind(tokens, pos) let v = tok_value(tokens, pos) // Integer literal if k == "Int" { return make_result({ "expr": "Int", "value": v }, pos + 1) } // Float literal if k == "Float" { return make_result({ "expr": "Float", "value": v }, pos + 1) } // String literal if k == "Str" { return make_result({ "expr": "Str", "value": v }, pos + 1) } // Bool literal if k == "Bool" { return make_result({ "expr": "Bool", "value": v }, pos + 1) } // Identifier if k == "Ident" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } // Grouped expression if k == "LParen" { let r = parse_expr(tokens, pos + 1) let node = r["node"] let p = r["pos"] let p = expect(tokens, p, "RParen") return make_result(node, p) } // Array literal: [e1, e2, ...] if k == "LBracket" { let p = pos + 1 let elems: [Map] = native_list_empty() let running = true while running { let k2 = tok_kind(tokens, p) if k2 == "RBracket" { let running = false } else { if k2 == "Eof" { let running = false } else { let r = parse_expr(tokens, p) let elem = r["node"] let p = r["pos"] let elems = native_list_append(elems, elem) let k3 = tok_kind(tokens, p) if k3 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RBracket") return make_result({ "expr": "Array", "elems": elems }, p) } // Map literal: { "key": val, ... } if k == "LBrace" { let p = pos + 1 let pairs: [Map] = native_list_empty() let running = true while running { let k2 = tok_kind(tokens, p) if k2 == "RBrace" { let running = false } else { if k2 == "Eof" { let running = false } else { // key: Str token let key = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "Colon") let r = parse_expr(tokens, p) let val_node = r["node"] let p = r["pos"] let pair = { "key": key, "value": val_node } let pairs = native_list_append(pairs, pair) let k3 = tok_kind(tokens, p) if k3 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RBrace") return make_result({ "expr": "Map", "pairs": pairs }, p) } // if expression if k == "If" { let r = parse_if(tokens, pos) return r } // match expression if k == "Match" { let r = parse_match(tokens, pos) return r } // for expression (used as statement) if k == "For" { let r = parse_for_expr(tokens, pos) return r } // Unary not if k == "Not" { let r = parse_primary(tokens, pos + 1) let inner = r["node"] let p = r["pos"] return make_result({ "expr": "Not", "inner": inner }, p) } // Unary minus if k == "Minus" { let r = parse_primary(tokens, pos + 1) let inner = r["node"] let p = r["pos"] return make_result({ "expr": "Neg", "inner": inner }, p) } // Fallback: skip unknown token make_result({ "expr": "Nil" }, pos + 1) } fn parse_if(tokens: [Map], pos: Int) -> Map { let p = expect(tokens, pos, "If") let r = parse_expr(tokens, p) let cond = r["node"] let p = r["pos"] let r2 = parse_block(tokens, p) let then_stmts = r2["stmts"] let p = r2["pos"] let has_else = false let else_stmts: [Map] = native_list_empty() let k2 = tok_kind(tokens, p) if k2 == "Else" { let p = p + 1 let k3 = tok_kind(tokens, p) if k3 == "If" { // else-if chain: parse as nested if let r3 = parse_if(tokens, p) let nested = r3["node"] let p = r3["pos"] let else_stmts = native_list_append(else_stmts, { "stmt": "Expr", "value": nested }) let has_else = true } else { let r3 = parse_block(tokens, p) let else_stmts = r3["stmts"] let p = r3["pos"] let has_else = true } } make_result({ "expr": "If", "cond": cond, "then": then_stmts, "else": else_stmts, "has_else": has_else }, p) } fn parse_match(tokens: [Map], pos: Int) -> Map { let p = expect(tokens, pos, "Match") let r = parse_expr(tokens, p) let subject = r["node"] let p = r["pos"] let p = expect(tokens, p, "LBrace") let arms: [Map] = native_list_empty() let running = true while running { let k = tok_kind(tokens, p) if k == "RBrace" { let running = false } else { if k == "Eof" { let running = false } else { // parse pattern => body let r2 = parse_pattern(tokens, p) let pattern = r2["node"] let p = r2["pos"] let p = expect(tokens, p, "FatArrow") let r3 = parse_expr(tokens, p) let body = r3["node"] let p = r3["pos"] let arm = { "pattern": pattern, "body": body } let arms = native_list_append(arms, arm) let k2 = tok_kind(tokens, p) if k2 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RBrace") make_result({ "expr": "Match", "subject": subject, "arms": arms }, p) } fn parse_pattern(tokens: [Map], pos: Int) -> Map { let k = tok_kind(tokens, pos) if k == "Ident" { let v = tok_value(tokens, pos) if v == "_" { return make_result({ "pattern": "Wildcard" }, pos + 1) } return make_result({ "pattern": "Binding", "name": v }, pos + 1) } if k == "Int" { return make_result({ "pattern": "LitInt", "value": tok_value(tokens, pos) }, pos + 1) } if k == "Str" { return make_result({ "pattern": "LitStr", "value": tok_value(tokens, pos) }, pos + 1) } if k == "Bool" { return make_result({ "pattern": "LitBool", "value": tok_value(tokens, pos) }, pos + 1) } // Wildcard _ make_result({ "pattern": "Wildcard" }, pos + 1) } fn parse_for_expr(tokens: [Map], pos: Int) -> Map { let p = expect(tokens, pos, "For") let item_name = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "In") let r = parse_expr(tokens, p) let list_expr = r["node"] let p = r["pos"] let r2 = parse_block(tokens, p) let body = r2["stmts"] let p = r2["pos"] make_result({ "expr": "For", "item": item_name, "list": list_expr, "body": body }, p) } fn parse_block(tokens: [Map], pos: Int) -> Map { let p = expect(tokens, pos, "LBrace") let stmts: [Map] = native_list_empty() let running = true while running { let k = tok_kind(tokens, p) if k == "RBrace" { let running = false } else { if k == "Eof" { let running = false } else { let r = parse_stmt(tokens, p) let stmt = r["node"] let p = r["pos"] let stmts = native_list_append(stmts, stmt) } } } let p = expect(tokens, p, "RBrace") { "stmts": stmts, "pos": p } } // ── Postfix expressions (calls, field access, index) ───────────────────────── fn parse_postfix(tokens: [Map], pos: Int) -> Map { let r = parse_primary(tokens, pos) let node = r["node"] let p = r["pos"] let running = true while running { let k = tok_kind(tokens, p) if k == "LParen" { // function call let p = p + 1 let args: [Map] = native_list_empty() let run2 = true while run2 { let k2 = tok_kind(tokens, p) if k2 == "RParen" { let run2 = false } else { if k2 == "Eof" { let run2 = false } else { let r2 = parse_expr(tokens, p) let arg = r2["node"] let p = r2["pos"] let args = native_list_append(args, arg) let k3 = tok_kind(tokens, p) if k3 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RParen") let node = { "expr": "Call", "func": node, "args": args } } else { if k == "Dot" { let field = tok_value(tokens, p + 1) let p = p + 2 let node = { "expr": "Field", "object": node, "field": field } } else { if k == "LBracket" { let r2 = parse_expr(tokens, p + 1) let idx = r2["node"] let p = r2["pos"] let p = expect(tokens, p, "RBracket") let node = { "expr": "Index", "object": node, "index": idx } } else { if k == "QuestionMark" { let p = p + 1 let node = { "expr": "Try", "inner": node } } else { let running = false } } } } } make_result(node, p) } // ── Binary expression precedence climbing ──────────────────────────────────── fn op_precedence(kind: String) -> Int { if kind == "Or" { return 1 } if kind == "And" { return 2 } if kind == "EqEq" { return 3 } if kind == "NotEq" { return 3 } if kind == "Lt" { return 4 } if kind == "Gt" { return 4 } if kind == "LtEq" { return 4 } if kind == "GtEq" { return 4 } if kind == "Plus" { return 5 } if kind == "Minus" { return 5 } if kind == "Star" { return 6 } if kind == "Slash" { return 6 } 0 } fn is_binop(kind: String) -> Bool { if kind == "Or" { return true } if kind == "And" { return true } if kind == "EqEq" { return true } if kind == "NotEq" { return true } if kind == "Lt" { return true } if kind == "Gt" { return true } if kind == "LtEq" { return true } if kind == "GtEq" { return true } if kind == "Plus" { return true } if kind == "Minus" { return true } if kind == "Star" { return true } if kind == "Slash" { return true } false } fn parse_binop(tokens: [Map], pos: Int, min_prec: Int) -> Map { let r = parse_postfix(tokens, pos) let left = r["node"] let p = r["pos"] let running = true while running { let k = tok_kind(tokens, p) let prec = op_precedence(k) if is_binop(k) { if prec >= min_prec { let op = k let r2 = parse_binop(tokens, p + 1, prec + 1) let right = r2["node"] let p = r2["pos"] let left = { "expr": "BinOp", "op": op, "left": left, "right": right } } else { let running = false } } else { let running = false } } make_result(left, p) } fn parse_expr(tokens: [Map], pos: Int) -> Map { parse_binop(tokens, pos, 1) } // ── Statement parsing ───────────────────────────────────────────────────────── fn parse_stmt(tokens: [Map], pos: Int) -> Map { let k = tok_kind(tokens, pos) // let binding if k == "Let" { let p = pos + 1 let name = tok_value(tokens, p) let p = p + 1 let ltype = "" let k2 = tok_kind(tokens, p) // optional type annotation: name: Type — capture the leading // identifier so codegen can dispatch arithmetic vs concat on // `+` between two typed Idents. if k2 == "Colon" { let p = p + 1 let kt = tok_kind(tokens, p) if kt == "Ident" { let ltype = tok_value(tokens, p) } let p = skip_type(tokens, p) } let p = expect(tokens, p, "Eq") let r = parse_expr(tokens, p) let val = r["node"] let p = r["pos"] return make_result({ "stmt": "Let", "name": name, "value": val, "type": ltype }, p) } // return statement if k == "Return" { let p = pos + 1 let k2 = tok_kind(tokens, p) if k2 == "RBrace" { return make_result({ "stmt": "Return", "value": { "expr": "Nil" } }, p) } if k2 == "Eof" { return make_result({ "stmt": "Return", "value": { "expr": "Nil" } }, p) } let r = parse_expr(tokens, p) let val = r["node"] let p = r["pos"] return make_result({ "stmt": "Return", "value": val }, p) } // fn definition if k == "Fn" { let p = pos + 1 let name = tok_value(tokens, p) let p = p + 1 let r = parse_params(tokens, p) let params = r["params"] let p = r["pos"] // return type annotation: -> Type. Capture the leading identifier // so codegen can distinguish Void-returning functions from value- // returning ones. Anything not "Void" is treated as a value type. let ret_type = "" let k2 = tok_kind(tokens, p) if k2 == "Arrow" { let p = p + 1 let kt = tok_kind(tokens, p) if kt == "Ident" { let ret_type = tok_value(tokens, p) } let p = skip_type(tokens, p) } let r2 = parse_block(tokens, p) let body = r2["stmts"] let p = r2["pos"] return make_result({ "stmt": "FnDef", "name": name, "params": params, "body": body, "ret_type": ret_type }, p) } // type definition if k == "Type" { let p = pos + 1 let name = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "LBrace") let fields: [Map] = native_list_empty() let running = true while running { let k2 = tok_kind(tokens, p) if k2 == "RBrace" { let running = false } else { if k2 == "Eof" { let running = false } else { let fname = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "Colon") let p = skip_type(tokens, p) let fields = native_list_append(fields, { "name": fname }) let k3 = tok_kind(tokens, p) if k3 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RBrace") return make_result({ "stmt": "TypeDef", "name": name, "fields": fields }, p) } // enum definition if k == "Enum" { let p = pos + 1 let name = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "LBrace") let variants: [Map] = native_list_empty() let running = true while running { let k2 = tok_kind(tokens, p) if k2 == "RBrace" { let running = false } else { if k2 == "Eof" { let running = false } else { let vname = tok_value(tokens, p) let p = p + 1 let variants = native_list_append(variants, { "name": vname }) let k3 = tok_kind(tokens, p) if k3 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RBrace") return make_result({ "stmt": "EnumDef", "name": name, "variants": variants }, p) } // import statement if k == "Import" { let p = pos + 1 let path = tok_value(tokens, p) let p = p + 1 return make_result({ "stmt": "Import", "path": path }, p) } // from ... import { ... } if k == "From" { let p = pos + 1 let module_name = tok_value(tokens, p) let p = p + 1 // skip "import" keyword let k2 = tok_kind(tokens, p) if k2 == "Import" { let p = p + 1 } // skip { Name, ... } let k3 = tok_kind(tokens, p) if k3 == "LBrace" { let p = p + 1 let running = true while running { let k4 = tok_kind(tokens, p) if k4 == "RBrace" { let running = false } else { if k4 == "Eof" { let running = false } else { let p = p + 1 let k5 = tok_kind(tokens, p) if k5 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RBrace") } return make_result({ "stmt": "Import", "path": module_name }, p) } // while loop if k == "While" { let p = pos + 1 let r = parse_expr(tokens, p) let cond = r["node"] let p = r["pos"] let r2 = parse_block(tokens, p) let body = r2["stmts"] let p = r2["pos"] return make_result({ "stmt": "While", "cond": cond, "body": body }, p) } // for loop if k == "For" { let p = pos + 1 let item_name = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "In") let r = parse_expr(tokens, p) let list_expr = r["node"] let p = r["pos"] let r2 = parse_block(tokens, p) let body = r2["stmts"] let p = r2["pos"] return make_result({ "stmt": "For", "item": item_name, "list": list_expr, "body": body }, p) } // @decorator — capture decorator name and attach to following stmt if k == "At" { let p = pos + 1 let dec_name = tok_value(tokens, p) let p = p + 1 let r = parse_stmt(tokens, p) let inner = r["node"] let p2 = r["pos"] let inner_kind: String = inner["stmt"] if str_eq(inner_kind, "FnDef") { let with_dec = { "stmt": "FnDef", "name": inner["name"], "params": inner["params"], "body": inner["body"], "ret_type": inner["ret_type"], "decorator": dec_name } return make_result(with_dec, p2) } return r } // cgi block: cgi "name" { field: "val", ... } if k == "Cgi" { let p = pos + 1 let name = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "LBrace") let dharma_id = "" let principal = "" let network = "" let engram = "" let has_dharma_id = false let has_principal = false let has_network = false let has_engram = false let running = true while running { let k2 = tok_kind(tokens, p) if k2 == "RBrace" { let running = false } else { if k2 == "Eof" { let running = false } else { let fname = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "Colon") let fval = tok_value(tokens, p) let p = p + 1 if str_eq(fname, "dharma_id") { let dharma_id = fval let has_dharma_id = true } if str_eq(fname, "principal") { let principal = fval let has_principal = true } if str_eq(fname, "network") { let network = fval let has_network = true } if str_eq(fname, "engram") { let engram = fval let has_engram = true } let k3 = tok_kind(tokens, p) if k3 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RBrace") return make_result({ "stmt": "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) } // service block: service "name" { sponsor: "...", domain: "...", ... } // // A `service` declaration restricts the program's capabilities at // compile time: services CANNOT call self-formation primitives // (llm_call_agentic, llm_register_tool, dharma_emit, dharma_field, // mindlink-creation). Codegen enforces this with #error directives. if k == "Service" { let p = pos + 1 let name = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "LBrace") let sponsor = "" let domain = "" let running = true while running { let k2 = tok_kind(tokens, p) if k2 == "RBrace" { let running = false } else { if k2 == "Eof" { let running = false } else { let fname = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "Colon") let fval = tok_value(tokens, p) let p = p + 1 if str_eq(fname, "sponsor") { let sponsor = fval } if str_eq(fname, "domain") { let domain = fval } let k3 = tok_kind(tokens, p) if k3 == "Comma" { let p = p + 1 } } } } let p = expect(tokens, p, "RBrace") return make_result({ "stmt": "ServiceBlock", "name": name, "sponsor": sponsor, "domain": domain }, p) } // bare expression or if/match statement let r = parse_expr(tokens, pos) let val = r["node"] let p = r["pos"] make_result({ "stmt": "Expr", "value": val }, p) } // ── Top-level parse ──────────────────────────────────────────────────────────── fn parse(tokens: [Map]) -> [Map] { let total: Int = native_list_len(tokens) let stmts: [Map] = native_list_empty() let pos: Int = 0 let running = true while running { if pos >= total { let running = false } else { let k = tok_kind(tokens, pos) if k == "Eof" { let running = false } else { let r = parse_stmt(tokens, pos) let stmt = r["node"] let new_pos: Int = r["pos"] let stmts = native_list_append(stmts, stmt) // Guard against infinite loops — if pos didn't advance, force it if new_pos <= pos { let pos = pos + 1 } else { let pos = new_pos } } } } stmts } // codegen.el — El compiler C source code generator // // Input: list of AST statement maps (from parser.el) // Output: C source printed to stdout (streamed, one line at a time) // // Each El program compiles to a single .c file that #includes el_runtime.h. // Functions map directly to C functions; top-level statements become main(). // // Entry point: fn codegen(stmts: [Map], source: String) -> String // Returns "" — output goes to stdout via println(). // // Streaming output avoids O(n²) string concatenation: each emitted line is // printed immediately rather than appended to a growing string. // ── String helpers ──────────────────────────────────────────────────────────── // Escape a C string literal (double-quotes and backslashes). fn c_escape(s: String) -> String { let chars: [String] = native_string_chars(s) let total: Int = native_list_len(chars) let out = "" let i = 0 while i < total { let ch: String = native_list_get(chars, i) if ch == "\"" { let out = out + "\\\"" } else { if ch == "\\" { let out = out + "\\\\" } else { if ch == "\n" { let out = out + "\\n" } else { if ch == "\r" { let out = out + "\\r" } else { if ch == "\t" { let out = out + "\\t" } else { let out = out + ch } } } } } let i = i + 1 } out } fn c_str_lit(s: String) -> String { "\"" + c_escape(s) + "\"" } // ── Type mapping ────────────────────────────────────────────────────────────── fn el_type_to_c(type_str: String) -> String { if type_str == "String" { return "const char*" } if type_str == "Int" { return "int64_t" } if type_str == "Bool" { return "int" } if type_str == "Float" { return "double" } if type_str == "Void" { return "void" } if type_str == "void" { return "void" } "void*" } // ── Code emission ───────────────────────────────────────────────────────────── // // emit_line/emit_blank stream output directly via println. // This avoids building a large string in memory. fn emit_line(line: String) -> Void { println(line) } fn emit_blank() -> Void { println("") } // ── Operator helpers ────────────────────────────────────────────────────────── fn binop_to_c(op: String) -> String { if op == "Plus" { return "+" } if op == "Minus" { return "-" } if op == "Star" { return "*" } if op == "Slash" { return "/" } if op == "EqEq" { return "==" } if op == "NotEq" { return "!=" } if op == "Lt" { return "<" } if op == "Gt" { return ">" } if op == "LtEq" { return "<=" } if op == "GtEq" { return ">=" } if op == "And" { return "&&" } if op == "Or" { return "||" } op } // ── Expression codegen ──────────────────────────────────────────────────────── // // cg_expr returns a C expression string (not a statement). fn cg_expr(expr: Map) -> String { let kind: String = expr["expr"] if kind == "Int" { let v: String = expr["value"] return v } if kind == "Float" { // Wrap Float literals in el_from_float() so the bit pattern is // preserved through the el_val_t (int64) slot. Without this, // implicit double→int64 conversion in C truncates `0.8` to `0` // when passed to a builtin that expects el_val_t. let v: String = expr["value"] return "el_from_float(" + v + ")" } if kind == "Str" { let v: String = expr["value"] return "EL_STR(" + c_str_lit(v) + ")" } if kind == "Bool" { let v: String = expr["value"] if v == "true" { return "1" } return "0" } if kind == "Nil" { return "EL_NULL" } if kind == "Ident" { let name: String = expr["name"] return name } if kind == "Not" { let inner = expr["inner"] let inner_c: String = cg_expr(inner) return "!" + inner_c } if kind == "Neg" { let inner = expr["inner"] let inner_c: String = cg_expr(inner) return "(-" + inner_c + ")" } if kind == "BinOp" { let op: String = expr["op"] let left = expr["left"] let right = expr["right"] let left_c: String = cg_expr(left) let right_c: String = cg_expr(right) let left_kind: String = left["expr"] let right_kind: String = right["expr"] if op == "Plus" { // If either side is a string literal, always concat if left_kind == "Str" { return "el_str_concat(" + left_c + ", " + right_c + ")" } if right_kind == "Str" { return "el_str_concat(" + left_c + ", " + right_c + ")" } // If either side is an integer literal, this is arithmetic (not string concat) if left_kind == "Int" { let op_c: String = binop_to_c(op) return "(" + left_c + " " + op_c + " " + right_c + ")" } if right_kind == "Int" { let op_c: String = binop_to_c(op) return "(" + left_c + " " + op_c + " " + right_c + ")" } // Type-driven dispatch: if both sides are Idents declared // with type Int (parameters annotated `: Int` or let bindings // annotated `: Int`), this is arithmetic, not concat. The // current-function int-name set is maintained by cg_fn / // cg_stmt via state_set("__int_names", csv). if left_kind == "Ident" { if right_kind == "Ident" { let lname: String = left["name"] let rname: String = right["name"] if is_int_name(lname) { if is_int_name(rname) { let op_c: String = binop_to_c(op) return "(" + left_c + " " + op_c + " " + right_c + ")" } } } } // Same dispatch for Ident-Int + Call-to-known-Int-builtin (and the // mirror). Without this, expressions like `pos + str_len(s)` get // string-concatenated. is_int_call walks a known-builtin list. if left_kind == "Ident" { if right_kind == "Call" { let lname: String = left["name"] if is_int_name(lname) { if is_int_call(right) { let op_c: String = binop_to_c(op) return "(" + left_c + " " + op_c + " " + right_c + ")" } } } } if right_kind == "Ident" { if left_kind == "Call" { let rname: String = right["name"] if is_int_name(rname) { if is_int_call(left) { let op_c: String = binop_to_c(op) return "(" + left_c + " " + op_c + " " + right_c + ")" } } } } if left_kind == "Call" { if right_kind == "Call" { if is_int_call(left) { if is_int_call(right) { let op_c: String = binop_to_c(op) return "(" + left_c + " " + op_c + " " + right_c + ")" } } } return "el_str_concat(" + left_c + ", " + right_c + ")" } if right_kind == "Call" { return "el_str_concat(" + left_c + ", " + right_c + ")" } if left_kind == "BinOp" { let left_op: String = left["op"] if left_op == "Plus" { return "el_str_concat(" + left_c + ", " + right_c + ")" } } if right_kind == "BinOp" { let right_op: String = right["op"] if right_op == "Plus" { return "el_str_concat(" + left_c + ", " + right_c + ")" } } // Ident + Ident or Ident + unknown without int-typed evidence — // fall back to string concat (the historical heuristic). if left_kind == "Ident" { return "el_str_concat(" + left_c + ", " + right_c + ")" } if right_kind == "Ident" { return "el_str_concat(" + left_c + ", " + right_c + ")" } } // String equality: use str_eq() when either side is a string literal or ident. // Use plain == when comparing integer literals. if op == "EqEq" { // Integer literal on either side → arithmetic comparison if left_kind == "Int" { return "(" + left_c + " == " + right_c + ")" } if right_kind == "Int" { return "(" + left_c + " == " + right_c + ")" } if left_kind == "Bool" { return "(" + left_c + " == " + right_c + ")" } if right_kind == "Bool" { return "(" + left_c + " == " + right_c + ")" } if left_kind == "Str" { return "str_eq(" + left_c + ", " + right_c + ")" } if right_kind == "Str" { return "str_eq(" + left_c + ", " + right_c + ")" } if left_kind == "Ident" { return "str_eq(" + left_c + ", " + right_c + ")" } if right_kind == "Ident" { return "str_eq(" + left_c + ", " + right_c + ")" } if left_kind == "Call" { return "str_eq(" + left_c + ", " + right_c + ")" } if right_kind == "Call" { return "str_eq(" + left_c + ", " + right_c + ")" } } if op == "NotEq" { if left_kind == "Int" { return "(" + left_c + " != " + right_c + ")" } if right_kind == "Int" { return "(" + left_c + " != " + right_c + ")" } if left_kind == "Bool" { return "(" + left_c + " != " + right_c + ")" } if right_kind == "Bool" { return "(" + left_c + " != " + right_c + ")" } if left_kind == "Str" { return "!str_eq(" + left_c + ", " + right_c + ")" } if right_kind == "Str" { return "!str_eq(" + left_c + ", " + right_c + ")" } if left_kind == "Ident" { return "!str_eq(" + left_c + ", " + right_c + ")" } if right_kind == "Ident" { return "!str_eq(" + left_c + ", " + right_c + ")" } if left_kind == "Call" { return "!str_eq(" + left_c + ", " + right_c + ")" } if right_kind == "Call" { return "!str_eq(" + left_c + ", " + right_c + ")" } } let op_c: String = binop_to_c(op) return "(" + left_c + " " + op_c + " " + right_c + ")" } if kind == "Call" { let func = expr["func"] let args = expr["args"] let arity: Int = native_list_len(args) let func_kind: String = func["expr"] let args_c = "" let i = 0 while i < arity { let arg = native_list_get(args, i) let arg_c: String = cg_expr(arg) if i > 0 { let args_c = args_c + ", " } let args_c = args_c + arg_c let i = i + 1 } if func_kind == "Ident" { let fn_name: String = func["name"] // Capability-kind enforcement: services can't call // self-formation primitives; utilities can't call any // DHARMA or LLM primitives. cap_check_call records // violations to be emitted as #error directives at the // top of the generated C, so cc fails with a clear msg. cap_check_call(fn_name) return fn_name + "(" + args_c + ")" } if func_kind == "Field" { let obj = func["object"] let field: String = func["field"] let obj_c: String = cg_expr(obj) if arity > 0 { return field + "(" + obj_c + ", " + args_c + ")" } return field + "(" + obj_c + ")" } let fn_c: String = cg_expr(func) return fn_c + "(" + args_c + ")" } if kind == "Field" { let obj = expr["object"] let field: String = expr["field"] let obj_c: String = cg_expr(obj) return "el_get_field(" + obj_c + ", " + c_str_lit(field) + ")" } if kind == "Index" { // El programs use `t["field"]` for map access and `arr[i]` for // list access. The parser emits the same Index node for both. // Dispatch at codegen time on the index expression kind: string- // literal index → map field access (`el_get_field`); anything // else → list element access (`el_list_get`). let obj = expr["object"] let idx = expr["index"] let obj_c: String = cg_expr(obj) let idx_c: String = cg_expr(idx) let idx_kind: String = idx["expr"] if str_eq(idx_kind, "Str") { return "el_get_field(" + obj_c + ", " + idx_c + ")" } return "el_list_get(" + obj_c + ", " + idx_c + ")" } if kind == "Array" { let elems = expr["elems"] let n: Int = native_list_len(elems) let items = "" let i = 0 while i < n { let elem = native_list_get(elems, i) let elem_c: String = cg_expr(elem) if i > 0 { let items = items + ", " } let items = items + elem_c let i = i + 1 } return "el_list_new(" + native_int_to_str(n) + ", " + items + ")" } if kind == "Map" { let pairs = expr["pairs"] let n: Int = native_list_len(pairs) let items = "" let i = 0 while i < n { let pair = native_list_get(pairs, i) let key: String = pair["key"] let val = pair["value"] let val_c: String = cg_expr(val) if i > 0 { let items = items + ", " } let items = items + c_str_lit(key) + ", " + val_c let i = i + 1 } return "el_map_new(" + native_int_to_str(n) + ", " + items + ")" } if kind == "Try" { let inner = expr["inner"] return cg_expr(inner) } if kind == "If" { let cond = expr["cond"] let cond_c: String = cg_expr(cond) return "/* if-expr */ ((" + cond_c + ") ? (el_val_t)1 : (el_val_t)0)" } if kind == "Match" { return cg_match(expr) } "EL_NULL" } // ── Match codegen ───────────────────────────────────────────────────────────── // // Lower a match expression to a GCC/Clang statement-expression. // A unique label suffix is allocated per match via state_set("__match_counter"). fn next_match_id() -> String { let csv: String = state_get("__match_counter") let n = 0 if !str_eq(csv, "") { let n = str_to_int(csv) } let n = n + 1 state_set("__match_counter", native_int_to_str(n)) native_int_to_str(n) } fn cg_match(expr: Map) -> String { let subject = expr["subject"] let arms = expr["arms"] let subj_c: String = cg_expr(subject) let id: String = next_match_id() let subj_var: String = "_match_subj_" + id let result_var: String = "_match_result_" + id let done_label: String = "_match_done_" + id let out: String = "({ el_val_t " + subj_var + " = " + subj_c + "; el_val_t " + result_var + " = 0; " let n: Int = native_list_len(arms) let i = 0 while i < n { let arm = native_list_get(arms, i) let pat = arm["pattern"] let body = arm["body"] let pkind: String = pat["pattern"] let body_c: String = cg_expr(body) if str_eq(pkind, "Wildcard") { let out = out + "{ " + result_var + " = (" + body_c + "); goto " + done_label + "; } " } else { if str_eq(pkind, "Binding") { let bname: String = pat["name"] let out = out + "{ el_val_t " + bname + " = " + subj_var + "; " + result_var + " = (" + body_c + "); goto " + done_label + "; } " } else { if str_eq(pkind, "LitInt") { let v: String = pat["value"] let out = out + "if (" + subj_var + " == " + v + ") { " + result_var + " = (" + body_c + "); goto " + done_label + "; } " } else { if str_eq(pkind, "LitStr") { let v: String = pat["value"] let out = out + "if (str_eq(" + subj_var + ", EL_STR(" + c_str_lit(v) + "))) { " + result_var + " = (" + body_c + "); goto " + done_label + "; } " } else { if str_eq(pkind, "LitBool") { let v: String = pat["value"] let bv = "0" if str_eq(v, "true") { let bv = "1" } let out = out + "if (" + subj_var + " == " + bv + ") { " + result_var + " = (" + body_c + "); goto " + done_label + "; } " } else { // unknown pattern → wildcard let out = out + "{ " + result_var + " = (" + body_c + "); goto " + done_label + "; } " } } } } } let i = i + 1 } let out = out + done_label + ":; " + result_var + "; })" out } // ── Variable scope tracking ─────────────────────────────────────────────────── // // El allows `let x = expr` to both declare and reassign x in the same scope. // C doesn't allow redeclaring the same name in the same block. // We track declared names in a list and emit `x = expr` (no type prefix) // when x is already declared. The declared list is passed through all // statement emitters. fn list_contains(lst: [String], s: String) -> Bool { let n: Int = native_list_len(lst) let i = 0 while i < n { let item: String = native_list_get(lst, i) if item == s { return true } let i = i + 1 } false } // ── Statement codegen ───────────────────────────────────────────────────────── // // cg_stmt emits C lines via println. declared is a list of already-declared // variable names in the current C scope; returns updated declared list. fn cg_stmt(stmt: Map, indent: String, declared: [String]) -> [String] { let kind: String = stmt["stmt"] if kind == "Let" { let name: String = stmt["name"] let val = stmt["value"] let val_c: String = cg_expr(val) // If the binding is annotated `: Int` and val is an Int literal, // register `name` in the per-function int-name set so that later // `name + ...` dispatches to arithmetic, not concat. let ltype: String = stmt["type"] if str_eq(ltype, "Int") { add_int_name(name) } let vk: String = val["expr"] if str_eq(vk, "Int") { add_int_name(name) } if list_contains(declared, name) { emit_line(indent + name + " = " + val_c + ";") return declared } else { emit_line(indent + "el_val_t " + name + " = " + val_c + ";") return native_list_append(declared, name) } } if kind == "Return" { let val = stmt["value"] let val_kind: String = val["expr"] if val_kind == "Nil" { emit_line(indent + "return 0;") } else { let val_c: String = cg_expr(val) emit_line(indent + "return " + val_c + ";") } return declared } if kind == "Expr" { let val = stmt["value"] let val_kind: String = val["expr"] if val_kind == "If" { cg_if_stmt(val, indent, declared) return declared } if val_kind == "For" { cg_for_stmt(val, indent, declared) return declared } let val_c: String = cg_expr(val) emit_line(indent + val_c + ";") return declared } if kind == "While" { let cond = stmt["cond"] let body = stmt["body"] let cond_c: String = cg_expr(cond) let cond_c = strip_outer_parens(cond_c) emit_line(indent + "while (" + cond_c + ") {") cg_stmts(body, indent + " ", declared) emit_line(indent + "}") return declared } if kind == "For" { let item: String = stmt["item"] let list_expr = stmt["list"] let body = stmt["body"] cg_for_body(item, list_expr, body, indent, declared) return declared } if kind == "FnDef" { return declared } if kind == "TypeDef" { return declared } if kind == "EnumDef" { return declared } if kind == "Import" { return declared } if kind == "CgiBlock" { return declared } declared } // Strip a single layer of surrounding parentheses from a C expression string. fn strip_outer_parens(s: String) -> String { let chars: [String] = native_string_chars(s) let n: Int = native_list_len(chars) if n < 2 { return s } let first: String = native_list_get(chars, 0) let last: String = native_list_get(chars, n - 1) if first == "(" { if last == ")" { let depth = 1 let i = 1 let balanced = true while i < n - 1 { let ch: String = native_list_get(chars, i) if ch == "(" { let depth = depth + 1 } if ch == ")" { let depth = depth - 1 if depth == 0 { let balanced = false let i = n } } let i = i + 1 } if balanced { let inner = "" let j = 1 while j < n - 1 { let ch: String = native_list_get(chars, j) let inner = inner + ch let j = j + 1 } return inner } } } s } fn cg_if_stmt(expr: Map, indent: String, declared: [String]) -> Void { let cond = expr["cond"] let then_stmts = expr["then"] let else_stmts = expr["else"] let has_else: Bool = expr["has_else"] let cond_c: String = cg_expr(cond) let cond_c = strip_outer_parens(cond_c) emit_line(indent + "if (" + cond_c + ") {") cg_stmts(then_stmts, indent + " ", declared) if has_else { emit_line(indent + "} else {") cg_stmts(else_stmts, indent + " ", declared) } emit_line(indent + "}") } fn cg_for_body(item: String, list_expr: Map, body: [Map], indent: String, declared: [String]) -> Void { let list_c: String = cg_expr(list_expr) let idx = "_el_i" let list_tmp = "_el_lst" let len_tmp = "_el_len" emit_line(indent + "{") emit_line(indent + " el_val_t " + list_tmp + " = " + list_c + ";") emit_line(indent + " el_val_t " + len_tmp + " = el_list_len(" + list_tmp + ");") emit_line(indent + " for (el_val_t " + idx + " = 0; " + idx + " < " + len_tmp + "; " + idx + "++) {") emit_line(indent + " el_val_t " + item + " = el_list_get(" + list_tmp + ", " + idx + ");") cg_stmts(body, indent + " ", declared) emit_line(indent + " }") emit_line(indent + "}") } fn cg_for_stmt(expr: Map, indent: String, declared: [String]) -> Void { let item: String = expr["item"] let list_expr = expr["list"] let body = expr["body"] cg_for_body(item, list_expr, body, indent, declared) } fn cg_stmts(stmts: [Map], indent: String, declared: [String]) -> [String] { let n: Int = native_list_len(stmts) let i = 0 let decl = declared while i < n { let stmt = native_list_get(stmts, i) let decl = cg_stmt(stmt, indent, decl) let i = i + 1 } decl } // ── Function declaration codegen ─────────────────────────────────────────────── fn param_decl(param: Map, idx: Int) -> String { let name: String = param["name"] "el_val_t " + name } fn params_to_c(params: [Map]) -> String { let n: Int = native_list_len(params) if n == 0 { return "void" } let out = "" let i = 0 while i < n { let param = native_list_get(params, i) let decl: String = param_decl(param, i) if i > 0 { let out = out + ", " } let out = out + decl let i = i + 1 } out } // Transform a function body so that an implicit-return final expression // becomes an explicit Return. El allows the last expression in a function // body to be the return value (e.g. `fn lex(s) { ... tokens }` returns // `tokens`). Without this transform, the codegen emits the bare expression // and falls through to the trailing `return 0;`, losing the value. // // Rules: a body ending in a bare Expr whose inner expr is NOT a control- // flow construct (If/For) is rewritten so that final Expr becomes a // Return statement carrying the same value. Bodies whose final statement // is already a Return, While, For, or a non-value-producing form pass // through unchanged. fn transform_implicit_return(body: [Map]) -> [Map] { let n: Int = native_list_len(body) if n == 0 { return body } let last: Map = native_list_get(body, n - 1) let last_kind: String = last["stmt"] if last_kind == "Expr" { let val = last["value"] let val_kind: String = val["expr"] // Skip control-flow expressions used as statements if val_kind == "If" { return body } if val_kind == "For" { return body } // Replace the last bare Expr with a Return carrying the same value let new_body: [Map] = native_list_empty() let i = 0 while i < n - 1 { let new_body = native_list_append(new_body, native_list_get(body, i)) let i = i + 1 } let return_stmt: Map = { "stmt": "Return", "value": val } let new_body = native_list_append(new_body, return_stmt) return new_body } body } // Test whether `name` is currently registered as an Int-typed identifier // for the function being codegened. The set is maintained as a comma- // bounded CSV in process state; cg_fn seeds it from typed parameters, // cg_stmt extends it from typed `let` bindings. fn is_int_name(name: String) -> Bool { let csv: String = state_get("__int_names") if str_eq(csv, "") { return false } return str_contains(csv, "," + name + ",") } // Known runtime builtins that return Int. Used to dispatch arithmetic vs // string-concat on `+` when one side is a Call. New builtins must be added // here when they return Int and may participate in arithmetic. fn is_int_call(call_expr: Map) -> Bool { let func = call_expr["func"] let fk: String = func["expr"] if !str_eq(fk, "Ident") { return false } let name: String = func["name"] if str_eq(name, "str_len") { return true } if str_eq(name, "str_index_of") { return true } if str_eq(name, "str_to_int") { return true } if str_eq(name, "str_char_code") { return true } if str_eq(name, "native_list_len") { return true } if str_eq(name, "el_list_len") { return true } if str_eq(name, "len") { return true } if str_eq(name, "json_get_int") { return true } if str_eq(name, "json_array_len") { return true } if str_eq(name, "engram_node_count") { return true } if str_eq(name, "engram_edge_count") { return true } if str_eq(name, "time_now") { return true } if str_eq(name, "time_now_utc") { return true } if str_eq(name, "time_diff") { return true } if str_eq(name, "time_add") { return true } if str_eq(name, "time_from_parts") { return true } if str_eq(name, "el_abs") { return true } if str_eq(name, "el_max") { return true } if str_eq(name, "el_min") { return true } if str_eq(name, "float_to_int") { return true } return false } // ── Capability-kind enforcement ────────────────────────────────────────────── // // A program's top-level block (cgi / service / none) determines which // runtime primitives it may call. The compiler records violations in // process state during cg_expr's Call emission; codegen's entry point // then emits #error directives at the top of the generated C so the // downstream cc step fails with a clear message. // // Capability tiers: // "cgi" — full self-formation. All primitives. // "service" — bounded. Cannot call self-formation primitives: // llm_call_agentic, llm_register_tool, dharma_emit, // dharma_field. Single-turn LLM calls are allowed. // "utility" — default. No DHARMA, no LLM. Pure compute + I/O. // // The compiler-level rule is structural: the binary either CAN or CANNOT // emit the call. There is no runtime check, no opt-in, no override. fn cap_record_violation(kind: String, fn_name: String) -> Bool { let csv: String = state_get("__cap_violations") if str_eq(csv, "") { let csv = "," } let entry: String = kind + ":" + fn_name let key: String = "," + entry + "," if str_contains(csv, key) { return true } state_set("__cap_violations", csv + entry + ",") return true } // Self-formation primitives — the cut between CGI and service. A program // that emits these calls IS structurally a CGI; we forbid them everywhere // else. fn is_self_formation_call(fn_name: String) -> Bool { if str_eq(fn_name, "llm_call_agentic") { return true } if str_eq(fn_name, "llm_register_tool") { return true } if str_eq(fn_name, "dharma_emit") { return true } if str_eq(fn_name, "dharma_field") { return true } return false } // Any DHARMA primitive — utilities have zero network presence. fn is_dharma_call(fn_name: String) -> Bool { if str_eq(fn_name, "dharma_connect") { return true } if str_eq(fn_name, "dharma_send") { return true } if str_eq(fn_name, "dharma_activate") { return true } if str_eq(fn_name, "dharma_emit") { return true } if str_eq(fn_name, "dharma_field") { return true } if str_eq(fn_name, "dharma_strengthen") { return true } if str_eq(fn_name, "dharma_relationship") { return true } if str_eq(fn_name, "dharma_peers") { return true } return false } // Any LLM primitive — utilities have no LLM access at all. fn is_llm_call(fn_name: String) -> Bool { if str_eq(fn_name, "llm_call") { return true } if str_eq(fn_name, "llm_call_system") { return true } if str_eq(fn_name, "llm_call_agentic") { return true } if str_eq(fn_name, "llm_vision") { return true } if str_eq(fn_name, "llm_register_tool") { return true } if str_eq(fn_name, "llm_models") { return true } return false } fn cap_check_call(fn_name: String) -> Bool { let kind: String = state_get("__program_kind") if str_eq(kind, "cgi") { return true } if str_eq(kind, "service") { if is_self_formation_call(fn_name) { cap_record_violation("service", fn_name) return false } return true } // utility (default) if is_dharma_call(fn_name) { cap_record_violation("utility", fn_name) return false } if is_llm_call(fn_name) { cap_record_violation("utility", fn_name) return false } return true } // Emit collected capability violations as #error directives. Called // from codegen()'s entry point right after the cgi/service-block scan, // so they appear at the very top of the generated C. fn emit_cap_violations() -> Void { let csv: String = state_get("__cap_violations") if str_eq(csv, "") { return } if str_eq(csv, ",") { return } let n: Int = str_len(csv) let i: Int = 1 while i < n { let next_comma: Int = str_index_of(str_slice(csv, i, n), ",") if next_comma < 0 { return } let entry: String = str_slice(csv, i, i + next_comma) let colon: Int = str_index_of(entry, ":") if colon > 0 { let kind: String = str_slice(entry, 0, colon) let fn_name: String = str_slice(entry, colon + 1, str_len(entry)) emit_line("#error \"capability violation: '" + kind + "' programs may not call '" + fn_name + "' (self-formation primitive — only 'cgi' programs may use it)\"") } let i = i + next_comma + 1 } } fn add_int_name(name: String) -> Bool { let csv: String = state_get("__int_names") if str_eq(csv, "") { csv = "," } let key: String = "," + name + "," if str_contains(csv, key) { return true } state_set("__int_names", csv + name + ",") return true } fn build_int_names_for_params(params: [Map]) -> Bool { state_set("__int_names", ",") let np: Int = native_list_len(params) let pi = 0 while pi < np { let param = native_list_get(params, pi) let pname: String = param["name"] let ptype: String = param["type"] if str_eq(ptype, "Int") { add_int_name(pname) } let pi = pi + 1 } return true } fn cg_fn(stmt: Map) -> Void { let fn_name: String = stmt["name"] // Skip El's `fn main()` — C provides its own main() for top-level stmts // and a duplicate `el_val_t main(void)` would collide with it. if fn_name == "main" { return } let params = stmt["params"] let body = stmt["body"] let ret_type: String = stmt["ret_type"] let params_c: String = params_to_c(params) // VBD role enforcement: dharma_emit / dharma_field may only be called // from @manager-decorated functions. Surface violations to the C compiler // via #error directives emitted before the function definition. let decorator: String = stmt["decorator"] if vbd_has_restricted_call(body) { if !str_eq(decorator, "manager") { emit_line("#error \"VBD violation: dharma_emit/dharma_field called from non-@manager fn '" + fn_name + "'\"") } } // Seed the per-function int-name set so the `+` codegen can dispatch // arithmetic vs concat on type-annotated identifiers. build_int_names_for_params(params) emit_line("el_val_t " + fn_name + "(" + params_c + ") {") // Seed declared with parameter names so reassignment works let decl = native_list_empty() let np: Int = native_list_len(params) let pi = 0 while pi < np { let param = native_list_get(params, pi) let pname: String = param["name"] let decl = native_list_append(decl, pname) let pi = pi + 1 } // Lift the final bare expression into an explicit return so implicit // returns ("fn lex(s) { ... tokens }") actually return their value. // Void-returning functions skip this — wrapping `println(x)` in // `return …` is a C type error. let body_xformed = body if !str_eq(ret_type, "Void") { let body_xformed = transform_implicit_return(body) } cg_stmts(body_xformed, " ", decl) emit_line(" return 0;") emit_line("}") emit_blank() } // ── Top-level codegen ───────────────────────────────────────────────────────── fn is_fndef(stmt: Map) -> Bool { let kind: String = stmt["stmt"] if kind == "FnDef" { return true } false } fn is_top_level_decl(stmt: Map) -> Bool { let kind: String = stmt["stmt"] if kind == "TypeDef" { return true } if kind == "EnumDef" { return true } if kind == "Import" { return true } if kind == "CgiBlock" { return true } false } // Format a string-or-EL_NULL argument for el_cgi_init. fn cgi_arg(value: String, has_value: Bool) -> String { if has_value { return "EL_STR(" + c_str_lit(value) + ")" } return "EL_NULL" } // ── VBD role enforcement ────────────────────────────────────────────────────── // // Scan a function body for direct calls to DHARMA-restricted builtins // (dharma_emit, dharma_field). These may only appear inside @manager fns. fn vbd_is_restricted_name(name: String) -> Bool { if str_eq(name, "dharma_emit") { return true } if str_eq(name, "dharma_field") { return true } false } fn vbd_expr_has_restricted_call(expr: Map) -> Bool { let kind: String = expr["expr"] if str_eq(kind, "Call") { let func = expr["func"] let fk: String = func["expr"] if str_eq(fk, "Ident") { let fname: String = func["name"] if vbd_is_restricted_name(fname) { return true } } if vbd_expr_has_restricted_call(func) { return true } let args = expr["args"] let an: Int = native_list_len(args) let ai = 0 while ai < an { let a = native_list_get(args, ai) if vbd_expr_has_restricted_call(a) { return true } let ai = ai + 1 } return false } if str_eq(kind, "BinOp") { let l = expr["left"] let r = expr["right"] if vbd_expr_has_restricted_call(l) { return true } if vbd_expr_has_restricted_call(r) { return true } return false } if str_eq(kind, "Not") { return vbd_expr_has_restricted_call(expr["inner"]) } if str_eq(kind, "Neg") { return vbd_expr_has_restricted_call(expr["inner"]) } if str_eq(kind, "Field") { return vbd_expr_has_restricted_call(expr["object"]) } if str_eq(kind, "Index") { if vbd_expr_has_restricted_call(expr["object"]) { return true } if vbd_expr_has_restricted_call(expr["index"]) { return true } return false } if str_eq(kind, "Try") { return vbd_expr_has_restricted_call(expr["inner"]) } if str_eq(kind, "Array") { let elems = expr["elems"] let n: Int = native_list_len(elems) let i = 0 while i < n { let e = native_list_get(elems, i) if vbd_expr_has_restricted_call(e) { return true } let i = i + 1 } return false } if str_eq(kind, "Map") { let pairs = expr["pairs"] let n: Int = native_list_len(pairs) let i = 0 while i < n { let pair = native_list_get(pairs, i) let v = pair["value"] if vbd_expr_has_restricted_call(v) { return true } let i = i + 1 } return false } if str_eq(kind, "If") { if vbd_expr_has_restricted_call(expr["cond"]) { return true } if vbd_has_restricted_call(expr["then"]) { return true } if vbd_has_restricted_call(expr["else"]) { return true } return false } if str_eq(kind, "For") { if vbd_expr_has_restricted_call(expr["list"]) { return true } if vbd_has_restricted_call(expr["body"]) { return true } return false } if str_eq(kind, "Match") { if vbd_expr_has_restricted_call(expr["subject"]) { return true } let arms = expr["arms"] let n: Int = native_list_len(arms) let i = 0 while i < n { let arm = native_list_get(arms, i) let body = arm["body"] if vbd_expr_has_restricted_call(body) { return true } let i = i + 1 } return false } false } fn vbd_has_restricted_call(stmts: [Map]) -> Bool { let n: Int = native_list_len(stmts) let i = 0 while i < n { let s = native_list_get(stmts, i) let sk: String = s["stmt"] if str_eq(sk, "Let") { if vbd_expr_has_restricted_call(s["value"]) { return true } } if str_eq(sk, "Return") { if vbd_expr_has_restricted_call(s["value"]) { return true } } if str_eq(sk, "Expr") { if vbd_expr_has_restricted_call(s["value"]) { return true } } if str_eq(sk, "While") { if vbd_expr_has_restricted_call(s["cond"]) { return true } if vbd_has_restricted_call(s["body"]) { return true } } if str_eq(sk, "For") { if vbd_expr_has_restricted_call(s["list"]) { return true } if vbd_has_restricted_call(s["body"]) { return true } } let i = i + 1 } false } // ── Entry point ──────────────────────────────────────────────────────────────── fn codegen(stmts: [Map], source: String) -> String { // Detect cgi/service blocks: at most one declarative top-level block. // The block determines the program's CAPABILITY KIND: // "cgi" — full self-formation. Calls all primitives. // "service" — bounded. Cannot call self-formation primitives // (llm_call_agentic, llm_register_tool, dharma_emit, // dharma_field, mindlink-creation). // "utility" — default; no DHARMA membership, no LLM, no agentic. // Codegen enforces this with #error directives at every restricted // call site. The capability boundary is structural: a binary either // CAN or CANNOT do a thing, and the compiler decides at emission time. let n_top: Int = native_list_len(stmts) let cgi_count = 0 let cgi_block: Map = { "stmt": "None" } let svc_count = 0 let svc_block: Map = { "stmt": "None" } let ti = 0 while ti < n_top { let s = native_list_get(stmts, ti) let sk: String = s["stmt"] if str_eq(sk, "CgiBlock") { let cgi_count = cgi_count + 1 if cgi_count == 1 { let cgi_block = s } } if str_eq(sk, "ServiceBlock") { let svc_count = svc_count + 1 if svc_count == 1 { let svc_block = s } } let ti = ti + 1 } if cgi_count > 1 { emit_line("#error \"El: multiple cgi blocks in program (only one allowed)\"") } if svc_count > 1 { emit_line("#error \"El: multiple service blocks in program (only one allowed)\"") } if cgi_count >= 1 { if svc_count >= 1 { emit_line("#error \"El: program declares both cgi and service blocks (mutually exclusive — pick one)\"") } } // Stash the program kind so cg_expr's Call branch can enforce // per-kind capability restrictions on every emitted call. let kind: String = "utility" if cgi_count >= 1 { let kind = "cgi" } if svc_count >= 1 { let kind = "service" } state_set("__program_kind", kind) // Clear capability-violation accumulator from any prior compile. state_set("__cap_violations", "") // Preamble emit_line("#include ") emit_line("#include ") emit_line("#include \"el_runtime.h\"") emit_blank() // Forward declarations (skip `main` — C provides its own) let n: Int = native_list_len(stmts) let i = 0 while i < n { let stmt = native_list_get(stmts, i) let kind: String = stmt["stmt"] if kind == "FnDef" { let fn_name: String = stmt["name"] if !str_eq(fn_name, "main") { let params = stmt["params"] let params_c: String = params_to_c(params) emit_line("el_val_t " + fn_name + "(" + params_c + ");") } } let i = i + 1 } emit_blank() // Function definitions let i = 0 while i < n { let stmt = native_list_get(stmts, i) if is_fndef(stmt) { cg_fn(stmt) } let i = i + 1 } // main() emit_line("int main(int argc, char** argv) {") emit_line(" el_runtime_init_args(argc, argv);") if cgi_count >= 1 { let cname: String = cgi_block["name"] let cdid: String = cgi_block["dharma_id"] let cprin: String = cgi_block["principal"] let cnet: String = cgi_block["network"] let ceng: String = cgi_block["engram"] let has_did: Bool = cgi_block["has_dharma_id"] let has_prin: Bool = cgi_block["has_principal"] let has_net: Bool = cgi_block["has_network"] let has_eng: Bool = cgi_block["has_engram"] let arg_name: String = "EL_STR(" + c_str_lit(cname) + ")" let arg_did: String = cgi_arg(cdid, has_did) let arg_prin: String = cgi_arg(cprin, has_prin) let arg_net: String = cgi_arg(cnet, has_net) let arg_eng: String = cgi_arg(ceng, has_eng) emit_line(" el_cgi_init(" + arg_name + ", " + arg_did + ", " + arg_prin + ", " + arg_net + ", " + arg_eng + ");") } let main_decl = native_list_empty() let i = 0 while i < n { let stmt = native_list_get(stmts, i) if is_fndef(stmt) { // skip } else { if is_top_level_decl(stmt) { // skip } else { let main_decl = cg_stmt(stmt, " ", main_decl) } } let i = i + 1 } emit_line(" return 0;") emit_line("}") emit_blank() // Emit any accumulated capability-violation #error directives. cc // will fail on the first one and surface the message; placement at // the bottom is fine — preprocessor errors halt the build wherever // they appear. emit_cap_violations() // Return empty string — output was streamed via println "" } // compiler.el — el self-hosting compiler pipeline // // Wires lexer -> parser -> codegen into a single compile() function. // This is the bootstrap entry point: compiled once by the Rust el-compiler, // then self-hosted from that point forward. // // The returned string is C source code. Compile the output with: // cc -o .c el_runtime.c // compile — full pipeline: source string -> C source string fn compile(source: String) -> String { let tokens: [Map] = lex(source) let stmts: [Map] = parse(tokens) codegen(stmts, source) } // main — CLI entry point for self-hosted compilation. // // Called by: elc // // Reads El source from args()[0], compiles it to C source, and writes the // result to args()[1]. Then run: // cc -o el_runtime.c let _argv: [String] = args() let _src_path: String = native_list_get(_argv, 0) let _source: String = fs_read(_src_path) compile(_source)