selfhost: fix pointer field access, algebraic enums, string literal escaping

- hir_lower: use per-function scope instead of global scope for params
  fixes p->error emission for pointer parameters
- parser: assign variant4..variant7 in enum decls (was only 0..3)
- c_backend: escape quotes, backslash, newline, tab, cr in string literals
- runtime: bux_write_file now writes raw bytes (was un-escaping sequences)
- c_backend: emit algebraic enums as tagged unions (tag enum + data union + struct)
- hir_lower: add hBreak/hContinue lowering
- sema: skip empty variant names when collecting enum globals
This commit is contained in:
2026-06-06 23:54:09 +03:00
parent 3ae8c60ebb
commit 0bdef58182
6 changed files with 370 additions and 65 deletions
+228 -11
View File
@@ -2,6 +2,7 @@
// Generates C code from the HIR.
module CBackend {
// ---------------------------------------------------------------------------
// Type → C type name
// ---------------------------------------------------------------------------
@@ -102,13 +103,49 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
if node.intValue == tkNull {
StringBuilder_Append(&cbe.sb, "0");
} else {
StringBuilder_Append(&cbe.sb, node.strValue);
let s: String = node.strValue;
let slen: int = String_Len(s) as int;
var start: int = 0;
var end: int = slen;
// If the literal is wrapped in quotes (from lexer), preserve outer quotes
// and only escape the inner content.
if slen >= 2 && s[0] == 34 as char8 && s[slen - 1] == 34 as char8 {
StringBuilder_Append(&cbe.sb, "\"");
start = 1;
end = slen - 1;
}
var i: int = start;
while i < end {
let c: int = s[i] as int;
if c == 34 {
StringBuilder_Append(&cbe.sb, "\\\"");
} else if c == 92 {
StringBuilder_Append(&cbe.sb, "\\\\");
} else if c == 10 {
StringBuilder_Append(&cbe.sb, "\\n");
} else if c == 9 {
StringBuilder_Append(&cbe.sb, "\\t");
} else if c == 13 {
StringBuilder_Append(&cbe.sb, "\\r");
} else {
StringBuilder_AppendChar(&cbe.sb, c as char8);
}
i = i + 1;
}
if slen >= 2 && s[0] == 34 as char8 && s[slen - 1] == 34 as char8 {
StringBuilder_Append(&cbe.sb, "\"");
}
}
return;
}
// Variable
if kind == hVar {
if node.intValue != 0 {
StringBuilder_Append(&cbe.sb, "/* hVar tk=");
StringBuilder_AppendInt(&cbe.sb, node.intValue);
StringBuilder_Append(&cbe.sb, " */");
}
StringBuilder_Append(&cbe.sb, node.strValue);
return;
}
@@ -228,6 +265,7 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
// If
if kind == hIf {
StringBuilder_Append(&cbe.sb, "if (");
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ") {\n");
@@ -296,10 +334,32 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
return;
}
// Field access: obj.field — emit as obj->field (since most are pointers)
// Break / Continue
if kind == hBreak {
StringBuilder_Append(&cbe.sb, "break");
return;
}
if kind == hContinue {
StringBuilder_Append(&cbe.sb, "continue");
return;
}
// Field access: obj.field — use -> if base is pointer, else .
if kind == hFieldPtr {
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, "->");
var isPtr: bool = false;
if node.child1 != null as *HirNode {
if node.child1.typeKind == tyPointer {
isPtr = true;
} else if String_EndsWith(node.child1.typeName, "*") {
isPtr = true;
}
}
if isPtr {
StringBuilder_Append(&cbe.sb, "->");
} else {
StringBuilder_Append(&cbe.sb, ".");
}
StringBuilder_Append(&cbe.sb, node.strValue);
return;
}
@@ -326,7 +386,53 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
}
// Load: *ptr — emit as *ptr (dereference)
// Optimize common patterns to avoid & / * temporaries
if kind == hLoad {
if node.child1 != null as *HirNode {
let ptrNode: *HirNode = node.child1;
let ptrKind: int = ptrNode.kind;
// field access: load(field_ptr(base, field)) → base.field
if ptrKind == hFieldPtr {
CBE_EmitExpr(cbe, ptrNode.child1);
var isPtr: bool = false;
if ptrNode.child1 != null as *HirNode {
if ptrNode.child1.typeKind == tyPointer {
isPtr = true;
} else if String_EndsWith(ptrNode.child1.typeName, "*") {
isPtr = true;
}
}
if isPtr {
StringBuilder_Append(&cbe.sb, "->");
} else {
StringBuilder_Append(&cbe.sb, ".");
}
StringBuilder_Append(&cbe.sb, ptrNode.strValue);
return;
}
// arrow field: load(arrow_field(base, field)) → base->field
if ptrKind == hArrowField {
CBE_EmitExpr(cbe, ptrNode.child1);
StringBuilder_Append(&cbe.sb, "->");
StringBuilder_Append(&cbe.sb, ptrNode.strValue);
return;
}
// arrow field: load(arrow_field(base, field)) → base->field
if ptrKind == hArrowField {
CBE_EmitExpr(cbe, ptrNode.child1);
StringBuilder_Append(&cbe.sb, "->");
StringBuilder_Append(&cbe.sb, ptrNode.strValue);
return;
}
// index: load(index_ptr(base, idx)) → base[idx]
if ptrKind == hIndexPtr {
CBE_EmitExpr(cbe, ptrNode.child1);
StringBuilder_Append(&cbe.sb, "[");
CBE_EmitExpr(cbe, ptrNode.child2);
StringBuilder_Append(&cbe.sb, "]");
return;
}
}
StringBuilder_Append(&cbe.sb, "(*");
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ")");
@@ -467,11 +573,18 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
func CBE_IsGenericTypeName(name: String) -> bool {
if String_Eq(name, "T") || String_Eq(name, "K") || String_Eq(name, "V") { return true; }
if String_Eq(name, "T*") || String_Eq(name, "K*") || String_Eq(name, "V*") { return true; }
// Any type name containing '<' is a generic instantiation or parameter
if String_Contains(name, "<") { return true; }
// Generic container structs and their pointer variants
if String_Eq(name, "Array") || String_Eq(name, "Array*") { return true; }
if String_Eq(name, "Channel") || String_Eq(name, "Channel*") { return true; }
if String_Eq(name, "Iter") || String_Eq(name, "Iter*") { return true; }
if String_Eq(name, "Set") || String_Eq(name, "Set*") { return true; }
if String_Eq(name, "SetEntry") || String_Eq(name, "SetEntry*") { return true; }
if String_Eq(name, "StringMapEntry") || String_Eq(name, "StringMapEntry*") { return true; }
if String_Eq(name, "Map") || String_Eq(name, "Map*") { return true; }
if String_Eq(name, "MapEntry") || String_Eq(name, "MapEntry*") { return true; }
if String_Eq(name, "StringMap") || String_Eq(name, "StringMap*") { return true; }
if String_Eq(name, "StringMapEntry") || String_Eq(name, "StringMapEntry*") { return true; }
return false;
}
@@ -550,17 +663,105 @@ func CBackend_Generate(mod: *HirModule) -> String {
}
StringBuilder_Append(&cbe.sb, "\n");
// Enum definitions (typedef to int)
// Enum definitions
var ei: int = 0;
while ei < mod.enumCount {
StringBuilder_Append(&cbe.sb, "typedef int ");
StringBuilder_Append(&cbe.sb, mod.enums[ei].name);
StringBuilder_Append(&cbe.sb, ";\n");
let en: *HirEnum = &mod.enums[ei];
var hasData: bool = false;
var vi: int = 0;
while vi < en.variantCount {
if en.variants[vi].fieldCount > 0 {
hasData = true;
}
vi = vi + 1;
}
if !hasData {
// Simple enum: typedef enum { A, B } Name;
StringBuilder_Append(&cbe.sb, "typedef enum {\n");
vi = 0;
while vi < en.variantCount {
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, en.name);
StringBuilder_Append(&cbe.sb, "_");
StringBuilder_Append(&cbe.sb, en.variants[vi].name);
if vi < en.variantCount - 1 {
StringBuilder_Append(&cbe.sb, ",");
}
StringBuilder_Append(&cbe.sb, "\n");
vi = vi + 1;
}
StringBuilder_Append(&cbe.sb, "} ");
StringBuilder_Append(&cbe.sb, en.name);
StringBuilder_Append(&cbe.sb, ";\n\n");
} else {
// Algebraic enum: tag enum + data union + struct
// 1. Tag enum
StringBuilder_Append(&cbe.sb, "typedef enum {\n");
vi = 0;
while vi < en.variantCount {
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, en.name);
StringBuilder_Append(&cbe.sb, "_");
StringBuilder_Append(&cbe.sb, en.variants[vi].name);
if vi < en.variantCount - 1 {
StringBuilder_Append(&cbe.sb, ",");
}
StringBuilder_Append(&cbe.sb, "\n");
vi = vi + 1;
}
StringBuilder_Append(&cbe.sb, "} ");
StringBuilder_Append(&cbe.sb, en.name);
StringBuilder_Append(&cbe.sb, "_Tag;\n\n");
// 2. Data union
StringBuilder_Append(&cbe.sb, "typedef union {\n");
vi = 0;
while vi < en.variantCount {
let ev: *HirEnumVariant = &en.variants[vi];
if ev.fieldCount > 0 {
if ev.fieldCount == 1 {
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType0));
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, ev.name);
StringBuilder_Append(&cbe.sb, "_0;\n");
} else {
StringBuilder_Append(&cbe.sb, " struct {\n");
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType0));
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, ev.fieldName0);
StringBuilder_Append(&cbe.sb, ";\n");
if ev.fieldCount > 1 {
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(ev.fieldType1));
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, ev.fieldName1);
StringBuilder_Append(&cbe.sb, ";\n");
}
StringBuilder_Append(&cbe.sb, " } ");
StringBuilder_Append(&cbe.sb, ev.name);
StringBuilder_Append(&cbe.sb, ";\n");
}
}
vi = vi + 1;
}
StringBuilder_Append(&cbe.sb, "} ");
StringBuilder_Append(&cbe.sb, en.name);
StringBuilder_Append(&cbe.sb, "_Data;\n\n");
// 3. Main struct
StringBuilder_Append(&cbe.sb, "typedef struct {\n");
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, en.name);
StringBuilder_Append(&cbe.sb, "_Tag tag;\n");
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, en.name);
StringBuilder_Append(&cbe.sb, "_Data data;\n");
StringBuilder_Append(&cbe.sb, "} ");
StringBuilder_Append(&cbe.sb, en.name);
StringBuilder_Append(&cbe.sb, ";\n\n");
}
ei = ei + 1;
}
if mod.enumCount > 0 {
StringBuilder_Append(&cbe.sb, "\n");
}
// Constant definitions
var ci: int = 0;
@@ -611,6 +812,22 @@ func CBackend_Generate(mod: *HirModule) -> String {
si = si + 1;
}
// Opaque forward declarations for generic structs (used only by pointer)
si = 0;
while si < mod.structCount {
if CBE_StructHasGeneric(&mod.structs[si]) {
StringBuilder_Append(&cbe.sb, "typedef struct ");
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
StringBuilder_Append(&cbe.sb, ";\n");
}
si = si + 1;
}
if mod.structCount > 0 {
StringBuilder_Append(&cbe.sb, "\n");
}
// Forward declarations for all functions (skip generics)
var i: int = 0;
while i < mod.funcCount {