fix(codegen): add c_safe_name and fix http_serve handler cast

- c_safe_name(): mangle all C reserved keywords (short, int, long, etc.)
  to <keyword>_el so El variable names never collide with C syntax
- Apply c_safe_name in Ident expression codegen and let-binding emission
- http_serve special case: cast handler via (el_val_t)(uintptr_t) so
  function pointers are stored correctly as el_val_t integers
- Handle both 1-arg (auto-inject handle_request) and 2-arg explicit form
This commit is contained in:
Will Anderson
2026-05-04 18:40:42 -05:00
parent ca0149acb9
commit f9b1d44d38
+57 -5
View File
@@ -175,6 +175,47 @@ fn duration_unit_nanos(unit: String) -> String {
"1LL"
}
// c_safe_name mangles El variable names that are C reserved keywords.
// El allows any identifier; C does not. Append _el to avoid clashes with
// keywords like `short`, `int`, `long`, `char`, `auto`, `register`, etc.
fn c_safe_name(name: String) -> String {
if str_eq(name, "auto") { return "auto_el" }
if str_eq(name, "break") { return "break_el" }
if str_eq(name, "case") { return "case_el" }
if str_eq(name, "char") { return "char_el" }
if str_eq(name, "const") { return "const_el" }
if str_eq(name, "continue") { return "continue_el" }
if str_eq(name, "default") { return "default_el" }
if str_eq(name, "do") { return "do_el" }
if str_eq(name, "double") { return "double_el" }
if str_eq(name, "else") { return "else_el" }
if str_eq(name, "enum") { return "enum_el" }
if str_eq(name, "extern") { return "extern_el" }
if str_eq(name, "float") { return "float_el" }
if str_eq(name, "for") { return "for_el" }
if str_eq(name, "goto") { return "goto_el" }
if str_eq(name, "if") { return "if_el" }
if str_eq(name, "inline") { return "inline_el" }
if str_eq(name, "int") { return "int_el" }
if str_eq(name, "long") { return "long_el" }
if str_eq(name, "register") { return "register_el" }
if str_eq(name, "restrict") { return "restrict_el" }
if str_eq(name, "return") { return "return_el" }
if str_eq(name, "short") { return "short_el" }
if str_eq(name, "signed") { return "signed_el" }
if str_eq(name, "sizeof") { return "sizeof_el" }
if str_eq(name, "static") { return "static_el" }
if str_eq(name, "struct") { return "struct_el" }
if str_eq(name, "switch") { return "switch_el" }
if str_eq(name, "typedef") { return "typedef_el" }
if str_eq(name, "union") { return "union_el" }
if str_eq(name, "unsigned") { return "unsigned_el" }
if str_eq(name, "void") { return "void_el" }
if str_eq(name, "volatile") { return "volatile_el" }
if str_eq(name, "while") { return "while_el" }
return name
}
fn cg_expr(expr: Map<String, Any>) -> String {
let kind: String = expr["expr"]
@@ -220,7 +261,7 @@ fn cg_expr(expr: Map<String, Any>) -> String {
if kind == "Ident" {
let name: String = expr["name"]
return name
return c_safe_name(name)
}
if kind == "Not" {
@@ -690,10 +731,18 @@ fn cg_expr(expr: Map<String, Any>) -> String {
}
}
}
// http_serve(port): backward-compat 1-arg form — auto-inject handle_request
// http_serve: handler must be cast (el_val_t)(uintptr_t)fn_ptr because
// the runtime stores function pointers as tagged el_val_t integers.
// 1-arg backward-compat form auto-injects the conventional handle_request name.
if str_eq(fn_name, "http_serve") {
if arity == 1 {
return "http_serve(" + args_c + ", handle_request)"
let port_c: String = cg_expr(native_list_get(args, 0))
return "http_serve(" + port_c + ", (el_val_t)(uintptr_t)handle_request)"
}
if arity == 2 {
let port_c: String = cg_expr(native_list_get(args, 0))
let handler_c: String = cg_expr(native_list_get(args, 1))
return "http_serve(" + port_c + ", (el_val_t)(uintptr_t)" + handler_c + ")"
}
}
return fn_name + "(" + args_c + ")"
@@ -1063,11 +1112,14 @@ fn cg_stmt(stmt: Map<String, Any>, indent: String, declared: [String]) -> [Strin
if str_eq(vk, "Int") {
add_int_name(name)
}
// Mangle C reserved keywords (e.g. `short`, `int`) so the emitted
// variable name is valid C. `declared` tracking uses the El name.
let cname: String = c_safe_name(name)
if list_contains(declared, name) {
emit_line(indent + name + " = " + val_c + ";")
emit_line(indent + cname + " = " + val_c + ";")
return declared
} else {
emit_line(indent + "el_val_t " + name + " = " + val_c + ";")
emit_line(indent + "el_val_t " + cname + " = " + val_c + ";")
return native_list_append(declared, name)
}
}