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,
|
line: uint32,
|
||||||
column: uint32,
|
column: uint32,
|
||||||
stmtCount: int,
|
stmtCount: int,
|
||||||
|
firstStmt: *Stmt,
|
||||||
|
lastStmt: *Stmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -170,6 +172,8 @@ struct Stmt {
|
|||||||
refStmtElse: *Block, // else block
|
refStmtElse: *Block, // else block
|
||||||
// Else-if chain
|
// Else-if chain
|
||||||
elseIfCount: int,
|
elseIfCount: int,
|
||||||
|
// Linked list
|
||||||
|
nextStmt: *Stmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+134
-15
@@ -160,14 +160,58 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store
|
// Store: combine alloca + value into single declaration
|
||||||
if kind == hStore {
|
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);
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
StringBuilder_Append(&cbe.sb, " = ");
|
StringBuilder_Append(&cbe.sb, " = ");
|
||||||
CBE_EmitExpr(cbe, node.child2);
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
return;
|
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
|
// Cast
|
||||||
if kind == hCast {
|
if kind == hCast {
|
||||||
StringBuilder_Append(&cbe.sb, "((");
|
StringBuilder_Append(&cbe.sb, "((");
|
||||||
@@ -177,6 +221,27 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
|||||||
StringBuilder_Append(&cbe.sb, ")");
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
return;
|
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
|
// Return type
|
||||||
if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
|
if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
|
||||||
StringBuilder_Append(&cbe.sb, "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 {
|
} else {
|
||||||
StringBuilder_Append(&cbe.sb, "int "); // simplified
|
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder_Append(&cbe.sb, f.name);
|
StringBuilder_Append(&cbe.sb, f.name);
|
||||||
@@ -200,14 +277,31 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
|||||||
if i > 0 {
|
if i > 0 {
|
||||||
StringBuilder_Append(&cbe.sb, ", ");
|
StringBuilder_Append(&cbe.sb, ", ");
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "int "); // simplified param type
|
// Use param type info
|
||||||
var pname: String = "";
|
var pname: String = "";
|
||||||
if i == 0 { pname = f.param0.name; }
|
var ptype: String = "";
|
||||||
if i == 1 { pname = f.param1.name; }
|
if i == 0 { pname = f.param0.name; ptype = f.param0.typeName; }
|
||||||
if i == 2 { pname = f.param2.name; }
|
if i == 1 { pname = f.param1.name; ptype = f.param1.typeName; }
|
||||||
if i == 3 { pname = f.param3.name; }
|
if i == 2 { pname = f.param2.name; ptype = f.param2.typeName; }
|
||||||
if i == 4 { pname = f.param4.name; }
|
if i == 3 { pname = f.param3.name; ptype = f.param3.typeName; }
|
||||||
if i == 5 { pname = f.param5.name; }
|
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);
|
StringBuilder_Append(&cbe.sb, pname);
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
@@ -242,20 +336,45 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
// Function definitions
|
// Function definitions
|
||||||
|
var hasMain: bool = false;
|
||||||
i = 0;
|
i = 0;
|
||||||
while i < mod.funcCount {
|
while i < mod.funcCount {
|
||||||
|
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
|
||||||
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||||
StringBuilder_Append(&cbe.sb, " {\n");
|
StringBuilder_Append(&cbe.sb, " {\n");
|
||||||
// Body (simplified)
|
// Body
|
||||||
if mod.funcs[i].body != null as *HirNode {
|
var hasReturn: bool = false;
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
let body: *HirNode = mod.funcs[i].body;
|
||||||
CBE_EmitExpr(cbe, mod.funcs[i].body);
|
if body != null as *HirNode {
|
||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
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;
|
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);
|
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);
|
n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1);
|
||||||
}
|
}
|
||||||
if stmt.refStmtElse != null as *Block {
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
@@ -203,15 +204,38 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
|||||||
|
|
||||||
func Lcx_LowerBlock(ctx: *LowerCtx, block: *Block, retTypeKind: int) -> *HirNode {
|
func Lcx_LowerBlock(ctx: *LowerCtx, block: *Block, retTypeKind: int) -> *HirNode {
|
||||||
if block == null as *Block { return null as *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;
|
let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
n.kind = hBlock;
|
n.kind = hBlock;
|
||||||
n.line = block.line;
|
n.line = block.line;
|
||||||
n.column = block.column;
|
n.column = block.column;
|
||||||
n.boolValue = true; // isScope
|
n.boolValue = true;
|
||||||
|
n.child1 = firstNode;
|
||||||
// In a full implementation, lower all statements
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -583,6 +583,8 @@ func parserParseBlock(p: *Parser) -> *Block {
|
|||||||
b.line = parserCurToken(p).line;
|
b.line = parserCurToken(p).line;
|
||||||
b.column = parserCurToken(p).column;
|
b.column = parserCurToken(p).column;
|
||||||
b.stmtCount = 0;
|
b.stmtCount = 0;
|
||||||
|
b.firstStmt = null as *Stmt;
|
||||||
|
b.lastStmt = null as *Stmt;
|
||||||
|
|
||||||
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
||||||
if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
|
if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
|
||||||
@@ -591,6 +593,14 @@ func parserParseBlock(p: *Parser) -> *Block {
|
|||||||
}
|
}
|
||||||
let s: *Stmt = parserParseStmt(p);
|
let s: *Stmt = parserParseStmt(p);
|
||||||
if s != null as *Stmt {
|
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;
|
b.stmtCount = b.stmtCount + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,8 @@ struct Block {
|
|||||||
line: uint32,
|
line: uint32,
|
||||||
column: uint32,
|
column: uint32,
|
||||||
stmtCount: int,
|
stmtCount: int,
|
||||||
|
firstStmt: *Stmt,
|
||||||
|
lastStmt: *Stmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -170,6 +172,8 @@ struct Stmt {
|
|||||||
refStmtElse: *Block, // else block
|
refStmtElse: *Block, // else block
|
||||||
// Else-if chain
|
// Else-if chain
|
||||||
elseIfCount: int,
|
elseIfCount: int,
|
||||||
|
// Linked list
|
||||||
|
nextStmt: *Stmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
+134
-15
@@ -160,14 +160,58 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store
|
// Store: combine alloca + value into single declaration
|
||||||
if kind == hStore {
|
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);
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
StringBuilder_Append(&cbe.sb, " = ");
|
StringBuilder_Append(&cbe.sb, " = ");
|
||||||
CBE_EmitExpr(cbe, node.child2);
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
return;
|
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
|
// Cast
|
||||||
if kind == hCast {
|
if kind == hCast {
|
||||||
StringBuilder_Append(&cbe.sb, "((");
|
StringBuilder_Append(&cbe.sb, "((");
|
||||||
@@ -177,6 +221,27 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
|||||||
StringBuilder_Append(&cbe.sb, ")");
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
return;
|
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
|
// Return type
|
||||||
if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
|
if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
|
||||||
StringBuilder_Append(&cbe.sb, "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 {
|
} else {
|
||||||
StringBuilder_Append(&cbe.sb, "int "); // simplified
|
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder_Append(&cbe.sb, f.name);
|
StringBuilder_Append(&cbe.sb, f.name);
|
||||||
@@ -200,14 +277,31 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
|||||||
if i > 0 {
|
if i > 0 {
|
||||||
StringBuilder_Append(&cbe.sb, ", ");
|
StringBuilder_Append(&cbe.sb, ", ");
|
||||||
}
|
}
|
||||||
StringBuilder_Append(&cbe.sb, "int "); // simplified param type
|
// Use param type info
|
||||||
var pname: String = "";
|
var pname: String = "";
|
||||||
if i == 0 { pname = f.param0.name; }
|
var ptype: String = "";
|
||||||
if i == 1 { pname = f.param1.name; }
|
if i == 0 { pname = f.param0.name; ptype = f.param0.typeName; }
|
||||||
if i == 2 { pname = f.param2.name; }
|
if i == 1 { pname = f.param1.name; ptype = f.param1.typeName; }
|
||||||
if i == 3 { pname = f.param3.name; }
|
if i == 2 { pname = f.param2.name; ptype = f.param2.typeName; }
|
||||||
if i == 4 { pname = f.param4.name; }
|
if i == 3 { pname = f.param3.name; ptype = f.param3.typeName; }
|
||||||
if i == 5 { pname = f.param5.name; }
|
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);
|
StringBuilder_Append(&cbe.sb, pname);
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
@@ -242,20 +336,45 @@ func CBackend_Generate(mod: *HirModule) -> String {
|
|||||||
StringBuilder_Append(&cbe.sb, "\n");
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
// Function definitions
|
// Function definitions
|
||||||
|
var hasMain: bool = false;
|
||||||
i = 0;
|
i = 0;
|
||||||
while i < mod.funcCount {
|
while i < mod.funcCount {
|
||||||
|
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
|
||||||
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||||
StringBuilder_Append(&cbe.sb, " {\n");
|
StringBuilder_Append(&cbe.sb, " {\n");
|
||||||
// Body (simplified)
|
// Body
|
||||||
if mod.funcs[i].body != null as *HirNode {
|
var hasReturn: bool = false;
|
||||||
StringBuilder_Append(&cbe.sb, " ");
|
let body: *HirNode = mod.funcs[i].body;
|
||||||
CBE_EmitExpr(cbe, mod.funcs[i].body);
|
if body != null as *HirNode {
|
||||||
StringBuilder_Append(&cbe.sb, ";\n");
|
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;
|
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);
|
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);
|
n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1);
|
||||||
}
|
}
|
||||||
if stmt.refStmtElse != null as *Block {
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
@@ -203,15 +204,38 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
|||||||
|
|
||||||
func Lcx_LowerBlock(ctx: *LowerCtx, block: *Block, retTypeKind: int) -> *HirNode {
|
func Lcx_LowerBlock(ctx: *LowerCtx, block: *Block, retTypeKind: int) -> *HirNode {
|
||||||
if block == null as *Block { return null as *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;
|
let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
n.kind = hBlock;
|
n.kind = hBlock;
|
||||||
n.line = block.line;
|
n.line = block.line;
|
||||||
n.column = block.column;
|
n.column = block.column;
|
||||||
n.boolValue = true; // isScope
|
n.boolValue = true;
|
||||||
|
n.child1 = firstNode;
|
||||||
// In a full implementation, lower all statements
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -583,6 +583,8 @@ func parserParseBlock(p: *Parser) -> *Block {
|
|||||||
b.line = parserCurToken(p).line;
|
b.line = parserCurToken(p).line;
|
||||||
b.column = parserCurToken(p).column;
|
b.column = parserCurToken(p).column;
|
||||||
b.stmtCount = 0;
|
b.stmtCount = 0;
|
||||||
|
b.firstStmt = null as *Stmt;
|
||||||
|
b.lastStmt = null as *Stmt;
|
||||||
|
|
||||||
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile {
|
||||||
if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
|
if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
|
||||||
@@ -591,6 +593,14 @@ func parserParseBlock(p: *Parser) -> *Block {
|
|||||||
}
|
}
|
||||||
let s: *Stmt = parserParseStmt(p);
|
let s: *Stmt = parserParseStmt(p);
|
||||||
if s != null as *Stmt {
|
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;
|
b.stmtCount = b.stmtCount + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user