diff --git a/_selfhost/src/ast.bux b/_selfhost/src/ast.bux index 871aeb0..b0c895b 100644 --- a/_selfhost/src/ast.bux +++ b/_selfhost/src/ast.bux @@ -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, } // --------------------------------------------------------------------------- diff --git a/_selfhost/src/c_backend.bux b/_selfhost/src/c_backend.bux index 9782339..884fc2a 100644 --- a/_selfhost/src/c_backend.bux +++ b/_selfhost/src/c_backend.bux @@ -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); } diff --git a/_selfhost/src/hir_lower.bux b/_selfhost/src/hir_lower.bux index ddcc490..719badf 100644 --- a/_selfhost/src/hir_lower.bux +++ b/_selfhost/src/hir_lower.bux @@ -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; } diff --git a/_selfhost/src/parser.bux b/_selfhost/src/parser.bux index 6761fc6..eb9d3ac 100644 --- a/_selfhost/src/parser.bux +++ b/_selfhost/src/parser.bux @@ -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; } } diff --git a/src_bux/ast.bux b/src_bux/ast.bux index 871aeb0..b0c895b 100644 --- a/src_bux/ast.bux +++ b/src_bux/ast.bux @@ -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, } // --------------------------------------------------------------------------- diff --git a/src_bux/c_backend.bux b/src_bux/c_backend.bux index 9782339..884fc2a 100644 --- a/src_bux/c_backend.bux +++ b/src_bux/c_backend.bux @@ -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); } diff --git a/src_bux/hir_lower.bux b/src_bux/hir_lower.bux index ddcc490..719badf 100644 --- a/src_bux/hir_lower.bux +++ b/src_bux/hir_lower.bux @@ -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; } diff --git a/src_bux/parser.bux b/src_bux/parser.bux index 6761fc6..eb9d3ac 100644 --- a/src_bux/parser.bux +++ b/src_bux/parser.bux @@ -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; } }