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:
@@ -862,6 +862,97 @@ fn cg_match(expr: Map<String, Any>) -> String {
|
|||||||
str_join(parts, "")
|
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 -------------------------------------------------
|
// -- If-as-expression codegen -------------------------------------------------
|
||||||
//
|
//
|
||||||
// Lower `if cond { thenBody } else { elseBody }` used in expression position
|
// 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)
|
cg_for_stmt(val, indent, declared)
|
||||||
return declared
|
return declared
|
||||||
}
|
}
|
||||||
|
if val_kind == "Match" {
|
||||||
|
cg_match_stmt(val, indent, declared)
|
||||||
|
return declared
|
||||||
|
}
|
||||||
let val_c: String = cg_expr(val)
|
let val_c: String = cg_expr(val)
|
||||||
emit_line(indent + val_c + ";")
|
emit_line(indent + val_c + ";")
|
||||||
return declared
|
return declared
|
||||||
|
|||||||
Reference in New Issue
Block a user