fix(qbe): any-child terminator check in QBE_IsTerminator for hBlock — QBE SSA now fully valid
- QBE_IsTerminator(hBlock) checks ALL children, not just last - Removed legacy c_backend.bux and nim_backend.bux from _selfhost/src/ - QBE self-hosting QBE phase: PASSES (SSA valid, assembled to .s) - Remaining: linker errors from missing String_Eq/StringBuilder in runtime
This commit is contained in:
@@ -1,585 +0,0 @@
|
||||
// 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"; }
|
||||
return "int";
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
func CBE_Init() -> CEmitter {
|
||||
var sb: StringBuilder = StringBuilder_NewCap(4096);
|
||||
return CEmitter { sb: sb, indent: 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 {
|
||||
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Variable
|
||||
if kind == hVar {
|
||||
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, "(");
|
||||
if node.child1 != null as *HirNode {
|
||||
CBE_EmitExpr(cbe, node.child1);
|
||||
}
|
||||
if node.child2 != null as *HirNode {
|
||||
StringBuilder_Append(&cbe.sb, ", ");
|
||||
CBE_EmitExpr(cbe, node.child2);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// Return
|
||||
if kind == hReturn {
|
||||
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 {
|
||||
if !String_Eq(node.typeName, "") {
|
||||
StringBuilder_Append(&cbe.sb, node.typeName);
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, "int");
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, 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;
|
||||
if !String_Eq(node.child1.typeName, "") {
|
||||
StringBuilder_Append(&cbe.sb, node.child1.typeName);
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(node.child1.intValue));
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, node.child1.strValue);
|
||||
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;
|
||||
}
|
||||
|
||||
// Field access: obj.field — emit as obj->field (since most are pointers)
|
||||
if kind == hFieldPtr {
|
||||
CBE_EmitExpr(cbe, node.child1);
|
||||
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)
|
||||
if kind == hLoad {
|
||||
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 {
|
||||
// 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; }
|
||||
// Emit type
|
||||
if String_Eq(ptype, "String") || String_Eq(ptype, "str") {
|
||||
StringBuilder_Append(&cbe.sb, "String ");
|
||||
} else if String_Eq(ptype, "bool") {
|
||||
StringBuilder_Append(&cbe.sb, "bool ");
|
||||
} else if String_Eq(ptype, "int") {
|
||||
StringBuilder_Append(&cbe.sb, "int ");
|
||||
} else if String_Eq(ptype, "int64") {
|
||||
StringBuilder_Append(&cbe.sb, "int64 ");
|
||||
} else if String_Eq(ptype, "uint") {
|
||||
StringBuilder_Append(&cbe.sb, "uint ");
|
||||
} else if String_Eq(ptype, "float64") {
|
||||
StringBuilder_Append(&cbe.sb, "float64 ");
|
||||
} else if !String_Eq(ptype, "") {
|
||||
StringBuilder_Append(&cbe.sb, ptype);
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, pname);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
StringBuilder_Append(&cbe.sb, ")");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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;
|
||||
|
||||
// Header
|
||||
StringBuilder_Append(&cbe.sb, "// Generated by Bux C Backend\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\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
|
||||
var si: int = 0;
|
||||
while si < mod.structCount {
|
||||
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");
|
||||
|
||||
// Struct definitions
|
||||
si = 0;
|
||||
while si < mod.structCount {
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct {\n");
|
||||
var fi: int = 0;
|
||||
while fi < mod.structs[si].fieldCount {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
var ft: String = mod.structs[si].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 {
|
||||
// Use the type name directly (handles "Foo*", "Bar", etc.)
|
||||
StringBuilder_Append(&cbe.sb, ft);
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
StringBuilder_Append(&cbe.sb, mod.structs[si].fields[fi].name);
|
||||
StringBuilder_Append(&cbe.sb, ";\n");
|
||||
fi = fi + 1;
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "} ");
|
||||
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||
StringBuilder_Append(&cbe.sb, ";\n\n");
|
||||
si = si + 1;
|
||||
}
|
||||
|
||||
// Forward declarations for all functions
|
||||
var i: int = 0;
|
||||
while i < mod.funcCount {
|
||||
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
|
||||
var hasMain: bool = false;
|
||||
i = 0;
|
||||
while i < mod.funcCount {
|
||||
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
|
||||
// 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.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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,812 +0,0 @@
|
||||
// nim_backend.bux — Nim transpiler backend
|
||||
// Generates Nim code from the HIR. Much simpler than C backend.
|
||||
|
||||
import Std::String;
|
||||
import Std::Io::{Print, PrintLine};
|
||||
|
||||
module NimBackend {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type → Nim type name
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func NimBackend_TypeToNim(kind: int) -> String {
|
||||
if kind == tyVoid { return "void"; }
|
||||
if kind == tyBool || kind == tyBool8 || kind == tyBool16 || 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 "pointer"; }
|
||||
return "int";
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Operator → Nim
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func NimBackend_OpToNim(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 "and"; }
|
||||
if op == tkPipePipe { return "or"; }
|
||||
if op == tkBang { return "not"; }
|
||||
if op == tkAmp { return "and"; }
|
||||
if op == tkPipe { return "or"; }
|
||||
if op == tkCaret { return "xor"; }
|
||||
if op == tkShl { return "shl"; }
|
||||
if op == tkShr { return "shr"; }
|
||||
if op == tkAssign { return "="; }
|
||||
return "?";
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Emitter
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct NBEmitter {
|
||||
sb: StringBuilder;
|
||||
indent: int;
|
||||
}
|
||||
|
||||
func NBE_Init() -> NBEmitter {
|
||||
var sb: StringBuilder = StringBuilder_NewCap(4096);
|
||||
return NBEmitter { sb: sb, indent: 0 };
|
||||
}
|
||||
|
||||
func NBE_EmitIndent(nbe: *NBEmitter) {
|
||||
var i: int = 0;
|
||||
while i < nbe.indent {
|
||||
StringBuilder_Append(&nbe.sb, " ");
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
func NBE_Emit(nbe: *NBEmitter, text: String) {
|
||||
NBE_EmitIndent(nbe);
|
||||
StringBuilder_Append(&nbe.sb, text);
|
||||
}
|
||||
|
||||
func NBE_EmitLine(nbe: *NBEmitter, text: String) {
|
||||
NBE_Emit(nbe, text);
|
||||
StringBuilder_Append(&nbe.sb, "\n");
|
||||
}
|
||||
|
||||
func NBE_Build(nbe: *NBEmitter) -> String {
|
||||
return StringBuilder_Build(&nbe.sb);
|
||||
}
|
||||
|
||||
func NBE_Append(nbe: *NBEmitter, text: String) {
|
||||
StringBuilder_Append(&nbe.sb, text);
|
||||
}
|
||||
|
||||
func NBE_AppendLine(nbe: *NBEmitter, text: String) {
|
||||
StringBuilder_Append(&nbe.sb, text);
|
||||
StringBuilder_Append(&nbe.sb, "\n");
|
||||
}
|
||||
|
||||
func NBE_AppendChar(nbe: *NBEmitter, c: char8) {
|
||||
var tmp: *char8 = bux_alloc(2) as *char8;
|
||||
tmp[0] = c;
|
||||
tmp[1] = 0 as char8;
|
||||
StringBuilder_Append(&nbe.sb, tmp);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Expression emission
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func NBE_EmitExpr(nbe: *NBEmitter, node: *HirNode) {
|
||||
if node == null as *HirNode { return; }
|
||||
let kind: int = node.kind;
|
||||
|
||||
// Literal
|
||||
if kind == hLit {
|
||||
if node.intValue == tkNull {
|
||||
NBE_Append(nbe, "nil");
|
||||
} else if node.intValue == tkStringLiteral {
|
||||
// Nim string: emit quoted with escapes
|
||||
NBE_Append(nbe, "\"");
|
||||
var s: String = node.strValue;
|
||||
if s != null as String {
|
||||
var slen: int = bux_strlen(s) as int;
|
||||
var start: int = 0;
|
||||
var end: int = slen;
|
||||
// Strip outer quotes if present
|
||||
if slen >= 2 {
|
||||
var fc: char8 = s[0];
|
||||
var lc: char8 = s[slen - 1];
|
||||
if fc == 34 as char8 && lc == 34 as char8 {
|
||||
start = 1;
|
||||
end = slen - 1;
|
||||
}
|
||||
}
|
||||
var i: int = start;
|
||||
while i < end {
|
||||
var c: char8 = s[i];
|
||||
if c == 10 as char8 { NBE_Append(nbe, "\\n"); } // \n
|
||||
else if c == 13 as char8 { NBE_Append(nbe, "\\r"); } // \r
|
||||
else if c == 9 as char8 { NBE_Append(nbe, "\\t"); } // \t
|
||||
else if c == 34 as char8 { NBE_Append(nbe, "\\\""); } // \"
|
||||
else if c == 92 as char8 { NBE_Append(nbe, "\\\\"); } // \\
|
||||
else { NBE_AppendChar(nbe, c); }
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
NBE_Append(nbe, "\"");
|
||||
} else if node.intValue == tkBoolLiteral {
|
||||
NBE_Append(nbe, node.strValue);
|
||||
} else {
|
||||
NBE_Append(nbe, node.strValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Variable
|
||||
if kind == hVar {
|
||||
NBE_Append(nbe, NimBackend_EscapeIdent(node.strValue));
|
||||
return;
|
||||
}
|
||||
|
||||
// Binary
|
||||
if kind == hBinary {
|
||||
// Special case: String == null → check for empty string
|
||||
if node.intValue == tkEq || node.intValue == tkNe {
|
||||
var isNullCmp: bool = false;
|
||||
var other: *HirNode = null as *HirNode;
|
||||
if node.child1 != null as *HirNode && node.child1.kind == hLit && node.child1.intValue == tkNull {
|
||||
isNullCmp = true;
|
||||
other = node.child2;
|
||||
}
|
||||
if !isNullCmp && node.child2 != null as *HirNode && node.child2.kind == hLit && node.child2.intValue == tkNull {
|
||||
isNullCmp = true;
|
||||
other = node.child1;
|
||||
}
|
||||
if isNullCmp && other != null as *HirNode && other.typeKind == tyStr {
|
||||
if node.intValue == tkEq {
|
||||
NBE_EmitExpr(nbe, other);
|
||||
NBE_Append(nbe, " == \"\"");
|
||||
} else {
|
||||
NBE_EmitExpr(nbe, other);
|
||||
NBE_Append(nbe, " != \"\"");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if node.child1 != null as *HirNode {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, " ");
|
||||
}
|
||||
// Emit operator only if we have a left operand
|
||||
if node.child1 != null as *HirNode {
|
||||
NBE_Append(nbe, NimBackend_OpToNim(node.intValue));
|
||||
NBE_Append(nbe, " ");
|
||||
}
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unary
|
||||
if kind == hUnary {
|
||||
// Skip address-of operator (&) — Nim uses automatic dereference
|
||||
if node.intValue == tkAmp {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
return;
|
||||
}
|
||||
NBE_Append(nbe, NimBackend_OpToNim(node.intValue));
|
||||
NBE_Append(nbe, "(");
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// Call
|
||||
if kind == hCall {
|
||||
var fname: String = node.strValue;
|
||||
// Translate StringBuilder calls to Nim native operations
|
||||
if String_Eq(fname, "StringBuilder_Append") {
|
||||
// sb = sb & text (string concatenation)
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, " = ");
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, " & ");
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
return;
|
||||
}
|
||||
if String_Eq(fname, "StringBuilder_NewCap") {
|
||||
// Just empty string
|
||||
NBE_Append(nbe, "\"\"");
|
||||
return;
|
||||
}
|
||||
if String_Eq(fname, "StringBuilder_Build") {
|
||||
// Just return the string field
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
return;
|
||||
}
|
||||
NBE_Append(nbe, NimBackend_EscapeIdent(fname));
|
||||
NBE_Append(nbe, "(");
|
||||
if node.child1 != null as *HirNode {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
}
|
||||
if node.child2 != null as *HirNode {
|
||||
NBE_Append(nbe, ", ");
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
}
|
||||
NBE_Append(nbe, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// Field access: obj.field → obj.field (Nim auto-dereferences)
|
||||
if kind == hFieldPtr || kind == hArrowField {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, ".");
|
||||
NBE_Append(nbe, node.strValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// Index: arr[idx]
|
||||
if kind == hIndexPtr {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, "[");
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
NBE_Append(nbe, "]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Cast: expr as Type
|
||||
if kind == hCast {
|
||||
// Special case: null as Type → appropriate default
|
||||
if node.child1 != null as *HirNode && node.child1.kind == hLit && node.child1.intValue == tkNull {
|
||||
var typeName: String = node.typeName;
|
||||
if !String_Eq(typeName, "") {
|
||||
var converted: String = NimBackend_ConvertTypeName(typeName);
|
||||
if String_Eq(converted, "string") {
|
||||
NBE_Append(nbe, "\"\"");
|
||||
return;
|
||||
}
|
||||
if String_Eq(converted, "pointer") {
|
||||
NBE_Append(nbe, "nil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// For pointer types (ending in * or "ptr "), emit nil
|
||||
var tlen: int = bux_strlen(typeName) as int;
|
||||
if tlen > 1 {
|
||||
var lastCh: char8 = typeName[tlen - 1];
|
||||
if lastCh == 42 as char8 { // '*'
|
||||
NBE_Append(nbe, "nil");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
NBE_Append(nbe, "cast[");
|
||||
if !String_Eq(node.typeName, "") {
|
||||
NBE_Append(nbe, NimBackend_ConvertTypeName(node.typeName));
|
||||
} else {
|
||||
NBE_Append(nbe, NimBackend_TypeToNim(node.typeKind));
|
||||
}
|
||||
NBE_Append(nbe, "](");
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// Is
|
||||
if kind == hIs {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, " is ");
|
||||
if !String_Eq(node.typeName, "") {
|
||||
NBE_Append(nbe, node.typeName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// SizeOf
|
||||
if kind == hSizeOf {
|
||||
NBE_Append(nbe, "sizeof(");
|
||||
if !String_Eq(node.typeName, "") {
|
||||
NBE_Append(nbe, node.typeName);
|
||||
} else {
|
||||
NBE_Append(nbe, "int");
|
||||
}
|
||||
NBE_Append(nbe, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// Alloca: variable declaration (used within Store for full decl)
|
||||
if kind == hAlloca {
|
||||
if !String_Eq(node.typeName, "") {
|
||||
NBE_Append(nbe, NimBackend_ConvertTypeNameParam(node.typeName));
|
||||
} else {
|
||||
NBE_Append(nbe, NimBackend_TypeToNim(node.typeKind));
|
||||
}
|
||||
NBE_Append(nbe, " ");
|
||||
NBE_Append(nbe, NimBackend_EscapeIdent(node.strValue));
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign: target = value
|
||||
if kind == hAssign {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, " = ");
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store: assignment or declaration with init
|
||||
if kind == hStore {
|
||||
if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
|
||||
// Declaration: var x: Type = value
|
||||
NBE_Append(nbe, "var ");
|
||||
NBE_Append(nbe, NimBackend_EscapeIdent(node.child1.strValue));
|
||||
if !String_Eq(node.child1.typeName, "") {
|
||||
NBE_Append(nbe, ": ");
|
||||
NBE_Append(nbe, NimBackend_ConvertTypeNameParam(node.child1.typeName));
|
||||
}
|
||||
if node.child2 != null as *HirNode {
|
||||
NBE_Append(nbe, " = ");
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Plain assignment
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, " = ");
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
return;
|
||||
}
|
||||
|
||||
// If
|
||||
if kind == hIf {
|
||||
NBE_Append(nbe, "if ");
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_AppendLine(nbe, ":");
|
||||
if node.child2 != null as *HirNode {
|
||||
nbe.indent = nbe.indent + 1;
|
||||
if node.child2.kind != hBlock { NBE_EmitIndent(nbe); }
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
nbe.indent = nbe.indent - 1;
|
||||
} else {
|
||||
nbe.indent = nbe.indent + 1;
|
||||
NBE_EmitLine(nbe, "discard");
|
||||
nbe.indent = nbe.indent - 1;
|
||||
}
|
||||
if node.child3 != null as *HirNode {
|
||||
NBE_EmitIndent(nbe); // indent else at same level as if
|
||||
NBE_AppendLine(nbe, "else:");
|
||||
nbe.indent = nbe.indent + 1;
|
||||
if node.child3.kind != hBlock { NBE_EmitIndent(nbe); }
|
||||
NBE_EmitExpr(nbe, node.child3);
|
||||
nbe.indent = nbe.indent - 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// While
|
||||
if kind == hWhile {
|
||||
NBE_Append(nbe, "while ");
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_AppendLine(nbe, ":");
|
||||
if node.child2 != null as *HirNode {
|
||||
nbe.indent = nbe.indent + 1;
|
||||
if node.child2.kind != hBlock { NBE_EmitIndent(nbe); }
|
||||
NBE_EmitExpr(nbe, node.child2);
|
||||
nbe.indent = nbe.indent - 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop (infinite)
|
||||
if kind == hLoop {
|
||||
NBE_AppendLine(nbe, "while true:");
|
||||
if node.child1 != null as *HirNode {
|
||||
nbe.indent = nbe.indent + 1;
|
||||
if node.child1.kind != hBlock { NBE_EmitIndent(nbe); }
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
nbe.indent = nbe.indent - 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Break / Continue
|
||||
if kind == hBreak {
|
||||
NBE_AppendLine(nbe, "break");
|
||||
return;
|
||||
}
|
||||
if kind == hContinue {
|
||||
NBE_AppendLine(nbe, "continue");
|
||||
return;
|
||||
}
|
||||
|
||||
// Return
|
||||
if kind == hReturn {
|
||||
NBE_Append(nbe, "return ");
|
||||
if node.child1 != null as *HirNode {
|
||||
if node.child1.kind == hBinary {
|
||||
NBE_Append(nbe, "(");
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
NBE_Append(nbe, ")");
|
||||
} else {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Struct init: TypeName { field: value, ... }
|
||||
if kind == hStructInit {
|
||||
NBE_Append(nbe, node.strValue);
|
||||
NBE_Append(nbe, "(");
|
||||
// Emit fields (chained via child3)
|
||||
var field: *HirNode = node.child3;
|
||||
var first: bool = true;
|
||||
while field != null as *HirNode {
|
||||
if !first { NBE_Append(nbe, ", "); }
|
||||
NBE_Append(nbe, field.strValue);
|
||||
NBE_Append(nbe, ": ");
|
||||
NBE_EmitExpr(nbe, field.child1);
|
||||
first = false;
|
||||
field = field.child3;
|
||||
}
|
||||
NBE_Append(nbe, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// Load: dereference — in Nim just emit the pointer expression
|
||||
if kind == hLoad {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Block — sequence of statements
|
||||
if kind == hBlock {
|
||||
var child: *HirNode = node.child1;
|
||||
if child == null as *HirNode {
|
||||
NBE_EmitIndent(nbe);
|
||||
NBE_AppendLine(nbe, "discard");
|
||||
return;
|
||||
}
|
||||
// First pass: emit statements, merging continuation lines
|
||||
while child != null as *HirNode {
|
||||
NBE_EmitIndent(nbe);
|
||||
// Check if this is a return/binary that has continuation siblings
|
||||
if child.kind == hReturn && child.child1 != null as *HirNode {
|
||||
// Emit return and open paren before expression
|
||||
NBE_Append(nbe, "return (");
|
||||
NBE_EmitExpr(nbe, child.child1);
|
||||
child = child.child3;
|
||||
// Merge all continuation siblings
|
||||
while child != null as *HirNode && child.kind == hBinary && child.child1 == null as *HirNode {
|
||||
NBE_Append(nbe, " ");
|
||||
NBE_EmitExpr(nbe, child);
|
||||
child = child.child3;
|
||||
}
|
||||
NBE_AppendLine(nbe, ")");
|
||||
} else {
|
||||
// Wrap with discard for non-control-flow statements
|
||||
if child.kind == hCall || child.kind == hAssign || child.kind == hStore {
|
||||
NBE_Append(nbe, "discard (");
|
||||
NBE_EmitExpr(nbe, child);
|
||||
NBE_Append(nbe, ")");
|
||||
} else {
|
||||
NBE_EmitExpr(nbe, child);
|
||||
}
|
||||
child = child.child3;
|
||||
// Merge continuation siblings for other statement types
|
||||
while child != null as *HirNode && child.kind == hBinary && child.child1 == null as *HirNode {
|
||||
NBE_Append(nbe, " ");
|
||||
NBE_EmitExpr(nbe, child);
|
||||
child = child.child3;
|
||||
}
|
||||
NBE_AppendLine(nbe, "");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Match — simplified
|
||||
if kind == hMatch {
|
||||
NBE_EmitLine(nbe, "# match (simplified)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawn
|
||||
if kind == hSpawn {
|
||||
NBE_Append(nbe, "spawn ");
|
||||
NBE_Append(nbe, node.strValue);
|
||||
NBE_Append(nbe, "(");
|
||||
if node.child1 != null as *HirNode {
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
}
|
||||
NBE_Append(nbe, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
// Await
|
||||
if kind == hAwait {
|
||||
NBE_Append(nbe, "await ");
|
||||
NBE_EmitExpr(nbe, node.child1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback
|
||||
NBE_Append(nbe, "nil");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Function declaration emission
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func NBE_EmitFuncDecl(nbe: *NBEmitter, f: *HirFunc) {
|
||||
// Return type
|
||||
var retType: String = f.retTypeName;
|
||||
if String_Eq(retType, "") || String_Eq(retType, "void") {
|
||||
// No return type annotation needed
|
||||
} else if String_Eq(retType, "int") {
|
||||
NBE_Append(nbe, "int");
|
||||
} else if String_Eq(retType, "bool") {
|
||||
NBE_Append(nbe, "bool");
|
||||
} else if String_Eq(retType, "string") || String_Eq(retType, "String") {
|
||||
NBE_Append(nbe, "string");
|
||||
} else {
|
||||
NBE_Append(nbe, retType);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Convert type name from Bux to Nim
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func NimBackend_EscapeIdent(name: String) -> String {
|
||||
if name == null as String || String_Eq(name, "") { return "x"; }
|
||||
// Nim keywords that need escaping
|
||||
if String_Eq(name, "block") { return "`block`"; }
|
||||
if String_Eq(name, "type") { return "`type`"; }
|
||||
if String_Eq(name, "object") { return "`object`"; }
|
||||
if String_Eq(name, "proc") { return "`proc`"; }
|
||||
if String_Eq(name, "var") { return "`var`"; }
|
||||
if String_Eq(name, "let") { return "`let`"; }
|
||||
if String_Eq(name, "const") { return "`const`"; }
|
||||
if String_Eq(name, "from") { return "`from`"; }
|
||||
if String_Eq(name, "import") { return "`import`"; }
|
||||
if String_Eq(name, "export") { return "`export`"; }
|
||||
if String_Eq(name, "include") { return "`include`"; }
|
||||
if String_Eq(name, "iterator") { return "`iterator`"; }
|
||||
if String_Eq(name, "method") { return "`method`"; }
|
||||
if String_Eq(name, "func") { return "`func`"; }
|
||||
if String_Eq(name, "string") { return "`string`"; }
|
||||
if String_Eq(name, "int") { return "`int`"; }
|
||||
if String_Eq(name, "bool") { return "`bool`"; }
|
||||
if String_Eq(name, "float") { return "`float`"; }
|
||||
if String_Eq(name, "char") { return "`char`"; }
|
||||
if String_Eq(name, "ptr") { return "`ptr`"; }
|
||||
if String_Eq(name, "ref") { return "`ref`"; }
|
||||
if String_Eq(name, "nil") { return "`nil`"; }
|
||||
if String_Eq(name, "true") { return "`true`"; }
|
||||
if String_Eq(name, "false") { return "`false`"; }
|
||||
if String_Eq(name, "and") { return "`and`"; }
|
||||
if String_Eq(name, "or") { return "`or`"; }
|
||||
if String_Eq(name, "not") { return "`not`"; }
|
||||
if String_Eq(name, "xor") { return "`xor`"; }
|
||||
if String_Eq(name, "shl") { return "`shl`"; }
|
||||
if String_Eq(name, "shr") { return "`shr`"; }
|
||||
if String_Eq(name, "div") { return "`div`"; }
|
||||
if String_Eq(name, "mod") { return "`mod`"; }
|
||||
if String_Eq(name, "is") { return "`is`"; }
|
||||
if String_Eq(name, "of") { return "`of`"; }
|
||||
if String_Eq(name, "in") { return "`in`"; }
|
||||
if String_Eq(name, "out") { return "`out`"; }
|
||||
if String_Eq(name, "static") { return "`static`"; }
|
||||
if String_Eq(name, "enum") { return "`enum`"; }
|
||||
if String_Eq(name, "tuple") { return "`tuple`"; }
|
||||
if String_Eq(name, "concept") { return "`concept`"; }
|
||||
if String_Eq(name, "distinct") { return "`distinct`"; }
|
||||
if String_Eq(name, "Result") { return "`Result`"; }
|
||||
return name;
|
||||
}
|
||||
|
||||
func NimBackend_ConvertTypeNameParam(tn: String) -> String {
|
||||
// For function parameters: strip pointer indirection, Nim passes by ref
|
||||
if tn == null as String || String_Eq(tn, "") { return "int"; }
|
||||
if String_Eq(tn, "String") { return "string"; }
|
||||
if String_Eq(tn, "bool") { return "bool"; }
|
||||
if String_Eq(tn, "int") { return "int"; }
|
||||
if String_Eq(tn, "int64") { return "int64"; }
|
||||
if String_Eq(tn, "uint32") { return "uint32"; }
|
||||
if String_Eq(tn, "uint") { return "uint"; }
|
||||
if String_Eq(tn, "float64") { return "float64"; }
|
||||
// Pointer types → ptr types in Nim
|
||||
if !String_Eq(tn, "") {
|
||||
var tlen: int = bux_strlen(tn) as int;
|
||||
if tlen > 1 {
|
||||
var lastCh: char8 = tn[tlen - 1];
|
||||
if lastCh == 42 as char8 {
|
||||
var base: String = String_Slice(tn, 0 as uint, (tlen - 1) as uint);
|
||||
if String_Eq(base, "void") { return "pointer"; }
|
||||
return String_Concat("ptr ", base);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tn;
|
||||
}
|
||||
|
||||
func NimBackend_ConvertTypeName(tn: String) -> String {
|
||||
if tn == null as String || String_Eq(tn, "") { return "int"; }
|
||||
if String_Eq(tn, "String") { return "string"; }
|
||||
if String_Eq(tn, "bool") { return "bool"; }
|
||||
if String_Eq(tn, "int") { return "int"; }
|
||||
if String_Eq(tn, "int64") { return "int64"; }
|
||||
if String_Eq(tn, "uint32") { return "uint32"; }
|
||||
if String_Eq(tn, "uint") { return "uint"; }
|
||||
if String_Eq(tn, "float64") { return "float64"; }
|
||||
// Convert pointer types: "Foo*" → "ptr Foo"
|
||||
if !String_Eq(tn, "") {
|
||||
var tlen: int = bux_strlen(tn) as int;
|
||||
if tlen > 1 {
|
||||
var lastCh: char8 = tn[tlen - 1];
|
||||
if lastCh == 42 as char8 { // '*'
|
||||
// Extract base name (without *)
|
||||
var base: String = String_Slice(tn, 0 as uint, (tlen - 1) as uint);
|
||||
if String_Eq(base, "void") { return "pointer"; }
|
||||
return String_Concat("ptr ", base);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tn;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Generate complete Nim module
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func NimBackend_Generate(mod: *HirModule) -> String {
|
||||
let nbe: *NBEmitter = bux_alloc(sizeof(NBEmitter)) as *NBEmitter;
|
||||
nbe.sb = StringBuilder_NewCap(8192);
|
||||
nbe.indent = 0;
|
||||
|
||||
// Header
|
||||
NBE_AppendLine(nbe, "# Generated by Bux Nim Backend");
|
||||
NBE_AppendLine(nbe, "");
|
||||
NBE_AppendLine(nbe, "import std/strutils");
|
||||
NBE_AppendLine(nbe, "import std/tables");
|
||||
NBE_AppendLine(nbe, "import std/sequtils");
|
||||
NBE_AppendLine(nbe, "import std/os");
|
||||
NBE_AppendLine(nbe, "");
|
||||
|
||||
// Type definitions — skip builtins that Nim already has
|
||||
NBE_AppendLine(nbe, "# Type aliases");
|
||||
NBE_AppendLine(nbe, "type");
|
||||
nbe.indent = nbe.indent + 1;
|
||||
NBE_EmitLine(nbe, "String* = string");
|
||||
NBE_EmitLine(nbe, "char8* = char");
|
||||
// StringBuild is just a string in Nim
|
||||
NBE_EmitLine(nbe, "StringBuilder* = string");
|
||||
// Close type block
|
||||
nbe.indent = nbe.indent - 1;
|
||||
NBE_AppendLine(nbe, "");
|
||||
|
||||
NBE_AppendLine(nbe, "");
|
||||
|
||||
// Struct definitions
|
||||
if mod.structCount > 0 {
|
||||
NBE_AppendLine(nbe, "# Struct types");
|
||||
NBE_AppendLine(nbe, "type");
|
||||
var si: int = 0;
|
||||
while si < mod.structCount {
|
||||
NBE_Append(nbe, " ");
|
||||
NBE_Append(nbe, NimBackend_EscapeIdent(mod.structs[si].name));
|
||||
NBE_AppendLine(nbe, " = object");
|
||||
var fi: int = 0;
|
||||
while fi < mod.structs[si].fieldCount {
|
||||
var fname: String = mod.structs[si].fields[fi].name;
|
||||
if !String_Eq(fname, "") {
|
||||
NBE_Append(nbe, " ");
|
||||
NBE_Append(nbe, NimBackend_EscapeIdent(fname));
|
||||
NBE_Append(nbe, ": ");
|
||||
var ft: String = mod.structs[si].fields[fi].typeName;
|
||||
NBE_Append(nbe, NimBackend_ConvertTypeName(ft));
|
||||
NBE_AppendLine(nbe, "");
|
||||
}
|
||||
fi = fi + 1;
|
||||
}
|
||||
si = si + 1;
|
||||
}
|
||||
NBE_AppendLine(nbe, "");
|
||||
}
|
||||
|
||||
// Const definitions
|
||||
if mod.constCount > 0 {
|
||||
NBE_AppendLine(nbe, "# Constants");
|
||||
var ci: int = 0;
|
||||
while ci < mod.constCount {
|
||||
NBE_Emit(nbe, "const ");
|
||||
NBE_Append(nbe, mod.consts[ci].name);
|
||||
NBE_Append(nbe, " = ");
|
||||
NBE_Append(nbe, String_FromInt(mod.consts[ci].value as int64));
|
||||
NBE_AppendLine(nbe, "");
|
||||
ci = ci + 1;
|
||||
}
|
||||
NBE_AppendLine(nbe, "");
|
||||
}
|
||||
|
||||
// Extern declarations — skip (Nim doesn't need them)
|
||||
// Bux extern functions are C runtime functions that Nim can't directly call.
|
||||
// Instead, we use Nim native equivalents (e.g., len() instead of bux_strlen).
|
||||
|
||||
// Function definitions (reverse order: dependencies first)
|
||||
var i: int = mod.funcCount;
|
||||
while i > 0 {
|
||||
i = i - 1;
|
||||
let f: *HirFunc = &mod.funcs[i];
|
||||
if f.body == null as *HirNode { continue; }
|
||||
|
||||
NBE_Emit(nbe, "proc ");
|
||||
NBE_Append(nbe, NimBackend_EscapeIdent(f.name));
|
||||
NBE_Append(nbe, "(");
|
||||
|
||||
// Parameters
|
||||
var pi: int = 0;
|
||||
var pnames: *HirParam = &f.param0;
|
||||
while pi < f.paramCount {
|
||||
if pi > 0 { NBE_Append(nbe, ", "); }
|
||||
NBE_Append(nbe, NimBackend_EscapeIdent(pnames[pi].name));
|
||||
NBE_Append(nbe, ": ");
|
||||
var pt: String = pnames[pi].typeName;
|
||||
NBE_Append(nbe, NimBackend_ConvertTypeNameParam(pt));
|
||||
pi = pi + 1;
|
||||
}
|
||||
NBE_Append(nbe, "): ");
|
||||
// Return type
|
||||
var rtn: String = NimBackend_ConvertTypeNameParam(f.retTypeName);
|
||||
if String_Eq(rtn, "") || String_Eq(rtn, "void") {
|
||||
NBE_AppendLine(nbe, "void =");
|
||||
} else {
|
||||
NBE_Append(nbe, rtn);
|
||||
NBE_AppendLine(nbe, " =");
|
||||
}
|
||||
|
||||
// Body
|
||||
nbe.indent = nbe.indent + 1;
|
||||
if f.body != null as *HirNode && f.body.kind != hBlock {
|
||||
NBE_EmitIndent(nbe);
|
||||
}
|
||||
NBE_EmitExpr(nbe, f.body);
|
||||
nbe.indent = nbe.indent - 1;
|
||||
NBE_AppendLine(nbe, "");
|
||||
NBE_AppendLine(nbe, "");
|
||||
}
|
||||
|
||||
// Main entry point
|
||||
NBE_AppendLine(nbe, "# Entry point");
|
||||
NBE_AppendLine(nbe, "when isMainModule:");
|
||||
NBE_EmitLine(nbe, " var args: seq[string] = @[]");
|
||||
NBE_EmitLine(nbe, " var argCount = paramCount()");
|
||||
NBE_EmitLine(nbe, " discard Main()");
|
||||
NBE_EmitLine(nbe, "");
|
||||
|
||||
let result: String = NBE_Build(nbe);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // module NimBackend
|
||||
@@ -268,12 +268,14 @@ func QBE_IsTerminator(node: *HirNode) -> bool {
|
||||
return false;
|
||||
}
|
||||
if node.kind == hBlock {
|
||||
var last: *HirNode = node.child1;
|
||||
if last == null as *HirNode { return false; }
|
||||
while last.child3 != null as *HirNode {
|
||||
last = last.child3;
|
||||
var child: *HirNode = node.child1;
|
||||
while child != null as *HirNode {
|
||||
if QBE_IsTerminator(child) {
|
||||
return true;
|
||||
}
|
||||
child = child.child3;
|
||||
}
|
||||
return QBE_IsTerminator(last);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user