v0.3.0: restructure directories
- src/ ← compiler/selfhost/ (canonical Bux compiler) - bootstrap/ ← compiler/bootstrap/ (Nim bootstrap) - lib/ ← library/std/ (standard library) - rt/ ← library/runtime/ (C runtime) - tests/ ← compiler/tests/ (unit tests) - Remove _selfhost/ (built into build/selfhost/ now) - Update all path references (Makefile, cli.nim, cli.bux, docs) - Bump version to 0.3.0
This commit is contained in:
@@ -0,0 +1,740 @@
|
||||
// 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_ResolveTypeKind(te: *TypeExpr) -> int {
|
||||
if te == null as *TypeExpr { return tyUnknown; }
|
||||
let name: String = te.typeName;
|
||||
|
||||
if te.kind == tekPointer { return tyPointer; }
|
||||
if te.kind == tekSlice { return tySlice; }
|
||||
if te.kind == tekTuple { return tyTuple; }
|
||||
|
||||
// Named types: resolve by name
|
||||
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;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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);
|
||||
return n;
|
||||
}
|
||||
|
||||
// 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 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;
|
||||
}
|
||||
// Get callee name
|
||||
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||
n.strValue = expr.child1.strValue;
|
||||
}
|
||||
// Lower 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.child1 = lowered;
|
||||
} else if argIdx == 1 {
|
||||
n.child2 = lowered;
|
||||
} else if argIdx == 2 {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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 pointer types, construct "PointeeType*"
|
||||
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
|
||||
var sym: Symbol;
|
||||
sym.kind = skVar;
|
||||
sym.name = stmt.strValue;
|
||||
sym.typeKind = alloca.typeKind;
|
||||
sym.typeName = alloca.typeName;
|
||||
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 {
|
||||
// Store else block in extraData (child3 is reserved for block chaining)
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
// Use typeName directly if set (parser may have constructed "String*")
|
||||
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;
|
||||
Lcx_LowerParam(&f.param0, &decl.param0);
|
||||
Lcx_LowerParam(&f.param1, &decl.param1);
|
||||
Lcx_LowerParam(&f.param2, &decl.param2);
|
||||
Lcx_LowerParam(&f.param3, &decl.param3);
|
||||
Lcx_LowerParam(&f.param4, &decl.param4);
|
||||
Lcx_LowerParam(&f.param5, &decl.param5);
|
||||
Lcx_LowerParam(&f.param6, &decl.param6);
|
||||
Lcx_LowerParam(&f.param7, &decl.param7);
|
||||
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;
|
||||
}
|
||||
|
||||
// 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; }
|
||||
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.typeName = p.refParamType.typeName;
|
||||
sym.isMutable = false;
|
||||
sym.isPublic = false;
|
||||
sym.decl = null as *Decl;
|
||||
discard Scope_Define(ctx.scope, sym);
|
||||
}
|
||||
pi = pi + 1;
|
||||
}
|
||||
|
||||
if decl.refBody != null as *Block {
|
||||
f.body = Lcx_LowerBlock(ctx, decl.refBody, -1);
|
||||
} else {
|
||||
f.body = null as *HirNode;
|
||||
}
|
||||
|
||||
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(256 as uint * sizeof(HirFunc)) as *HirFunc;
|
||||
ctx.funcCount = 0;
|
||||
ctx.externFuncs = bux_alloc(256 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 {
|
||||
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 {
|
||||
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;
|
||||
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 == 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;
|
||||
hm.enumCount = hm.enumCount + 1;
|
||||
// Emit enum variants as constants: EnumName_VariantName = value
|
||||
var vi: int = 0;
|
||||
while vi < decl.variantCount && hm.constCount < 512 {
|
||||
var vname: String = "";
|
||||
if vi == 0 { vname = decl.variant0.name; }
|
||||
if vi == 1 { vname = decl.variant1.name; }
|
||||
if vi == 2 { vname = decl.variant2.name; }
|
||||
if vi == 3 { vname = decl.variant3.name; }
|
||||
if !String_Eq(vname, "") {
|
||||
let ci: int = hm.constCount;
|
||||
hm.consts[ci].name = String_Concat(String_Concat(decl.strValue, "_"), vname);
|
||||
hm.consts[ci].value = vi;
|
||||
hm.constCount = hm.constCount + 1;
|
||||
}
|
||||
vi = vi + 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user