// hir_lower.bux — HIR lowering: AST → HIR transformation (ported from hir_lower.nim) // Transforms the typed AST into a lower-level IR suitable for code generation. module HirLower { // --------------------------------------------------------------------------- // Lowering context // --------------------------------------------------------------------------- struct LowerCtx { module: *Module, scope: *Scope, funcs: *HirFunc, funcCount: int, externFuncs: *HirFunc, externCount: int, varCounter: int, tryCounter: int, } // --------------------------------------------------------------------------- // TypeExpr.kind → Type.kind resolver // TypeExpr.kind values (0-5) overlap with Type.kind values — this // resolves the correct Type.kind for codegen. // --------------------------------------------------------------------------- func Lcx_ResolveTypeKindFromName(name: String) -> int { if String_Eq(name, "void") { return tyVoid; } if String_Eq(name, "bool") { return tyBool; } if String_Eq(name, "bool8") { return tyBool8; } if String_Eq(name, "bool16") { return tyBool16; } if String_Eq(name, "bool32") { return tyBool32; } if String_Eq(name, "char8") { return tyChar8; } if String_Eq(name, "char16") { return tyChar16; } if String_Eq(name, "char32") { return tyChar32; } if String_Eq(name, "String") { return tyStr; } if String_Eq(name, "str") { return tyStr; } if String_Eq(name, "int8") { return tyInt8; } if String_Eq(name, "int16") { return tyInt16; } if String_Eq(name, "int32") { return tyInt32; } if String_Eq(name, "int64") { return tyInt64; } if String_Eq(name, "int") { return tyInt; } if String_Eq(name, "uint8") { return tyUInt8; } if String_Eq(name, "uint16") { return tyUInt16; } if String_Eq(name, "uint32") { return tyUInt32; } if String_Eq(name, "uint64") { return tyUInt64; } if String_Eq(name, "uint") { return tyUInt; } if String_Eq(name, "float32") { return tyFloat32; } if String_Eq(name, "float64") { return tyFloat64; } if String_Eq(name, "float") { return tyFloat64; } return tyNamed; } func Lcx_ResolveTypeKind(te: *TypeExpr) -> int { if te == null as *TypeExpr { return tyUnknown; } if te.kind == tekPointer { return tyPointer; } if te.kind == tekSlice { return tySlice; } if te.kind == tekTuple { return tyTuple; } if te.kind == tekFunc { return tyFunc; } return Lcx_ResolveTypeKindFromName(te.typeName); } // Build C function-pointer type string from a tekFunc TypeExpr, e.g. "int (*)(int)" func Lcx_BuildFuncTypeName(te: *TypeExpr) -> String { if te == null as *TypeExpr || te.kind != tekFunc { return "void (*)(void)"; } var retName: String = "void"; if te.funcRet != null as *TypeExpr { if te.funcRet.kind == tekPointer && te.funcRet.pointerPointee != null as *TypeExpr { retName = String_Concat(te.funcRet.pointerPointee.typeName, "*"); } else { retName = te.funcRet.typeName; } if String_Eq(retName, "") { retName = "int"; } } var result: String = retName; result = String_Concat(result, " (*)("); var cur: *TypeExprList = te.funcParams; var first: bool = true; while cur != null as *TypeExprList { if !first { result = String_Concat(result, ", "); } var pName: String = "int"; if cur.te.kind == tekPointer && cur.te.pointerPointee != null as *TypeExpr { pName = String_Concat(cur.te.pointerPointee.typeName, "*"); } else { pName = cur.te.typeName; } if String_Eq(pName, "") { pName = "int"; } result = String_Concat(result, pName); first = false; cur = cur.next; } result = String_Concat(result, ")"); return result; } // --------------------------------------------------------------------------- // Fresh name generation // --------------------------------------------------------------------------- func Lcx_FreshName(ctx: *LowerCtx) -> String { ctx.varCounter = ctx.varCounter + 1; return String_FromInt(ctx.varCounter as int64); } // --------------------------------------------------------------------------- // Type → HIR type // --------------------------------------------------------------------------- func Lcx_LowerType(typeKind: int, typeName: String) -> int { return typeKind; } // --------------------------------------------------------------------------- // Expression lowering // --------------------------------------------------------------------------- func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { if expr == null as *Expr { return null as *HirNode; } let line: uint32 = expr.line; let col: uint32 = expr.column; let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; n.kind = hBlock; n.line = line; n.column = col; let kind: int = expr.kind; // Literal if kind == ekLiteral { n.kind = hLit; n.intValue = expr.tokKind; n.strValue = expr.tokText; return n; } // Identifier → variable reference if kind == ekIdent { n.kind = hVar; 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; } // Binary if kind == ekBinary { // Assignment operator → use hAssign if expr.intValue == tkAssign { n.kind = hAssign; n.child1 = Lcx_LowerExpr(ctx, expr.child1); n.child2 = Lcx_LowerExpr(ctx, expr.child2); return n; } n.kind = hBinary; n.intValue = expr.intValue; // operator n.child1 = Lcx_LowerExpr(ctx, expr.child1); n.child2 = Lcx_LowerExpr(ctx, expr.child2); return n; } // Unary if kind == ekUnary { n.kind = hUnary; n.intValue = expr.intValue; n.child1 = Lcx_LowerExpr(ctx, expr.child1); if expr.intValue == tkAmp { n.typeKind = tyPointer; if expr.child1.refType != null as *TypeExpr && expr.child1.refType.kind == tekFunc { n.typeName = Lcx_BuildFuncTypeName(expr.child1.refType); } } return n; } // Call if kind == ekCall { // Method call desugaring: obj.method(args) → Type_method(obj, args) if expr.child1 != null as *Expr && expr.child1.kind == ekField { n.kind = hCall; 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 from linked list var arg: *ExprList = expr.callArgs; var argIdx: int = 0; while arg != null as *ExprList { let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); if argIdx == 0 { n.child2 = lowered; } else if argIdx == 1 { // Third argument — start linked list let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; firstExtra.node = lowered; firstExtra.next = null as *HirArgList; n.extraData = firstExtra as *void; n.extraCount = 1; } else { // Additional args — append to linked list var cur: *HirArgList = n.extraData as *HirArgList; while cur.next != null as *HirArgList { cur = cur.next; } let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; newNode.node = lowered; newNode.next = null as *HirArgList; cur.next = newNode; n.extraCount = n.extraCount + 1; } arg = arg.next; argIdx = argIdx + 1; } return n; } // Decide direct vs indirect call var isDirectFunc: bool = false; if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue); if sym.kind == skFunc { isDirectFunc = true; } } if isDirectFunc { n.kind = hCall; n.strValue = expr.child1.strValue; // Lower arguments into child1/child2/extraData var arg: *ExprList = expr.callArgs; var argIdx: int = 0; while arg != null as *ExprList { let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); if argIdx == 0 { n.child1 = lowered; } else if argIdx == 1 { n.child2 = lowered; } else if argIdx == 2 { let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; firstExtra.node = lowered; firstExtra.next = null as *HirArgList; n.extraData = firstExtra as *void; n.extraCount = 1; } else { var cur: *HirArgList = n.extraData as *HirArgList; while cur.next != null as *HirArgList { cur = cur.next; } let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; newNode.node = lowered; newNode.next = null as *HirArgList; cur.next = newNode; n.extraCount = n.extraCount + 1; } arg = arg.next; argIdx = argIdx + 1; } } else { n.kind = hCallIndirect; n.child1 = Lcx_LowerExpr(ctx, expr.child1); // Lower arguments into child2/child3/extraData (child1 is callee) var arg: *ExprList = expr.callArgs; var argIdx: int = 0; while arg != null as *ExprList { let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); if argIdx == 0 { n.child2 = lowered; } else if argIdx == 1 { n.child3 = lowered; } else if argIdx == 2 { let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; firstExtra.node = lowered; firstExtra.next = null as *HirArgList; n.extraData = firstExtra as *void; n.extraCount = 1; } else { var cur: *HirArgList = n.extraData as *HirArgList; while cur.next != null as *HirArgList { cur = cur.next; } let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; newNode.node = lowered; newNode.next = null as *HirArgList; cur.next = newNode; n.extraCount = n.extraCount + 1; } arg = arg.next; argIdx = argIdx + 1; } } return n; } // Sizeof if kind == ekSizeOf { n.kind = hSizeOf; if expr.refType != null as *TypeExpr { n.typeName = expr.refType.typeName; } return n; } // Field access if kind == ekField { // Check if this is enum variant access: Color::Green if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue); if sym.decl != null as *Decl && sym.decl.kind == dkEnum { // Emit as variable reference: Color_Green n.kind = hVar; n.strValue = String_Concat(String_Concat(expr.child1.strValue, "_"), expr.strValue); return n; } } 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; } // spawn Callee(args) if kind == ekSpawn { n.kind = hSpawn; if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { n.strValue = expr.child1.strValue; } if expr.child2 != null as *Expr { n.child1 = Lcx_LowerExpr(ctx, expr.child2); } return n; } // expr.await if kind == ekAwait { n.kind = hAwait; n.child1 = Lcx_LowerExpr(ctx, expr.child1); return n; } // Index: arr[idx] if kind == ekIndex { n.kind = hIndexPtr; n.child1 = Lcx_LowerExpr(ctx, expr.child1); n.child2 = Lcx_LowerExpr(ctx, expr.child2); return n; } // Assign: target = value if kind == ekAssign { n.kind = hAssign; n.child1 = Lcx_LowerExpr(ctx, expr.child1); // target n.child2 = Lcx_LowerExpr(ctx, expr.child2); // value return n; } // Cast if kind == ekCast { n.kind = hCast; n.child1 = Lcx_LowerExpr(ctx, expr.child1); if expr.refType != null as *TypeExpr { let resolvedKind: int = Lcx_ResolveTypeKind(expr.refType); n.typeKind = resolvedKind; // For pointer types, construct "PointeeType*" if expr.refType.kind == tekPointer && expr.refType.pointerPointee != null as *TypeExpr { n.typeName = String_Concat(expr.refType.pointerPointee.typeName, "*"); } else if !String_Eq(expr.refType.typeName, "") { n.typeName = expr.refType.typeName; } } return n; } // Struct init: TypeName { field: value, ... } if kind == ekStructInit { n.kind = hStructInit; n.strValue = expr.structName; // Lower each field (fields are chained via child3 on synthetic ekField exprs) var field: *Expr = expr.child1; var firstField: *HirNode = null as *HirNode; var lastField: *HirNode = null as *HirNode; while field != null as *Expr { let fNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; fNode.kind = hBlock; // placeholder, field is identified by name+value fNode.line = expr.line; fNode.column = expr.column; fNode.strValue = field.strValue; // field name fNode.child1 = Lcx_LowerExpr(ctx, field.child1); // field value if firstField == null as *HirNode { firstField = fNode; lastField = fNode; } else { lastField.child3 = fNode; lastField = fNode; } field = field.child3; } n.child1 = firstField; return n; } return n; } // --------------------------------------------------------------------------- // Statement lowering // --------------------------------------------------------------------------- func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode { if stmt == null as *Stmt { return null as *HirNode; } let line: uint32 = stmt.line; let col: uint32 = stmt.column; let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; n.kind = hBlock; n.line = line; n.column = col; let kind: int = stmt.kind; // Let/var → alloca + store if kind == skLet { let init: *HirNode = Lcx_LowerExpr(ctx, stmt.child1); // alloca for the variable let alloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; alloca.kind = hAlloca; alloca.line = line; alloca.column = col; alloca.strValue = stmt.strValue; // Set type from the declared type expression alloca.typeName = ""; if stmt.refStmtType != null as *TypeExpr { alloca.intValue = stmt.refStmtType.kind; alloca.typeKind = Lcx_ResolveTypeKind(stmt.refStmtType); // For function types, build C function-pointer syntax if stmt.refStmtType.kind == tekFunc { alloca.typeName = Lcx_BuildFuncTypeName(stmt.refStmtType); } else if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr { alloca.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*"); } else if !String_Eq(stmt.refStmtType.typeName, "") { alloca.typeName = stmt.refStmtType.typeName; } } // Add to scope for field offset lookups (skip if already defined) var sym: Symbol; sym.kind = skVar; sym.name = stmt.strValue; sym.typeKind = alloca.typeKind; sym.typeName = alloca.typeName; sym.refType = stmt.refStmtType; sym.isMutable = false; sym.isPublic = false; sym.decl = null as *Decl; discard Scope_Define(ctx.scope, sym); // store the init value n.kind = hStore; n.child1 = alloca; n.child2 = init; return n; } // Return if kind == skReturn { n.kind = hReturn; if stmt.child1 != null as *Expr { n.child1 = Lcx_LowerExpr(ctx, stmt.child1); } return n; } // Expression statement if kind == skExpr && stmt.child1 != null as *Expr { return Lcx_LowerExpr(ctx, stmt.child1); } // If if kind == skIf { n.kind = hIf; n.child1 = Lcx_LowerExpr(ctx, stmt.child1); // condition if stmt.refStmtBlock != null as *Block { n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1); } if stmt.refStmtElse != null as *Block { n.extraData = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1) as *void; } return n; } // While if kind == skWhile { n.kind = hWhile; n.child1 = Lcx_LowerExpr(ctx, stmt.child1); if stmt.refStmtBlock != null as *Block { n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1); } return n; } // Loop if kind == skLoop { n.kind = hLoop; if stmt.refStmtBlock != null as *Block { n.child1 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1); } return n; } // Break if kind == skBreak { n.kind = hBreak; return n; } // Continue if kind == skContinue { n.kind = hContinue; return n; } // Defer if kind == skDefer { n.kind = hDefer; if stmt.child1 != null as *Expr { n.child1 = Lcx_LowerExpr(ctx, stmt.child1); } return n; } // Switch — desugar to if-else chain if kind == skSwitch { let subject: *HirNode = Lcx_LowerExpr(ctx, stmt.child1); var current: *HirNode = null as *HirNode; // Default first (bottom of chain) if stmt.refStmtElse != null as *Block { current = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1); } // Cases in reverse order (from caseBlock) if stmt.refStmtBlock != null as *Block { let caseBlock: *Block = stmt.refStmtBlock; var caseCount: int = caseBlock.stmtCount; // Collect cases into array for reverse iteration var c0: *Stmt = null as *Stmt; var c1: *Stmt = null as *Stmt; var c2: *Stmt = null as *Stmt; var c3: *Stmt = null as *Stmt; var c4: *Stmt = null as *Stmt; var c5: *Stmt = null as *Stmt; var c6: *Stmt = null as *Stmt; var c7: *Stmt = null as *Stmt; var ci: int = 0; var cs: *Stmt = caseBlock.firstStmt; while cs != null as *Stmt && ci < 8 { if ci == 0 { c0 = cs; } if ci == 1 { c1 = cs; } if ci == 2 { c2 = cs; } if ci == 3 { c3 = cs; } if ci == 4 { c4 = cs; } if ci == 5 { c5 = cs; } if ci == 6 { c6 = cs; } if ci == 7 { c7 = cs; } ci = ci + 1; cs = cs.nextStmt; } while caseCount > 0 { caseCount = caseCount - 1; var c: *Stmt = null as *Stmt; if caseCount == 0 { c = c0; } if caseCount == 1 { c = c1; } if caseCount == 2 { c = c2; } if caseCount == 3 { c = c3; } if caseCount == 4 { c = c4; } if caseCount == 5 { c = c5; } if caseCount == 6 { c = c6; } if caseCount == 7 { c = c7; } if c != null as *Stmt { let caseVal: *HirNode = Lcx_LowerExpr(ctx, c.child1); let caseBody: *HirNode = Lcx_LowerBlock(ctx, c.refStmtBlock, -1); let cond: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; cond.kind = hBinary; cond.intValue = 74; // tkEq cond.child1 = subject; cond.child2 = caseVal; let ifNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; ifNode.kind = hIf; ifNode.child1 = cond; ifNode.child2 = caseBody; ifNode.child3 = current; current = ifNode; } } } return current; } return n; } // --------------------------------------------------------------------------- // Block lowering // --------------------------------------------------------------------------- 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; } // 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; n.child1 = firstNode; return n; } // --------------------------------------------------------------------------- // Param → HirParam conversion // --------------------------------------------------------------------------- func Lcx_LowerParam(out: *HirParam, p: *Param) { out.name = p.name; if p.refParamType != null as *TypeExpr { out.typeKind = Lcx_ResolveTypeKind(p.refParamType); // Function type: build C function-pointer syntax if p.refParamType.kind == tekFunc { out.typeName = Lcx_BuildFuncTypeName(p.refParamType); } else if !String_Eq(p.refParamType.typeName, "") { out.typeName = p.refParamType.typeName; } else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr { out.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*"); } else { out.typeName = ""; } } else { out.typeKind = 0; out.typeName = ""; } } // --------------------------------------------------------------------------- // Function lowering // --------------------------------------------------------------------------- func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { let f: *HirFunc = bux_alloc(sizeof(HirFunc)) as *HirFunc; f.name = decl.strValue; f.isPublic = decl.isPublic; f.paramCount = decl.paramCount; f.param0 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param0, &decl.param0); f.param1 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param1, &decl.param1); f.param2 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param2, &decl.param2); f.param3 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param3, &decl.param3); f.param4 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param4, &decl.param4); f.param5 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param5, &decl.param5); f.param6 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param6, &decl.param6); f.param7 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param7, &decl.param7); f.param8 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param8, &decl.param8); if decl.retType != null as *TypeExpr { f.retTypeName = decl.retType.typeName; f.retTypeKind = Lcx_ResolveTypeKind(decl.retType); } else { f.retTypeName = ""; f.retTypeKind = 0; } // Create function scope as child of current scope var funcScope: Scope = Scope_NewChild(ctx.scope); // Add parameters to function 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; } else if pi == 6 { p = &decl.param6; } else if pi == 7 { p = &decl.param7; } else if pi == 8 { p = &decl.param8; } 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.refType = p.refParamType; // Build typeName same as Lcx_LowerParam if p.refParamType.kind == tekFunc { sym.typeName = Lcx_BuildFuncTypeName(p.refParamType); } else if !String_Eq(p.refParamType.typeName, "") { sym.typeName = p.refParamType.typeName; } else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr { sym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*"); } else { sym.typeName = ""; } sym.isMutable = false; sym.isPublic = false; sym.decl = null as *Decl; discard Scope_Define(&funcScope, sym); } pi = pi + 1; } // Lower body with function scope active let prevScope: *Scope = ctx.scope; ctx.scope = &funcScope; if decl.refBody != null as *Block { f.body = Lcx_LowerBlock(ctx, decl.refBody, -1); } else { f.body = null as *HirNode; } ctx.scope = prevScope; return f; } // --------------------------------------------------------------------------- // Module lowering — main entry point // --------------------------------------------------------------------------- func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { let ctx: *LowerCtx = bux_alloc(sizeof(LowerCtx)) as *LowerCtx; ctx.module = mod; ctx.scope = sema.scope; ctx.funcs = bux_alloc(512 as uint * sizeof(HirFunc)) as *HirFunc; ctx.funcCount = 0; ctx.externFuncs = bux_alloc(512 as uint * sizeof(HirFunc)) as *HirFunc; ctx.externCount = 0; ctx.varCounter = 0; 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; hm.enumCount = 0; hm.enums = bux_alloc(64 as uint * sizeof(HirEnum)) as *HirEnum; hm.constCount = 0; hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst; // 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; while decl != null as *Decl { if decl.kind == dkFunc && decl.refBody != null as *Block { let f: *HirFunc = Lcx_LowerFunc(ctx, decl); 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 && implDecl.refBody != null as *Block { 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; 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; fname = decl.fields[fi].name; ftype = decl.fields[fi].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 == dkConst && hm.constCount < 512 { let ci: int = hm.constCount; hm.consts[ci].name = decl.strValue; var val: int = 0; if decl.constValue != null as *Expr { if decl.constValue.kind == ekLiteral { val = decl.constValue.intValue; } } hm.consts[ci].value = val; hm.constCount = hm.constCount + 1; } if decl.kind == dkEnum { let ei: int = hm.enumCount; hm.enums[ei].name = decl.strValue; // Populate variants hm.enums[ei].variantCount = decl.variantCount; if decl.variantCount > 0 { hm.enums[ei].variants = bux_alloc(decl.variantCount as uint * sizeof(HirEnumVariant)) as *HirEnumVariant; } var vi: int = 0; while vi < decl.variantCount { var v: *EnumVariant = null as *EnumVariant; if vi == 0 { v = &decl.variant0; } if vi == 1 { v = &decl.variant1; } if vi == 2 { v = &decl.variant2; } if vi == 3 { v = &decl.variant3; } if vi == 4 { v = &decl.variant4; } if vi == 5 { v = &decl.variant5; } if vi == 6 { v = &decl.variant6; } if vi == 7 { v = &decl.variant7; } if vi == 8 { v = &decl.variant8; } if v != null as *EnumVariant { hm.enums[ei].variants[vi].name = v.name; hm.enums[ei].variants[vi].fieldCount = v.fieldCount; if v.fieldCount > 0 { hm.enums[ei].variants[vi].fieldName0 = "value"; hm.enums[ei].variants[vi].fieldType0 = Lcx_ResolveTypeKindFromName(v.fieldTypeName0); } if v.fieldCount > 1 { hm.enums[ei].variants[vi].fieldName1 = "value2"; hm.enums[ei].variants[vi].fieldType1 = Lcx_ResolveTypeKindFromName(v.fieldTypeName1); } } vi = vi + 1; } hm.enumCount = hm.enumCount + 1; } decl = decl.childDecl2; } hm.funcCount = ctx.funcCount; hm.funcs = ctx.funcs; hm.externCount = ctx.externCount; hm.externFuncs = ctx.externFuncs; return hm; } func HirLower_Free(ctx: *LowerCtx) { bux_free(ctx.funcs as *void); bux_free(ctx.externFuncs as *void); bux_free(ctx as *void); } }