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=");
|
Print("HIR funcCount=");
|
||||||
PrintInt(hirMod.funcCount);
|
PrintInt(hirMod.funcCount);
|
||||||
PrintLine("");
|
|
||||||
|
|
||||||
// Phase 5: QBE SSA code generation
|
// Phase 5: QBE SSA code generation
|
||||||
let qbeCode: String = QbeBackend_Generate(hirMod);
|
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=");
|
Print("DEBUG: MergeFileInto done, added=");
|
||||||
PrintInt(added);
|
PrintInt(added);
|
||||||
PrintLine("");
|
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cli_CopyModuleDecls(target: *Module, source: *Module) {
|
func Cli_CopyModuleDecls(target: *Module, source: *Module) {
|
||||||
if source == null as *Module || source.firstItem == null as *Decl { return; }
|
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 decl: *Decl = source.firstItem;
|
||||||
var limit: int = 0;
|
var limit: int = 0;
|
||||||
while decl != null as *Decl && limit < 10000 {
|
while decl != null as *Decl && limit < 10000 {
|
||||||
@@ -365,7 +360,6 @@ func Cli_CopyModuleDecls(target: *Module, source: *Module) {
|
|||||||
}
|
}
|
||||||
source.firstItem = null as *Decl;
|
source.firstItem = null as *Decl;
|
||||||
source.itemCount = 0;
|
source.itemCount = 0;
|
||||||
PrintLine("DEBUG: copy end");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cli_BuildProject(projectDir: String) -> int {
|
func Cli_BuildProject(projectDir: String) -> int {
|
||||||
@@ -457,7 +451,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
|
|
||||||
Print("DEBUG: userMerged.itemCount=");
|
Print("DEBUG: userMerged.itemCount=");
|
||||||
PrintInt(userMerged.itemCount);
|
PrintInt(userMerged.itemCount);
|
||||||
PrintLine("");
|
|
||||||
|
|
||||||
// Collect user declaration names for shadow detection
|
// Collect user declaration names for shadow detection
|
||||||
let maxNames: int = 2048;
|
let maxNames: int = 2048;
|
||||||
@@ -482,7 +475,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount);
|
let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount);
|
||||||
Print("DEBUG: stdFileCount=");
|
Print("DEBUG: stdFileCount=");
|
||||||
PrintInt(stdFileCount);
|
PrintInt(stdFileCount);
|
||||||
PrintLine("");
|
|
||||||
if stdFileCount > 0 {
|
if stdFileCount > 0 {
|
||||||
Print("Merging ");
|
Print("Merging ");
|
||||||
PrintInt(stdFileCount);
|
PrintInt(stdFileCount);
|
||||||
@@ -492,7 +484,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
while si < stdFileCount {
|
while si < stdFileCount {
|
||||||
Print("DEBUG: merging stdlib file ");
|
Print("DEBUG: merging stdlib file ");
|
||||||
PrintInt(si);
|
PrintInt(si);
|
||||||
PrintLine("");
|
|
||||||
// Skip Channel.bux for debugging
|
// Skip Channel.bux for debugging
|
||||||
if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") {
|
if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") {
|
||||||
PrintLine("DEBUG: skipping Channel.bux");
|
PrintLine("DEBUG: skipping Channel.bux");
|
||||||
@@ -505,7 +496,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
}
|
}
|
||||||
Print("Stdlib declarations added: ");
|
Print("Stdlib declarations added: ");
|
||||||
PrintInt(stdAdded);
|
PrintInt(stdAdded);
|
||||||
PrintLine("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,23 +510,29 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
}
|
}
|
||||||
Print("userMerged actual count=");
|
Print("userMerged actual count=");
|
||||||
PrintInt(countTest);
|
PrintInt(countTest);
|
||||||
PrintLine("");
|
|
||||||
Print("userMerged.itemCount=");
|
Print("userMerged.itemCount=");
|
||||||
PrintInt(userMerged.itemCount);
|
PrintInt(userMerged.itemCount);
|
||||||
PrintLine("");
|
|
||||||
var du: *Decl = userMerged.firstItem;
|
var du: *Decl = userMerged.firstItem;
|
||||||
while du != null as *Decl {
|
while du != null as *Decl {
|
||||||
if du.kind == dkFunc {
|
if du.kind == dkFunc {
|
||||||
Print("user func ");
|
Print("user func ");
|
||||||
PrintLine(du.strValue);
|
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;
|
du = du.childDecl2;
|
||||||
}
|
}
|
||||||
Cli_CopyModuleDecls(merged, userMerged);
|
Cli_CopyModuleDecls(merged, userMerged);
|
||||||
PrintLine("DEBUG: copy done");
|
PrintLine("DEBUG: copy done");
|
||||||
Print("merged.itemCount=");
|
Print("merged.itemCount=");
|
||||||
PrintInt(merged.itemCount);
|
PrintInt(merged.itemCount);
|
||||||
PrintLine("");
|
|
||||||
PrintLine("DEBUG: copy done");
|
PrintLine("DEBUG: copy done");
|
||||||
|
|
||||||
Print("Merged ");
|
Print("Merged ");
|
||||||
@@ -679,12 +675,10 @@ func Cli_RunProject(projectDir: String) -> int {
|
|||||||
func Cli_Run(args: *String, argCount: int) -> int {
|
func Cli_Run(args: *String, argCount: int) -> int {
|
||||||
Print("Cli_Run argCount=");
|
Print("Cli_Run argCount=");
|
||||||
PrintInt(argCount);
|
PrintInt(argCount);
|
||||||
PrintLine("");
|
|
||||||
if argCount < 2 {
|
if argCount < 2 {
|
||||||
PrintLine("Bux Self-Hosting Compiler v0.2.0");
|
PrintLine("Bux Self-Hosting Compiler v0.2.0");
|
||||||
PrintLine("Usage: buxc <command> [args]");
|
PrintLine("Usage: buxc <command> [args]");
|
||||||
PrintLine("Commands: build, check, run, version");
|
PrintLine("Commands: build, check, run, version");
|
||||||
PrintLine("");
|
|
||||||
PrintLine("Pipeline modules:");
|
PrintLine("Pipeline modules:");
|
||||||
PrintLine(" Lexer ✅ 695 lines");
|
PrintLine(" Lexer ✅ 695 lines");
|
||||||
PrintLine(" Parser ✅ 1004 lines");
|
PrintLine(" Parser ✅ 1004 lines");
|
||||||
|
|||||||
+102
-1
@@ -105,6 +105,24 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
n.strValue = expr.strValue;
|
n.strValue = expr.strValue;
|
||||||
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
|
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
|
||||||
n.typeKind = sym.typeKind;
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +153,41 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
// Call
|
// Call
|
||||||
if kind == ekCall {
|
if kind == ekCall {
|
||||||
n.kind = hCall;
|
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
|
// Get callee name
|
||||||
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||||
n.strValue = expr.child1.strValue;
|
n.strValue = expr.child1.strValue;
|
||||||
@@ -163,6 +216,10 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
n.kind = hFieldPtr;
|
n.kind = hFieldPtr;
|
||||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||||
n.strValue = expr.strValue;
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +343,13 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
|||||||
alloca.typeName = 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;
|
||||||
|
discard Scope_Define(ctx.scope, sym);
|
||||||
// store the init value
|
// store the init value
|
||||||
n.kind = hStore;
|
n.kind = hStore;
|
||||||
n.child1 = alloca;
|
n.child1 = alloca;
|
||||||
@@ -430,6 +494,27 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
|
|||||||
f.retTypeKind = 0;
|
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 {
|
if decl.refBody != null as *Block {
|
||||||
f.body = Lcx_LowerBlock(ctx, decl.refBody, -1);
|
f.body = Lcx_LowerBlock(ctx, decl.refBody, -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -449,7 +534,7 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
ctx.scope = sema.scope;
|
ctx.scope = sema.scope;
|
||||||
ctx.funcs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc;
|
ctx.funcs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc;
|
||||||
ctx.funcCount = 0;
|
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.externCount = 0;
|
||||||
ctx.varCounter = 0;
|
ctx.varCounter = 0;
|
||||||
|
|
||||||
@@ -473,6 +558,20 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
ctx.funcs[ctx.funcCount] = *f;
|
ctx.funcs[ctx.funcCount] = *f;
|
||||||
ctx.funcCount = ctx.funcCount + 1;
|
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 {
|
if decl.kind == dkExternFunc {
|
||||||
let f: *HirFunc = Lcx_LowerFunc(ctx, decl);
|
let f: *HirFunc = Lcx_LowerFunc(ctx, decl);
|
||||||
ctx.externFuncs[ctx.externCount] = *f;
|
ctx.externFuncs[ctx.externCount] = *f;
|
||||||
@@ -534,11 +633,13 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
decl = decl.childDecl2;
|
decl = decl.childDecl2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
hm.funcCount = ctx.funcCount;
|
hm.funcCount = ctx.funcCount;
|
||||||
hm.funcs = ctx.funcs;
|
hm.funcs = ctx.funcs;
|
||||||
hm.externCount = ctx.externCount;
|
hm.externCount = ctx.externCount;
|
||||||
hm.externFuncs = ctx.externFuncs;
|
hm.externFuncs = ctx.externFuncs;
|
||||||
|
|
||||||
|
|
||||||
return hm;
|
return hm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,29 @@ func parserPeek(p: *Parser, ahead: int) -> int {
|
|||||||
return tkEndOfFile;
|
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 {
|
func parserAdvance(p: *Parser) -> LexToken {
|
||||||
let tok: LexToken = parserCurToken(p);
|
let tok: LexToken = parserCurToken(p);
|
||||||
if p.pos < p.tokenCount {
|
if p.pos < p.tokenCount {
|
||||||
@@ -331,6 +354,25 @@ func parserParsePostfix(p: *Parser) -> *Expr {
|
|||||||
continue;
|
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]
|
// Index: expr[expr]
|
||||||
if kind == tkLBracket {
|
if kind == tkLBracket {
|
||||||
discard parserAdvance(p);
|
discard parserAdvance(p);
|
||||||
@@ -1285,7 +1327,7 @@ func parserParseDecl(p: *Parser) -> *Decl {
|
|||||||
if d.methodCount == 0 {
|
if d.methodCount == 0 {
|
||||||
d.childDecl1 = m;
|
d.childDecl1 = m;
|
||||||
} else if d.methodCount == 1 {
|
} else if d.methodCount == 1 {
|
||||||
d.childDecl2 = m;
|
d.childDecl1.childDecl2 = m;
|
||||||
}
|
}
|
||||||
d.methodCount = d.methodCount + 1;
|
d.methodCount = d.methodCount + 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ func QBE_IsTerminator(node: *HirNode) -> bool {
|
|||||||
if node.kind == hBreak { return true; }
|
if node.kind == hBreak { return true; }
|
||||||
if node.kind == hContinue { return true; }
|
if node.kind == hContinue { return true; }
|
||||||
if node.kind == hIf {
|
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 QBE_IsTerminator(node.child2) && QBE_IsTerminator(node.extraData as *HirNode);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -307,6 +307,74 @@ func QBE_FindFunc(qbe: *QbeEmitter, name: String) -> *HirFunc {
|
|||||||
return null as *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
|
// Helper: determine QBE type for any HIR node
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -411,6 +479,23 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
|
|||||||
var target: String = "";
|
var target: String = "";
|
||||||
if node.child1 != null as *HirNode && node.child1.kind == hVar {
|
if node.child1 != null as *HirNode && node.child1.kind == hVar {
|
||||||
target = QBE_LocalName(node.child1.strValue);
|
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 {
|
} else {
|
||||||
target = QBE_EmitExpr(qbe, node.child1);
|
target = QBE_EmitExpr(qbe, node.child1);
|
||||||
if String_Eq(target, "") { return "0"; }
|
if String_Eq(target, "") { return "0"; }
|
||||||
@@ -659,18 +744,84 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
|
|||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field pointer: &obj.field
|
// Field pointer: &obj.field (with auto-load for rvalue contexts)
|
||||||
if kind == hFieldPtr {
|
if kind == hFieldPtr {
|
||||||
let base: String = QBE_EmitExpr(qbe, node.child1);
|
let base: String = QBE_EmitExpr(qbe, node.child1);
|
||||||
let tmp: String = QBE_FreshTemp(qbe);
|
let ptrTmp: String = QBE_FreshTemp(qbe);
|
||||||
// add base, offset — need to know offset; for now assume 0 for first field
|
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_EmitIndent(qbe);
|
||||||
QBE_Emit(qbe, tmp);
|
QBE_Emit(qbe, ptrTmp);
|
||||||
QBE_Emit(qbe, " =l add ");
|
QBE_Emit(qbe, " =l add ");
|
||||||
QBE_Emit(qbe, QBE_EnsureLocal(base));
|
QBE_Emit(qbe, QBE_EnsureLocal(base));
|
||||||
QBE_Emit(qbe, ", 0");
|
QBE_Emit(qbe, ", ");
|
||||||
|
QBE_Emit(qbe, String_FromInt(offset as int64));
|
||||||
QBE_EmitLine(qbe, "");
|
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)
|
// Index pointer: &arr[idx] (for pointer indexing)
|
||||||
@@ -744,6 +895,34 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
|
|||||||
}
|
}
|
||||||
return;
|
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
|
// Plain assignment
|
||||||
let target: String = QBE_EmitExpr(qbe, node.child1);
|
let target: String = QBE_EmitExpr(qbe, node.child1);
|
||||||
let val: String = QBE_EmitExpr(qbe, node.child2);
|
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, ", ");
|
||||||
QBE_Emit(qbe, lThen);
|
QBE_Emit(qbe, lThen);
|
||||||
QBE_Emit(qbe, ", ");
|
QBE_Emit(qbe, ", ");
|
||||||
var hasElse: bool = node.extraData != null as *HirNode;
|
var hasElse: bool = node.extraData != null as *void;
|
||||||
if hasElse {
|
if hasElse {
|
||||||
QBE_EmitLine(qbe, lElse);
|
QBE_EmitLine(qbe, lElse);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+26
-1
@@ -2,6 +2,8 @@
|
|||||||
// Validates types, resolves identifiers, checks function calls.
|
// Validates types, resolves identifiers, checks function calls.
|
||||||
module Sema {
|
module Sema {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Sema context
|
// Sema context
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -130,6 +132,12 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
Sema_EmitError(sema, expr.line, expr.column, "undeclared identifier");
|
Sema_EmitError(sema, expr.line, expr.column, "undeclared identifier");
|
||||||
return tyUnknown;
|
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;
|
return sym.typeKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +220,10 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
sym.kind = skVar;
|
sym.kind = skVar;
|
||||||
sym.name = stmt.strValue;
|
sym.name = stmt.strValue;
|
||||||
sym.typeKind = initType;
|
sym.typeKind = initType;
|
||||||
|
sym.typeName = "";
|
||||||
|
if stmt.refStmtType != null as *TypeExpr {
|
||||||
|
sym.typeName = stmt.refStmtType.typeName;
|
||||||
|
}
|
||||||
sym.isMutable = stmt.boolValue;
|
sym.isMutable = stmt.boolValue;
|
||||||
sym.isPublic = false;
|
sym.isPublic = false;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
@@ -350,9 +362,22 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
// Add parameters to scope
|
// Add parameters to scope
|
||||||
var i: int = 0;
|
var i: int = 0;
|
||||||
while i < decl.paramCount {
|
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;
|
var pSym: Symbol;
|
||||||
pSym.kind = skVar;
|
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;
|
pSym.isMutable = false;
|
||||||
if i == 0 { pSym.name = decl.param0.name; }
|
if i == 0 { pSym.name = decl.param0.name; }
|
||||||
else if i == 1 { pSym.name = decl.param1.name; }
|
else if i == 1 { pSym.name = decl.param1.name; }
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[Package]
|
||||||
|
Name = "demo_app"
|
||||||
|
Version = "0.1.0"
|
||||||
|
Type = "bin"
|
||||||
|
|
||||||
|
[Build]
|
||||||
|
Output = "Bin"
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Structs - Basic struct usage
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
x: int;
|
||||||
|
y: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddPoints(a: Point, b: Point) -> Point {
|
||||||
|
let result: Point = Point { x: a.x + b.x, y: a.y + b.y };
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let p1: Point = Point { x: 10, y: 20 };
|
||||||
|
let p2: Point = Point { x: 5, y: 15 };
|
||||||
|
let sum: Point = AddPoints(p1, p2);
|
||||||
|
|
||||||
|
PrintLine("Point sum:");
|
||||||
|
PrintLine("x = ");
|
||||||
|
PrintInt(sum.x);
|
||||||
|
PrintLine("");
|
||||||
|
PrintLine("y = ");
|
||||||
|
PrintInt(sum.y);
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+9
-15
@@ -96,7 +96,6 @@ func Cli_Compile(source: String, sourceName: String) -> String {
|
|||||||
}
|
}
|
||||||
Print("HIR funcCount=");
|
Print("HIR funcCount=");
|
||||||
PrintInt(hirMod.funcCount);
|
PrintInt(hirMod.funcCount);
|
||||||
PrintLine("");
|
|
||||||
|
|
||||||
// Phase 5: QBE SSA code generation
|
// Phase 5: QBE SSA code generation
|
||||||
let qbeCode: String = QbeBackend_Generate(hirMod);
|
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=");
|
Print("DEBUG: MergeFileInto done, added=");
|
||||||
PrintInt(added);
|
PrintInt(added);
|
||||||
PrintLine("");
|
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cli_CopyModuleDecls(target: *Module, source: *Module) {
|
func Cli_CopyModuleDecls(target: *Module, source: *Module) {
|
||||||
if source == null as *Module || source.firstItem == null as *Decl { return; }
|
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 decl: *Decl = source.firstItem;
|
||||||
var limit: int = 0;
|
var limit: int = 0;
|
||||||
while decl != null as *Decl && limit < 10000 {
|
while decl != null as *Decl && limit < 10000 {
|
||||||
@@ -365,7 +360,6 @@ func Cli_CopyModuleDecls(target: *Module, source: *Module) {
|
|||||||
}
|
}
|
||||||
source.firstItem = null as *Decl;
|
source.firstItem = null as *Decl;
|
||||||
source.itemCount = 0;
|
source.itemCount = 0;
|
||||||
PrintLine("DEBUG: copy end");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cli_BuildProject(projectDir: String) -> int {
|
func Cli_BuildProject(projectDir: String) -> int {
|
||||||
@@ -457,7 +451,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
|
|
||||||
Print("DEBUG: userMerged.itemCount=");
|
Print("DEBUG: userMerged.itemCount=");
|
||||||
PrintInt(userMerged.itemCount);
|
PrintInt(userMerged.itemCount);
|
||||||
PrintLine("");
|
|
||||||
|
|
||||||
// Collect user declaration names for shadow detection
|
// Collect user declaration names for shadow detection
|
||||||
let maxNames: int = 2048;
|
let maxNames: int = 2048;
|
||||||
@@ -482,7 +475,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount);
|
let stdFiles: *String = bux_list_dir(stdSubdir, ".bux", &stdFileCount);
|
||||||
Print("DEBUG: stdFileCount=");
|
Print("DEBUG: stdFileCount=");
|
||||||
PrintInt(stdFileCount);
|
PrintInt(stdFileCount);
|
||||||
PrintLine("");
|
|
||||||
if stdFileCount > 0 {
|
if stdFileCount > 0 {
|
||||||
Print("Merging ");
|
Print("Merging ");
|
||||||
PrintInt(stdFileCount);
|
PrintInt(stdFileCount);
|
||||||
@@ -492,7 +484,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
while si < stdFileCount {
|
while si < stdFileCount {
|
||||||
Print("DEBUG: merging stdlib file ");
|
Print("DEBUG: merging stdlib file ");
|
||||||
PrintInt(si);
|
PrintInt(si);
|
||||||
PrintLine("");
|
|
||||||
// Skip Channel.bux for debugging
|
// Skip Channel.bux for debugging
|
||||||
if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") {
|
if String_Eq(stdFiles[si], "./../stdlib/Std/Channel.bux") {
|
||||||
PrintLine("DEBUG: skipping Channel.bux");
|
PrintLine("DEBUG: skipping Channel.bux");
|
||||||
@@ -505,7 +496,6 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
}
|
}
|
||||||
Print("Stdlib declarations added: ");
|
Print("Stdlib declarations added: ");
|
||||||
PrintInt(stdAdded);
|
PrintInt(stdAdded);
|
||||||
PrintLine("");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,23 +510,29 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
}
|
}
|
||||||
Print("userMerged actual count=");
|
Print("userMerged actual count=");
|
||||||
PrintInt(countTest);
|
PrintInt(countTest);
|
||||||
PrintLine("");
|
|
||||||
Print("userMerged.itemCount=");
|
Print("userMerged.itemCount=");
|
||||||
PrintInt(userMerged.itemCount);
|
PrintInt(userMerged.itemCount);
|
||||||
PrintLine("");
|
|
||||||
var du: *Decl = userMerged.firstItem;
|
var du: *Decl = userMerged.firstItem;
|
||||||
while du != null as *Decl {
|
while du != null as *Decl {
|
||||||
if du.kind == dkFunc {
|
if du.kind == dkFunc {
|
||||||
Print("user func ");
|
Print("user func ");
|
||||||
PrintLine(du.strValue);
|
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;
|
du = du.childDecl2;
|
||||||
}
|
}
|
||||||
Cli_CopyModuleDecls(merged, userMerged);
|
Cli_CopyModuleDecls(merged, userMerged);
|
||||||
PrintLine("DEBUG: copy done");
|
PrintLine("DEBUG: copy done");
|
||||||
Print("merged.itemCount=");
|
Print("merged.itemCount=");
|
||||||
PrintInt(merged.itemCount);
|
PrintInt(merged.itemCount);
|
||||||
PrintLine("");
|
|
||||||
PrintLine("DEBUG: copy done");
|
PrintLine("DEBUG: copy done");
|
||||||
|
|
||||||
Print("Merged ");
|
Print("Merged ");
|
||||||
@@ -679,12 +675,10 @@ func Cli_RunProject(projectDir: String) -> int {
|
|||||||
func Cli_Run(args: *String, argCount: int) -> int {
|
func Cli_Run(args: *String, argCount: int) -> int {
|
||||||
Print("Cli_Run argCount=");
|
Print("Cli_Run argCount=");
|
||||||
PrintInt(argCount);
|
PrintInt(argCount);
|
||||||
PrintLine("");
|
|
||||||
if argCount < 2 {
|
if argCount < 2 {
|
||||||
PrintLine("Bux Self-Hosting Compiler v0.2.0");
|
PrintLine("Bux Self-Hosting Compiler v0.2.0");
|
||||||
PrintLine("Usage: buxc <command> [args]");
|
PrintLine("Usage: buxc <command> [args]");
|
||||||
PrintLine("Commands: build, check, run, version");
|
PrintLine("Commands: build, check, run, version");
|
||||||
PrintLine("");
|
|
||||||
PrintLine("Pipeline modules:");
|
PrintLine("Pipeline modules:");
|
||||||
PrintLine(" Lexer ✅ 695 lines");
|
PrintLine(" Lexer ✅ 695 lines");
|
||||||
PrintLine(" Parser ✅ 1004 lines");
|
PrintLine(" Parser ✅ 1004 lines");
|
||||||
|
|||||||
+102
-1
@@ -105,6 +105,24 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
n.strValue = expr.strValue;
|
n.strValue = expr.strValue;
|
||||||
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
|
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
|
||||||
n.typeKind = sym.typeKind;
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +153,41 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
// Call
|
// Call
|
||||||
if kind == ekCall {
|
if kind == ekCall {
|
||||||
n.kind = hCall;
|
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
|
// Get callee name
|
||||||
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
|
||||||
n.strValue = expr.child1.strValue;
|
n.strValue = expr.child1.strValue;
|
||||||
@@ -163,6 +216,10 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
n.kind = hFieldPtr;
|
n.kind = hFieldPtr;
|
||||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||||
n.strValue = expr.strValue;
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +343,13 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
|||||||
alloca.typeName = 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;
|
||||||
|
discard Scope_Define(ctx.scope, sym);
|
||||||
// store the init value
|
// store the init value
|
||||||
n.kind = hStore;
|
n.kind = hStore;
|
||||||
n.child1 = alloca;
|
n.child1 = alloca;
|
||||||
@@ -430,6 +494,27 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
|
|||||||
f.retTypeKind = 0;
|
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 {
|
if decl.refBody != null as *Block {
|
||||||
f.body = Lcx_LowerBlock(ctx, decl.refBody, -1);
|
f.body = Lcx_LowerBlock(ctx, decl.refBody, -1);
|
||||||
} else {
|
} else {
|
||||||
@@ -449,7 +534,7 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
ctx.scope = sema.scope;
|
ctx.scope = sema.scope;
|
||||||
ctx.funcs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc;
|
ctx.funcs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc;
|
||||||
ctx.funcCount = 0;
|
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.externCount = 0;
|
||||||
ctx.varCounter = 0;
|
ctx.varCounter = 0;
|
||||||
|
|
||||||
@@ -473,6 +558,20 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
ctx.funcs[ctx.funcCount] = *f;
|
ctx.funcs[ctx.funcCount] = *f;
|
||||||
ctx.funcCount = ctx.funcCount + 1;
|
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 {
|
if decl.kind == dkExternFunc {
|
||||||
let f: *HirFunc = Lcx_LowerFunc(ctx, decl);
|
let f: *HirFunc = Lcx_LowerFunc(ctx, decl);
|
||||||
ctx.externFuncs[ctx.externCount] = *f;
|
ctx.externFuncs[ctx.externCount] = *f;
|
||||||
@@ -534,11 +633,13 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule {
|
|||||||
decl = decl.childDecl2;
|
decl = decl.childDecl2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
hm.funcCount = ctx.funcCount;
|
hm.funcCount = ctx.funcCount;
|
||||||
hm.funcs = ctx.funcs;
|
hm.funcs = ctx.funcs;
|
||||||
hm.externCount = ctx.externCount;
|
hm.externCount = ctx.externCount;
|
||||||
hm.externFuncs = ctx.externFuncs;
|
hm.externFuncs = ctx.externFuncs;
|
||||||
|
|
||||||
|
|
||||||
return hm;
|
return hm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+43
-1
@@ -44,6 +44,29 @@ func parserPeek(p: *Parser, ahead: int) -> int {
|
|||||||
return tkEndOfFile;
|
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 {
|
func parserAdvance(p: *Parser) -> LexToken {
|
||||||
let tok: LexToken = parserCurToken(p);
|
let tok: LexToken = parserCurToken(p);
|
||||||
if p.pos < p.tokenCount {
|
if p.pos < p.tokenCount {
|
||||||
@@ -331,6 +354,25 @@ func parserParsePostfix(p: *Parser) -> *Expr {
|
|||||||
continue;
|
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]
|
// Index: expr[expr]
|
||||||
if kind == tkLBracket {
|
if kind == tkLBracket {
|
||||||
discard parserAdvance(p);
|
discard parserAdvance(p);
|
||||||
@@ -1285,7 +1327,7 @@ func parserParseDecl(p: *Parser) -> *Decl {
|
|||||||
if d.methodCount == 0 {
|
if d.methodCount == 0 {
|
||||||
d.childDecl1 = m;
|
d.childDecl1 = m;
|
||||||
} else if d.methodCount == 1 {
|
} else if d.methodCount == 1 {
|
||||||
d.childDecl2 = m;
|
d.childDecl1.childDecl2 = m;
|
||||||
}
|
}
|
||||||
d.methodCount = d.methodCount + 1;
|
d.methodCount = d.methodCount + 1;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+187
-8
@@ -266,7 +266,7 @@ func QBE_IsTerminator(node: *HirNode) -> bool {
|
|||||||
if node.kind == hBreak { return true; }
|
if node.kind == hBreak { return true; }
|
||||||
if node.kind == hContinue { return true; }
|
if node.kind == hContinue { return true; }
|
||||||
if node.kind == hIf {
|
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 QBE_IsTerminator(node.child2) && QBE_IsTerminator(node.extraData as *HirNode);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -307,6 +307,74 @@ func QBE_FindFunc(qbe: *QbeEmitter, name: String) -> *HirFunc {
|
|||||||
return null as *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
|
// Helper: determine QBE type for any HIR node
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -411,6 +479,23 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
|
|||||||
var target: String = "";
|
var target: String = "";
|
||||||
if node.child1 != null as *HirNode && node.child1.kind == hVar {
|
if node.child1 != null as *HirNode && node.child1.kind == hVar {
|
||||||
target = QBE_LocalName(node.child1.strValue);
|
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 {
|
} else {
|
||||||
target = QBE_EmitExpr(qbe, node.child1);
|
target = QBE_EmitExpr(qbe, node.child1);
|
||||||
if String_Eq(target, "") { return "0"; }
|
if String_Eq(target, "") { return "0"; }
|
||||||
@@ -659,18 +744,84 @@ func QBE_EmitExpr(qbe: *QbeEmitter, node: *HirNode) -> String {
|
|||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field pointer: &obj.field
|
// Field pointer: &obj.field (with auto-load for rvalue contexts)
|
||||||
if kind == hFieldPtr {
|
if kind == hFieldPtr {
|
||||||
let base: String = QBE_EmitExpr(qbe, node.child1);
|
let base: String = QBE_EmitExpr(qbe, node.child1);
|
||||||
let tmp: String = QBE_FreshTemp(qbe);
|
let ptrTmp: String = QBE_FreshTemp(qbe);
|
||||||
// add base, offset — need to know offset; for now assume 0 for first field
|
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_EmitIndent(qbe);
|
||||||
QBE_Emit(qbe, tmp);
|
QBE_Emit(qbe, ptrTmp);
|
||||||
QBE_Emit(qbe, " =l add ");
|
QBE_Emit(qbe, " =l add ");
|
||||||
QBE_Emit(qbe, QBE_EnsureLocal(base));
|
QBE_Emit(qbe, QBE_EnsureLocal(base));
|
||||||
QBE_Emit(qbe, ", 0");
|
QBE_Emit(qbe, ", ");
|
||||||
|
QBE_Emit(qbe, String_FromInt(offset as int64));
|
||||||
QBE_EmitLine(qbe, "");
|
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)
|
// Index pointer: &arr[idx] (for pointer indexing)
|
||||||
@@ -744,6 +895,34 @@ func QBE_EmitStmt(qbe: *QbeEmitter, node: *HirNode) {
|
|||||||
}
|
}
|
||||||
return;
|
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
|
// Plain assignment
|
||||||
let target: String = QBE_EmitExpr(qbe, node.child1);
|
let target: String = QBE_EmitExpr(qbe, node.child1);
|
||||||
let val: String = QBE_EmitExpr(qbe, node.child2);
|
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, ", ");
|
||||||
QBE_Emit(qbe, lThen);
|
QBE_Emit(qbe, lThen);
|
||||||
QBE_Emit(qbe, ", ");
|
QBE_Emit(qbe, ", ");
|
||||||
var hasElse: bool = node.extraData != null as *HirNode;
|
var hasElse: bool = node.extraData != null as *void;
|
||||||
if hasElse {
|
if hasElse {
|
||||||
QBE_EmitLine(qbe, lElse);
|
QBE_EmitLine(qbe, lElse);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+26
-1
@@ -2,6 +2,8 @@
|
|||||||
// Validates types, resolves identifiers, checks function calls.
|
// Validates types, resolves identifiers, checks function calls.
|
||||||
module Sema {
|
module Sema {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Sema context
|
// Sema context
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -130,6 +132,12 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
Sema_EmitError(sema, expr.line, expr.column, "undeclared identifier");
|
Sema_EmitError(sema, expr.line, expr.column, "undeclared identifier");
|
||||||
return tyUnknown;
|
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;
|
return sym.typeKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +220,10 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
sym.kind = skVar;
|
sym.kind = skVar;
|
||||||
sym.name = stmt.strValue;
|
sym.name = stmt.strValue;
|
||||||
sym.typeKind = initType;
|
sym.typeKind = initType;
|
||||||
|
sym.typeName = "";
|
||||||
|
if stmt.refStmtType != null as *TypeExpr {
|
||||||
|
sym.typeName = stmt.refStmtType.typeName;
|
||||||
|
}
|
||||||
sym.isMutable = stmt.boolValue;
|
sym.isMutable = stmt.boolValue;
|
||||||
sym.isPublic = false;
|
sym.isPublic = false;
|
||||||
discard Scope_Define(sema.scope, sym);
|
discard Scope_Define(sema.scope, sym);
|
||||||
@@ -350,9 +362,22 @@ func Sema_Analyze(mod: *Module) -> *Sema {
|
|||||||
// Add parameters to scope
|
// Add parameters to scope
|
||||||
var i: int = 0;
|
var i: int = 0;
|
||||||
while i < decl.paramCount {
|
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;
|
var pSym: Symbol;
|
||||||
pSym.kind = skVar;
|
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;
|
pSym.isMutable = false;
|
||||||
if i == 0 { pSym.name = decl.param0.name; }
|
if i == 0 { pSym.name = decl.param0.name; }
|
||||||
else if i == 1 { pSym.name = decl.param1.name; }
|
else if i == 1 { pSym.name = decl.param1.name; }
|
||||||
|
|||||||
Reference in New Issue
Block a user