463 lines
16 KiB
Plaintext
463 lines
16 KiB
Plaintext
// 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,
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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;
|
|
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;
|
|
// Get callee name
|
|
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
|
n.strValue = expr.child1.strValue;
|
|
}
|
|
// Lower arguments
|
|
if expr.child2 != null as *Expr {
|
|
n.child1 = Lcx_LowerExpr(ctx, expr.child2);
|
|
}
|
|
if expr.child3 != null as *Expr {
|
|
n.child2 = Lcx_LowerExpr(ctx, expr.child3);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
// Field access
|
|
if kind == ekField {
|
|
n.kind = hFieldPtr;
|
|
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
|
n.strValue = expr.strValue;
|
|
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 {
|
|
n.typeKind = expr.refType.kind;
|
|
// 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;
|
|
// 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;
|
|
}
|
|
}
|
|
// 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 = p.refParamType.kind;
|
|
// 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);
|
|
|
|
if decl.retType != null as *TypeExpr {
|
|
f.retTypeName = decl.retType.typeName;
|
|
f.retTypeKind = decl.retType.kind;
|
|
} else {
|
|
f.retTypeName = "";
|
|
f.retTypeKind = 0;
|
|
}
|
|
|
|
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(64 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;
|
|
|
|
// 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 == 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 == dkEnum {
|
|
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);
|
|
}
|
|
}
|