diff --git a/_selfhost/src/cli.bux b/_selfhost/src/cli.bux index 2607c48..5dde63d 100644 --- a/_selfhost/src/cli.bux +++ b/_selfhost/src/cli.bux @@ -96,7 +96,6 @@ func Cli_Compile(source: String, sourceName: String) -> String { } Print("HIR funcCount="); PrintInt(hirMod.funcCount); - PrintLine(""); // Phase 5: QBE SSA code generation let qbeCode: String = QbeBackend_Generate(hirMod); @@ -344,15 +343,11 @@ func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCo } Print("DEBUG: MergeFileInto done, added="); PrintInt(added); - PrintLine(""); return added; } func Cli_CopyModuleDecls(target: *Module, source: *Module) { if source == null as *Module || source.firstItem == null as *Decl { return; } - Print("DEBUG: copy start, source.itemCount="); - PrintInt(source.itemCount); - PrintLine(""); var decl: *Decl = source.firstItem; var limit: int = 0; while decl != null as *Decl && limit < 10000 { @@ -365,7 +360,6 @@ func Cli_CopyModuleDecls(target: *Module, source: *Module) { } source.firstItem = null as *Decl; source.itemCount = 0; - PrintLine("DEBUG: copy end"); } func Cli_BuildProject(projectDir: String) -> int { @@ -457,7 +451,6 @@ func Cli_BuildProject(projectDir: String) -> int { Print("DEBUG: userMerged.itemCount="); PrintInt(userMerged.itemCount); - PrintLine(""); // Collect user declaration names for shadow detection let maxNames: int = 2048; @@ -482,7 +475,6 @@ func Cli_BuildProject(projectDir: String) -> int { let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount); Print("DEBUG: stdFileCount="); PrintInt(stdFileCount); - PrintLine(""); if stdFileCount > 0 { Print("Merging "); PrintInt(stdFileCount); @@ -492,7 +484,6 @@ func Cli_BuildProject(projectDir: String) -> int { while si < stdFileCount { Print("DEBUG: merging stdlib file "); PrintInt(si); - PrintLine(""); // Skip Channel.bux for debugging if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") { PrintLine("DEBUG: skipping Channel.bux"); @@ -505,7 +496,6 @@ func Cli_BuildProject(projectDir: String) -> int { } Print("Stdlib declarations added: "); PrintInt(stdAdded); - PrintLine(""); } } } @@ -520,23 +510,29 @@ func Cli_BuildProject(projectDir: String) -> int { } Print("userMerged actual count="); PrintInt(countTest); - PrintLine(""); Print("userMerged.itemCount="); PrintInt(userMerged.itemCount); - PrintLine(""); var du: *Decl = userMerged.firstItem; while du != null as *Decl { if du.kind == dkFunc { Print("user func "); PrintLine(du.strValue); } + if du.kind == dkStruct { + Print("user struct "); + Print(du.strValue); + PrintInt(du.fieldCount); + } + if du.kind == dkImpl { + Print("user impl "); + PrintLine(du.strValue); + } du = du.childDecl2; } Cli_CopyModuleDecls(merged, userMerged); PrintLine("DEBUG: copy done"); Print("merged.itemCount="); PrintInt(merged.itemCount); - PrintLine(""); PrintLine("DEBUG: copy done"); Print("Merged "); @@ -679,12 +675,10 @@ func Cli_RunProject(projectDir: String) -> int { func Cli_Run(args: *String, argCount: int) -> int { Print("Cli_Run argCount="); PrintInt(argCount); - PrintLine(""); if argCount < 2 { PrintLine("Bux Self-Hosting Compiler v0.2.0"); PrintLine("Usage: buxc [args]"); PrintLine("Commands: build, check, run, version"); - PrintLine(""); PrintLine("Pipeline modules:"); PrintLine(" Lexer ✅ 695 lines"); PrintLine(" Parser ✅ 1004 lines"); diff --git a/_selfhost/src/hir_lower.bux b/_selfhost/src/hir_lower.bux index f122571..97417df 100644 --- a/_selfhost/src/hir_lower.bux +++ b/_selfhost/src/hir_lower.bux @@ -105,6 +105,24 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { n.strValue = expr.strValue; let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue); n.typeKind = sym.typeKind; + if expr.refType != null as *TypeExpr { + n.typeName = expr.refType.typeName; + } + if sym.typeName != null as String && !String_Eq(sym.typeName, "") { + n.typeName = sym.typeName; + } + return n; + } + + // self → variable reference named "self" + if kind == ekSelf { + n.kind = hVar; + n.strValue = "self"; + let sym: Symbol = Scope_Lookup(ctx.scope, "self"); + n.typeKind = sym.typeKind; + if sym.typeName != null as String && !String_Eq(sym.typeName, "") { + n.typeName = sym.typeName; + } return n; } @@ -135,6 +153,41 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { // Call if kind == ekCall { n.kind = hCall; + // Method call desugaring: obj.method(args) → Type_method(obj, args) + if expr.child1 != null as *Expr && expr.child1.kind == ekField { + let methodName: String = expr.child1.strValue; + var receiverTypeName: String = ""; + if expr.child1.child1 != null as *Expr && expr.child1.child1.kind == ekIdent { + let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.child1.strValue); + receiverTypeName = sym.typeName; + } + if String_Eq(receiverTypeName, "") && expr.child1.child1 != null as *Expr && expr.child1.child1.refType != null as *TypeExpr { + receiverTypeName = expr.child1.child1.refType.typeName; + } + if !String_Eq(receiverTypeName, "") { + // Strip trailing '*' from pointer type names (e.g. "Box*" → "Box") + var baseName: String = receiverTypeName; + let len: int = bux_strlen(baseName) as int; + if len > 0 { + let lastChar: String = bux_str_slice(baseName, (len - 1) as uint, 1); + if String_Eq(lastChar, "*") { + baseName = bux_str_slice(baseName, 0, (len - 1) as uint); + } + } + n.strValue = String_Concat(baseName, "_"); + n.strValue = String_Concat(n.strValue, methodName); + } + // Lower receiver as first argument + let recv: *HirNode = Lcx_LowerExpr(ctx, expr.child1.child1); + n.child1 = recv; + // Lower remaining arguments + if expr.child2 != null as *Expr { + let extra: *HirNode = Lcx_LowerExpr(ctx, expr.child2); + // Chain extra args after receiver via child2/child3 + n.child2 = extra; + } + return n; + } // Get callee name if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { n.strValue = expr.child1.strValue; @@ -163,6 +216,10 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { n.kind = hFieldPtr; n.child1 = Lcx_LowerExpr(ctx, expr.child1); n.strValue = expr.strValue; + // Get struct type from base expr refType + if expr.child1 != null as *Expr && expr.child1.refType != null as *TypeExpr { + n.typeName = expr.child1.refType.typeName; + } return n; } @@ -286,6 +343,13 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode { alloca.typeName = stmt.refStmtType.typeName; } } + // Add to scope for field offset lookups + var sym: Symbol; + sym.kind = skVar; + sym.name = stmt.strValue; + sym.typeKind = alloca.typeKind; + sym.typeName = alloca.typeName; + discard Scope_Define(ctx.scope, sym); // store the init value n.kind = hStore; n.child1 = alloca; @@ -430,6 +494,27 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { f.retTypeKind = 0; } + // Add parameters to hir_lower scope for field offset lookups + var pi: int = 0; + while pi < decl.paramCount { + var p: *Param = null as *Param; + if pi == 0 { p = &decl.param0; } + else if pi == 1 { p = &decl.param1; } + else if pi == 2 { p = &decl.param2; } + else if pi == 3 { p = &decl.param3; } + else if pi == 4 { p = &decl.param4; } + else if pi == 5 { p = &decl.param5; } + if p != null as *Param && p.refParamType != null as *TypeExpr { + var sym: Symbol; + sym.kind = skVar; + sym.name = p.name; + sym.typeKind = Lcx_ResolveTypeKind(p.refParamType); + sym.typeName = p.refParamType.typeName; + discard Scope_Define(ctx.scope, sym); + } + pi = pi + 1; + } + if decl.refBody != null as *Block { f.body = Lcx_LowerBlock(ctx, decl.refBody, -1); } else { @@ -449,7 +534,7 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { ctx.scope = sema.scope; ctx.funcs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc; ctx.funcCount = 0; - ctx.externFuncs = bux_alloc(64 as uint * sizeof(HirFunc)) as *HirFunc; + ctx.externFuncs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc; ctx.externCount = 0; ctx.varCounter = 0; @@ -473,6 +558,20 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { ctx.funcs[ctx.funcCount] = *f; ctx.funcCount = ctx.funcCount + 1; } + if decl.kind == dkImpl { + let implTypeName: String = decl.strValue; + var implDecl: *Decl = decl.childDecl1; + while implDecl != null as *Decl { + if implDecl.kind == dkFunc { + let mangled: String = String_Concat(implTypeName, "_"); + implDecl.strValue = String_Concat(mangled, implDecl.strValue); + let f: *HirFunc = Lcx_LowerFunc(ctx, implDecl); + ctx.funcs[ctx.funcCount] = *f; + ctx.funcCount = ctx.funcCount + 1; + } + implDecl = implDecl.childDecl2; + } + } if decl.kind == dkExternFunc { let f: *HirFunc = Lcx_LowerFunc(ctx, decl); ctx.externFuncs[ctx.externCount] = *f; @@ -534,11 +633,13 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { decl = decl.childDecl2; } + hm.funcCount = ctx.funcCount; hm.funcs = ctx.funcs; hm.externCount = ctx.externCount; hm.externFuncs = ctx.externFuncs; + return hm; } diff --git a/_selfhost/src/parser.bux b/_selfhost/src/parser.bux index 8ad0e5b..f6279c3 100644 --- a/_selfhost/src/parser.bux +++ b/_selfhost/src/parser.bux @@ -44,6 +44,29 @@ func parserPeek(p: *Parser, ahead: int) -> int { return tkEndOfFile; } +// Lookahead to determine if '<' starts a type argument list. +func parserIsTypeArgListAhead(p: *Parser) -> bool { + if !parserCheck(p, tkLt) { return false; } + var depth: int = 0; + var ahead: int = 0; + while true { + let kind: int = parserPeek(p, ahead); + if kind == tkEndOfFile || kind == tkLBrace || kind == tkSemicolon { + return false; + } + if kind == tkLt { + depth = depth + 1; + } else if kind == tkGt { + depth = depth - 1; + if depth == 0 { + return true; + } + } + ahead = ahead + 1; + } + return false; +} + func parserAdvance(p: *Parser) -> LexToken { let tok: LexToken = parserCurToken(p); if p.pos < p.tokenCount { @@ -331,6 +354,25 @@ func parserParsePostfix(p: *Parser) -> *Expr { continue; } + // Generic call: Func(args) or Type { ... } + if kind == tkLt { + if left.kind == ekIdent && parserIsTypeArgListAhead(p) { + discard parserAdvance(p); // < + let ta0: LexToken = parserExpect(p, tkIdent, "expected type argument"); + left.genericCallee = left.strValue; + left.genericTypeArg0 = ta0.text; + left.genericTypeArgCount = 1; + if parserMatch(p, tkComma) { + let ta1: LexToken = parserExpect(p, tkIdent, "expected type argument"); + left.genericTypeArg1 = ta1.text; + left.genericTypeArgCount = 2; + } + discard parserExpect(p, tkGt, "expected '>' to close type arguments"); + // After generic args, continue loop to handle call or struct init + continue; + } + } + // Index: expr[expr] if kind == tkLBracket { discard parserAdvance(p); @@ -1285,7 +1327,7 @@ func parserParseDecl(p: *Parser) -> *Decl { if d.methodCount == 0 { d.childDecl1 = m; } else if d.methodCount == 1 { - d.childDecl2 = m; + d.childDecl1.childDecl2 = m; } d.methodCount = d.methodCount + 1; } else { diff --git a/_selfhost/src/qbe_backend.bux b/_selfhost/src/qbe_backend.bux index e64fb19..8a46583 100644 --- a/_selfhost/src/qbe_backend.bux +++ b/_selfhost/src/qbe_backend.bux @@ -266,7 +266,7 @@ func QBE_IsTerminator(node: *HirNode) -> bool { if node.kind == hBreak { return true; } if node.kind == hContinue { return true; } if node.kind == hIf { - if node.extraData != null as *HirNode { + if node.extraData != null as *void { return QBE_IsTerminator(node.child2) && QBE_IsTerminator(node.extraData as *HirNode); } return false; @@ -307,6 +307,74 @@ func QBE_FindFunc(qbe: *QbeEmitter, name: String) -> *HirFunc { return null as *HirFunc; } +// --------------------------------------------------------------------------- +// Helper: find struct by name in module +// --------------------------------------------------------------------------- + +func QBE_FindStruct(qbe: *QbeEmitter, name: String) -> *HirStruct { + if qbe.mod == null as *HirModule || name == null as String { return null as *HirStruct; } + var i: int = 0; + while i < qbe.mod.structCount { + if String_Eq(qbe.mod.structs[i].name, name) { + return &qbe.mod.structs[i]; + } + i = i + 1; + } + return null as *HirStruct; +} + +// --------------------------------------------------------------------------- +// Helper: compute field offset in a struct +// --------------------------------------------------------------------------- + +func QBE_FieldOffset(qbe: *QbeEmitter, structName: String, fieldName: String) -> int { + let st: *HirStruct = QBE_FindStruct(qbe, structName); + if st == null as *HirStruct { return 0; } + var off: int = 0; + var fi: int = 0; + while fi < st.fieldCount { + if String_Eq(st.fields[fi].name, fieldName) { + return off; + } + let ftype: String = st.fields[fi].typeName; + var fsz: int = 8; + if String_Eq(ftype, "int8") || String_Eq(ftype, "uint8") || String_Eq(ftype, "bool8") || String_Eq(ftype, "char8") { fsz = 1; } + else if String_Eq(ftype, "int16") || String_Eq(ftype, "uint16") || String_Eq(ftype, "bool16") || String_Eq(ftype, "char16") { fsz = 2; } + else if String_Eq(ftype, "int32") || String_Eq(ftype, "uint32") || String_Eq(ftype, "bool32") || String_Eq(ftype, "char32") || String_Eq(ftype, "float32") { fsz = 4; } + off = off + fsz; + fi = fi + 1; + } + return 0; +} + +// --------------------------------------------------------------------------- +// Helper: type size in bytes +// --------------------------------------------------------------------------- + +func QBE_TypeSize(qbe: *QbeEmitter, kind: int, typeName: String) -> int { + if kind == tyNamed && typeName != null as String && !String_Eq(typeName, "") { + let st: *HirStruct = QBE_FindStruct(qbe, typeName); + if st != null as *HirStruct && st.fields != null as *HirStructField { + var sz: int = 0; + var fi: int = 0; + while fi < st.fieldCount { + let ftype: String = st.fields[fi].typeName; + var fsz: int = 8; + if String_Eq(ftype, "int8") || String_Eq(ftype, "uint8") || String_Eq(ftype, "bool8") || String_Eq(ftype, "char8") { fsz = 1; } + else if String_Eq(ftype, "int16") || String_Eq(ftype, "uint16") || String_Eq(ftype, "bool16") || String_Eq(ftype, "char16") { fsz = 2; } + else if String_Eq(ftype, "int32") || String_Eq(ftype, "uint32") || String_Eq(ftype, "bool32") || String_Eq(ftype, "char32") || String_Eq(ftype, "float32") { fsz = 4; } + sz = sz + fsz; + fi = fi + 1; + } + return sz; + } + } + if kind == tyBool8 || kind == tyChar8 || kind == tyInt8 || kind == tyUInt8 { return 1; } + if kind == tyBool16 || kind == tyChar16 || kind == tyInt16 || kind == tyUInt16 { return 2; } + if kind == tyBool32 || kind == tyChar32 || kind == tyInt32 || kind == tyUInt32 || kind == tyFloat32 { return 4; } + return 8; +} + // --------------------------------------------------------------------------- // Helper: determine QBE type for any HIR node // --------------------------------------------------------------------------- @@ -411,6 +479,23 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String { var target: String = ""; if node.child1 != null as *HirNode && node.child1.kind == hVar { target = QBE_LocalName(node.child1.strValue); + } else if node.child1 != null as *HirNode && node.child1.kind == hFieldPtr { + // Field assignment: generate pointer, not auto-load value + let base: String = QBE_EmitExpr(qbe, node.child1.child1); + let ptrTmp: String = QBE_FreshTemp(qbe); + var structName: String = node.child1.typeName; + if structName == null as String || String_Eq(structName, "") { + structName = node.child1.child1.typeName; + } + let offset: int = QBE_FieldOffset(qbe, structName, node.child1.strValue); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, ptrTmp); + QBE_Emit(qbe, " =l add "); + QBE_Emit(qbe, QBE_EnsureLocal(base)); + QBE_Emit(qbe, ", "); + QBE_Emit(qbe, String_FromInt(offset as int64)); + QBE_EmitLine(qbe, ""); + target = ptrTmp; } else { target = QBE_EmitExpr(qbe, node.child1); if String_Eq(target, "") { return "0"; } @@ -659,18 +744,84 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String { return tmp; } - // Field pointer: &obj.field + // Field pointer: &obj.field (with auto-load for rvalue contexts) if kind == hFieldPtr { let base: String = QBE_EmitExpr(qbe, node.child1); - let tmp: String = QBE_FreshTemp(qbe); - // add base, offset — need to know offset; for now assume 0 for first field + let ptrTmp: String = QBE_FreshTemp(qbe); + var structName: String = node.typeName; + if structName == null as String || String_Eq(structName, "") { + structName = node.child1.typeName; + } + let offset: int = QBE_FieldOffset(qbe, structName, node.strValue); QBE_EmitIndent(qbe); - QBE_Emit(qbe, tmp); + QBE_Emit(qbe, ptrTmp); QBE_Emit(qbe, " =l add "); QBE_Emit(qbe, QBE_EnsureLocal(base)); - QBE_Emit(qbe, ", 0"); + QBE_Emit(qbe, ", "); + QBE_Emit(qbe, String_FromInt(offset as int64)); QBE_EmitLine(qbe, ""); - return tmp; + // Auto-load value from field address (rvalue context) + let valTmp: String = QBE_FreshTemp(qbe); + let fty: String = QbeBackend_TypeWord(node.typeKind); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, valTmp); + QBE_Emit(qbe, " ="); + QBE_Emit(qbe, fty); + QBE_Emit(qbe, " load"); + QBE_Emit(qbe, fty); + QBE_Emit(qbe, " "); + QBE_Emit(qbe, QBE_EnsureLocal(ptrTmp)); + QBE_EmitLine(qbe, ""); + return valTmp; + } + + // Struct init: allocate and fill fields + if kind == hStructInit { + let st: *HirStruct = QBE_FindStruct(qbe, node.strValue); + var structSize: int = 0; + if st != null as *HirStruct { + var si: int = 0; + while si < st.fieldCount { + let ftype: String = st.fields[si].typeName; + var fsz: int = 8; + if String_Eq(ftype, "int8") || String_Eq(ftype, "uint8") || String_Eq(ftype, "bool8") || String_Eq(ftype, "char8") { fsz = 1; } + else if String_Eq(ftype, "int16") || String_Eq(ftype, "uint16") || String_Eq(ftype, "bool16") || String_Eq(ftype, "char16") { fsz = 2; } + else if String_Eq(ftype, "int32") || String_Eq(ftype, "uint32") || String_Eq(ftype, "bool32") || String_Eq(ftype, "char32") || String_Eq(ftype, "float32") { fsz = 4; } + structSize = structSize + fsz; + si = si + 1; + } + } + let ptr: String = QBE_FreshTemp(qbe); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, ptr); + QBE_Emit(qbe, " =l alloc4 "); + let szStr: String = String_FromInt(structSize as int64); + QBE_Emit(qbe, szStr); + QBE_EmitLine(qbe, ""); + // Store each field + var field: *HirNode = node.child1; + while field != null as *HirNode { + let fval: String = QBE_EmitExpr(qbe, field.child1); + let foff: int = QBE_FieldOffset(qbe, node.strValue, field.strValue); + let fptr: String = QBE_FreshTemp(qbe); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, fptr); + QBE_Emit(qbe, " =l add "); + QBE_Emit(qbe, QBE_EnsureLocal(ptr)); + QBE_Emit(qbe, ", "); + QBE_Emit(qbe, String_FromInt(foff as int64)); + QBE_EmitLine(qbe, ""); + let fty: String = QbeBackend_TypeWord(field.child1.typeKind); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, "store"); + QBE_Emit(qbe, fty); + QBE_Emit(qbe, " "); + QBE_Emit(qbe, QBE_EnsureLocal(fval)); + QBE_Emit(qbe, ", "); + QBE_EmitLine(qbe, QBE_EnsureLocal(fptr)); + field = field.child3; + } + return ptr; } // Index pointer: &arr[idx] (for pointer indexing) @@ -744,6 +895,34 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) { } return; } + // Field assignment: target is hFieldPtr, need pointer not value + if node.child1 != null as *HirNode && node.child1.kind == hFieldPtr { + let base: String = QBE_EmitExpr(qbe, node.child1.child1); + let ptrTmp: String = QBE_FreshTemp(qbe); + var structName: String = node.child1.typeName; + if structName == null as String || String_Eq(structName, "") { + structName = node.child1.child1.typeName; + } + let offset: int = QBE_FieldOffset(qbe, structName, node.child1.strValue); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, ptrTmp); + QBE_Emit(qbe, " =l add "); + QBE_Emit(qbe, QBE_EnsureLocal(base)); + QBE_Emit(qbe, ", "); + QBE_Emit(qbe, String_FromInt(offset as int64)); + QBE_EmitLine(qbe, ""); + let val: String = QBE_EmitExpr(qbe, node.child2); + let ty: String = QBE_NodeType(qbe, node.child2); + if String_Eq(val, "") { val = "0"; } + QBE_EmitIndent(qbe); + QBE_Emit(qbe, "store"); + QBE_Emit(qbe, ty); + QBE_Emit(qbe, " "); + QBE_Emit(qbe, QBE_EnsureLocal(val)); + QBE_Emit(qbe, ", "); + QBE_EmitLine(qbe, QBE_EnsureLocal(ptrTmp)); + return; + } // Plain assignment let target: String = QBE_EmitExpr(qbe, node.child1); let val: String = QBE_EmitExpr(qbe, node.child2); @@ -786,7 +965,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) { QBE_Emit(qbe, ", "); QBE_Emit(qbe, lThen); QBE_Emit(qbe, ", "); - var hasElse: bool = node.extraData != null as *HirNode; + var hasElse: bool = node.extraData != null as *void; if hasElse { QBE_EmitLine(qbe, lElse); } else { diff --git a/_selfhost/src/sema.bux b/_selfhost/src/sema.bux index 668ae7d..4de8a7e 100644 --- a/_selfhost/src/sema.bux +++ b/_selfhost/src/sema.bux @@ -2,6 +2,8 @@ // Validates types, resolves identifiers, checks function calls. module Sema { + + // --------------------------------------------------------------------------- // Sema context // --------------------------------------------------------------------------- @@ -130,6 +132,12 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { Sema_EmitError(sema, expr.line, expr.column, "undeclared identifier"); return tyUnknown; } + if sym.typeName != null as String && !String_Eq(sym.typeName, "") { + let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; + te.kind = tekNamed; + te.typeName = sym.typeName; + expr.refType = te; + } return sym.typeKind; } @@ -212,6 +220,10 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) { sym.kind = skVar; sym.name = stmt.strValue; sym.typeKind = initType; + sym.typeName = ""; + if stmt.refStmtType != null as *TypeExpr { + sym.typeName = stmt.refStmtType.typeName; + } sym.isMutable = stmt.boolValue; sym.isPublic = false; discard Scope_Define(sema.scope, sym); @@ -350,9 +362,22 @@ func Sema_Analyze(mod: *Module) -> *Sema { // Add parameters to scope var i: int = 0; while i < decl.paramCount { + var p: *Param = null as *Param; + if i == 0 { p = &decl.param0; } + else if i == 1 { p = &decl.param1; } + else if i == 2 { p = &decl.param2; } + else if i == 3 { p = &decl.param3; } + else if i == 4 { p = &decl.param4; } + else if i == 5 { p = &decl.param5; } var pSym: Symbol; pSym.kind = skVar; - pSym.typeKind = tyInt; // simplified + if p != null as *Param && p.refParamType != null as *TypeExpr { + pSym.typeKind = Sema_ResolveType(s, p.refParamType); + pSym.typeName = p.refParamType.typeName; + } else { + pSym.typeKind = tyInt; + pSym.typeName = ""; + } pSym.isMutable = false; if i == 0 { pSym.name = decl.param0.name; } else if i == 1 { pSym.name = decl.param1.name; } diff --git a/demo_app/bux.toml b/demo_app/bux.toml new file mode 100644 index 0000000..9e3d5ed --- /dev/null +++ b/demo_app/bux.toml @@ -0,0 +1,7 @@ +[Package] +Name = "demo_app" +Version = "0.1.0" +Type = "bin" + +[Build] +Output = "Bin" diff --git a/demo_app/src/Main.bux b/demo_app/src/Main.bux new file mode 100644 index 0000000..d4da35b --- /dev/null +++ b/demo_app/src/Main.bux @@ -0,0 +1,29 @@ +// Structs - Basic struct usage +import Std::Io::{PrintLine, PrintInt}; + + +struct Point { + x: int; + y: int; +} + +func AddPoints(a: Point, b: Point) -> Point { + let result: Point = Point { x: a.x + b.x, y: a.y + b.y }; + return result; +} + +func Main() -> int { + let p1: Point = Point { x: 10, y: 20 }; + let p2: Point = Point { x: 5, y: 15 }; + let sum: Point = AddPoints(p1, p2); + + PrintLine("Point sum:"); + PrintLine("x = "); + PrintInt(sum.x); + PrintLine(""); + PrintLine("y = "); + PrintInt(sum.y); + PrintLine(""); + + return 0; +} diff --git a/src_bux/cli.bux b/src_bux/cli.bux index 2607c48..5dde63d 100644 --- a/src_bux/cli.bux +++ b/src_bux/cli.bux @@ -96,7 +96,6 @@ func Cli_Compile(source: String, sourceName: String) -> String { } Print("HIR funcCount="); PrintInt(hirMod.funcCount); - PrintLine(""); // Phase 5: QBE SSA code generation let qbeCode: String = QbeBackend_Generate(hirMod); @@ -344,15 +343,11 @@ func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCo } Print("DEBUG: MergeFileInto done, added="); PrintInt(added); - PrintLine(""); return added; } func Cli_CopyModuleDecls(target: *Module, source: *Module) { if source == null as *Module || source.firstItem == null as *Decl { return; } - Print("DEBUG: copy start, source.itemCount="); - PrintInt(source.itemCount); - PrintLine(""); var decl: *Decl = source.firstItem; var limit: int = 0; while decl != null as *Decl && limit < 10000 { @@ -365,7 +360,6 @@ func Cli_CopyModuleDecls(target: *Module, source: *Module) { } source.firstItem = null as *Decl; source.itemCount = 0; - PrintLine("DEBUG: copy end"); } func Cli_BuildProject(projectDir: String) -> int { @@ -457,7 +451,6 @@ func Cli_BuildProject(projectDir: String) -> int { Print("DEBUG: userMerged.itemCount="); PrintInt(userMerged.itemCount); - PrintLine(""); // Collect user declaration names for shadow detection let maxNames: int = 2048; @@ -482,7 +475,6 @@ func Cli_BuildProject(projectDir: String) -> int { let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount); Print("DEBUG: stdFileCount="); PrintInt(stdFileCount); - PrintLine(""); if stdFileCount > 0 { Print("Merging "); PrintInt(stdFileCount); @@ -492,7 +484,6 @@ func Cli_BuildProject(projectDir: String) -> int { while si < stdFileCount { Print("DEBUG: merging stdlib file "); PrintInt(si); - PrintLine(""); // Skip Channel.bux for debugging if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") { PrintLine("DEBUG: skipping Channel.bux"); @@ -505,7 +496,6 @@ func Cli_BuildProject(projectDir: String) -> int { } Print("Stdlib declarations added: "); PrintInt(stdAdded); - PrintLine(""); } } } @@ -520,23 +510,29 @@ func Cli_BuildProject(projectDir: String) -> int { } Print("userMerged actual count="); PrintInt(countTest); - PrintLine(""); Print("userMerged.itemCount="); PrintInt(userMerged.itemCount); - PrintLine(""); var du: *Decl = userMerged.firstItem; while du != null as *Decl { if du.kind == dkFunc { Print("user func "); PrintLine(du.strValue); } + if du.kind == dkStruct { + Print("user struct "); + Print(du.strValue); + PrintInt(du.fieldCount); + } + if du.kind == dkImpl { + Print("user impl "); + PrintLine(du.strValue); + } du = du.childDecl2; } Cli_CopyModuleDecls(merged, userMerged); PrintLine("DEBUG: copy done"); Print("merged.itemCount="); PrintInt(merged.itemCount); - PrintLine(""); PrintLine("DEBUG: copy done"); Print("Merged "); @@ -679,12 +675,10 @@ func Cli_RunProject(projectDir: String) -> int { func Cli_Run(args: *String, argCount: int) -> int { Print("Cli_Run argCount="); PrintInt(argCount); - PrintLine(""); if argCount < 2 { PrintLine("Bux Self-Hosting Compiler v0.2.0"); PrintLine("Usage: buxc [args]"); PrintLine("Commands: build, check, run, version"); - PrintLine(""); PrintLine("Pipeline modules:"); PrintLine(" Lexer ✅ 695 lines"); PrintLine(" Parser ✅ 1004 lines"); diff --git a/src_bux/hir_lower.bux b/src_bux/hir_lower.bux index f122571..97417df 100644 --- a/src_bux/hir_lower.bux +++ b/src_bux/hir_lower.bux @@ -105,6 +105,24 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { n.strValue = expr.strValue; let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue); n.typeKind = sym.typeKind; + if expr.refType != null as *TypeExpr { + n.typeName = expr.refType.typeName; + } + if sym.typeName != null as String && !String_Eq(sym.typeName, "") { + n.typeName = sym.typeName; + } + return n; + } + + // self → variable reference named "self" + if kind == ekSelf { + n.kind = hVar; + n.strValue = "self"; + let sym: Symbol = Scope_Lookup(ctx.scope, "self"); + n.typeKind = sym.typeKind; + if sym.typeName != null as String && !String_Eq(sym.typeName, "") { + n.typeName = sym.typeName; + } return n; } @@ -135,6 +153,41 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { // Call if kind == ekCall { n.kind = hCall; + // Method call desugaring: obj.method(args) → Type_method(obj, args) + if expr.child1 != null as *Expr && expr.child1.kind == ekField { + let methodName: String = expr.child1.strValue; + var receiverTypeName: String = ""; + if expr.child1.child1 != null as *Expr && expr.child1.child1.kind == ekIdent { + let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.child1.strValue); + receiverTypeName = sym.typeName; + } + if String_Eq(receiverTypeName, "") && expr.child1.child1 != null as *Expr && expr.child1.child1.refType != null as *TypeExpr { + receiverTypeName = expr.child1.child1.refType.typeName; + } + if !String_Eq(receiverTypeName, "") { + // Strip trailing '*' from pointer type names (e.g. "Box*" → "Box") + var baseName: String = receiverTypeName; + let len: int = bux_strlen(baseName) as int; + if len > 0 { + let lastChar: String = bux_str_slice(baseName, (len - 1) as uint, 1); + if String_Eq(lastChar, "*") { + baseName = bux_str_slice(baseName, 0, (len - 1) as uint); + } + } + n.strValue = String_Concat(baseName, "_"); + n.strValue = String_Concat(n.strValue, methodName); + } + // Lower receiver as first argument + let recv: *HirNode = Lcx_LowerExpr(ctx, expr.child1.child1); + n.child1 = recv; + // Lower remaining arguments + if expr.child2 != null as *Expr { + let extra: *HirNode = Lcx_LowerExpr(ctx, expr.child2); + // Chain extra args after receiver via child2/child3 + n.child2 = extra; + } + return n; + } // Get callee name if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { n.strValue = expr.child1.strValue; @@ -163,6 +216,10 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { n.kind = hFieldPtr; n.child1 = Lcx_LowerExpr(ctx, expr.child1); n.strValue = expr.strValue; + // Get struct type from base expr refType + if expr.child1 != null as *Expr && expr.child1.refType != null as *TypeExpr { + n.typeName = expr.child1.refType.typeName; + } return n; } @@ -286,6 +343,13 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode { alloca.typeName = stmt.refStmtType.typeName; } } + // Add to scope for field offset lookups + var sym: Symbol; + sym.kind = skVar; + sym.name = stmt.strValue; + sym.typeKind = alloca.typeKind; + sym.typeName = alloca.typeName; + discard Scope_Define(ctx.scope, sym); // store the init value n.kind = hStore; n.child1 = alloca; @@ -430,6 +494,27 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { f.retTypeKind = 0; } + // Add parameters to hir_lower scope for field offset lookups + var pi: int = 0; + while pi < decl.paramCount { + var p: *Param = null as *Param; + if pi == 0 { p = &decl.param0; } + else if pi == 1 { p = &decl.param1; } + else if pi == 2 { p = &decl.param2; } + else if pi == 3 { p = &decl.param3; } + else if pi == 4 { p = &decl.param4; } + else if pi == 5 { p = &decl.param5; } + if p != null as *Param && p.refParamType != null as *TypeExpr { + var sym: Symbol; + sym.kind = skVar; + sym.name = p.name; + sym.typeKind = Lcx_ResolveTypeKind(p.refParamType); + sym.typeName = p.refParamType.typeName; + discard Scope_Define(ctx.scope, sym); + } + pi = pi + 1; + } + if decl.refBody != null as *Block { f.body = Lcx_LowerBlock(ctx, decl.refBody, -1); } else { @@ -449,7 +534,7 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { ctx.scope = sema.scope; ctx.funcs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc; ctx.funcCount = 0; - ctx.externFuncs = bux_alloc(64 as uint * sizeof(HirFunc)) as *HirFunc; + ctx.externFuncs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc; ctx.externCount = 0; ctx.varCounter = 0; @@ -473,6 +558,20 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { ctx.funcs[ctx.funcCount] = *f; ctx.funcCount = ctx.funcCount + 1; } + if decl.kind == dkImpl { + let implTypeName: String = decl.strValue; + var implDecl: *Decl = decl.childDecl1; + while implDecl != null as *Decl { + if implDecl.kind == dkFunc { + let mangled: String = String_Concat(implTypeName, "_"); + implDecl.strValue = String_Concat(mangled, implDecl.strValue); + let f: *HirFunc = Lcx_LowerFunc(ctx, implDecl); + ctx.funcs[ctx.funcCount] = *f; + ctx.funcCount = ctx.funcCount + 1; + } + implDecl = implDecl.childDecl2; + } + } if decl.kind == dkExternFunc { let f: *HirFunc = Lcx_LowerFunc(ctx, decl); ctx.externFuncs[ctx.externCount] = *f; @@ -534,11 +633,13 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { decl = decl.childDecl2; } + hm.funcCount = ctx.funcCount; hm.funcs = ctx.funcs; hm.externCount = ctx.externCount; hm.externFuncs = ctx.externFuncs; + return hm; } diff --git a/src_bux/parser.bux b/src_bux/parser.bux index 8ad0e5b..f6279c3 100644 --- a/src_bux/parser.bux +++ b/src_bux/parser.bux @@ -44,6 +44,29 @@ func parserPeek(p: *Parser, ahead: int) -> int { return tkEndOfFile; } +// Lookahead to determine if '<' starts a type argument list. +func parserIsTypeArgListAhead(p: *Parser) -> bool { + if !parserCheck(p, tkLt) { return false; } + var depth: int = 0; + var ahead: int = 0; + while true { + let kind: int = parserPeek(p, ahead); + if kind == tkEndOfFile || kind == tkLBrace || kind == tkSemicolon { + return false; + } + if kind == tkLt { + depth = depth + 1; + } else if kind == tkGt { + depth = depth - 1; + if depth == 0 { + return true; + } + } + ahead = ahead + 1; + } + return false; +} + func parserAdvance(p: *Parser) -> LexToken { let tok: LexToken = parserCurToken(p); if p.pos < p.tokenCount { @@ -331,6 +354,25 @@ func parserParsePostfix(p: *Parser) -> *Expr { continue; } + // Generic call: Func(args) or Type { ... } + if kind == tkLt { + if left.kind == ekIdent && parserIsTypeArgListAhead(p) { + discard parserAdvance(p); // < + let ta0: LexToken = parserExpect(p, tkIdent, "expected type argument"); + left.genericCallee = left.strValue; + left.genericTypeArg0 = ta0.text; + left.genericTypeArgCount = 1; + if parserMatch(p, tkComma) { + let ta1: LexToken = parserExpect(p, tkIdent, "expected type argument"); + left.genericTypeArg1 = ta1.text; + left.genericTypeArgCount = 2; + } + discard parserExpect(p, tkGt, "expected '>' to close type arguments"); + // After generic args, continue loop to handle call or struct init + continue; + } + } + // Index: expr[expr] if kind == tkLBracket { discard parserAdvance(p); @@ -1285,7 +1327,7 @@ func parserParseDecl(p: *Parser) -> *Decl { if d.methodCount == 0 { d.childDecl1 = m; } else if d.methodCount == 1 { - d.childDecl2 = m; + d.childDecl1.childDecl2 = m; } d.methodCount = d.methodCount + 1; } else { diff --git a/src_bux/qbe_backend.bux b/src_bux/qbe_backend.bux index e64fb19..8a46583 100644 --- a/src_bux/qbe_backend.bux +++ b/src_bux/qbe_backend.bux @@ -266,7 +266,7 @@ func QBE_IsTerminator(node: *HirNode) -> bool { if node.kind == hBreak { return true; } if node.kind == hContinue { return true; } if node.kind == hIf { - if node.extraData != null as *HirNode { + if node.extraData != null as *void { return QBE_IsTerminator(node.child2) && QBE_IsTerminator(node.extraData as *HirNode); } return false; @@ -307,6 +307,74 @@ func QBE_FindFunc(qbe: *QbeEmitter, name: String) -> *HirFunc { return null as *HirFunc; } +// --------------------------------------------------------------------------- +// Helper: find struct by name in module +// --------------------------------------------------------------------------- + +func QBE_FindStruct(qbe: *QbeEmitter, name: String) -> *HirStruct { + if qbe.mod == null as *HirModule || name == null as String { return null as *HirStruct; } + var i: int = 0; + while i < qbe.mod.structCount { + if String_Eq(qbe.mod.structs[i].name, name) { + return &qbe.mod.structs[i]; + } + i = i + 1; + } + return null as *HirStruct; +} + +// --------------------------------------------------------------------------- +// Helper: compute field offset in a struct +// --------------------------------------------------------------------------- + +func QBE_FieldOffset(qbe: *QbeEmitter, structName: String, fieldName: String) -> int { + let st: *HirStruct = QBE_FindStruct(qbe, structName); + if st == null as *HirStruct { return 0; } + var off: int = 0; + var fi: int = 0; + while fi < st.fieldCount { + if String_Eq(st.fields[fi].name, fieldName) { + return off; + } + let ftype: String = st.fields[fi].typeName; + var fsz: int = 8; + if String_Eq(ftype, "int8") || String_Eq(ftype, "uint8") || String_Eq(ftype, "bool8") || String_Eq(ftype, "char8") { fsz = 1; } + else if String_Eq(ftype, "int16") || String_Eq(ftype, "uint16") || String_Eq(ftype, "bool16") || String_Eq(ftype, "char16") { fsz = 2; } + else if String_Eq(ftype, "int32") || String_Eq(ftype, "uint32") || String_Eq(ftype, "bool32") || String_Eq(ftype, "char32") || String_Eq(ftype, "float32") { fsz = 4; } + off = off + fsz; + fi = fi + 1; + } + return 0; +} + +// --------------------------------------------------------------------------- +// Helper: type size in bytes +// --------------------------------------------------------------------------- + +func QBE_TypeSize(qbe: *QbeEmitter, kind: int, typeName: String) -> int { + if kind == tyNamed && typeName != null as String && !String_Eq(typeName, "") { + let st: *HirStruct = QBE_FindStruct(qbe, typeName); + if st != null as *HirStruct && st.fields != null as *HirStructField { + var sz: int = 0; + var fi: int = 0; + while fi < st.fieldCount { + let ftype: String = st.fields[fi].typeName; + var fsz: int = 8; + if String_Eq(ftype, "int8") || String_Eq(ftype, "uint8") || String_Eq(ftype, "bool8") || String_Eq(ftype, "char8") { fsz = 1; } + else if String_Eq(ftype, "int16") || String_Eq(ftype, "uint16") || String_Eq(ftype, "bool16") || String_Eq(ftype, "char16") { fsz = 2; } + else if String_Eq(ftype, "int32") || String_Eq(ftype, "uint32") || String_Eq(ftype, "bool32") || String_Eq(ftype, "char32") || String_Eq(ftype, "float32") { fsz = 4; } + sz = sz + fsz; + fi = fi + 1; + } + return sz; + } + } + if kind == tyBool8 || kind == tyChar8 || kind == tyInt8 || kind == tyUInt8 { return 1; } + if kind == tyBool16 || kind == tyChar16 || kind == tyInt16 || kind == tyUInt16 { return 2; } + if kind == tyBool32 || kind == tyChar32 || kind == tyInt32 || kind == tyUInt32 || kind == tyFloat32 { return 4; } + return 8; +} + // --------------------------------------------------------------------------- // Helper: determine QBE type for any HIR node // --------------------------------------------------------------------------- @@ -411,6 +479,23 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String { var target: String = ""; if node.child1 != null as *HirNode && node.child1.kind == hVar { target = QBE_LocalName(node.child1.strValue); + } else if node.child1 != null as *HirNode && node.child1.kind == hFieldPtr { + // Field assignment: generate pointer, not auto-load value + let base: String = QBE_EmitExpr(qbe, node.child1.child1); + let ptrTmp: String = QBE_FreshTemp(qbe); + var structName: String = node.child1.typeName; + if structName == null as String || String_Eq(structName, "") { + structName = node.child1.child1.typeName; + } + let offset: int = QBE_FieldOffset(qbe, structName, node.child1.strValue); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, ptrTmp); + QBE_Emit(qbe, " =l add "); + QBE_Emit(qbe, QBE_EnsureLocal(base)); + QBE_Emit(qbe, ", "); + QBE_Emit(qbe, String_FromInt(offset as int64)); + QBE_EmitLine(qbe, ""); + target = ptrTmp; } else { target = QBE_EmitExpr(qbe, node.child1); if String_Eq(target, "") { return "0"; } @@ -659,18 +744,84 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String { return tmp; } - // Field pointer: &obj.field + // Field pointer: &obj.field (with auto-load for rvalue contexts) if kind == hFieldPtr { let base: String = QBE_EmitExpr(qbe, node.child1); - let tmp: String = QBE_FreshTemp(qbe); - // add base, offset — need to know offset; for now assume 0 for first field + let ptrTmp: String = QBE_FreshTemp(qbe); + var structName: String = node.typeName; + if structName == null as String || String_Eq(structName, "") { + structName = node.child1.typeName; + } + let offset: int = QBE_FieldOffset(qbe, structName, node.strValue); QBE_EmitIndent(qbe); - QBE_Emit(qbe, tmp); + QBE_Emit(qbe, ptrTmp); QBE_Emit(qbe, " =l add "); QBE_Emit(qbe, QBE_EnsureLocal(base)); - QBE_Emit(qbe, ", 0"); + QBE_Emit(qbe, ", "); + QBE_Emit(qbe, String_FromInt(offset as int64)); QBE_EmitLine(qbe, ""); - return tmp; + // Auto-load value from field address (rvalue context) + let valTmp: String = QBE_FreshTemp(qbe); + let fty: String = QbeBackend_TypeWord(node.typeKind); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, valTmp); + QBE_Emit(qbe, " ="); + QBE_Emit(qbe, fty); + QBE_Emit(qbe, " load"); + QBE_Emit(qbe, fty); + QBE_Emit(qbe, " "); + QBE_Emit(qbe, QBE_EnsureLocal(ptrTmp)); + QBE_EmitLine(qbe, ""); + return valTmp; + } + + // Struct init: allocate and fill fields + if kind == hStructInit { + let st: *HirStruct = QBE_FindStruct(qbe, node.strValue); + var structSize: int = 0; + if st != null as *HirStruct { + var si: int = 0; + while si < st.fieldCount { + let ftype: String = st.fields[si].typeName; + var fsz: int = 8; + if String_Eq(ftype, "int8") || String_Eq(ftype, "uint8") || String_Eq(ftype, "bool8") || String_Eq(ftype, "char8") { fsz = 1; } + else if String_Eq(ftype, "int16") || String_Eq(ftype, "uint16") || String_Eq(ftype, "bool16") || String_Eq(ftype, "char16") { fsz = 2; } + else if String_Eq(ftype, "int32") || String_Eq(ftype, "uint32") || String_Eq(ftype, "bool32") || String_Eq(ftype, "char32") || String_Eq(ftype, "float32") { fsz = 4; } + structSize = structSize + fsz; + si = si + 1; + } + } + let ptr: String = QBE_FreshTemp(qbe); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, ptr); + QBE_Emit(qbe, " =l alloc4 "); + let szStr: String = String_FromInt(structSize as int64); + QBE_Emit(qbe, szStr); + QBE_EmitLine(qbe, ""); + // Store each field + var field: *HirNode = node.child1; + while field != null as *HirNode { + let fval: String = QBE_EmitExpr(qbe, field.child1); + let foff: int = QBE_FieldOffset(qbe, node.strValue, field.strValue); + let fptr: String = QBE_FreshTemp(qbe); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, fptr); + QBE_Emit(qbe, " =l add "); + QBE_Emit(qbe, QBE_EnsureLocal(ptr)); + QBE_Emit(qbe, ", "); + QBE_Emit(qbe, String_FromInt(foff as int64)); + QBE_EmitLine(qbe, ""); + let fty: String = QbeBackend_TypeWord(field.child1.typeKind); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, "store"); + QBE_Emit(qbe, fty); + QBE_Emit(qbe, " "); + QBE_Emit(qbe, QBE_EnsureLocal(fval)); + QBE_Emit(qbe, ", "); + QBE_EmitLine(qbe, QBE_EnsureLocal(fptr)); + field = field.child3; + } + return ptr; } // Index pointer: &arr[idx] (for pointer indexing) @@ -744,6 +895,34 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) { } return; } + // Field assignment: target is hFieldPtr, need pointer not value + if node.child1 != null as *HirNode && node.child1.kind == hFieldPtr { + let base: String = QBE_EmitExpr(qbe, node.child1.child1); + let ptrTmp: String = QBE_FreshTemp(qbe); + var structName: String = node.child1.typeName; + if structName == null as String || String_Eq(structName, "") { + structName = node.child1.child1.typeName; + } + let offset: int = QBE_FieldOffset(qbe, structName, node.child1.strValue); + QBE_EmitIndent(qbe); + QBE_Emit(qbe, ptrTmp); + QBE_Emit(qbe, " =l add "); + QBE_Emit(qbe, QBE_EnsureLocal(base)); + QBE_Emit(qbe, ", "); + QBE_Emit(qbe, String_FromInt(offset as int64)); + QBE_EmitLine(qbe, ""); + let val: String = QBE_EmitExpr(qbe, node.child2); + let ty: String = QBE_NodeType(qbe, node.child2); + if String_Eq(val, "") { val = "0"; } + QBE_EmitIndent(qbe); + QBE_Emit(qbe, "store"); + QBE_Emit(qbe, ty); + QBE_Emit(qbe, " "); + QBE_Emit(qbe, QBE_EnsureLocal(val)); + QBE_Emit(qbe, ", "); + QBE_EmitLine(qbe, QBE_EnsureLocal(ptrTmp)); + return; + } // Plain assignment let target: String = QBE_EmitExpr(qbe, node.child1); let val: String = QBE_EmitExpr(qbe, node.child2); @@ -786,7 +965,7 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) { QBE_Emit(qbe, ", "); QBE_Emit(qbe, lThen); QBE_Emit(qbe, ", "); - var hasElse: bool = node.extraData != null as *HirNode; + var hasElse: bool = node.extraData != null as *void; if hasElse { QBE_EmitLine(qbe, lElse); } else { diff --git a/src_bux/sema.bux b/src_bux/sema.bux index 668ae7d..4de8a7e 100644 --- a/src_bux/sema.bux +++ b/src_bux/sema.bux @@ -2,6 +2,8 @@ // Validates types, resolves identifiers, checks function calls. module Sema { + + // --------------------------------------------------------------------------- // Sema context // --------------------------------------------------------------------------- @@ -130,6 +132,12 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { Sema_EmitError(sema, expr.line, expr.column, "undeclared identifier"); return tyUnknown; } + if sym.typeName != null as String && !String_Eq(sym.typeName, "") { + let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; + te.kind = tekNamed; + te.typeName = sym.typeName; + expr.refType = te; + } return sym.typeKind; } @@ -212,6 +220,10 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) { sym.kind = skVar; sym.name = stmt.strValue; sym.typeKind = initType; + sym.typeName = ""; + if stmt.refStmtType != null as *TypeExpr { + sym.typeName = stmt.refStmtType.typeName; + } sym.isMutable = stmt.boolValue; sym.isPublic = false; discard Scope_Define(sema.scope, sym); @@ -350,9 +362,22 @@ func Sema_Analyze(mod: *Module) -> *Sema { // Add parameters to scope var i: int = 0; while i < decl.paramCount { + var p: *Param = null as *Param; + if i == 0 { p = &decl.param0; } + else if i == 1 { p = &decl.param1; } + else if i == 2 { p = &decl.param2; } + else if i == 3 { p = &decl.param3; } + else if i == 4 { p = &decl.param4; } + else if i == 5 { p = &decl.param5; } var pSym: Symbol; pSym.kind = skVar; - pSym.typeKind = tyInt; // simplified + if p != null as *Param && p.refParamType != null as *TypeExpr { + pSym.typeKind = Sema_ResolveType(s, p.refParamType); + pSym.typeName = p.refParamType.typeName; + } else { + pSym.typeKind = tyInt; + pSym.typeName = ""; + } pSym.isMutable = false; if i == 0 { pSym.name = decl.param0.name; } else if i == 1 { pSym.name = decl.param1.name; }