Files
bux-lang/src/c_backend.bux
T
dimgigov 64bd41067a cleanup(selfhost): remove 24 DEBUG prints + stale parser HACK
- Remove DEBUG prints from c_backend.bux, hir_lower.bux, lexer.bux, parser.bux
- Remove HACK for stray } in parserParseFuncDecl (no longer needed after
  Decl refactor in 4a86009)
- Clean selfhost check/build output
2026-06-09 15:48:38 +03:00

1104 lines
38 KiB
Plaintext

// c_backend.bux — C transpiler backend (ported from c_backend.nim)
// Generates C code from the HIR.
module CBackend {
// ---------------------------------------------------------------------------
// Type → C type name
// ---------------------------------------------------------------------------
func CBackend_TypeToC(kind: int) -> String {
if kind == tyVoid { return "void"; }
if kind == tyBool { return "bool"; }
if kind == tyBool8 { return "bool"; }
if kind == tyBool16 { return "bool"; }
if kind == tyBool32 { return "bool"; }
if kind == tyChar8 { return "char"; }
if kind == tyChar16 { return "uint16"; }
if kind == tyChar32 { return "uint32"; }
if kind == tyStr { return "String"; }
if kind == tyInt8 { return "int8"; }
if kind == tyInt16 { return "int16"; }
if kind == tyInt32 { return "int32"; }
if kind == tyInt64 { return "int64"; }
if kind == tyInt{ return "int"; }
if kind == tyUInt8 { return "uint8"; }
if kind == tyUInt16 { return "uint16"; }
if kind == tyUInt32 { return "uint32"; }
if kind == tyUInt64 { return "uint64"; }
if kind == tyUInt { return "uint"; }
if kind == tyFloat32 { return "float32"; }
if kind == tyFloat64 { return "float64"; }
if kind == tyPointer { return "void*"; }
if kind == tyNamed { return "int"; }
if kind == tyFunc { return "void (*)(void)"; }
return "int";
}
// Emit a C parameter/variable declaration, embedding the name inside function-pointer syntax.
func CBE_CParamDecl(typeStr: String, name: String) -> String {
if String_Contains(typeStr, "(*)") {
let replacement: String = String_Concat("(*", String_Concat(name, ")"));
return String_Replace(typeStr, "(*)", replacement);
}
return String_Concat(typeStr, String_Concat(" ", name));
}
func CBackend_OpToC(op: int) -> String {
if op == tkPlus { return "+"; }
if op == tkMinus { return "-"; }
if op == tkStar { return "*"; }
if op == tkSlash { return "/"; }
if op == tkPercent { return "%"; }
if op == tkEq { return "=="; }
if op == tkNe { return "!="; }
if op == tkLt { return "<"; }
if op == tkLe { return "<="; }
if op == tkGt { return ">"; }
if op == tkGe { return ">="; }
if op == tkAmpAmp { return "&&"; }
if op == tkPipePipe { return "||"; }
if op == tkBang { return "!"; }
if op == tkAmp { return "&"; }
if op == tkPipe { return "|"; }
if op == tkCaret { return "^"; }
if op == tkShl { return "<<"; }
if op == tkShr { return ">>"; }
if op == tkAssign { return "="; }
return "?";
}
// ---------------------------------------------------------------------------
// StringBuilder-based C emitter
// ---------------------------------------------------------------------------
struct CEmitter {
sb: StringBuilder,
indent: int,
mod: *HirModule,
deferCount: int,
defer0: *HirNode,
defer1: *HirNode,
defer2: *HirNode,
defer3: *HirNode,
defer4: *HirNode,
defer5: *HirNode,
defer6: *HirNode,
defer7: *HirNode,
}
func CBE_Init() -> CEmitter {
var sb: StringBuilder = StringBuilder_NewCap(4096);
return CEmitter { sb: sb, indent: 0, mod: null as *HirModule };
}
func CBE_PushDefer(cbe: *CEmitter, node: *HirNode) {
if cbe.deferCount == 0 { cbe.defer0 = node; }
if cbe.deferCount == 1 { cbe.defer1 = node; }
if cbe.deferCount == 2 { cbe.defer2 = node; }
if cbe.deferCount == 3 { cbe.defer3 = node; }
if cbe.deferCount == 4 { cbe.defer4 = node; }
if cbe.deferCount == 5 { cbe.defer5 = node; }
if cbe.deferCount == 6 { cbe.defer6 = node; }
if cbe.deferCount == 7 { cbe.defer7 = node; }
cbe.deferCount = cbe.deferCount + 1;
}
func CBE_EmitDefers(cbe: *CEmitter) -> int {
if cbe.deferCount == 0 { return 0; }
var i: int = cbe.deferCount - 1;
while i >= 0 {
StringBuilder_Append(&cbe.sb, "\n");
var sp: int = 0;
while sp < cbe.indent {
StringBuilder_Append(&cbe.sb, " ");
sp = sp + 1;
}
var dn: *HirNode = null as *HirNode;
if i == 0 { dn = cbe.defer0; }
if i == 1 { dn = cbe.defer1; }
if i == 2 { dn = cbe.defer2; }
if i == 3 { dn = cbe.defer3; }
if i == 4 { dn = cbe.defer4; }
if i == 5 { dn = cbe.defer5; }
if i == 6 { dn = cbe.defer6; }
if i == 7 { dn = cbe.defer7; }
CBE_EmitExpr(cbe, dn);
StringBuilder_Append(&cbe.sb, ";");
i = i - 1;
}
cbe.deferCount = 0;
return 1;
}
func CBE_ClearDefers(cbe: *CEmitter) {
cbe.deferCount = 0;
}
func CBE_Emit(cbe: *CEmitter, text: String) {
var i: int = 0;
while i < cbe.indent {
StringBuilder_Append(&cbe.sb, " ");
i = i + 1;
}
StringBuilder_Append(&cbe.sb, text);
}
func CBE_EmitLine(cbe: *CEmitter, text: String) {
CBE_Emit(cbe, text);
StringBuilder_Append(&cbe.sb, "\n");
}
func CBE_Build(cbe: *CEmitter) -> String {
return StringBuilder_Build(&cbe.sb);
}
// ---------------------------------------------------------------------------
// Emit HIR node
// ---------------------------------------------------------------------------
func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
if node == null as *HirNode { return; }
let kind: int = node.kind;
// Literal
if kind == hLit {
if node.intValue == tkNull {
StringBuilder_Append(&cbe.sb, "0");
} else {
let s: String = node.strValue;
let slen: int = String_Len(s) as int;
var start: int = 0;
var end: int = slen;
var isQuoted: bool = false;
var isBacktick: bool = false;
// 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 {
isQuoted = true;
start = 1;
end = slen - 1;
}
// Backtick raw string: strip backticks, escape inner content for C
if slen >= 2 && s[0] == 96 as char8 && s[slen - 1] == 96 as char8 {
isBacktick = true;
start = 1;
end = slen - 1;
}
if isQuoted || isBacktick {
StringBuilder_Append(&cbe.sb, "\"");
}
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 isQuoted || isBacktick {
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;
}
// Binary
if kind == hBinary {
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, CBackend_OpToC(node.intValue));
StringBuilder_Append(&cbe.sb, " ");
CBE_EmitExpr(cbe, node.child2);
return;
}
// Unary
if kind == hUnary {
StringBuilder_Append(&cbe.sb, CBackend_OpToC(node.intValue));
CBE_EmitExpr(cbe, node.child1);
return;
}
// Call
if kind == hCall {
StringBuilder_Append(&cbe.sb, node.strValue);
StringBuilder_Append(&cbe.sb, "(");
var needsComma: bool = false;
if node.child1 != null as *HirNode {
CBE_EmitExpr(cbe, node.child1);
needsComma = true;
}
if node.child2 != null as *HirNode {
if needsComma {
StringBuilder_Append(&cbe.sb, ", ");
}
CBE_EmitExpr(cbe, node.child2);
needsComma = true;
}
// Emit extra args from linked list
var ai: int = 0;
var curExtra: *HirArgList = node.extraData as *HirArgList;
while ai < node.extraCount {
if needsComma {
StringBuilder_Append(&cbe.sb, ", ");
}
CBE_EmitExpr(cbe, curExtra.node);
needsComma = true;
curExtra = curExtra.next;
ai = ai + 1;
}
StringBuilder_Append(&cbe.sb, ")");
return;
}
// Indirect call through function pointer
if kind == hCallIndirect {
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, "(");
var needsComma: bool = false;
if node.child2 != null as *HirNode {
CBE_EmitExpr(cbe, node.child2);
needsComma = true;
}
if node.child3 != null as *HirNode {
if needsComma {
StringBuilder_Append(&cbe.sb, ", ");
}
CBE_EmitExpr(cbe, node.child3);
needsComma = true;
}
// Emit extra args from linked list
var ai: int = 0;
var curExtra: *HirArgList = node.extraData as *HirArgList;
while ai < node.extraCount {
if needsComma {
StringBuilder_Append(&cbe.sb, ", ");
}
CBE_EmitExpr(cbe, curExtra.node);
needsComma = true;
curExtra = curExtra.next;
ai = ai + 1;
}
StringBuilder_Append(&cbe.sb, ")");
return;
}
// spawn Callee(args)
if kind == hSpawn {
StringBuilder_Append(&cbe.sb, "bux_async_spawn(");
StringBuilder_Append(&cbe.sb, node.strValue);
if node.child1 != null as *HirNode {
StringBuilder_Append(&cbe.sb, ", (void*)");
CBE_EmitExpr(cbe, node.child1);
}
StringBuilder_Append(&cbe.sb, ")");
return;
}
// await
if kind == hAwait {
StringBuilder_Append(&cbe.sb, "bux_async_await(");
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ")");
return;
}
// Defer
if kind == hDefer {
CBE_PushDefer(cbe, node.child1);
return;
}
// Return
if kind == hReturn {
let hadDefers: int = CBE_EmitDefers(cbe);
if hadDefers != 0 {
StringBuilder_Append(&cbe.sb, "\n");
var sp: int = 0;
while sp < cbe.indent {
StringBuilder_Append(&cbe.sb, " ");
sp = sp + 1;
}
}
StringBuilder_Append(&cbe.sb, "return");
if node.child1 != null as *HirNode {
StringBuilder_Append(&cbe.sb, " ");
CBE_EmitExpr(cbe, node.child1);
}
return;
}
// Alloca
if kind == hAlloca {
var ct: String = "int";
if !String_Eq(node.typeName, "") {
ct = node.typeName;
}
StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ct, node.strValue));
return;
}
// Store: combine alloca + value into single declaration
if kind == hStore {
if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
// Declaration with initializer: Type x = value;
var ct: String = CBackend_TypeToC(node.child1.intValue);
if !String_Eq(node.child1.typeName, "") {
ct = node.child1.typeName;
}
StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ct, node.child1.strValue));
if node.child2 != null as *HirNode {
StringBuilder_Append(&cbe.sb, " = ");
CBE_EmitExpr(cbe, node.child2);
}
return;
}
// Plain assignment
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, " = ");
CBE_EmitExpr(cbe, node.child2);
return;
}
// If
if kind == hIf {
StringBuilder_Append(&cbe.sb, "if (");
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ") {\n");
if node.child2 != null as *HirNode {
cbe.indent = cbe.indent + 1;
CBE_EmitExpr(cbe, node.child2);
cbe.indent = cbe.indent - 1;
}
var sp: int = 0;
while sp < cbe.indent {
StringBuilder_Append(&cbe.sb, " ");
sp = sp + 1;
}
StringBuilder_Append(&cbe.sb, "}");
let elseBlock: *HirNode = node.extraData as *HirNode;
if elseBlock != null as *HirNode {
StringBuilder_Append(&cbe.sb, " else {\n");
cbe.indent = cbe.indent + 1;
CBE_EmitExpr(cbe, elseBlock);
cbe.indent = cbe.indent - 1;
sp = 0;
while sp < cbe.indent {
StringBuilder_Append(&cbe.sb, " ");
sp = sp + 1;
}
StringBuilder_Append(&cbe.sb, "}\n");
} else {
StringBuilder_Append(&cbe.sb, "\n");
}
return;
}
// While
if kind == hWhile {
StringBuilder_Append(&cbe.sb, "while (");
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ") {\n");
if node.child2 != null as *HirNode {
cbe.indent = cbe.indent + 1;
CBE_EmitExpr(cbe, node.child2);
cbe.indent = cbe.indent - 1;
}
var sp: int = 0;
while sp < cbe.indent {
StringBuilder_Append(&cbe.sb, " ");
sp = sp + 1;
}
StringBuilder_Append(&cbe.sb, "}");
return;
}
// Loop (infinite)
if kind == hLoop {
StringBuilder_Append(&cbe.sb, "while (1) {\n");
if node.child1 != null as *HirNode {
cbe.indent = cbe.indent + 1;
CBE_EmitExpr(cbe, node.child1);
cbe.indent = cbe.indent - 1;
}
var sp: int = 0;
while sp < cbe.indent {
StringBuilder_Append(&cbe.sb, " ");
sp = sp + 1;
}
StringBuilder_Append(&cbe.sb, "}");
return;
}
// 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);
var isPtr: bool = false;
if node.child1 != null as *HirNode {
let childType: String = CBE_GetExprTypeName(cbe.mod, node.child1);
if String_EndsWith(childType, "*") {
isPtr = true;
}
}
if isPtr {
StringBuilder_Append(&cbe.sb, "->");
} else {
StringBuilder_Append(&cbe.sb, ".");
}
StringBuilder_Append(&cbe.sb, node.strValue);
return;
}
// Sizeof: sizeof(Type) — emit as sizeof(Type)
if kind == hSizeOf {
StringBuilder_Append(&cbe.sb, "sizeof(");
if !String_Eq(node.typeName, "") {
StringBuilder_Append(&cbe.sb, node.typeName);
} else {
StringBuilder_Append(&cbe.sb, "int");
}
StringBuilder_Append(&cbe.sb, ")");
return;
}
// Index: arr[idx] — emit as arr[idx]
if kind == hIndexPtr {
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, "[");
CBE_EmitExpr(cbe, node.child2);
StringBuilder_Append(&cbe.sb, "]");
return;
}
// 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 {
let childType: String = CBE_GetExprTypeName(cbe.mod, ptrNode.child1);
if String_EndsWith(childType, "*") {
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, ")");
return;
}
// Assign: target = value
if kind == hAssign {
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, " = ");
CBE_EmitExpr(cbe, node.child2);
return;
}
// Cast
if kind == hCast {
StringBuilder_Append(&cbe.sb, "((");
if !String_Eq(node.typeName, "") {
StringBuilder_Append(&cbe.sb, node.typeName);
} else {
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(node.typeKind));
}
StringBuilder_Append(&cbe.sb, ")");
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ")");
return;
}
// Struct init: ((TypeName){.field = value, ...})
if kind == hStructInit {
StringBuilder_Append(&cbe.sb, "((");
StringBuilder_Append(&cbe.sb, node.strValue);
StringBuilder_Append(&cbe.sb, "){");
// Emit fields (chained via child3)
var field: *HirNode = node.child1;
var first: bool = true;
while field != null as *HirNode {
if !first {
StringBuilder_Append(&cbe.sb, ", ");
}
StringBuilder_Append(&cbe.sb, ".");
StringBuilder_Append(&cbe.sb, field.strValue);
StringBuilder_Append(&cbe.sb, " = ");
CBE_EmitExpr(cbe, field.child1);
first = false;
field = field.child3;
}
StringBuilder_Append(&cbe.sb, "})");
return;
}
// Block — emit each statement via child3 linked list
if kind == hBlock {
var child: *HirNode = node.child1;
while child != null as *HirNode {
if child.kind == hDefer {
CBE_EmitExpr(cbe, child);
child = child.child3;
continue;
}
// Indent
var sp: int = 0;
while sp < cbe.indent {
StringBuilder_Append(&cbe.sb, " ");
sp = sp + 1;
}
CBE_EmitExpr(cbe, child);
// Add semicolon for statements that need it
if child.kind != hBlock && child.kind != hIf && child.kind != hWhile && child.kind != hLoop {
StringBuilder_Append(&cbe.sb, ";");
}
StringBuilder_Append(&cbe.sb, "\n");
child = child.child3;
}
return;
}
}
// ---------------------------------------------------------------------------
// Emit function declaration
// ---------------------------------------------------------------------------
func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
// Return type
if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
StringBuilder_Append(&cbe.sb, "void ");
} else {
StringBuilder_Append(&cbe.sb, f.retTypeName);
StringBuilder_Append(&cbe.sb, " ");
}
StringBuilder_Append(&cbe.sb, f.name);
StringBuilder_Append(&cbe.sb, "(");
// Parameters
var i: int = 0;
while i < f.paramCount {
if i > 0 {
StringBuilder_Append(&cbe.sb, ", ");
}
// Use param type info
var pname: String = "";
var ptype: String = "";
if i == 0 { pname = f.param0.name; ptype = f.param0.typeName; }
if i == 1 { pname = f.param1.name; ptype = f.param1.typeName; }
if i == 2 { pname = f.param2.name; ptype = f.param2.typeName; }
if i == 3 { pname = f.param3.name; ptype = f.param3.typeName; }
if i == 4 { pname = f.param4.name; ptype = f.param4.typeName; }
if i == 5 { pname = f.param5.name; ptype = f.param5.typeName; }
if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; }
if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; }
if i == 8 { pname = f.param8.name; ptype = f.param8.typeName; }
// Emit type + name
if String_Eq(ptype, "") {
ptype = "int";
}
StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ptype, pname));
i = i + 1;
}
StringBuilder_Append(&cbe.sb, ")");
}
// ---------------------------------------------------------------------------
// Helpers for detecting generic declarations (not yet monomorphized)
// ---------------------------------------------------------------------------
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, "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;
}
func CBE_StructHasGeneric(st: *HirStruct) -> bool {
var fi: int = 0;
while fi < st.fieldCount {
if CBE_IsGenericTypeName(st.fields[fi].typeName) { return true; }
fi = fi + 1;
}
return false;
}
func CBE_FuncHasGeneric(f: *HirFunc) -> bool {
var pi: int = 0;
while pi < f.paramCount {
var ptype: String = "";
if pi == 0 { ptype = f.param0.typeName; }
if pi == 1 { ptype = f.param1.typeName; }
if pi == 2 { ptype = f.param2.typeName; }
if pi == 3 { ptype = f.param3.typeName; }
if pi == 4 { ptype = f.param4.typeName; }
if pi == 5 { ptype = f.param5.typeName; }
if pi == 6 { ptype = f.param6.typeName; }
if pi == 7 { ptype = f.param7.typeName; }
if pi == 8 { ptype = f.param8.typeName; }
if CBE_IsGenericTypeName(ptype) { return true; }
pi = pi + 1;
}
if CBE_IsGenericTypeName(f.retTypeName) { return true; }
return false;
}
func CBE_IsPrimitiveTypeName(name: String) -> bool {
if String_Eq(name, "int") || String_Eq(name, "") { return true; }
if String_Eq(name, "String") { return true; }
if String_Eq(name, "bool") { return true; }
if String_Eq(name, "uint32") { return true; }
if String_Eq(name, "uint64") { return true; }
if String_Eq(name, "int64") { return true; }
if String_Eq(name, "float32") { return true; }
if String_Eq(name, "float64") { return true; }
if String_Eq(name, "uint8") { return true; }
if String_Eq(name, "uint16") { return true; }
if String_Eq(name, "int8") { return true; }
if String_Eq(name, "int16") { return true; }
if String_Eq(name, "uint") { return true; }
if String_Eq(name, "char8") { return true; }
if String_Eq(name, "void") { return true; }
if String_Eq(name, "size_t") { return true; }
return false;
}
func CBE_StructHasValueStructField(st: *HirStruct) -> bool {
var fi: int = 0;
while fi < st.fieldCount {
let ft: String = st.fields[fi].typeName;
if !CBE_IsPrimitiveTypeName(ft) && !String_EndsWith(ft, "*") {
return true;
}
fi = fi + 1;
}
return false;
}
func CBE_EmitStructDef(cbe: *CEmitter, st: *HirStruct) {
StringBuilder_Append(&cbe.sb, "struct ");
StringBuilder_Append(&cbe.sb, st.name);
StringBuilder_Append(&cbe.sb, " {\n");
var fi: int = 0;
while fi < st.fieldCount {
StringBuilder_Append(&cbe.sb, " ");
var ft: String = st.fields[fi].typeName;
if String_Eq(ft, "int") || String_Eq(ft, "") {
StringBuilder_Append(&cbe.sb, "int");
} else if String_Eq(ft, "String") {
StringBuilder_Append(&cbe.sb, "String");
} else if String_Eq(ft, "bool") {
StringBuilder_Append(&cbe.sb, "bool");
} else if String_Eq(ft, "uint32") {
StringBuilder_Append(&cbe.sb, "uint32");
} else {
StringBuilder_Append(&cbe.sb, ft);
}
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, st.fields[fi].name);
StringBuilder_Append(&cbe.sb, ";\n");
fi = fi + 1;
}
StringBuilder_Append(&cbe.sb, "};\n\n");
}
func CBE_LookupFieldType(mod: *HirModule, structName: String, fieldName: String) -> String {
var si: int = 0;
while si < mod.structCount {
if String_Eq(mod.structs[si].name, structName) {
var fi: int = 0;
while fi < mod.structs[si].fieldCount {
if String_Eq(mod.structs[si].fields[fi].name, fieldName) {
let ft: String = mod.structs[si].fields[fi].typeName;
if ft != null as String { return ft; }
return "";
}
fi = fi + 1;
}
}
si = si + 1;
}
return "";
}
func CBE_GetExprTypeName(mod: *HirModule, node: *HirNode) -> String {
if node == null as *HirNode { return ""; }
if node.kind == hVar {
if node.typeName != null as String { return node.typeName; }
return "";
}
if node.kind == hFieldPtr {
let baseType: String = CBE_GetExprTypeName(mod, node.child1);
var structName: String = baseType;
if String_EndsWith(baseType, "*") {
let len: uint = String_Len(baseType);
if len > 1 {
structName = String_Slice(baseType, 0, len - 1);
}
}
if !String_Eq(structName, "") {
return CBE_LookupFieldType(mod, structName, node.strValue);
}
}
if node.typeName != null as String { return node.typeName; }
return "";
}
// ---------------------------------------------------------------------------
// Generate complete C module
// ---------------------------------------------------------------------------
func CBackend_Generate(mod: *HirModule) -> String {
let cbe: *CEmitter = bux_alloc(sizeof(CEmitter)) as *CEmitter;
cbe.sb = StringBuilder_NewCap(8192);
cbe.indent = 0;
cbe.mod = mod;
// Header
StringBuilder_Append(&cbe.sb, "// Generated by Bux C Backend v2\n");
StringBuilder_Append(&cbe.sb, "#include <stdint.h>\n");
StringBuilder_Append(&cbe.sb, "#include <stdbool.h>\n");
StringBuilder_Append(&cbe.sb, "#include <string.h>\n");
StringBuilder_Append(&cbe.sb, "#include <stdio.h>\n");
StringBuilder_Append(&cbe.sb, "#include <stdlib.h>\n\n");
// Type aliases
StringBuilder_Append(&cbe.sb, "typedef const char* String;\n");
StringBuilder_Append(&cbe.sb, "typedef unsigned char uint8;\n");
StringBuilder_Append(&cbe.sb, "typedef unsigned short uint16;\n");
StringBuilder_Append(&cbe.sb, "typedef unsigned int uint32;\n");
StringBuilder_Append(&cbe.sb, "typedef unsigned long long uint64;\n");
StringBuilder_Append(&cbe.sb, "typedef signed char int8;\n");
StringBuilder_Append(&cbe.sb, "typedef short int16;\n");
StringBuilder_Append(&cbe.sb, "typedef long long int64;\n");
StringBuilder_Append(&cbe.sb, "typedef float float32;\n");
StringBuilder_Append(&cbe.sb, "typedef double float64;\n");
StringBuilder_Append(&cbe.sb, "typedef char char8;\n\n");
// Runtime declarations
StringBuilder_Append(&cbe.sb, "void* bux_alloc(unsigned int size);\n");
StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n\n");
// Forward declare all struct types (skip generics)
var si: int = 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;
}
StringBuilder_Append(&cbe.sb, "\n");
// Enum definitions
var ei: int = 0;
while ei < mod.enumCount {
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;
}
// Emit tag enum for all enums (simple and algebraic)
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");
if !hasData {
// Simple enum: struct wrapper with only tag
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, ";\n\n");
} else {
// Algebraic enum: data union + struct (tag enum already emitted above)
// 1. 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;
}
// Constant definitions
var ci: int = 0;
while ci < mod.constCount {
StringBuilder_Append(&cbe.sb, "#define ");
StringBuilder_Append(&cbe.sb, mod.consts[ci].name);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, String_FromInt(mod.consts[ci].value as int64));
StringBuilder_Append(&cbe.sb, "\n");
ci = ci + 1;
}
if mod.constCount > 0 {
StringBuilder_Append(&cbe.sb, "\n");
}
// Struct definitions (skip generics)
// Pass 1: emit structs with no value-typed struct fields (leaf structs)
si = 0;
while si < mod.structCount {
if CBE_StructHasGeneric(&mod.structs[si]) {
si = si + 1;
continue;
}
if CBE_StructHasValueStructField(&mod.structs[si]) {
si = si + 1;
continue;
}
CBE_EmitStructDef(cbe, &mod.structs[si]);
si = si + 1;
}
// Pass 2: emit structs that contain value-typed struct fields
si = 0;
while si < mod.structCount {
if CBE_StructHasGeneric(&mod.structs[si]) {
si = si + 1;
continue;
}
if !CBE_StructHasValueStructField(&mod.structs[si]) {
si = si + 1;
continue;
}
CBE_EmitStructDef(cbe, &mod.structs[si]);
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 {
if !CBE_FuncHasGeneric(&mod.funcs[i]) {
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
StringBuilder_Append(&cbe.sb, ";\n");
}
i = i + 1;
}
StringBuilder_Append(&cbe.sb, "\n");
// Extern declarations
i = 0;
while i < mod.externCount {
CBE_EmitFuncDecl(cbe, &mod.externFuncs[i]);
StringBuilder_Append(&cbe.sb, ";\n");
i = i + 1;
}
StringBuilder_Append(&cbe.sb, "\n");
// Function definitions (skip generics)
var hasMain: bool = false;
i = 0;
while i < mod.funcCount {
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
// Skip generic functions
if CBE_FuncHasGeneric(&mod.funcs[i]) {
i = i + 1;
continue;
}
// Skip forward declarations (functions without body)
let body: *HirNode = mod.funcs[i].body;
if body == null as *HirNode {
i = i + 1;
continue;
}
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
StringBuilder_Append(&cbe.sb, " {\n");
// Body
var hasReturn: bool = false;
cbe.indent = 1;
CBE_EmitExpr(cbe, body);
CBE_EmitDefers(cbe);
cbe.indent = 0;
// Check if body has a return statement
var stmt: *HirNode = body.child1;
while stmt != null as *HirNode {
if stmt.kind == hReturn { hasReturn = true; }
stmt = stmt.child3;
}
// Add default return 0 only if function returns int and has no explicit return
if String_Eq(mod.funcs[i].retTypeName, "int") && !hasReturn {
StringBuilder_Append(&cbe.sb, " return 0;\n");
}
StringBuilder_Append(&cbe.sb, "\n}\n\n");
i = i + 1;
}
// Generate C main wrapper if Main function exists
if hasMain {
StringBuilder_Append(&cbe.sb, "extern int g_argc;\n");
StringBuilder_Append(&cbe.sb, "extern char** g_argv;\n");
StringBuilder_Append(&cbe.sb, "int main(int argc, char** argv) {\n");
StringBuilder_Append(&cbe.sb, " g_argc = argc;\n");
StringBuilder_Append(&cbe.sb, " g_argv = argv;\n");
StringBuilder_Append(&cbe.sb, " return Main();\n");
StringBuilder_Append(&cbe.sb, "}\n");
}
return StringBuilder_Build(&cbe.sb);
}
func CBackend_Free(cbe: *CEmitter) {
StringBuilder_Free(&cbe.sb);
bux_free(cbe as *void);
}
}