Phase 7.10: buxc2 now compiles multi-statement Bux programs
Parser (src_bux/parser.bux): - Block stores statements as linked list (firstStmt/lastStmt/nextStmt) - Block struct: added firstStmt, lastStmt fields - Stmt struct: added nextStmt field HIR Lowering (src_bux/hir_lower.bux): - Lcx_LowerBlock: iterates statement linked list, chains HirNodes via child3 - hIf: else block stored in extraData (child3 reserved for block chaining) C Backend (src_bux/c_backend.bux): - hBlock: emits statements via child3 linked list with proper indentation - hStore: combines alloca + value into single declaration (int x = value;) - hIf: full if/else emission with proper newlines - Function decl: proper return types and parameter types (not just "int") - int main() wrapper: generated when Main function exists - No duplicate return 0 when body already has return Tested: - buxc2 compiles hello world program → runs correctly - buxc2 compiles multi-statement program (let, if, function calls) → runs correctly - All 18 examples still pass, all unit tests pass
This commit is contained in:
@@ -127,6 +127,8 @@ struct Block {
|
||||
line: uint32,
|
||||
column: uint32,
|
||||
stmtCount: int,
|
||||
firstStmt: *Stmt,
|
||||
lastStmt: *Stmt,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -170,6 +172,8 @@ struct Stmt {
|
||||
refStmtElse: *Block, // else block
|
||||
// Else-if chain
|
||||
elseIfCount: int,
|
||||
// Linked list
|
||||
nextStmt: *Stmt,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
+134
-15
@@ -160,14 +160,58 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store
|
||||
// Store: combine alloca + value into single declaration
|
||||
if kind == hStore {
|
||||
if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
|
||||
// Declaration with initializer: int x = value;
|
||||
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;
|
||||
}
|
||||
|
||||
// Cast
|
||||
if kind == hCast {
|
||||
StringBuilder_Append(&cbe.sb, "((");
|
||||
@@ -177,6 +221,27 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -187,8 +252,20 @@ 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 if String_Eq(f.retTypeName, "String") || String_Eq(f.retTypeName, "str") {
|
||||
StringBuilder_Append(&cbe.sb, "const char* ");
|
||||
} else if String_Eq(f.retTypeName, "bool") {
|
||||
StringBuilder_Append(&cbe.sb, "bool ");
|
||||
} else if String_Eq(f.retTypeName, "int") {
|
||||
StringBuilder_Append(&cbe.sb, "int ");
|
||||
} else if String_Eq(f.retTypeName, "int64") {
|
||||
StringBuilder_Append(&cbe.sb, "int64_t ");
|
||||
} else if String_Eq(f.retTypeName, "uint") {
|
||||
StringBuilder_Append(&cbe.sb, "unsigned int ");
|
||||
} else if String_Eq(f.retTypeName, "float64") {
|
||||
StringBuilder_Append(&cbe.sb, "double ");
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, "int "); // simplified
|
||||
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||
}
|
||||
|
||||
StringBuilder_Append(&cbe.sb, f.name);
|
||||
@@ -200,14 +277,31 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
||||
if i > 0 {
|
||||
StringBuilder_Append(&cbe.sb, ", ");
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "int "); // simplified param type
|
||||
// Use param type info
|
||||
var pname: String = "";
|
||||
if i == 0 { pname = f.param0.name; }
|
||||
if i == 1 { pname = f.param1.name; }
|
||||
if i == 2 { pname = f.param2.name; }
|
||||
if i == 3 { pname = f.param3.name; }
|
||||
if i == 4 { pname = f.param4.name; }
|
||||
if i == 5 { pname = f.param5.name; }
|
||||
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, "const char* ");
|
||||
} 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_t ");
|
||||
} else if String_Eq(ptype, "uint") {
|
||||
StringBuilder_Append(&cbe.sb, "unsigned int ");
|
||||
} else if String_Eq(ptype, "float64") {
|
||||
StringBuilder_Append(&cbe.sb, "double ");
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, pname);
|
||||
i = i + 1;
|
||||
}
|
||||
@@ -242,20 +336,45 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
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; }
|
||||
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||
StringBuilder_Append(&cbe.sb, " {\n");
|
||||
// Body (simplified)
|
||||
if mod.funcs[i].body != null as *HirNode {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
CBE_EmitExpr(cbe, mod.funcs[i].body);
|
||||
StringBuilder_Append(&cbe.sb, ";\n");
|
||||
// Body
|
||||
var hasReturn: bool = false;
|
||||
let body: *HirNode = mod.funcs[i].body;
|
||||
if body != null as *HirNode {
|
||||
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;
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, " return 0;\n}\n\n");
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,8 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
||||
n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1);
|
||||
}
|
||||
if stmt.refStmtElse != null as *Block {
|
||||
n.child3 = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1);
|
||||
// Store else block in extraData (child3 is reserved for block chaining)
|
||||
n.extraData = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1) as *void;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -203,15 +204,38 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
||||
|
||||
func Lcx_LowerBlock(ctx: *LowerCtx, block: *Block, retTypeKind: int) -> *HirNode {
|
||||
if block == null as *Block { return null as *HirNode; }
|
||||
if block.stmtCount == 0 { return null as *HirNode; }
|
||||
|
||||
// For simplicity, create a block node with first statement
|
||||
// Build a linked list of HirNodes via child3:
|
||||
// node1 (stmt1) → child3 → node2 (stmt2) → child3 → node3 (stmt3) → null
|
||||
// child3 is safe for chaining because:
|
||||
// hStore: child1=alloca, child2=value, child3 unused
|
||||
// hReturn: child1=value, child2/child3 unused
|
||||
// hCall: child1=arg1, child2=arg2, child3 unused
|
||||
var firstNode: *HirNode = null as *HirNode;
|
||||
var prevNode: *HirNode = null as *HirNode;
|
||||
var stmt: *Stmt = block.firstStmt;
|
||||
while stmt != null as *Stmt {
|
||||
let lowered: *HirNode = Lcx_LowerStmt(ctx, stmt);
|
||||
if lowered != null as *HirNode {
|
||||
if firstNode == null as *HirNode {
|
||||
firstNode = lowered;
|
||||
prevNode = lowered;
|
||||
} else {
|
||||
prevNode.child3 = lowered;
|
||||
prevNode = lowered;
|
||||
}
|
||||
}
|
||||
stmt = stmt.nextStmt;
|
||||
}
|
||||
|
||||
// Wrap in an hBlock node with child1 = first statement in chain
|
||||
let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
n.kind = hBlock;
|
||||
n.line = block.line;
|
||||
n.column = block.column;
|
||||
n.boolValue = true; // isScope
|
||||
|
||||
// In a full implementation, lower all statements
|
||||
n.boolValue = true;
|
||||
n.child1 = firstNode;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
@@ -583,6 +583,8 @@ func parserParseBlock(p: *Parser) -> *Block {
|
||||
b.line = parserCurToken(p).line;
|
||||
b.column = parserCurToken(p).column;
|
||||
b.stmtCount = 0;
|
||||
b.firstStmt = null as *Stmt;
|
||||
b.lastStmt = null as *Stmt;
|
||||
|
||||
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
||||
if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
|
||||
@@ -591,6 +593,14 @@ func parserParseBlock(p: *Parser) -> *Block {
|
||||
}
|
||||
let s: *Stmt = parserParseStmt(p);
|
||||
if s != null as *Stmt {
|
||||
s.nextStmt = null as *Stmt;
|
||||
if b.firstStmt == null as *Stmt {
|
||||
b.firstStmt = s;
|
||||
b.lastStmt = s;
|
||||
} else {
|
||||
b.lastStmt.nextStmt = s;
|
||||
b.lastStmt = s;
|
||||
}
|
||||
b.stmtCount = b.stmtCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,8 @@ struct Block {
|
||||
line: uint32,
|
||||
column: uint32,
|
||||
stmtCount: int,
|
||||
firstStmt: *Stmt,
|
||||
lastStmt: *Stmt,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -170,6 +172,8 @@ struct Stmt {
|
||||
refStmtElse: *Block, // else block
|
||||
// Else-if chain
|
||||
elseIfCount: int,
|
||||
// Linked list
|
||||
nextStmt: *Stmt,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
+134
-15
@@ -160,14 +160,58 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store
|
||||
// Store: combine alloca + value into single declaration
|
||||
if kind == hStore {
|
||||
if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
|
||||
// Declaration with initializer: int x = value;
|
||||
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;
|
||||
}
|
||||
|
||||
// Cast
|
||||
if kind == hCast {
|
||||
StringBuilder_Append(&cbe.sb, "((");
|
||||
@@ -177,6 +221,27 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -187,8 +252,20 @@ 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 if String_Eq(f.retTypeName, "String") || String_Eq(f.retTypeName, "str") {
|
||||
StringBuilder_Append(&cbe.sb, "const char* ");
|
||||
} else if String_Eq(f.retTypeName, "bool") {
|
||||
StringBuilder_Append(&cbe.sb, "bool ");
|
||||
} else if String_Eq(f.retTypeName, "int") {
|
||||
StringBuilder_Append(&cbe.sb, "int ");
|
||||
} else if String_Eq(f.retTypeName, "int64") {
|
||||
StringBuilder_Append(&cbe.sb, "int64_t ");
|
||||
} else if String_Eq(f.retTypeName, "uint") {
|
||||
StringBuilder_Append(&cbe.sb, "unsigned int ");
|
||||
} else if String_Eq(f.retTypeName, "float64") {
|
||||
StringBuilder_Append(&cbe.sb, "double ");
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, "int "); // simplified
|
||||
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||
}
|
||||
|
||||
StringBuilder_Append(&cbe.sb, f.name);
|
||||
@@ -200,14 +277,31 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
||||
if i > 0 {
|
||||
StringBuilder_Append(&cbe.sb, ", ");
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, "int "); // simplified param type
|
||||
// Use param type info
|
||||
var pname: String = "";
|
||||
if i == 0 { pname = f.param0.name; }
|
||||
if i == 1 { pname = f.param1.name; }
|
||||
if i == 2 { pname = f.param2.name; }
|
||||
if i == 3 { pname = f.param3.name; }
|
||||
if i == 4 { pname = f.param4.name; }
|
||||
if i == 5 { pname = f.param5.name; }
|
||||
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, "const char* ");
|
||||
} 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_t ");
|
||||
} else if String_Eq(ptype, "uint") {
|
||||
StringBuilder_Append(&cbe.sb, "unsigned int ");
|
||||
} else if String_Eq(ptype, "float64") {
|
||||
StringBuilder_Append(&cbe.sb, "double ");
|
||||
} else {
|
||||
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, pname);
|
||||
i = i + 1;
|
||||
}
|
||||
@@ -242,20 +336,45 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
||||
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; }
|
||||
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||
StringBuilder_Append(&cbe.sb, " {\n");
|
||||
// Body (simplified)
|
||||
if mod.funcs[i].body != null as *HirNode {
|
||||
StringBuilder_Append(&cbe.sb, " ");
|
||||
CBE_EmitExpr(cbe, mod.funcs[i].body);
|
||||
StringBuilder_Append(&cbe.sb, ";\n");
|
||||
// Body
|
||||
var hasReturn: bool = false;
|
||||
let body: *HirNode = mod.funcs[i].body;
|
||||
if body != null as *HirNode {
|
||||
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;
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, " return 0;\n}\n\n");
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
+29
-5
@@ -170,7 +170,8 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
||||
n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1);
|
||||
}
|
||||
if stmt.refStmtElse != null as *Block {
|
||||
n.child3 = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1);
|
||||
// Store else block in extraData (child3 is reserved for block chaining)
|
||||
n.extraData = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1) as *void;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -203,15 +204,38 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
||||
|
||||
func Lcx_LowerBlock(ctx: *LowerCtx, block: *Block, retTypeKind: int) -> *HirNode {
|
||||
if block == null as *Block { return null as *HirNode; }
|
||||
if block.stmtCount == 0 { return null as *HirNode; }
|
||||
|
||||
// For simplicity, create a block node with first statement
|
||||
// Build a linked list of HirNodes via child3:
|
||||
// node1 (stmt1) → child3 → node2 (stmt2) → child3 → node3 (stmt3) → null
|
||||
// child3 is safe for chaining because:
|
||||
// hStore: child1=alloca, child2=value, child3 unused
|
||||
// hReturn: child1=value, child2/child3 unused
|
||||
// hCall: child1=arg1, child2=arg2, child3 unused
|
||||
var firstNode: *HirNode = null as *HirNode;
|
||||
var prevNode: *HirNode = null as *HirNode;
|
||||
var stmt: *Stmt = block.firstStmt;
|
||||
while stmt != null as *Stmt {
|
||||
let lowered: *HirNode = Lcx_LowerStmt(ctx, stmt);
|
||||
if lowered != null as *HirNode {
|
||||
if firstNode == null as *HirNode {
|
||||
firstNode = lowered;
|
||||
prevNode = lowered;
|
||||
} else {
|
||||
prevNode.child3 = lowered;
|
||||
prevNode = lowered;
|
||||
}
|
||||
}
|
||||
stmt = stmt.nextStmt;
|
||||
}
|
||||
|
||||
// Wrap in an hBlock node with child1 = first statement in chain
|
||||
let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
n.kind = hBlock;
|
||||
n.line = block.line;
|
||||
n.column = block.column;
|
||||
n.boolValue = true; // isScope
|
||||
|
||||
// In a full implementation, lower all statements
|
||||
n.boolValue = true;
|
||||
n.child1 = firstNode;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
@@ -583,6 +583,8 @@ func parserParseBlock(p: *Parser) -> *Block {
|
||||
b.line = parserCurToken(p).line;
|
||||
b.column = parserCurToken(p).column;
|
||||
b.stmtCount = 0;
|
||||
b.firstStmt = null as *Stmt;
|
||||
b.lastStmt = null as *Stmt;
|
||||
|
||||
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
||||
if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
|
||||
@@ -591,6 +593,14 @@ func parserParseBlock(p: *Parser) -> *Block {
|
||||
}
|
||||
let s: *Stmt = parserParseStmt(p);
|
||||
if s != null as *Stmt {
|
||||
s.nextStmt = null as *Stmt;
|
||||
if b.firstStmt == null as *Stmt {
|
||||
b.firstStmt = s;
|
||||
b.lastStmt = s;
|
||||
} else {
|
||||
b.lastStmt.nextStmt = s;
|
||||
b.lastStmt = s;
|
||||
}
|
||||
b.stmtCount = b.stmtCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user