feat: named and default parameters

Bootstrap compiler:
- ast.nim: add exprCallArgNames to ekCall for named arg tracking
- parser.nim: detect name: value syntax in call args
- sema.nim: add resolveCallArgs helper that injects default values
  for missing params and reorders named args into param order.
  Parser already parsed default param values; sema now uses them.

Selfhost compiler:
- ast.bux: add defaultExpr to Param, argName to ExprList
- parser.bux: parse = defaultExpr in param list, detect name: value
  syntax in call arguments
- sema.bux: add Sema_ResolveCallArgs with same logic as bootstrap

Tests:
- _test_named_params verifies defaults, named args, mixed positional+named,
  and named args with defaults in both compilers.

All verifications pass: build, selfhost-loop, test-examples, test-golden.
This commit is contained in:
2026-06-08 22:02:19 +03:00
parent 17c896c4dc
commit 3f0a3901ca
7 changed files with 210 additions and 13 deletions
+26 -1
View File
@@ -353,10 +353,22 @@ func parserParsePostfixExpr(p: *Parser) -> *Expr {
var firstArg: *ExprList = null as *ExprList;
var lastArg: *ExprList = null as *ExprList;
while !parserCheck(p, tkRParen) {
let argExpr: *Expr = parserParseExpr(p);
var argExpr: *Expr = null as *Expr;
var argName: String = "";
// Named argument: name: value
if parserPeek(p, 0) == tkIdent && parserPeek(p, 1) == tkColon {
let nameTok: LexToken = parserCurToken(p);
argName = nameTok.text;
discard parserAdvance(p); // ident
discard parserAdvance(p); // :
argExpr = parserParseExpr(p);
} else {
argExpr = parserParseExpr(p);
}
let argNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList;
argNode.expr = argExpr;
argNode.next = null as *ExprList;
argNode.argName = argName;
if firstArg == null as *ExprList {
firstArg = argNode;
lastArg = argNode;
@@ -1016,34 +1028,47 @@ func parserParseParamList(p: *Parser) -> *Decl {
let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name");
discard parserExpect(p, tkColon, "expected ':' in parameter");
let ptype: *TypeExpr = parserParseType(p);
var defExpr: *Expr = null as *Expr;
if parserMatch(p, tkAssign) {
defExpr = parserParseExpr(p);
}
if d.paramCount == 0 {
d.param0.name = nameTok.text;
d.param0.refParamType = ptype;
d.param0.defaultExpr = defExpr;
} else if d.paramCount == 1 {
d.param1.name = nameTok.text;
d.param1.refParamType = ptype;
d.param1.defaultExpr = defExpr;
} else if d.paramCount == 2 {
d.param2.name = nameTok.text;
d.param2.refParamType = ptype;
d.param2.defaultExpr = defExpr;
} else if d.paramCount == 3 {
d.param3.name = nameTok.text;
d.param3.refParamType = ptype;
d.param3.defaultExpr = defExpr;
} else if d.paramCount == 4 {
d.param4.name = nameTok.text;
d.param4.refParamType = ptype;
d.param4.defaultExpr = defExpr;
} else if d.paramCount == 5 {
d.param5.name = nameTok.text;
d.param5.refParamType = ptype;
d.param5.defaultExpr = defExpr;
} else if d.paramCount == 6 {
d.param6.name = nameTok.text;
d.param6.refParamType = ptype;
d.param6.defaultExpr = defExpr;
} else if d.paramCount == 7 {
d.param7.name = nameTok.text;
d.param7.refParamType = ptype;
d.param7.defaultExpr = defExpr;
} else if d.paramCount == 8 {
d.param8.name = nameTok.text;
d.param8.refParamType = ptype;
d.param8.defaultExpr = defExpr;
}
d.paramCount = d.paramCount + 1;