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