Files
el/elc-combined.el
T
Will Anderson 86b3ad070d compiler+runtime: codegen fixes for empty literal, == int idents, m.field; runtime body-loss fix and Linux feature macros
Three codegen bugs surfaced repeatedly across the parallel port-to-El
agents and were patched here:

1. Empty array literal '[]' was emitting el_list_new(0, ) — trailing
   comma in a varargs call, fails the C parse. Special-cased: n==0
   returns 'el_list_empty()' directly.

2. '==' between two identifiers both tracked in __int_names (typed
   Int via 'let x: Int = ...') was miscompiling to str_eq. With the
   tagged-pointer Int-as-int64 representation, str_eq strcmp's what
   are integer values dressed as char* and segfaults on the first
   non-printable byte. Added the int-name lookup, mirroring the
   dispatch already present for '+' between Int idents. NotEq got
   the same treatment.

3. 'm.field' codegen was passing the raw const char* field name to
   el_get_field, which expects el_val_t. C compiler warned about int
   conversion; runtime read garbage at the address. Wrapped in
   EL_STR(...) so the field name lands as a proper el_val_t.

Runtime additions in the same pass:

  - el_runtime.c http_read_request: the loop's boundary check was
    'line_end >= hdr_end' which broke before processing the LAST
    header line — its trailing \r\n IS hdr_end. Real curl clients
    put Content-Length last, so POST bodies were silently arriving
    as length 0. Changed to '> hdr_end' so the last line is processed.
    soma-server agent surfaced this during smoke testing.

  - _GNU_SOURCE feature macro: clock_gettime/CLOCK_REALTIME, strcasecmp,
    and the dlfcn extensions (RTLD_DEFAULT) all gated behind it on
    glibc/Debian. macOS is permissive without; the landing Docker
    build needed these for linux/amd64. Adds <strings.h> for
    strcasecmp.

  - Refactored slot semantics in el_runtime.c (already in tree from
    the morning ARC commit): magic-tagged ElHeader at offset 0,
    ElList/ElMap with separate elems/keys/values payload allocations,
    el_list_append and el_map_set mutate-in-place when refcount<=1
    and copy-on-write when shared.

Self-host fixpoint reached at v3: elc → elc.c → cc → elc binary →
elc.c reproduced byte-for-byte. dist/platform/elc and dist/platform/elc.c
updated. The codegen.el and elc-combined.el changes are mirror-edits;
both flow through the bootstrap chain to keep self-hosting clean.
2026-04-30 18:14:57 -05:00

2797 lines
107 KiB
EmacsLisp

