64bd41067a
- Remove DEBUG prints from c_backend.bux, hir_lower.bux, lexer.bux, parser.bux
- Remove HACK for stray } in parserParseFuncDecl (no longer needed after
Decl refactor in 4a86009)
- Clean selfhost check/build output
953 lines
36 KiB
Plaintext
953 lines
36 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,
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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);
|
|
}
|
|
}
|