// 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: [Any]) -> [Map] // -- Token access helpers ------------------------------------------------------ // Tokens is a flat [Any] list: tokens[2*i] = kind, tokens[2*i+1] = value. // This avoids one ElMap allocation per token (~112B each), saving ~4MB on large // programs. All callers use these helpers -- only these three need updating. fn tok_at(tokens: [Any], pos: Int) -> Map { let kind: String = native_list_get(tokens, pos * 2) let value: String = native_list_get(tokens, pos * 2 + 1) { "kind": kind, "value": value } } fn tok_kind(tokens: [Any], pos: Int) -> String { native_list_get(tokens, pos * 2) } fn tok_value(tokens: [Any], pos: Int) -> String { native_list_get(tokens, pos * 2 + 1) } fn expect(tokens: [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, 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: [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: [Any], 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 -------------------------------------------------------- // ── HTML template parser ────────────────────────────────────────────────────── // // HTML templates are written as unquoted HTML in expression position: // return

{title}

// // The parser detects an HTML template when parse_primary sees Lt followed // by a lowercase ident (a known or assumed HTML element name) or `!` (for // ). It then recursively parses the HTML into an AST. // // AST nodes produced: // { "expr": "HtmlTemplate", "root": child_node } // { "html": "Element", "tag": "div", "attrs": [...], "children": [...], "self_closing": bool } // { "html": "Text", "text": "..." } // { "html": "Interp", "value": expr_node } // { "html": "Each", "list": expr_node, "item": "name", "body": [...] } // { "html": "Doctype" } // { "html": "Raw", "value": expr_node } fn is_html_tag_name(name: String) -> Bool { if str_eq(name, "a") { return true } if str_eq(name, "abbr") { return true } if str_eq(name, "address") { return true } if str_eq(name, "area") { return true } if str_eq(name, "article") { return true } if str_eq(name, "aside") { return true } if str_eq(name, "audio") { return true } if str_eq(name, "b") { return true } if str_eq(name, "base") { return true } if str_eq(name, "blockquote") { return true } if str_eq(name, "body") { return true } if str_eq(name, "br") { return true } if str_eq(name, "button") { return true } if str_eq(name, "canvas") { return true } if str_eq(name, "caption") { return true } if str_eq(name, "cite") { return true } if str_eq(name, "code") { return true } if str_eq(name, "col") { return true } if str_eq(name, "colgroup") { return true } if str_eq(name, "data") { return true } if str_eq(name, "datalist") { return true } if str_eq(name, "dd") { return true } if str_eq(name, "del") { return true } if str_eq(name, "details") { return true } if str_eq(name, "dfn") { return true } if str_eq(name, "dialog") { return true } if str_eq(name, "div") { return true } if str_eq(name, "dl") { return true } if str_eq(name, "dt") { return true } if str_eq(name, "em") { return true } if str_eq(name, "embed") { return true } if str_eq(name, "fieldset") { return true } if str_eq(name, "figcaption") { return true } if str_eq(name, "figure") { return true } if str_eq(name, "footer") { return true } if str_eq(name, "form") { return true } if str_eq(name, "h1") { return true } if str_eq(name, "h2") { return true } if str_eq(name, "h3") { return true } if str_eq(name, "h4") { return true } if str_eq(name, "h5") { return true } if str_eq(name, "h6") { return true } if str_eq(name, "head") { return true } if str_eq(name, "header") { return true } if str_eq(name, "hr") { return true } if str_eq(name, "html") { return true } if str_eq(name, "i") { return true } if str_eq(name, "iframe") { return true } if str_eq(name, "img") { return true } if str_eq(name, "input") { return true } if str_eq(name, "ins") { return true } if str_eq(name, "kbd") { return true } if str_eq(name, "label") { return true } if str_eq(name, "legend") { return true } if str_eq(name, "li") { return true } if str_eq(name, "link") { return true } if str_eq(name, "main") { return true } if str_eq(name, "map") { return true } if str_eq(name, "mark") { return true } if str_eq(name, "menu") { return true } if str_eq(name, "meta") { return true } if str_eq(name, "meter") { return true } if str_eq(name, "nav") { return true } if str_eq(name, "noscript") { return true } if str_eq(name, "object") { return true } if str_eq(name, "ol") { return true } if str_eq(name, "optgroup") { return true } if str_eq(name, "option") { return true } if str_eq(name, "output") { return true } if str_eq(name, "p") { return true } if str_eq(name, "param") { return true } if str_eq(name, "picture") { return true } if str_eq(name, "pre") { return true } if str_eq(name, "progress") { return true } if str_eq(name, "q") { return true } if str_eq(name, "rp") { return true } if str_eq(name, "rt") { return true } if str_eq(name, "ruby") { return true } if str_eq(name, "s") { return true } if str_eq(name, "samp") { return true } if str_eq(name, "script") { return true } if str_eq(name, "section") { return true } if str_eq(name, "select") { return true } if str_eq(name, "small") { return true } if str_eq(name, "source") { return true } if str_eq(name, "span") { return true } if str_eq(name, "strong") { return true } if str_eq(name, "style") { return true } if str_eq(name, "sub") { return true } if str_eq(name, "summary") { return true } if str_eq(name, "sup") { return true } if str_eq(name, "table") { return true } if str_eq(name, "tbody") { return true } if str_eq(name, "td") { return true } if str_eq(name, "template") { return true } if str_eq(name, "textarea") { return true } if str_eq(name, "tfoot") { return true } if str_eq(name, "th") { return true } if str_eq(name, "thead") { return true } if str_eq(name, "time") { return true } if str_eq(name, "title") { return true } if str_eq(name, "tr") { return true } if str_eq(name, "track") { return true } if str_eq(name, "u") { return true } if str_eq(name, "ul") { return true } if str_eq(name, "var") { return true } if str_eq(name, "video") { return true } if str_eq(name, "wbr") { return true } false } fn is_void_element(name: String) -> Bool { if str_eq(name, "area") { return true } if str_eq(name, "base") { return true } if str_eq(name, "br") { return true } if str_eq(name, "col") { return true } if str_eq(name, "embed") { return true } if str_eq(name, "hr") { return true } if str_eq(name, "img") { return true } if str_eq(name, "input") { return true } if str_eq(name, "link") { return true } if str_eq(name, "meta") { return true } if str_eq(name, "param") { return true } if str_eq(name, "source") { return true } if str_eq(name, "track") { return true } if str_eq(name, "wbr") { return true } false } // Collect tokens as text content until we hit Lt, LBrace, Eof, or a // closing-tag marker (Lt Slash). Returns { "text": "...", "pos": p } fn parse_html_text_tokens(tokens: [Any], pos: Int) -> Map { let parts: [String] = native_list_empty() let p = pos let running = true while running { let k = tok_kind(tokens, p) if str_eq(k, "Eof") { let running = false } else { if str_eq(k, "Lt") { let running = false } else { if str_eq(k, "LBrace") { let running = false } else { // Check for ). fn parse_html_attrs(tokens: [Any], pos: Int) -> Map { let attrs: [Map] = native_list_empty() let p = pos let running = true while running { let k = tok_kind(tokens, p) if str_eq(k, "Gt") { let running = false } else { if str_eq(k, "Slash") { let running = false } else { if str_eq(k, "Eof") { let running = false } else { // Attribute name: could be Ident or keyword used as attr name let attr_name = tok_value(tokens, p) let p = p + 1 let k2 = tok_kind(tokens, p) if str_eq(k2, "Eq") { let p = p + 1 let k3 = tok_kind(tokens, p) if str_eq(k3, "Str") { // static: attr="value" let attr_val = tok_value(tokens, p) let p = p + 1 let attrs = native_list_append(attrs, { "name": attr_name, "kind": "static", "value": attr_val }) } else { if str_eq(k3, "LBrace") { // dynamic: attr={expr} let r = parse_expr(tokens, p + 1) let val_node = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let p = expect(tokens, p, "RBrace") let attrs = native_list_append(attrs, { "name": attr_name, "kind": "dynamic", "value": val_node }) } else { // malformed, skip } } } else { // boolean attribute let attrs = native_list_append(attrs, { "name": attr_name, "kind": "bool" }) } } } } } { "attrs": attrs, "pos": p } } // Parse the children of an HTML element until we see the closing tag // or EOF. Returns { "children": [...], "pos": p_after_closing_tag } fn parse_html_children(tokens: [Any], pos: Int, parent_tag: String) -> Map { let children: [Map] = native_list_empty() let p = pos let running = true while running { let k = tok_kind(tokens, p) if str_eq(k, "Eof") { let running = false } else { if str_eq(k, "Lt") { // Check for closing tag: — consume and stop let p = p + 2 // skip tag name let close_name = tok_value(tokens, p) let p = p + 1 // consume > let p = expect(tokens, p, "Gt") let running = false } else { if str_eq(k2, "Not") { // Possible let k3_v = tok_value(tokens, p + 2) if str_eq(k3_v, "doctype") { // consume let p = p + 2 // skip until > let scanning = true while scanning { let ck = tok_kind(tokens, p) if str_eq(ck, "Eof") { let scanning = false } if str_eq(ck, "Gt") { let p = p + 1 let scanning = false } else { let p = p + 1 } } let children = native_list_append(children, { "html": "Doctype" }) } else { let p = p + 1 } } else { // nested element let r = parse_html_element(tokens, p) let child = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let children = native_list_append(children, child) } } } else { if str_eq(k, "LBrace") { // Interpolation: {expr} or {#each ...} or {/each} let k2 = tok_kind(tokens, p + 1) if str_eq(k2, "Hash") { // {#each list as item} let k3_v = tok_value(tokens, p + 2) if str_eq(k3_v, "each") { let p = p + 3 // parse list expr up to "as" keyword let prev_no_block: String = state_get("__no_block_expr") state_set("__no_block_expr", "1") let r_list = parse_expr(tokens, p) state_set("__no_block_expr", prev_no_block) let list_expr = r_list["node"] let p = r_list["pos"] // r_list result map fully consumed — release to free peak heap. el_release(r_list) // expect "as" let p = expect(tokens, p, "As") // item variable name let item_name = tok_value(tokens, p) let p = p + 1 // consume closing } let p = expect(tokens, p, "RBrace") // parse body until {/each} let r_body = parse_html_each_body(tokens, p) let body_children = r_body["children"] let p = r_body["pos"] // r_body result map fully consumed — release to free peak heap. el_release(r_body) let each_node: Map = { "html": "Each", "list": list_expr, "item": item_name, "body": body_children } let children = native_list_append(children, each_node) } else { let p = p + 1 } } else { if str_eq(k2, "Slash") { // {/each} — end of each block, stop // skip {/each} let p = p + 2 // skip "each" let p = p + 1 // skip } let p = expect(tokens, p, "RBrace") let running = false } else { // regular {expr} let r = parse_expr(tokens, p + 1) let interp_val = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let p = expect(tokens, p, "RBrace") // Check if the expr is a call to raw() let is_raw_call = false let interp_kind: String = interp_val["expr"] if str_eq(interp_kind, "Call") { let fn_node = interp_val["func"] let fn_kind: String = fn_node["expr"] if str_eq(fn_kind, "Ident") { let fn_name_v: String = fn_node["name"] if str_eq(fn_name_v, "raw") { let is_raw_call = true } } } if is_raw_call { let raw_args = interp_val["args"] let raw_inner = native_list_get(raw_args, 0) let children = native_list_append(children, { "html": "Raw", "value": raw_inner }) } else { let children = native_list_append(children, { "html": "Interp", "value": interp_val }) } } } } else { // Text tokens — collect run of non-special tokens let r_text = parse_html_text_tokens(tokens, p) let text_str: String = r_text["text"] let p = r_text["pos"] // r_text result map fully consumed — release to free peak heap. el_release(r_text) let text_trimmed: String = str_trim(text_str) if !str_eq(text_trimmed, "") { let children = native_list_append(children, { "html": "Text", "text": text_trimmed }) } } } } } { "children": children, "pos": p } } // Parse body of {#each} until {/each}. Mirrors parse_html_children but // stops at the {/each} sentinel rather than a closing element tag. fn parse_html_each_body(tokens: [Any], pos: Int) -> Map { parse_html_children(tokens, pos, "__each__") } // Parse a single HTML element: children // or self-closing: // Pos points to the Lt token. fn parse_html_element(tokens: [Any], pos: Int) -> Map { let p = pos // consume < let p = expect(tokens, p, "Lt") // tag name let tag_name = tok_value(tokens, p) let p = p + 1 // parse attributes let r_attrs = parse_html_attrs(tokens, p) let attrs = r_attrs["attrs"] let p = r_attrs["pos"] // r_attrs result map fully consumed — release to free peak heap. el_release(r_attrs) // check for self-closing /> or void element let k = tok_kind(tokens, p) let self_closing = false if str_eq(k, "Slash") { // /> let p = p + 1 let p = expect(tokens, p, "Gt") let self_closing = true return make_result({ "html": "Element", "tag": tag_name, "attrs": attrs, "children": native_list_empty(), "self_closing": true }, p) } // consume > let p = expect(tokens, p, "Gt") // void elements have no children, no closing tag if is_void_element(tag_name) { return make_result({ "html": "Element", "tag": tag_name, "attrs": attrs, "children": native_list_empty(), "self_closing": true }, p) } // parse children let r_children = parse_html_children(tokens, p, tag_name) let children = r_children["children"] let p = r_children["pos"] // r_children result map fully consumed — release to free peak heap. el_release(r_children) make_result({ "html": "Element", "tag": tag_name, "attrs": attrs, "children": children, "self_closing": false }, p) } // Entry point for HTML template parsing. // Pos points to Lt (or Lt Not for ). // May parse an optional prefix followed by the root element. fn parse_html_template(tokens: [Any], pos: Int) -> Map { let p = pos // Check for let doctype = false let k = tok_kind(tokens, p) let k2 = tok_kind(tokens, p + 1) if str_eq(k, "Lt") { if str_eq(k2, "Not") { let k3_v = tok_value(tokens, p + 2) if str_eq(k3_v, "doctype") { let doctype = true // consume let p = p + 2 let scanning = true while scanning { let ck = tok_kind(tokens, p) if str_eq(ck, "Eof") { let scanning = false } if str_eq(ck, "Gt") { let p = p + 1 let scanning = false } else { let p = p + 1 } } } } } // Parse root element let r = parse_html_element(tokens, p) let root = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let root_with_doctype = root if doctype { let root_with_doctype = { "html": root["html"], "tag": root["tag"], "attrs": root["attrs"], "children": root["children"], "self_closing": root["self_closing"], "doctype": true } } make_result({ "expr": "HtmlTemplate", "root": root_with_doctype }, p) } fn parse_primary(tokens: [Any], 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) } // HTML template literal: ... or ... // Detected in value position only; `<` in comparison position is already // consumed by parse_binop before parse_primary is reached. if k == "Lt" { let k2 = tok_kind(tokens, pos + 1) if str_eq(k2, "Not") { return parse_html_template(tokens, pos) } if str_eq(k2, "Ident") { let tag_candidate = tok_value(tokens, pos + 1) if is_html_tag_name(tag_candidate) { return parse_html_template(tokens, pos) } } } // 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"] // r result map fully consumed — release to free peak heap. el_release(r) 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"] // r result map fully consumed — release to free peak heap. el_release(r) 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, ... } // // Suppression: when parse_if / parse_while / parse_for / parse_match // are parsing a head expression, they set __no_block_expr=1 so a stray // `{` here doesn't get gobbled as a Map literal - it belongs to the // following block. Without this, `if a || b { ... }` could mis-parse // (the `||` recursion lands at `{` and tries to read the if-body as a // Map, then loops forever when keys don't match `Str: expr`). if k == "LBrace" { let no_block: String = state_get("__no_block_expr") if str_eq(no_block, "1") { // Fall through to fallback - caller will see `{` and treat it // as the start of the block they're expecting. return make_result({ "expr": "Nil" }, pos) } 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 new_p: Int = p + 1 let new_p = expect(tokens, new_p, "Colon") let r = parse_expr(tokens, new_p) let val_node = r["node"] let new_p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let pair = { "key": key, "value": val_node } let pairs = native_list_append(pairs, pair) let k3 = tok_kind(tokens, new_p) if k3 == "Comma" { let new_p = new_p + 1 } // Non-progress guard: malformed map content can leave // parse_expr returning the same pos. Force advance. if new_p <= p { let p = p + 1 } else { let p = new_p } } } } 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 } // Anonymous function literal (lambda): fn(params) -> RetType { body } // Used for inline callbacks: dom_listen(el, "click", fn(e: Any) -> Void { ... }) // Produces a Lambda expression node (distinct from a named FnDef statement). if k == "Fn" { let p = pos + 1 let r = parse_params(tokens, p) let params = r["params"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) 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"] // r2 result map fully consumed — release to free peak heap. el_release(r2) return make_result({ "expr": "Lambda", "params": params, "body": body, "ret_type": ret_type }, p) } // Unary not if k == "Not" { let r = parse_primary(tokens, pos + 1) let inner = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) 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"] // r result map fully consumed — release to free peak heap. el_release(r) return make_result({ "expr": "Neg", "inner": inner }, p) } // Soft keywords usable as identifiers in expression position. The lexer // turns words like `target`, `to`, `via`, `deploy`, etc. into dedicated // token kinds for the deploy/retry DSLs, but they're also valid as // parameter names and local variables. When one of these appears in // expression position (where only an Ident makes sense), treat it as // an Ident carrying the original text - otherwise references to a // parameter named `target` compile to EL_NULL. if k == "Target" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "To" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Via" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Deploy" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Reason" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Times" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Fallback" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Retry" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Parallel" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Trace" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Requires" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Where" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "As" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "With" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Manager" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Engine" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Accessor" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } if k == "Vessel" { return make_result({ "expr": "Ident", "name": v }, pos + 1) } // Fallback: skip unknown token make_result({ "expr": "Nil" }, pos + 1) } fn parse_if(tokens: [Any], pos: Int) -> Map { let p = expect(tokens, pos, "If") // Suppress Map-literal parsing in the cond so a stray `{` (the start // of the then-block) isn't gobbled as a Map. let prev_no_block: String = state_get("__no_block_expr") state_set("__no_block_expr", "1") let r = parse_expr(tokens, p) state_set("__no_block_expr", prev_no_block) let cond = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let r2 = parse_block(tokens, p) let then_stmts = r2["stmts"] let p = r2["pos"] // r2 result map fully consumed — release to free peak heap. el_release(r2) 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"] // r3 result map fully consumed — release to free peak heap. el_release(r3) 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"] // r3 result map fully consumed — release to free peak heap. el_release(r3) 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: [Any], pos: Int) -> Map { let p = expect(tokens, pos, "Match") let prev_no_block: String = state_get("__no_block_expr") state_set("__no_block_expr", "1") let r = parse_expr(tokens, p) state_set("__no_block_expr", prev_no_block) let subject = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) 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"] // r2 result map fully consumed — release to free peak heap. el_release(r2) let p = expect(tokens, p, "FatArrow") let r3 = parse_expr(tokens, p) let body = r3["node"] let p = r3["pos"] // r3 result map fully consumed — release to free peak heap. el_release(r3) 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: [Any], 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) } // Check for Enum::Variant pattern (Color::Red, Status::Ok, etc.) // Lexed as: Ident ColonColon Ident let next_k = tok_kind(tokens, pos + 1) if next_k == "ColonColon" { let variant_name = tok_value(tokens, pos + 2) return make_result({ "pattern": "Variant", "enum_name": v, "variant": variant_name }, pos + 3) } 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: [Any], 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 prev_no_block: String = state_get("__no_block_expr") state_set("__no_block_expr", "1") let r = parse_expr(tokens, p) state_set("__no_block_expr", prev_no_block) let list_expr = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let r2 = parse_block(tokens, p) let body = r2["stmts"] let p = r2["pos"] // r2 result map fully consumed — release to free peak heap. el_release(r2) make_result({ "expr": "For", "item": item_name, "list": list_expr, "body": body }, p) } fn parse_block(tokens: [Any], 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 new_p: Int = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let stmts = native_list_append(stmts, stmt) // Non-progress guard: a malformed input (e.g. `||` that // dragged the parser into Map-literal mode partway through // the if-body) can leave parse_stmt returning the same pos. // Force advance so the outer compile doesn't hang. if new_p <= p { let p = p + 1 } else { let p = new_p } } } } let p = expect(tokens, p, "RBrace") { "stmts": stmts, "pos": p } } // -- Postfix expressions (calls, field access, index) ------------------------- // is_duration_unit - recognise the postfix unit suffix on a numeric literal. // Used by parse_postfix to detect `30.seconds`-shape time literals before // falling back to the generic `obj.field` field-access lowering. Singular // and plural forms map to the same nanosecond multiplier; codegen does the // arithmetic at compile time. fn is_duration_unit(name: String) -> Bool { if name == "nanos" { return true } if name == "nano" { return true } if name == "millis" { return true } if name == "milli" { return true } if name == "millisecond" { return true } if name == "milliseconds" { return true } if name == "second" { return true } if name == "seconds" { return true } if name == "minute" { return true } if name == "minutes" { return true } if name == "hour" { return true } if name == "hours" { return true } if name == "day" { return true } if name == "days" { return true } false } fn parse_postfix(tokens: [Any], pos: Int) -> Map { let r = parse_primary(tokens, pos) let node = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) // Postfix duration literal: `.` where is one of // nanos | millis | seconds | minutes | hours | days (each with an // optional plural). We recognise this before the generic Dot-as-field // path so `30.seconds` lowers to a DurationLit AST node carrying the // count and the unit, not a field access on an Int. let primary_kind: String = node["expr"] if primary_kind == "Int" { let dot_kind = tok_kind(tokens, p) if dot_kind == "Dot" { let unit_kind = tok_kind(tokens, p + 1) if unit_kind == "Ident" { let unit_name = tok_value(tokens, p + 1) if is_duration_unit(unit_name) { let count_str: String = node["value"] let node = { "expr": "DurationLit", "count": count_str, "unit": unit_name } let p = p + 2 } } } } 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"] // r2 result map fully consumed — release to free peak heap. el_release(r2) 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"] // r2 result map fully consumed — release to free peak heap. el_release(r2) 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 } if kind == "Percent" { 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 } if kind == "Percent" { return true } false } fn parse_binop(tokens: [Any], pos: Int, min_prec: Int) -> Map { let r = parse_postfix(tokens, pos) let left = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) 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"] // r2 result map fully consumed — release to free peak heap. el_release(r2) 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: [Any], pos: Int) -> Map { parse_binop(tokens, pos, 1) } // -- Statement parsing --------------------------------------------------------- fn parse_stmt(tokens: [Any], 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"] // r result map fully consumed — release to free peak heap. el_release(r) 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"] // r result map fully consumed — release to free peak heap. el_release(r) return make_result({ "stmt": "Return", "value": val }, p) } // extern fn declaration (no body - forward declaration for separate compilation) if k == "Extern" { let p = pos + 1 let k2: String = tok_kind(tokens, p) if str_eq(k2, "Fn") { let p = p + 1 let name: String = tok_value(tokens, p) let p = p + 1 let r = parse_params(tokens, p) let params = r["params"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let ret_type = "" let k3: String = tok_kind(tokens, p) if str_eq(k3, "Arrow") { let p = p + 1 let kt: String = tok_kind(tokens, p) if str_eq(kt, "Ident") { let ret_type = tok_value(tokens, p) } let p = skip_type(tokens, p) } return make_result({ "stmt": "ExternFn", "name": name, "params": params, "ret_type": ret_type }, 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"] // r result map fully consumed — release to free peak heap. el_release(r) // 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"] // r2 result map fully consumed — release to free peak heap. el_release(r2) return make_result({ "stmt": "FnDef", "name": name, "params": params, "body": body, "ret_type": ret_type }, p) } // type definition: `type Name = { field: Type, ... }` // The `=` between the name and the brace is optional in the spec but // present in practice. Skip it if present before consuming the LBrace. if k == "Type" { let p = pos + 1 let name = tok_value(tokens, p) let p = p + 1 // Consume optional `=` before the opening brace let pk = tok_kind(tokens, p) if pk == "Eq" { 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 prev_no_block: String = state_get("__no_block_expr") state_set("__no_block_expr", "1") let r = parse_expr(tokens, p) state_set("__no_block_expr", prev_no_block) let cond = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let r2 = parse_block(tokens, p) let body = r2["stmts"] let p = r2["pos"] // r2 result map fully consumed — release to free peak heap. el_release(r2) return make_result({ "stmt": "While", "cond": cond, "body": body }, p) } // break statement if k == "Break" { return make_result({ "stmt": "Break" }, pos + 1) } // continue statement if k == "Continue" { return make_result({ "stmt": "Continue" }, pos + 1) } // for loop (range or list iteration) 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 prev_no_block: String = state_get("__no_block_expr") state_set("__no_block_expr", "1") let r = parse_expr(tokens, p) state_set("__no_block_expr", prev_no_block) let start_expr = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) // Check for range operator: .. (exclusive) or ..= (inclusive) let range_k = tok_kind(tokens, p) if range_k == "DotDot" { // exclusive range: for i in start..end let p = p + 1 let r2 = parse_expr(tokens, p) let end_expr = r2["node"] let p = r2["pos"] el_release(r2) let r3 = parse_block(tokens, p) let body = r3["stmts"] let p = r3["pos"] el_release(r3) return make_result({ "stmt": "ForRange", "var": item_name, "start": start_expr, "end": end_expr, "inclusive": false, "body": body }, p) } if range_k == "DotDotEq" { // inclusive range: for i in start..=end let p = p + 1 let r2 = parse_expr(tokens, p) let end_expr = r2["node"] let p = r2["pos"] el_release(r2) let r3 = parse_block(tokens, p) let body = r3["stmts"] let p = r3["pos"] el_release(r3) return make_result({ "stmt": "ForRange", "var": item_name, "start": start_expr, "end": end_expr, "inclusive": true, "body": body }, p) } // No range operator: regular for-in (list iteration) let list_expr = start_expr let r2 = parse_block(tokens, p) let body = r2["stmts"] let p = r2["pos"] el_release(r2) return make_result({ "stmt": "For", "item": item_name, "list": list_expr, "body": body }, p) } // try/catch statement // try { body } catch (name: Type) { handler } // The catch variable name and type are both captured; type is skipped. if k == "Try" { let p = pos + 1 let r_try = parse_block(tokens, p) let try_body = r_try["stmts"] let p = r_try["pos"] // r_try result map fully consumed — release to free peak heap. el_release(r_try) let catch_name = "err" let k2 = tok_kind(tokens, p) if str_eq(k2, "Catch") { let p = p + 1 let p = expect(tokens, p, "LParen") // catch variable name let kn = tok_kind(tokens, p) if str_eq(kn, "Ident") { let catch_name = tok_value(tokens, p) let p = p + 1 } // optional type annotation: : Type let k3 = tok_kind(tokens, p) if str_eq(k3, "Colon") { let p = p + 1 let p = skip_type(tokens, p) } let p = expect(tokens, p, "RParen") let r_catch = parse_block(tokens, p) let catch_body = r_catch["stmts"] let p = r_catch["pos"] // r_catch result map fully consumed — release to free peak heap. el_release(r_catch) return make_result({ "stmt": "TryCatch", "try_body": try_body, "catch_name": catch_name, "catch_body": catch_body }, p) } return make_result({ "stmt": "TryCatch", "try_body": try_body, "catch_name": catch_name, "catch_body": native_list_empty() }, 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 } // r result map fully consumed — release to free peak heap. el_release(r) 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) } // assert [ , ] // The message is optional — if the next token after the condition is not a // Comma, emit an empty string placeholder so the test still works. if k == "Assert" { let p: Int = pos + 1 let cond_r = parse_expr(tokens, p) let cond_node = cond_r["node"] let p: Int = cond_r["pos"] el_release(cond_r) let after_k: String = tok_kind(tokens, p) if str_eq(after_k, "Comma") { let p = p + 1 let msg_r = parse_expr(tokens, p) let msg_node = msg_r["node"] let p: Int = msg_r["pos"] el_release(msg_r) return make_result({ "stmt": "Assert", "cond": cond_node, "msg": msg_node }, p) } // No message — use empty string placeholder. return make_result({ "stmt": "Assert", "cond": cond_node, "msg": { "expr": "Str", "value": "" } }, p) } // Bare reassignment: `name = expr`. Handled BEFORE the expression // fallback so we don't drop the assign on the floor and emit three // orphan expressions (the original silent-miscompile bug). El's `let` // already permits redeclaration, so this only applies when the parser // sees an Ident followed directly by `=`. `==` is a separate kind // (EqEq) so there's no ambiguity. if k == "Ident" { let k2 = tok_kind(tokens, pos + 1) if k2 == "Eq" { let name = tok_value(tokens, pos) let p = pos + 2 let r = parse_expr(tokens, p) let val = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) return make_result({ "stmt": "Assign", "name": name, "value": val }, p) } } // bare expression or if/match statement let r = parse_expr(tokens, pos) let val = r["node"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) make_result({ "stmt": "Expr", "value": val }, p) } // -- Top-level parse ------------------------------------------------------------ fn parse(tokens: [Any]) -> [Map] { // Flat list: 2 entries per token, so divide by 2 for token count. let total: Int = native_list_len(tokens) / 2 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"] // r result map fully consumed — release to free peak heap. el_release(r) 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 } // -- Streaming parse helpers --------------------------------------------------- // parse_one — parse exactly one top-level statement at position `pos`. // Returns { "node": stmt_map, "pos": new_pos }. // Enables the streaming compiler pipeline (parse one → emit C → discard). fn parse_one(tokens: [Any], pos: Int) -> Map { parse_stmt(tokens, pos) } // skip_to_rbrace — advance past a balanced { ... } block. // On entry, pos must point at the LBrace token. // Returns the position of the token AFTER the matching RBrace. fn skip_to_rbrace(tokens: [Any], pos: Int) -> Int { let total: Int = native_list_len(tokens) / 2 let p: Int = pos + 1 let depth: Int = 1 let going: Bool = true while going { if p >= total { let going = false } else { let kk: String = tok_kind(tokens, p) if str_eq(kk, "Eof") { let going = false } else { if str_eq(kk, "LBrace") { let depth = depth + 1 let p = p + 1 } else { if str_eq(kk, "RBrace") { let depth = depth - 1 let p = p + 1 if depth <= 0 { let going = false } } else { let p = p + 1 } } } } } p } // is_stmt_start_kind — true if `k` is a token kind that can start a new // top-level statement (used to find safe stopping points during token skips). fn is_stmt_start_kind(k: String) -> Bool { if str_eq(k, "Fn") { return true } if str_eq(k, "Let") { return true } if str_eq(k, "Extern") { return true } if str_eq(k, "Cgi") { return true } if str_eq(k, "Service") { return true } if str_eq(k, "Type") { return true } if str_eq(k, "Enum") { return true } if str_eq(k, "Import") { return true } if str_eq(k, "From") { return true } if str_eq(k, "Eof") { return true } false } // skip_expr_to_stmt_boundary — skip tokens from `pos` until we reach a // token that could start a new top-level statement, staying depth-aware // so that braces inside expressions don't fool us. fn skip_expr_to_stmt_boundary(tokens: [Any], pos: Int) -> Int { let total: Int = native_list_len(tokens) / 2 let p: Int = pos let depth: Int = 0 let going: Bool = true while going { if p >= total { let going = false } else { let kk: String = tok_kind(tokens, p) if str_eq(kk, "Eof") { let going = false } else { if str_eq(kk, "LBrace") { let depth = depth + 1 let p = p + 1 } else { if str_eq(kk, "RBrace") { if depth <= 0 { let going = false } else { let depth = depth - 1 let p = p + 1 } } else { if depth == 0 { if is_stmt_start_kind(kk) { let going = false } else { let p = p + 1 } } else { let p = p + 1 } } } } } } p } // scan_params_c — scan a parameter list `(name: Type, ...)` starting at // position `pos` (which should point at LParen) and return the C parameter // declaration string along with the new position. // Returns { "c": String, "pos": Int }. // This avoids allocating param-map objects during the pre-scan phase. fn scan_params_c(tokens: [Any], pos: Int) -> Map { let p: Int = expect(tokens, pos, "LParen") let parts: [String] = native_list_empty() let going: Bool = true while going { let kk: String = tok_kind(tokens, p) if str_eq(kk, "RParen") { let going = false } else { if str_eq(kk, "Eof") { let going = false } else { let pname: String = tok_value(tokens, p) let p = p + 1 let p = expect(tokens, p, "Colon") let p = skip_type(tokens, p) let parts = native_list_append(parts, "el_val_t " + pname) let k2: String = tok_kind(tokens, p) if str_eq(k2, "Comma") { let p = p + 1 } } } } let p = expect(tokens, p, "RParen") let c_str: String = str_join(parts, ", ") // parts list fully consumed — release to free peak heap. el_release(parts) if str_eq(c_str, "") { let c_str = "void" } { "c": c_str, "pos": p } } // scan_fn_sigs — lightweight token-level pre-scan. // // Returns a list of descriptor maps (one per top-level item) without building // full expression ASTs or param-map objects. Descriptors have these shapes: // // fn/extern_fn: { "kind": "fn"|"extern_fn", "name": String, // "params_c": String, <- C param decl string, e.g. "el_val_t a, el_val_t b" // "is_main": Bool } // toplevel_let: { "kind": "toplevel_let", "name": String, "ltype": String } // cgi_block: { "kind": "cgi_block", "name": String } // service_block: { "kind": "service_block", "name": String } // // Import/TypeDef/EnumDef nodes are skipped (codegen treats them as no-ops). // // The scan allocates only small string values per entry, keeping peak RSS low. fn scan_fn_sigs(tokens: [Any]) -> [Map] { let total: Int = native_list_len(tokens) / 2 let sigs: [Map] = native_list_empty() let pos: Int = 0 let going: Bool = true while going { if pos >= total { let going = false } else { let k: String = tok_kind(tokens, pos) if str_eq(k, "Eof") { let going = false } else { // --- fn definition --- if str_eq(k, "Fn") { let p: Int = pos + 1 let name: String = tok_value(tokens, p) let p = p + 1 let r = scan_params_c(tokens, p) let params_c: String = r["c"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) // skip return type let k2: String = tok_kind(tokens, p) if str_eq(k2, "Arrow") { let p = p + 1 let p = skip_type(tokens, p) } // skip body let k3: String = tok_kind(tokens, p) if str_eq(k3, "LBrace") { let p = skip_to_rbrace(tokens, p) } let is_main: Bool = str_eq(name, "main") let sigs = native_list_append(sigs, { "kind": "fn", "name": name, "params_c": params_c, "is_main": is_main }) let pos = p } else { // --- extern fn --- if str_eq(k, "Extern") { let p: Int = pos + 1 let k2: String = tok_kind(tokens, p) if str_eq(k2, "Fn") { let p = p + 1 let name: String = tok_value(tokens, p) let p = p + 1 let r = scan_params_c(tokens, p) let params_c: String = r["c"] let p = r["pos"] // r result map fully consumed — release to free peak heap. el_release(r) let k3: String = tok_kind(tokens, p) if str_eq(k3, "Arrow") { let p = p + 1 let p = skip_type(tokens, p) } let sigs = native_list_append(sigs, { "kind": "extern_fn", "name": name, "params_c": params_c, "is_main": false }) let pos = p } else { let pos = pos + 1 } } else { // --- top-level let --- if str_eq(k, "Let") { let p: Int = pos + 1 let name: String = tok_value(tokens, p) let p = p + 1 let ltype: String = "" let k2: String = tok_kind(tokens, p) if str_eq(k2, "Colon") { let p = p + 1 let kt: String = tok_kind(tokens, p) if str_eq(kt, "Ident") { let ltype = tok_value(tokens, p) } let p = skip_type(tokens, p) } let p = expect(tokens, p, "Eq") let p = skip_expr_to_stmt_boundary(tokens, p) let sigs = native_list_append(sigs, { "kind": "toplevel_let", "name": name, "ltype": ltype }) let pos = p } else { // --- cgi block --- if str_eq(k, "Cgi") { let p: Int = pos + 1 let name: String = tok_value(tokens, p) let p = p + 1 let k2: String = tok_kind(tokens, p) if str_eq(k2, "LBrace") { let p = skip_to_rbrace(tokens, p) } let sigs = native_list_append(sigs, { "kind": "cgi_block", "name": name }) let pos = p } else { // --- service block --- if str_eq(k, "Service") { let p: Int = pos + 1 let name: String = tok_value(tokens, p) let p = p + 1 let k2: String = tok_kind(tokens, p) if str_eq(k2, "LBrace") { let p = skip_to_rbrace(tokens, p) } let sigs = native_list_append(sigs, { "kind": "service_block", "name": name }) let pos = p } else { // Import, Type, Enum, From, or any other token. // Skip ahead to the next statement boundary. let p: Int = pos + 1 let p = skip_expr_to_stmt_boundary(tokens, p) let pos = p }}}}} } } } sigs }