implement match statement codegen in El

Add cg_match_stmt() to lower match-as-statement to proper C if/else if/else
chains. Previously, match in statement position fell through to cg_expr() which
emitted a GCC statement-expression — fine for expression arms but wrong for the
statement form. Now matched using the same dispatch pattern as If and For in the
Expr handler of cg_stmt().

Pattern dispatch mirrors cg_match (expression form):
  LitStr  -> str_eq(subj, EL_STR("..."))
  LitInt  -> subj == N
  LitBool -> subj == 1 / 0
  Binding -> else { el_val_t name = subj; body; }
  Wildcard -> else { body; }

Subject is evaluated once into a scoped temporary to avoid double evaluation.
This commit is contained in:
Will Anderson
2026-05-03 15:47:50 -05:00
parent cfa4301026
commit cfcedff7f4
+95
View File
@@ -862,6 +862,97 @@ fn cg_match(expr: Map<String, Any>) -> String {
str_join(parts, "")
}
// Lower a match statement (used for side effects, not as an expression) to a
// chain of C if/else if/else blocks. The subject is evaluated once into a
// scoped temporary; each arm generates a condition and a braced body; the
// wildcard/binding arm becomes the final `else` branch.
//
// Pattern dispatch:
// LitStr -> str_eq(subj, EL_STR("..."))
// LitInt -> subj == N
// LitBool -> subj == 1 / subj == 0
// Binding -> else { el_val_t name = subj; <body> }
// Wildcard -> else { <body> }
fn cg_match_stmt(expr: Map<String, Any>, indent: String, declared: [String]) -> Void {
let subject = expr["subject"]
let arms = expr["arms"]
let subj_c: String = cg_expr(subject)
let id: String = next_match_id()
let subj_var: String = "_match_subj_" + id
let inner: String = indent + " "
emit_line(indent + "{")
emit_line(inner + "el_val_t " + subj_var + " = " + subj_c + ";")
let n: Int = native_list_len(arms)
let i = 0
let first_cond: Bool = true
while i < n {
let arm = native_list_get(arms, i)
let pat = arm["pattern"]
let body = arm["body"]
let pkind: String = pat["pattern"]
let body_c: String = cg_expr(body)
if str_eq(pkind, "LitStr") {
let v: String = pat["value"]
let cond_str = "str_eq(" + subj_var + ", EL_STR(" + c_str_lit(v) + "))"
if first_cond {
emit_line(inner + "if (" + cond_str + ") {")
let first_cond = false
} else {
emit_line(inner + "} else if (" + cond_str + ") {")
}
emit_line(inner + " " + body_c + ";")
} else {
if str_eq(pkind, "LitInt") {
let v: String = pat["value"]
let cond_str = subj_var + " == " + v
if first_cond {
emit_line(inner + "if (" + cond_str + ") {")
let first_cond = false
} else {
emit_line(inner + "} else if (" + cond_str + ") {")
}
emit_line(inner + " " + body_c + ";")
} else {
if str_eq(pkind, "LitBool") {
let v: String = pat["value"]
let bv = "0"
if str_eq(v, "true") {
let bv = "1"
}
let cond_str = subj_var + " == " + bv
if first_cond {
emit_line(inner + "if (" + cond_str + ") {")
let first_cond = false
} else {
emit_line(inner + "} else if (" + cond_str + ") {")
}
emit_line(inner + " " + body_c + ";")
} else {
// Wildcard or Binding - becomes the else branch
if first_cond {
emit_line(inner + "{")
} else {
emit_line(inner + "} else {")
}
if str_eq(pkind, "Binding") {
let bname: String = pat["name"]
emit_line(inner + " el_val_t " + bname + " = " + subj_var + ";")
}
emit_line(inner + " " + body_c + ";")
emit_line(inner + "}")
let first_cond = true
}
}
}
let i = i + 1
}
// Close any open if/else-if chain (only reached when last arm was a literal pattern)
if !first_cond {
emit_line(inner + "}")
}
emit_line(indent + "}")
}
// -- If-as-expression codegen -------------------------------------------------
//
// Lower `if cond { thenBody } else { elseBody }` used in expression position
@@ -1114,6 +1205,10 @@ fn cg_stmt(stmt: Map<String, Any>, indent: String, declared: [String]) -> [Strin
cg_for_stmt(val, indent, declared)
return declared
}
if val_kind == "Match" {
cg_match_stmt(val, indent, declared)
return declared
}
let val_c: String = cg_expr(val)
emit_line(indent + val_c + ";")
return declared