fix: multi-arg calls, uninitialized symbols, param limit; add banner img; update docs

- Fix segfault from uninitialized Symbol structs in scope/hir_lower/sema
- Fix null ReadFile crash in Cli_MergeFileInto (missing stdlib files)
- Extend function params from 6 to 9 (bux_str_format needs 9 args)
- Add ExprList/HirArgList linked lists for >2 call arguments
- Add banner image to README
- Update docs: C backend is now primary (not QBE)
This commit is contained in:
2026-06-05 15:21:15 +03:00
parent 0c1f230286
commit 3e46e67d48
21 changed files with 368 additions and 72 deletions
+13 -1
View File
@@ -96,6 +96,11 @@ const ekMatch: int = 22;
const ekSpawn: int = 24;
const ekAwait: int = 25;
struct ExprList {
expr: *Expr,
next: *ExprList,
}
struct Expr {
kind: int,
line: uint32,
@@ -121,6 +126,9 @@ struct Expr {
// Struct init fields
structName: String,
structFieldCount: int,
// Call arguments (linked list for multi-arg support)
callArgs: *ExprList,
callArgCount: int,
}
// ---------------------------------------------------------------------------
@@ -241,6 +249,9 @@ struct Decl {
param3: Param,
param4: Param,
param5: Param,
param6: Param,
param7: Param,
param8: Param,
retType: *TypeExpr,
// Body
refBody: *Block,
@@ -361,7 +372,8 @@ func Ast_MakeExpr(kind: int, line: uint32, col: uint32) -> Expr {
child1: null as *Expr, child2: null as *Expr, child3: null as *Expr,
refType: null as *TypeExpr, refBlock: null as *Block,
genericCallee: "", genericTypeArg0: "", genericTypeArg1: "", genericTypeArgCount: 0,
structName: "", structFieldCount: 0 };
structName: "", structFieldCount: 0,
callArgs: null as *ExprList, callArgCount: 0 };
}
func Ast_MakeIdent(name: String, line: uint32, col: uint32) -> Expr {
+24 -1
View File
@@ -134,12 +134,29 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
if kind == hCall {
StringBuilder_Append(&cbe.sb, node.strValue);
StringBuilder_Append(&cbe.sb, "(");
var needsComma: bool = false;
if node.child1 != null as *HirNode {
CBE_EmitExpr(cbe, node.child1);
needsComma = true;
}
if node.child2 != null as *HirNode {
StringBuilder_Append(&cbe.sb, ", ");
if needsComma {
StringBuilder_Append(&cbe.sb, ", ");
}
CBE_EmitExpr(cbe, node.child2);
needsComma = true;
}
// Emit extra args from linked list
var ai: int = 0;
var curExtra: *HirArgList = node.extraData as *HirArgList;
while ai < node.extraCount {
if needsComma {
StringBuilder_Append(&cbe.sb, ", ");
}
CBE_EmitExpr(cbe, curExtra.node);
needsComma = true;
curExtra = curExtra.next;
ai = ai + 1;
}
StringBuilder_Append(&cbe.sb, ")");
return;
@@ -414,6 +431,9 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
if i == 3 { pname = f.param3.name; ptype = f.param3.typeName; }
if i == 4 { pname = f.param4.name; ptype = f.param4.typeName; }
if i == 5 { pname = f.param5.name; ptype = f.param5.typeName; }
if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; }
if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; }
if i == 8 { pname = f.param8.name; ptype = f.param8.typeName; }
// Emit type
if String_Eq(ptype, "String") || String_Eq(ptype, "str") {
StringBuilder_Append(&cbe.sb, "String ");
@@ -474,6 +494,9 @@ func CBE_FuncHasGeneric(f: *HirFunc) -> bool {
if pi == 3 { ptype = f.param3.typeName; }
if pi == 4 { ptype = f.param4.typeName; }
if pi == 5 { ptype = f.param5.typeName; }
if pi == 6 { ptype = f.param6.typeName; }
if pi == 7 { ptype = f.param7.typeName; }
if pi == 8 { ptype = f.param8.typeName; }
if CBE_IsGenericTypeName(ptype) { return true; }
pi = pi + 1;
}
+6 -1
View File
@@ -400,8 +400,13 @@ func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, st
func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int {
Print("DEBUG: MergeFileInto ");
PrintLine(path);
if !FileExists(path) {
Print("Error: stdlib file not found: ");
PrintLine(path);
return 0;
}
let source: String = ReadFile(path);
if String_Eq(source, "") { return 0; }
if source == null as String || String_Eq(source, "") { return 0; }
let lex: *Lexer = Lexer_Tokenize(source);
if Lexer_DiagCount(lex) > 0 { return 0; }
let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount);
+12
View File
@@ -34,6 +34,15 @@ const hMatch: int = 28;
const hSpawn: int = 29;
const hAwait: int = 30;
// ---------------------------------------------------------------------------
// HirArgList — linked list for call arguments beyond 2
// ---------------------------------------------------------------------------
struct HirArgList {
node: *HirNode,
next: *HirArgList,
}
// ---------------------------------------------------------------------------
// HirNode — unified struct with tagged union pattern
// ---------------------------------------------------------------------------
@@ -76,6 +85,9 @@ struct HirFunc {
param3: HirParam;
param4: HirParam;
param5: HirParam;
param6: HirParam;
param7: HirParam;
param8: HirParam;
retTypeKind: int;
retTypeName: String;
body: *HirNode;
+70 -11
View File
@@ -180,11 +180,34 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
// 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;
// Lower remaining arguments from linked list
var arg: *ExprList = expr.callArgs;
var argIdx: int = 0;
while arg != null as *ExprList {
let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr);
if argIdx == 0 {
n.child2 = lowered;
} else if argIdx == 1 {
// Third argument — start linked list
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
firstExtra.node = lowered;
firstExtra.next = null as *HirArgList;
n.extraData = firstExtra as *void;
n.extraCount = 1;
} else {
// Additional args — append to linked list
var cur: *HirArgList = n.extraData as *HirArgList;
while cur.next != null as *HirArgList {
cur = cur.next;
}
let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
newNode.node = lowered;
newNode.next = null as *HirArgList;
cur.next = newNode;
n.extraCount = n.extraCount + 1;
}
arg = arg.next;
argIdx = argIdx + 1;
}
return n;
}
@@ -192,12 +215,36 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
n.strValue = expr.child1.strValue;
}
// Lower arguments
if expr.child2 != null as *Expr {
n.child1 = Lcx_LowerExpr(ctx, expr.child2);
}
if expr.child3 != null as *Expr {
n.child2 = Lcx_LowerExpr(ctx, expr.child3);
// Lower arguments from linked list
var arg: *ExprList = expr.callArgs;
var argIdx: int = 0;
while arg != null as *ExprList {
let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr);
if argIdx == 0 {
n.child1 = lowered;
} else if argIdx == 1 {
n.child2 = lowered;
} else if argIdx == 2 {
// Third argument — start linked list
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
firstExtra.node = lowered;
firstExtra.next = null as *HirArgList;
n.extraData = firstExtra as *void;
n.extraCount = 1;
} else {
// Additional args — append to linked list
var cur: *HirArgList = n.extraData as *HirArgList;
while cur.next != null as *HirArgList {
cur = cur.next;
}
let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
newNode.node = lowered;
newNode.next = null as *HirArgList;
cur.next = newNode;
n.extraCount = n.extraCount + 1;
}
arg = arg.next;
argIdx = argIdx + 1;
}
return n;
}
@@ -359,6 +406,9 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
sym.name = stmt.strValue;
sym.typeKind = alloca.typeKind;
sym.typeName = alloca.typeName;
sym.isMutable = false;
sym.isPublic = false;
sym.decl = null as *Decl;
discard Scope_Define(ctx.scope, sym);
// store the init value
n.kind = hStore;
@@ -495,6 +545,9 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
Lcx_LowerParam(&f.param3, &decl.param3);
Lcx_LowerParam(&f.param4, &decl.param4);
Lcx_LowerParam(&f.param5, &decl.param5);
Lcx_LowerParam(&f.param6, &decl.param6);
Lcx_LowerParam(&f.param7, &decl.param7);
Lcx_LowerParam(&f.param8, &decl.param8);
if decl.retType != null as *TypeExpr {
f.retTypeName = decl.retType.typeName;
@@ -514,12 +567,18 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
else if pi == 3 { p = &decl.param3; }
else if pi == 4 { p = &decl.param4; }
else if pi == 5 { p = &decl.param5; }
else if pi == 6 { p = &decl.param6; }
else if pi == 7 { p = &decl.param7; }
else if pi == 8 { p = &decl.param8; }
if p != null as *Param && p.refParamType != null as *TypeExpr {
var sym: Symbol;
sym.kind = skVar;
sym.name = p.name;
sym.typeKind = Lcx_ResolveTypeKind(p.refParamType);
sym.typeName = p.refParamType.typeName;
sym.isMutable = false;
sym.isPublic = false;
sym.decl = null as *Decl;
discard Scope_Define(ctx.scope, sym);
}
pi = pi + 1;
+1 -1
View File
@@ -711,7 +711,7 @@ func Lexer_GetToken(lex: *Lexer, index: int) -> LexToken {
if index >= 0 && index < lex.tokenCount {
return lex.tokens[index];
}
var empty: LexToken;
var empty: LexToken = LexToken { kind: tkEndOfFile, text: "", line: 0, column: 0 };
return empty;
}
+34 -11
View File
@@ -214,6 +214,8 @@ func parserMakeExpr(kind: int, line: uint32, col: uint32) -> *Expr {
e.genericTypeArgCount = 0;
e.structName = "";
e.structFieldCount = 0;
e.callArgs = null as *ExprList;
e.callArgCount = 0;
return e;
}
@@ -337,18 +339,27 @@ func parserParsePostfix(p: *Parser) -> *Expr {
let col: uint32 = parserCurToken(p).column;
let e: *Expr = parserMakeExpr(ekCall, line, col);
e.child1 = left;
// Parse arguments (up to 8)
// child2 = first arg, child3 = second arg, extra = rest
if !parserCheck(p, tkRParen) {
e.child2 = parserParseExpr(p);
if parserMatch(p, tkComma) {
e.child3 = parserParseExpr(p);
// Consume any remaining arguments (we only store 2)
while parserMatch(p, tkComma) {
discard parserParseExpr(p);
}
// Parse all arguments into linked list
var argCount: int = 0;
var firstArg: *ExprList = null as *ExprList;
var lastArg: *ExprList = null as *ExprList;
while !parserCheck(p, tkRParen) {
let argExpr: *Expr = parserParseExpr(p);
let argNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList;
argNode.expr = argExpr;
argNode.next = null as *ExprList;
if firstArg == null as *ExprList {
firstArg = argNode;
lastArg = argNode;
} else {
lastArg.next = argNode;
lastArg = argNode;
}
argCount = argCount + 1;
if !parserMatch(p, tkComma) { break; }
}
e.callArgs = firstArg;
e.callArgCount = argCount;
discard parserExpect(p, tkRParen, "expected ')'");
left = e;
continue;
@@ -842,7 +853,7 @@ func parserParseParamList(p: *Parser) -> *Decl {
discard parserExpect(p, tkLParen, "expected '('");
while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile {
if d.paramCount >= 6 { break; }
if d.paramCount >= 9 { break; }
let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name");
discard parserExpect(p, tkColon, "expected ':' in parameter");
let ptype: *TypeExpr = parserParseType(p);
@@ -865,6 +876,15 @@ func parserParseParamList(p: *Parser) -> *Decl {
} else if d.paramCount == 5 {
d.param5.name = nameTok.text;
d.param5.refParamType = ptype;
} else if d.paramCount == 6 {
d.param6.name = nameTok.text;
d.param6.refParamType = ptype;
} else if d.paramCount == 7 {
d.param7.name = nameTok.text;
d.param7.refParamType = ptype;
} else if d.paramCount == 8 {
d.param8.name = nameTok.text;
d.param8.refParamType = ptype;
}
d.paramCount = d.paramCount + 1;
@@ -916,6 +936,9 @@ func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool, isAsync: bo
d.param3 = params.param3;
d.param4 = params.param4;
d.param5 = params.param5;
d.param6 = params.param6;
d.param7 = params.param7;
d.param8 = params.param8;
// Return type
if parserMatch(p, tkArrow) {
+2 -2
View File
@@ -72,7 +72,7 @@ func Scope_Lookup(scope: *Scope, name: String) -> Symbol {
}
cur = cur.parent;
}
var empty: Symbol;
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl };
return empty;
}
@@ -84,7 +84,7 @@ func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol {
}
i = i + 1;
}
var empty: Symbol;
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl };
return empty;
}
+12 -5
View File
@@ -231,11 +231,10 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
// Call
if kind == ekCall {
discard Sema_CheckExpr(sema, expr.child1);
if expr.child2 != null as *Expr {
discard Sema_CheckExpr(sema, expr.child2);
}
if expr.child3 != null as *Expr {
discard Sema_CheckExpr(sema, expr.child3);
var arg: *ExprList = expr.callArgs;
while arg != null as *ExprList {
discard Sema_CheckExpr(sema, arg.expr);
arg = arg.next;
}
// Try to resolve return type from function declaration
if expr.child1.kind == ekIdent {
@@ -635,6 +634,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
// Add type params to scope
if decl.typeParamCount >= 1 {
var tpSym: Symbol;
Sema_ZeroInitSymbol(&tpSym);
tpSym.kind = skType;
tpSym.name = decl.typeParam0;
tpSym.typeKind = tyTypeParam;
@@ -643,6 +643,7 @@ func Sema_Analyze(mod: *Module) -> *Sema {
}
if decl.typeParamCount >= 2 {
var tpSym2: Symbol;
Sema_ZeroInitSymbol(&tpSym2);
tpSym2.kind = skType;
tpSym2.name = decl.typeParam1;
tpSym2.typeKind = tyTypeParam;
@@ -660,6 +661,9 @@ func Sema_Analyze(mod: *Module) -> *Sema {
else if i == 3 { p = &decl.param3; }
else if i == 4 { p = &decl.param4; }
else if i == 5 { p = &decl.param5; }
else if i == 6 { p = &decl.param6; }
else if i == 7 { p = &decl.param7; }
else if i == 8 { p = &decl.param8; }
var pSym: Symbol;
Sema_ZeroInitSymbol(&pSym);
pSym.kind = skVar;
@@ -679,6 +683,9 @@ func Sema_Analyze(mod: *Module) -> *Sema {
else if i == 3 { pSym.name = decl.param3.name; }
else if i == 4 { pSym.name = decl.param4.name; }
else if i == 5 { pSym.name = decl.param5.name; }
else if i == 6 { pSym.name = decl.param6.name; }
else if i == 7 { pSym.name = decl.param7.name; }
else if i == 8 { pSym.name = decl.param8.name; }
discard Scope_Define(&funcScope, pSym);
i = i + 1;
}