perf: 81% RSS reduction — el_release, arena scoping, streaming codegen, libcurl stub
Chain of optimizations from swarm rounds 4-7: - Flat stride-2 token list: eliminate per-token Map allocation (~112B each × N tokens) - Systematic el_release() in parser.el: eagerly free intermediate parse result maps - Per-function and per-statement arena scoping in codegen_streaming() - Streaming codegen pipeline: parse one fn at a time, emit C, discard AST - HAVE_CURL guard: elc CLI binary drops libcurl, eliminating SSL/TLS init overhead - HTML codegen parts-list: O(n) instead of O(n²) string growth for nested templates - Batch c_escape: str_slice clean runs instead of char-at per byte Result: 33.4MB → 6.5MB RSS on web/src/main.el (-81%). Self-host: PASS.
This commit is contained in:
@@ -358,6 +358,8 @@ fn parse_html_attrs(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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 {
|
||||
@@ -426,6 +428,8 @@ fn parse_html_children(tokens: [Any], pos: Int, parent_tag: String) -> Map<Strin
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -445,6 +449,8 @@ fn parse_html_children(tokens: [Any], pos: Int, parent_tag: String) -> Map<Strin
|
||||
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
|
||||
@@ -456,6 +462,8 @@ fn parse_html_children(tokens: [Any], pos: Int, parent_tag: String) -> Map<Strin
|
||||
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<String, Any> = { "html": "Each", "list": list_expr, "item": item_name, "body": body_children }
|
||||
let children = native_list_append(children, each_node)
|
||||
} else {
|
||||
@@ -476,6 +484,8 @@ fn parse_html_children(tokens: [Any], pos: Int, parent_tag: String) -> Map<Strin
|
||||
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
|
||||
@@ -504,6 +514,8 @@ fn parse_html_children(tokens: [Any], pos: Int, parent_tag: String) -> Map<Strin
|
||||
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 })
|
||||
@@ -535,6 +547,8 @@ fn parse_html_element(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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
|
||||
@@ -555,6 +569,8 @@ fn parse_html_element(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -592,6 +608,8 @@ fn parse_html_template(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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 }
|
||||
@@ -649,6 +667,8 @@ fn parse_primary(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
@@ -669,6 +689,8 @@ fn parse_primary(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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" {
|
||||
@@ -714,6 +736,8 @@ fn parse_primary(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
@@ -760,6 +784,8 @@ fn parse_primary(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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" {
|
||||
@@ -773,6 +799,8 @@ fn parse_primary(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -781,6 +809,8 @@ fn parse_primary(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -789,6 +819,8 @@ fn parse_primary(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -832,9 +864,13 @@ fn parse_if(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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<String, Any>] = native_list_empty()
|
||||
let k2 = tok_kind(tokens, p)
|
||||
@@ -846,12 +882,16 @@ fn parse_if(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -866,6 +906,8 @@ fn parse_match(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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<String, Any>] = native_list_empty()
|
||||
let running = true
|
||||
@@ -881,10 +923,14 @@ fn parse_match(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
@@ -938,9 +984,13 @@ fn parse_for_expr(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -959,6 +1009,8 @@ fn parse_block(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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
|
||||
@@ -1005,6 +1057,8 @@ fn parse_postfix(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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: `<Int>.<unit>` where <unit> is one of
|
||||
// nanos | millis | seconds | minutes | hours | days (each with an
|
||||
@@ -1046,6 +1100,8 @@ fn parse_postfix(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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" {
|
||||
@@ -1066,6 +1122,8 @@ fn parse_postfix(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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 {
|
||||
@@ -1122,6 +1180,8 @@ fn parse_binop(tokens: [Any], pos: Int, min_prec: Int) -> Map<String, Any> {
|
||||
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)
|
||||
@@ -1132,6 +1192,8 @@ fn parse_binop(tokens: [Any], pos: Int, min_prec: Int) -> Map<String, Any> {
|
||||
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
|
||||
@@ -1174,6 +1236,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1190,6 +1254,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1204,6 +1270,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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") {
|
||||
@@ -1224,6 +1292,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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.
|
||||
@@ -1240,6 +1310,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1363,9 +1435,13 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1391,6 +1467,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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" {
|
||||
@@ -1399,9 +1477,11 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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" {
|
||||
@@ -1410,9 +1490,11 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
@@ -1420,6 +1502,7 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1431,6 +1514,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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") {
|
||||
@@ -1452,6 +1537,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
@@ -1475,6 +1562,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
"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
|
||||
@@ -1609,6 +1698,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1617,6 +1708,8 @@ fn parse_stmt(tokens: [Any], pos: Int) -> Map<String, Any> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1639,6 +1732,8 @@ fn parse(tokens: [Any]) -> [Map<String, Any>] {
|
||||
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 {
|
||||
@@ -1651,3 +1746,297 @@ fn parse(tokens: [Any]) -> [Map<String, Any>] {
|
||||
}
|
||||
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<String, Any> {
|
||||
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<String, Any> {
|
||||
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<String, Any>] {
|
||||
let total: Int = native_list_len(tokens) / 2
|
||||
let sigs: [Map<String, Any>] = 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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user