Add separate compilation: extern fn, --emit-header, elb build coordinator
This commit is contained in:
+100
-55
@@ -18,26 +18,26 @@
|
||||
fn c_escape(s: String) -> String {
|
||||
let chars: [String] = native_string_chars(s)
|
||||
let total: Int = native_list_len(chars)
|
||||
let out = ""
|
||||
let parts: [String] = native_list_empty()
|
||||
let i = 0
|
||||
while i < total {
|
||||
let ch: String = native_list_get(chars, i)
|
||||
if ch == "\"" {
|
||||
let out = out + "\\\""
|
||||
let parts = native_list_append(parts, "\\\"")
|
||||
} else {
|
||||
if ch == "\\" {
|
||||
let out = out + "\\\\"
|
||||
let parts = native_list_append(parts, "\\\\")
|
||||
} else {
|
||||
if ch == "\n" {
|
||||
let out = out + "\\n"
|
||||
let parts = native_list_append(parts, "\\n")
|
||||
} else {
|
||||
if ch == "\r" {
|
||||
let out = out + "\\r"
|
||||
let parts = native_list_append(parts, "\\r")
|
||||
} else {
|
||||
if ch == "\t" {
|
||||
let out = out + "\\t"
|
||||
let parts = native_list_append(parts, "\\t")
|
||||
} else {
|
||||
let out = out + ch
|
||||
let parts = native_list_append(parts, ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ fn c_escape(s: String) -> String {
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
out
|
||||
str_join(parts, "")
|
||||
}
|
||||
|
||||
fn c_str_lit(s: String) -> String {
|
||||
@@ -191,6 +191,25 @@ fn cg_expr(expr: Map<String, Any>) -> String {
|
||||
let left_kind: String = left["expr"]
|
||||
let right_kind: String = right["expr"]
|
||||
|
||||
// ── String/equality fast-path: skip O(N²) temporal traversals ────────
|
||||
// The 10 temporal predicates below each recurse into the left subtree:
|
||||
// O(depth) state_get calls per predicate, O(N²) total for a chain of N
|
||||
// string-concat BinOps (e.g. the 70-100-part HTML chains in soul.el).
|
||||
// When either operand is a bare Str literal the result is always concat
|
||||
// or str_eq — no temporal dispatch is possible. Exit immediately.
|
||||
if str_eq(op, "Plus") {
|
||||
if str_eq(left_kind, "Str") { return "el_str_concat(" + left_c + ", " + right_c + ")" }
|
||||
if str_eq(right_kind, "Str") { return "el_str_concat(" + left_c + ", " + right_c + ")" }
|
||||
}
|
||||
if str_eq(op, "EqEq") {
|
||||
if str_eq(left_kind, "Str") { return "str_eq(" + left_c + ", " + right_c + ")" }
|
||||
if str_eq(right_kind, "Str") { return "str_eq(" + left_c + ", " + right_c + ")" }
|
||||
}
|
||||
if str_eq(op, "NotEq") {
|
||||
if str_eq(left_kind, "Str") { return "!str_eq(" + left_c + ", " + right_c + ")" }
|
||||
if str_eq(right_kind, "Str") { return "!str_eq(" + left_c + ", " + right_c + ")" }
|
||||
}
|
||||
|
||||
// ── Temporal-type dispatch (Instant + Duration first-class) ────────
|
||||
// Run BEFORE the int / string / generic paths so typed temporal
|
||||
// operands route through the runtime wrappers and invalid combos
|
||||
@@ -565,17 +584,15 @@ fn cg_expr(expr: Map<String, Any>) -> String {
|
||||
let arity: Int = native_list_len(args)
|
||||
let func_kind: String = func["expr"]
|
||||
|
||||
let args_c = ""
|
||||
let args_parts: [String] = native_list_empty()
|
||||
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 args_parts = native_list_append(args_parts, arg_c)
|
||||
let i = i + 1
|
||||
}
|
||||
let args_c: String = str_join(args_parts, ", ")
|
||||
|
||||
if func_kind == "Ident" {
|
||||
let fn_name: String = func["name"]
|
||||
@@ -658,18 +675,15 @@ fn cg_expr(expr: Map<String, Any>) -> String {
|
||||
// Empty literal: el_list_new(0, ) generates malformed C (trailing
|
||||
// comma in a varargs call). Emit el_list_empty() directly.
|
||||
if n == 0 { return "el_list_empty()" }
|
||||
let items = ""
|
||||
let items_parts: [String] = native_list_empty()
|
||||
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 items_parts = native_list_append(items_parts, elem_c)
|
||||
let i = i + 1
|
||||
}
|
||||
return "el_list_new(" + native_int_to_str(n) + ", " + items + ")"
|
||||
return "el_list_new(" + native_int_to_str(n) + ", " + str_join(items_parts, ", ") + ")"
|
||||
}
|
||||
|
||||
if kind == "Map" {
|
||||
@@ -680,20 +694,17 @@ fn cg_expr(expr: Map<String, Any>) -> String {
|
||||
// shadowing inside for/while/if bodies — `let acc: Map = {}` —
|
||||
// doesn't fail downstream cc with parse errors.
|
||||
if n == 0 { return "el_map_new(0)" }
|
||||
let items = ""
|
||||
let items_parts: [String] = native_list_empty()
|
||||
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 items_parts = native_list_append(items_parts, c_str_lit(key) + ", " + val_c)
|
||||
let i = i + 1
|
||||
}
|
||||
return "el_map_new(" + native_int_to_str(n) + ", " + items + ")"
|
||||
return "el_map_new(" + native_int_to_str(n) + ", " + str_join(items_parts, ", ") + ")"
|
||||
}
|
||||
|
||||
if kind == "Try" {
|
||||
@@ -736,7 +747,9 @@ fn cg_match(expr: Map<String, Any>) -> String {
|
||||
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; "
|
||||
// Accumulate arm fragments into a list to avoid O(n²) string growth.
|
||||
let parts: [String] = native_list_empty()
|
||||
let parts = native_list_append(parts, "({ 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 {
|
||||
@@ -746,19 +759,19 @@ fn cg_match(expr: Map<String, Any>) -> String {
|
||||
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 + "; } "
|
||||
let parts = native_list_append(parts, "{ " + 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 + "; } "
|
||||
let parts = native_list_append(parts, "{ 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 + "; } "
|
||||
let parts = native_list_append(parts, "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 + "; } "
|
||||
let parts = native_list_append(parts, "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"]
|
||||
@@ -766,10 +779,10 @@ fn cg_match(expr: Map<String, Any>) -> String {
|
||||
if str_eq(v, "true") {
|
||||
let bv = "1"
|
||||
}
|
||||
let out = out + "if (" + subj_var + " == " + bv + ") { " + result_var + " = (" + body_c + "); goto " + done_label + "; } "
|
||||
let parts = native_list_append(parts, "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 parts = native_list_append(parts, "{ " + result_var + " = (" + body_c + "); goto " + done_label + "; } ")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -777,8 +790,8 @@ fn cg_match(expr: Map<String, Any>) -> String {
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
let out = out + done_label + ":; " + result_var + "; })"
|
||||
out
|
||||
let parts = native_list_append(parts, done_label + ":; " + result_var + "; })")
|
||||
str_join(parts, "")
|
||||
}
|
||||
|
||||
// ── If-as-expression codegen ─────────────────────────────────────────────────
|
||||
@@ -809,7 +822,8 @@ fn next_if_id() -> String {
|
||||
// result var stays at its initial 0.
|
||||
fn cg_if_expr_arm(stmts: [Map<String, Any>], result_var: String) -> String {
|
||||
let n: Int = native_list_len(stmts)
|
||||
let out = ""
|
||||
// Collect statement fragments into a list to avoid O(n²) string growth.
|
||||
let parts: [String] = native_list_empty()
|
||||
let i = 0
|
||||
while i < n {
|
||||
let s = native_list_get(stmts, i)
|
||||
@@ -820,20 +834,20 @@ fn cg_if_expr_arm(stmts: [Map<String, Any>], result_var: String) -> String {
|
||||
let name: String = s["name"]
|
||||
let val = s["value"]
|
||||
let val_c: String = cg_expr(val)
|
||||
let out = out + "el_val_t " + name + " = " + val_c + "; "
|
||||
let parts = native_list_append(parts, "el_val_t " + name + " = " + val_c + "; ")
|
||||
} else {
|
||||
if str_eq(sk, "Return") {
|
||||
let val = s["value"]
|
||||
let val_c: String = cg_expr(val)
|
||||
let out = out + result_var + " = (" + val_c + "); "
|
||||
let parts = native_list_append(parts, result_var + " = (" + val_c + "); ")
|
||||
} else {
|
||||
if str_eq(sk, "Expr") {
|
||||
let val = s["value"]
|
||||
let val_c: String = cg_expr(val)
|
||||
if is_last {
|
||||
let out = out + result_var + " = (" + val_c + "); "
|
||||
let parts = native_list_append(parts, result_var + " = (" + val_c + "); ")
|
||||
} else {
|
||||
let out = out + "(void)(" + val_c + "); "
|
||||
let parts = native_list_append(parts, "(void)(" + val_c + "); ")
|
||||
}
|
||||
} else {
|
||||
if str_eq(sk, "Assign") {
|
||||
@@ -844,7 +858,7 @@ fn cg_if_expr_arm(stmts: [Map<String, Any>], result_var: String) -> String {
|
||||
let aname: String = s["name"]
|
||||
let aval = s["value"]
|
||||
let aval_c: String = cg_expr(aval)
|
||||
let out = out + aname + " = " + aval_c + "; "
|
||||
let parts = native_list_append(parts, aname + " = " + aval_c + "; ")
|
||||
} else {
|
||||
// Non-trivial stmt kinds (While/For) shouldn't appear in
|
||||
// expression-position arm bodies; emit nothing rather
|
||||
@@ -855,7 +869,7 @@ fn cg_if_expr_arm(stmts: [Map<String, Any>], result_var: String) -> String {
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
out
|
||||
str_join(parts, "")
|
||||
}
|
||||
|
||||
fn cg_if_expr(expr: Map<String, Any>) -> String {
|
||||
@@ -1053,6 +1067,7 @@ fn cg_stmt(stmt: Map<String, Any>, indent: String, declared: [String]) -> [Strin
|
||||
if kind == "TypeDef" { return declared }
|
||||
if kind == "EnumDef" { return declared }
|
||||
if kind == "Import" { return declared }
|
||||
if kind == "ExternFn" { return declared }
|
||||
if kind == "CgiBlock" { return declared }
|
||||
declared
|
||||
}
|
||||
@@ -1084,14 +1099,7 @@ fn strip_outer_parens(s: String) -> String {
|
||||
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
|
||||
return str_slice(s, 1, n - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1168,18 +1176,15 @@ fn param_decl(param: Map<String, Any>, idx: Int) -> String {
|
||||
fn params_to_c(params: [Map<String, Any>]) -> String {
|
||||
let n: Int = native_list_len(params)
|
||||
if n == 0 { return "void" }
|
||||
let out = ""
|
||||
let parts: [String] = native_list_empty()
|
||||
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 parts = native_list_append(parts, decl)
|
||||
let i = i + 1
|
||||
}
|
||||
out
|
||||
str_join(parts, ", ")
|
||||
}
|
||||
|
||||
// Transform a function body so that an implicit-return final expression
|
||||
@@ -2295,6 +2300,7 @@ fn is_top_level_decl(stmt: Map<String, Any>) -> Bool {
|
||||
if kind == "EnumDef" { return true }
|
||||
if kind == "Import" { return true }
|
||||
if kind == "CgiBlock" { return true }
|
||||
if kind == "ExternFn" { return true }
|
||||
false
|
||||
}
|
||||
|
||||
@@ -2519,6 +2525,12 @@ fn codegen(stmts: [Map<String, Any>], source: String) -> String {
|
||||
emit_line("el_val_t " + fn_name + "(" + params_c + ");")
|
||||
}
|
||||
}
|
||||
if kind == "ExternFn" {
|
||||
let fn_name: String = stmt["name"]
|
||||
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()
|
||||
@@ -2557,6 +2569,36 @@ fn codegen(stmts: [Map<String, Any>], source: String) -> String {
|
||||
emit_blank()
|
||||
}
|
||||
|
||||
// Detect whether this compilation unit has an entry point.
|
||||
// A unit is a library (no C main emitted) when there is no fn main()
|
||||
// and no top-level executable statements. This supports separate
|
||||
// compilation: library .c files contain only function definitions.
|
||||
let has_el_main = false
|
||||
let has_toplevel_stmts = false
|
||||
let i = 0
|
||||
while i < n {
|
||||
let stmt = native_list_get(stmts, i)
|
||||
let sk: String = stmt["stmt"]
|
||||
if str_eq(sk, "FnDef") {
|
||||
let fn_name_chk: String = stmt["name"]
|
||||
if str_eq(fn_name_chk, "main") { let has_el_main = true }
|
||||
}
|
||||
if !is_fndef(stmt) {
|
||||
if !is_top_level_decl(stmt) {
|
||||
if !str_eq(sk, "Let") {
|
||||
let has_toplevel_stmts = true
|
||||
}
|
||||
}
|
||||
}
|
||||
let i = i + 1
|
||||
}
|
||||
let is_library = false
|
||||
if !has_el_main {
|
||||
if !has_toplevel_stmts {
|
||||
let is_library = true
|
||||
}
|
||||
}
|
||||
|
||||
// Function definitions
|
||||
let i = 0
|
||||
while i < n {
|
||||
@@ -2567,6 +2609,9 @@ fn codegen(stmts: [Map<String, Any>], source: String) -> String {
|
||||
let i = i + 1
|
||||
}
|
||||
|
||||
// Skip C main() for library units (no fn main, no top-level stmts)
|
||||
if is_library { return "" }
|
||||
|
||||
// main(). Use _argc/_argv so El programs are free to declare their own
|
||||
// local `argv` / `argc` (compiler.el itself does this) without colliding
|
||||
// with the C-side parameters when fn main()'s body is folded in below.
|
||||
|
||||
Reference in New Issue
Block a user