From cfcedff7f4be660f34afc4e3aa0e6aa56dabf326 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Sun, 3 May 2026 15:47:50 -0500 Subject: [PATCH] implement match statement codegen in El MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- el-compiler/src/codegen.el | 95 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/el-compiler/src/codegen.el b/el-compiler/src/codegen.el index 5eed473..6558816 100644 --- a/el-compiler/src/codegen.el +++ b/el-compiler/src/codegen.el @@ -862,6 +862,97 @@ fn cg_match(expr: Map) -> 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; } +// Wildcard -> else { } +fn cg_match_stmt(expr: Map, 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, 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