// 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<String, Any> with keys:
// "kind" -> String (e.g. "Int", "Ident", "Plus")
// "value" -> String (the raw text of the token)
//
// Entry point: fn lex(source: String) -> [Map<String, Any>]
//
// 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<String, Any> {
{ "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<String, Any> {
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<String, Any> {
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<String, Any> {
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<String, Any>] {
let chars: [String] = native_string_chars(source)
let total: Int = native_list_len(chars)
let tokens: [Map<String, Any>] = 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<String, Any>.
//
// The cursor (integer position into the token list) is threaded through every
// parse function. Functions return { "node": <map>, "pos": <int> }.
//
// 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<String, Any>]) -> [Map<String, Any>]
// Token access helpers
fn tok_at(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
native_list_get(tokens, pos)
}
fn tok_kind(tokens: [Map<String, Any>], pos: Int) -> String {
let t = native_list_get(tokens, pos)
t["kind"]
}
fn tok_value(tokens: [Map<String, Any>], pos: Int) -> String {
let t = native_list_get(tokens, pos)
t["value"]
}
fn expect(tokens: [Map<String, Any>], 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<String, Any>, pos: Int) -> Map<String, Any> {
{ "node": node, "pos": pos }
}
// Type annotation parser
// Skips over a type annotation, returning the new position.
// Types can be: Ident, [Type], Map<K,V>, Type?, Type<Type,...>
fn skip_type(tokens: [Map<String, Any>], 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<String, Any>], pos: Int) -> Map<String, Any> {
let p = expect(tokens, pos, "LParen")
let params: [Map<String, Any>] = 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<String, Any>], pos: Int) -> Map<String, Any> {
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<String, Any>] = 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<String, Any>] = 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<String, Any>], pos: Int) -> Map<String, Any> {
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<String, Any>] = 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<String, Any>], pos: Int) -> Map<String, Any> {
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<String, Any>] = 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<String, Any>], pos: Int) -> Map<String, Any> {
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<String, Any>], pos: Int) -> Map<String, Any> {
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<String, Any>], pos: Int) -> Map<String, Any> {
let p = expect(tokens, pos, "LBrace")
let stmts: [Map<String, Any>] = 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<String, Any>], pos: Int) -> Map<String, Any> {
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<String, Any>] = 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<String, Any>], pos: Int, min_prec: Int) -> Map<String, Any> {
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<String, Any>], pos: Int) -> Map<String, Any> {
parse_binop(tokens, pos, 1)
}
// Statement parsing
fn parse_stmt(tokens: [Map<String, Any>], pos: Int) -> Map<String, Any> {
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<String, Any>] = 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<String, Any>] = 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<String, Any>]) -> [Map<String, Any>] {
let total: Int = native_list_len(tokens)
let stmts: [Map<String, Any>] = 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<String, Any>], 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, Any>) -> 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 doubleint64 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 OR when both sides are
// identifiers tracked in __int_names. Without the int-name check,
// `seen == idx` between two Int locals miscompiles to str_eq on what
// are integer values dressed as char* segfault.
if op == "EqEq" {
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 == "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) {
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 == "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) {
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 + ", EL_STR(" + 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)
if n == 0 { return "el_list_empty()" }
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, Any>) -> 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<String, Any>, 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 + " ", native_list_clone(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<String, Any>, 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 + " ", native_list_clone(declared))
if has_else {
emit_line(indent + "} else {")
cg_stmts(else_stmts, indent + " ", native_list_clone(declared))
}
emit_line(indent + "}")
}
fn cg_for_body(item: String, list_expr: Map<String, Any>, body: [Map<String, Any>], 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 + ");")
let body_decl = native_list_clone(declared)
let body_decl = native_list_append(body_decl, item)
cg_stmts(body, indent + " ", body_decl)
emit_line(indent + " }")
emit_line(indent + "}")
}
fn cg_for_stmt(expr: Map<String, Any>, 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<String, Any>], 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<String, Any>, idx: Int) -> String {
let name: String = param["name"]
"el_val_t " + name
}
fn params_to_c(params: [Map<String, Any>]) -> 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<String, Any>]) -> [Map<String, Any>] {
let n: Int = native_list_len(body)
if n == 0 { return body }
let last: Map<String, Any> = 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<String, Any>] = 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<String, Any> = { "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<String, Any>) -> 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<String, Any>]) -> 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<String, Any>) -> 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<String, Any>) -> Bool {
let kind: String = stmt["stmt"]
if kind == "FnDef" { return true }
false
}
fn is_top_level_decl(stmt: Map<String, Any>) -> 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<String, Any>) -> 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<String, Any>]) -> 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<String, Any>], 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<String, Any> = { "stmt": "None" }
let svc_count = 0
let svc_block: Map<String, Any> = { "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 <stdint.h>")
emit_line("#include <stdlib.h>")
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 <prog> <prog>.c el_runtime.c
// compile full pipeline: source string -> C source string
fn compile(source: String) -> String {
let tokens: [Map<String, Any>] = lex(source)
let stmts: [Map<String, Any>] = parse(tokens)
codegen(stmts, source)
}
// main CLI entry point for self-hosted compilation.
//
// Called by: elc <source.el> <output.c>
//
// Reads El source from args()[0], compiles it to C source, and writes the
// result to args()[1]. Then run:
// cc -o <prog> <output.c> 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)