diff --git a/_selfhost/src/c_backend.bux b/_selfhost/src/c_backend.bux index eddb3ff..5f4166e 100644 --- a/_selfhost/src/c_backend.bux +++ b/_selfhost/src/c_backend.bux @@ -446,6 +446,49 @@ func CBackend_Generate(mod: *HirModule) -> String { 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 { diff --git a/_selfhost/src/hir.bux b/_selfhost/src/hir.bux index b1886f3..da4df48 100644 --- a/_selfhost/src/hir.bux +++ b/_selfhost/src/hir.bux @@ -97,12 +97,24 @@ struct HirEnumVariant { // HirModule // --------------------------------------------------------------------------- +struct HirStructField { + name: String, + typeName: String, +} + +struct HirStruct { + name: String, + fieldCount: int, + fields: *HirStructField, +} + struct HirModule { funcCount: int, funcs: *HirFunc, externCount: int, externFuncs: *HirFunc, structCount: int, + structs: *HirStruct, enumCount: int, } diff --git a/_selfhost/src/hir_lower.bux b/_selfhost/src/hir_lower.bux index afcd304..483f20b 100644 --- a/_selfhost/src/hir_lower.bux +++ b/_selfhost/src/hir_lower.bux @@ -382,6 +382,12 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { let hm: *HirModule = bux_alloc(sizeof(HirModule)) as *HirModule; hm.funcCount = 0; hm.funcs = ctx.funcs; + hm.structCount = 0; + hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct; + + // First pass: count structs (to allocate field arrays later) + // Second pass: actually collect them + // For simplicity, do single pass with pre-allocated field arrays // Iterate declarations var decl: *Decl = mod.firstItem; @@ -397,6 +403,41 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { ctx.externCount = ctx.externCount + 1; } if decl.kind == dkStruct { + // Collect struct definition for C codegen + let si: int = hm.structCount; + hm.structs[si].name = decl.strValue; + hm.structs[si].fieldCount = decl.fieldCount; + hm.structs[si].fields = bux_alloc(decl.fieldCount as uint * sizeof(HirStructField)) as *HirStructField; + var fi: int = 0; + while fi < decl.fieldCount { + var fname: String = ""; + var ftype: *TypeExpr = null as *TypeExpr; + if fi == 0 { fname = decl.field0.name; ftype = decl.field0.refFieldType; } + else if fi == 1 { fname = decl.field1.name; ftype = decl.field1.refFieldType; } + else if fi == 2 { fname = decl.field2.name; ftype = decl.field2.refFieldType; } + else if fi == 3 { fname = decl.field3.name; ftype = decl.field3.refFieldType; } + else if fi == 4 { fname = decl.field4.name; ftype = decl.field4.refFieldType; } + else if fi == 5 { fname = decl.field5.name; ftype = decl.field5.refFieldType; } + else if fi == 6 { fname = decl.field6.name; ftype = decl.field6.refFieldType; } + else if fi == 7 { fname = decl.field7.name; ftype = decl.field7.refFieldType; } + // Skip empty field names + if String_Eq(fname, "") { + fi = fi + 1; + continue; + } + hm.structs[si].fields[fi].name = fname; + if ftype != null as *TypeExpr { + if ftype.kind == tekPointer && ftype.pointerPointee != null as *TypeExpr { + // Pointer type: emit "TypeName*" + if !String_Eq(ftype.pointerPointee.typeName, "") { + hm.structs[si].fields[fi].typeName = String_Concat(ftype.pointerPointee.typeName, "*"); + } + } else if !String_Eq(ftype.typeName, "") { + hm.structs[si].fields[fi].typeName = ftype.typeName; + } + } + fi = fi + 1; + } hm.structCount = hm.structCount + 1; } if decl.kind == dkEnum { diff --git a/_selfhost/src/parser.bux b/_selfhost/src/parser.bux index 5ce6510..2019f16 100644 --- a/_selfhost/src/parser.bux +++ b/_selfhost/src/parser.bux @@ -92,6 +92,24 @@ func parserEmitDiag(p: *Parser, line: uint32, col: uint32, msg: String) { p.diagCount = p.diagCount + 1; } +func parserIsKeyword(kind: int) -> bool { + if kind >= tkIf && kind <= tkSuper { return true; } + if kind == tkSizeOf { return true; } + return false; +} + +func parserExpectIdentOrKeyword(p: *Parser, msg: String) -> LexToken { + let tok: LexToken = parserCurToken(p); + if tok.kind == tkIdent || parserIsKeyword(tok.kind) { + return parserAdvance(p); + } + p.diags[p.diagCount] = ParserDiag { + line: tok.line, column: tok.column, message: msg + }; + p.diagCount = p.diagCount + 1; + return tok; +} + // --------------------------------------------------------------------------- // Type parsing // --------------------------------------------------------------------------- @@ -379,7 +397,7 @@ func parserParsePostfix(p: *Parser) -> *Expr { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkRBrace) || parserPeek(p, 0) == tkEndOfFile { break; } let sip: int = p.pos; - let fName: LexToken = parserExpect(p, tkIdent, "expected field name"); + let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name"); discard parserExpect(p, tkColon, "expected ':'"); let fValue: *Expr = parserParseExpr(p); let fExpr: *Expr = parserMakeExpr(ekField, fName.line, fName.column); @@ -487,7 +505,7 @@ func parserParseStmt(p: *Parser) -> *Stmt { if kind == tkLet || kind == tkVar { let isVar: bool = (kind == tkVar); discard parserAdvance(p); - let nameTok: LexToken = parserExpect(p, tkIdent, "expected variable name"); + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected variable name"); var typeExpr: *TypeExpr = null as *TypeExpr; if parserMatch(p, tkColon) { typeExpr = parserParseType(p); @@ -711,7 +729,7 @@ func parserParseParamList(p: *Parser) -> *Decl { discard parserExpect(p, tkLParen, "expected '('"); while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { if d.paramCount >= 6 { break; } - let nameTok: LexToken = parserExpect(p, tkIdent, "expected parameter name"); + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name"); discard parserExpect(p, tkColon, "expected ':' in parameter"); let ptype: *TypeExpr = parserParseType(p); @@ -752,7 +770,7 @@ func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool) -> *Decl { let col: uint32 = parserCurToken(p).column; discard parserExpect(p, tkFunc, "expected 'func'"); - let nameTok: LexToken = parserExpect(p, tkIdent, "expected function name"); + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected function name"); let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; d.kind = dkFunc; d.line = line; @@ -803,7 +821,7 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl { let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; discard parserExpect(p, tkStruct, "expected 'struct'"); - let nameTok: LexToken = parserExpect(p, tkIdent, "expected struct name"); + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected struct name"); let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; d.kind = dkStruct; @@ -832,7 +850,7 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl { if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; } if parserCheck(p, tkSemicolon) { discard parserAdvance(p); continue; } let beforePos: int = p.pos; - let fName: LexToken = parserExpect(p, tkIdent, "expected field name"); + let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name"); discard parserExpect(p, tkColon, "expected ':' in struct field"); let fType: *TypeExpr = parserParseType(p); parserMatch(p, tkSemicolon); diff --git a/src_bux/c_backend.bux b/src_bux/c_backend.bux index eddb3ff..5f4166e 100644 --- a/src_bux/c_backend.bux +++ b/src_bux/c_backend.bux @@ -446,6 +446,49 @@ func CBackend_Generate(mod: *HirModule) -> String { 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 { diff --git a/src_bux/hir.bux b/src_bux/hir.bux index b1886f3..da4df48 100644 --- a/src_bux/hir.bux +++ b/src_bux/hir.bux @@ -97,12 +97,24 @@ struct HirEnumVariant { // HirModule // --------------------------------------------------------------------------- +struct HirStructField { + name: String, + typeName: String, +} + +struct HirStruct { + name: String, + fieldCount: int, + fields: *HirStructField, +} + struct HirModule { funcCount: int, funcs: *HirFunc, externCount: int, externFuncs: *HirFunc, structCount: int, + structs: *HirStruct, enumCount: int, } diff --git a/src_bux/hir_lower.bux b/src_bux/hir_lower.bux index afcd304..483f20b 100644 --- a/src_bux/hir_lower.bux +++ b/src_bux/hir_lower.bux @@ -382,6 +382,12 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { let hm: *HirModule = bux_alloc(sizeof(HirModule)) as *HirModule; hm.funcCount = 0; hm.funcs = ctx.funcs; + hm.structCount = 0; + hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct; + + // First pass: count structs (to allocate field arrays later) + // Second pass: actually collect them + // For simplicity, do single pass with pre-allocated field arrays // Iterate declarations var decl: *Decl = mod.firstItem; @@ -397,6 +403,41 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { ctx.externCount = ctx.externCount + 1; } if decl.kind == dkStruct { + // Collect struct definition for C codegen + let si: int = hm.structCount; + hm.structs[si].name = decl.strValue; + hm.structs[si].fieldCount = decl.fieldCount; + hm.structs[si].fields = bux_alloc(decl.fieldCount as uint * sizeof(HirStructField)) as *HirStructField; + var fi: int = 0; + while fi < decl.fieldCount { + var fname: String = ""; + var ftype: *TypeExpr = null as *TypeExpr; + if fi == 0 { fname = decl.field0.name; ftype = decl.field0.refFieldType; } + else if fi == 1 { fname = decl.field1.name; ftype = decl.field1.refFieldType; } + else if fi == 2 { fname = decl.field2.name; ftype = decl.field2.refFieldType; } + else if fi == 3 { fname = decl.field3.name; ftype = decl.field3.refFieldType; } + else if fi == 4 { fname = decl.field4.name; ftype = decl.field4.refFieldType; } + else if fi == 5 { fname = decl.field5.name; ftype = decl.field5.refFieldType; } + else if fi == 6 { fname = decl.field6.name; ftype = decl.field6.refFieldType; } + else if fi == 7 { fname = decl.field7.name; ftype = decl.field7.refFieldType; } + // Skip empty field names + if String_Eq(fname, "") { + fi = fi + 1; + continue; + } + hm.structs[si].fields[fi].name = fname; + if ftype != null as *TypeExpr { + if ftype.kind == tekPointer && ftype.pointerPointee != null as *TypeExpr { + // Pointer type: emit "TypeName*" + if !String_Eq(ftype.pointerPointee.typeName, "") { + hm.structs[si].fields[fi].typeName = String_Concat(ftype.pointerPointee.typeName, "*"); + } + } else if !String_Eq(ftype.typeName, "") { + hm.structs[si].fields[fi].typeName = ftype.typeName; + } + } + fi = fi + 1; + } hm.structCount = hm.structCount + 1; } if decl.kind == dkEnum { diff --git a/src_bux/parser.bux b/src_bux/parser.bux index 5ce6510..2019f16 100644 --- a/src_bux/parser.bux +++ b/src_bux/parser.bux @@ -92,6 +92,24 @@ func parserEmitDiag(p: *Parser, line: uint32, col: uint32, msg: String) { p.diagCount = p.diagCount + 1; } +func parserIsKeyword(kind: int) -> bool { + if kind >= tkIf && kind <= tkSuper { return true; } + if kind == tkSizeOf { return true; } + return false; +} + +func parserExpectIdentOrKeyword(p: *Parser, msg: String) -> LexToken { + let tok: LexToken = parserCurToken(p); + if tok.kind == tkIdent || parserIsKeyword(tok.kind) { + return parserAdvance(p); + } + p.diags[p.diagCount] = ParserDiag { + line: tok.line, column: tok.column, message: msg + }; + p.diagCount = p.diagCount + 1; + return tok; +} + // --------------------------------------------------------------------------- // Type parsing // --------------------------------------------------------------------------- @@ -379,7 +397,7 @@ func parserParsePostfix(p: *Parser) -> *Expr { while parserCheck(p, tkNewLine) { discard parserAdvance(p); } if parserCheck(p, tkRBrace) || parserPeek(p, 0) == tkEndOfFile { break; } let sip: int = p.pos; - let fName: LexToken = parserExpect(p, tkIdent, "expected field name"); + let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name"); discard parserExpect(p, tkColon, "expected ':'"); let fValue: *Expr = parserParseExpr(p); let fExpr: *Expr = parserMakeExpr(ekField, fName.line, fName.column); @@ -487,7 +505,7 @@ func parserParseStmt(p: *Parser) -> *Stmt { if kind == tkLet || kind == tkVar { let isVar: bool = (kind == tkVar); discard parserAdvance(p); - let nameTok: LexToken = parserExpect(p, tkIdent, "expected variable name"); + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected variable name"); var typeExpr: *TypeExpr = null as *TypeExpr; if parserMatch(p, tkColon) { typeExpr = parserParseType(p); @@ -711,7 +729,7 @@ func parserParseParamList(p: *Parser) -> *Decl { discard parserExpect(p, tkLParen, "expected '('"); while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { if d.paramCount >= 6 { break; } - let nameTok: LexToken = parserExpect(p, tkIdent, "expected parameter name"); + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name"); discard parserExpect(p, tkColon, "expected ':' in parameter"); let ptype: *TypeExpr = parserParseType(p); @@ -752,7 +770,7 @@ func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool) -> *Decl { let col: uint32 = parserCurToken(p).column; discard parserExpect(p, tkFunc, "expected 'func'"); - let nameTok: LexToken = parserExpect(p, tkIdent, "expected function name"); + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected function name"); let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; d.kind = dkFunc; d.line = line; @@ -803,7 +821,7 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl { let line: uint32 = parserCurToken(p).line; let col: uint32 = parserCurToken(p).column; discard parserExpect(p, tkStruct, "expected 'struct'"); - let nameTok: LexToken = parserExpect(p, tkIdent, "expected struct name"); + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected struct name"); let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; d.kind = dkStruct; @@ -832,7 +850,7 @@ func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl { if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; } if parserCheck(p, tkSemicolon) { discard parserAdvance(p); continue; } let beforePos: int = p.pos; - let fName: LexToken = parserExpect(p, tkIdent, "expected field name"); + let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name"); discard parserExpect(p, tkColon, "expected ':' in struct field"); let fType: *TypeExpr = parserParseType(p); parserMatch(p, tkSemicolon);