Skip to content

Commit b196b9e

Browse files
chqrliebvdberg
authored andcommitted
language: add support for named arguments
* named arguments must appear in the definition order * arguments with default values can be omitted before named arguments
1 parent 5f2d594 commit b196b9e

21 files changed

+341
-17
lines changed

analyser/conversion_checker_expr.c2

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ fn ExprWidth getExprWidth(const Expr* e) {
101101
ExprWidth lhs = getExprWidth(b.getLHS());
102102
ExprWidth rhs = getExprWidth(b.getRHS());
103103
return ExprWidth.mergeWider(lhs, rhs);
104+
case NamedArgument:
105+
const NamedArgument* n = (NamedArgument*)e;
106+
return getExprWidth(n.getInner());
104107
}
105108

106109
e.dump();

analyser/module_analyser_call.c2

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,15 +167,47 @@ fn QualType Analyser.analyseCallExpr(Analyser* ma, Expr** e_ptr) {
167167
while (1) {
168168
//stdio.printf("ARG [%d | %d]\n", func_arg_index, call_arg_index);
169169
if (func_arg_index >= func_num_args) break;
170-
if (call_arg_index >= call_num_args) break;
171170

172171
VarDecl* vd = func_args[func_arg_index];
172+
Decl* d = vd.asDecl();
173173
if (vd.hasAutoAttr()) {
174174
func_arg_index++;
175175
continue;
176176
}
177177

178-
if (!ma.analyseInitExpr(&call_args[call_arg_index], vd.asDecl().getType(), call_args[call_arg_index].getLoc(), false, false))
178+
if (call_arg_index >= call_num_args) break;
179+
180+
Expr* arg = call_args[call_arg_index];
181+
if (arg.isNamedArgument()) {
182+
NamedArgument* n = (NamedArgument*)arg;
183+
u32 arg_name_idx = n.getNameIdx();
184+
u32 param_name_idx;
185+
bool found = false;
186+
while (func_arg_index < func_num_args) {
187+
vd = func_args[func_arg_index];
188+
d = vd.asDecl();
189+
param_name_idx = d.getNameIdx();
190+
found = (param_name_idx == arg_name_idx);
191+
if (found)
192+
break;
193+
if (!vd.hasInit())
194+
break;
195+
func_arg_index++;
196+
}
197+
if (!found) {
198+
ma.error(arg.getLoc(), "unexpected named argument '%s'", ma.astPool.idx2str(arg_name_idx));
199+
if (func_arg_index < func_num_args) {
200+
if (param_name_idx) {
201+
ma.note(d.getLoc(), "expected argument '%s' instead", ma.astPool.idx2str(param_name_idx));
202+
} else {
203+
ma.note(d.getLoc(), "expected unnamed argument");
204+
}
205+
}
206+
return QualType_Invalid;
207+
}
208+
}
209+
210+
if (!ma.analyseInitExpr(&call_args[call_arg_index], d.getType(), arg.getLoc(), false, false))
179211
return QualType_Invalid;
180212

181213
if (vd.hasPrintfFormat()) {
@@ -188,7 +220,7 @@ fn QualType Analyser.analyseCallExpr(Analyser* ma, Expr** e_ptr) {
188220
}
189221

190222
u32 expected_args = func_num_args - num_auto_args - isTypeFuncCall;
191-
if (call_num_args < expected_args) {
223+
if (func_arg_index < func_num_args) {
192224
u32 missing_args = 0;
193225
for (u32 i = func_arg_index; i < func_num_args; i++) {
194226
if (!func_args[i].hasInit()) missing_args++;
@@ -456,6 +488,7 @@ fn QualType Analyser.analysePureCallExpr(Analyser* ma, Expr* e) {
456488
return QualType_Invalid;
457489
}
458490

491+
// TODO: handle default and named arguments
459492
u32 func_num_args = fd.getNumParams();
460493
u32 call_num_args = call.getNumArgs();
461494
//VarDecl** func_args = fd.getParams();

analyser/module_analyser_expr.c2

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ fn QualType Analyser.analyseExprInner(Analyser* ma, Expr** e_ptr, u32 side) {
116116
(*e_ptr).dump();
117117
assert(0);
118118
break;
119+
case NamedArgument:
120+
NamedArgument* n = (NamedArgument*)e;
121+
QualType qt = ma.analyseExpr(n.getInner2(), false, side);
122+
Expr* inner = n.getInner();
123+
e.copyConstantFlags(inner);
124+
e.copyValType(inner);
125+
return qt;
119126
}
120127
return QualType_Invalid;
121128
}

analyser/module_analyser_function.c2

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {
6868
VarDecl** params = fd.getParams();
6969
u32 auto_arg_count = 0;
7070
u32 first_auto_arg = 0;
71-
bool has_default = false;
7271
for (u32 i=0; i<num_params; i++) {
7372
VarDecl* vd = params[i];
7473
TypeRef* ref = vd.getTypeRef();
@@ -90,7 +89,6 @@ fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {
9089
if (is_public) setTypePublicUsed(res);
9190

9291
if (vd.hasInit()) {
93-
has_default = true;
9492
if (vd.hasAutoAttr()) {
9593
ma.error(vd.getAssignLoc(), "automatic argument cannot have default value");
9694
}
@@ -99,11 +97,6 @@ fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {
9997
}
10098
Expr** initExpr = vd.getInit2();
10199
ma.analyseInitExpr(initExpr, res, vd.getAssignLoc(), false, true);
102-
} else {
103-
if (has_default) {
104-
Decl* d = (Decl*)vd;
105-
ma.error(d.getLoc(), "parameter must have a default value");
106-
}
107100
}
108101

109102
if (vd.hasAutoAttr()) {

analyser/module_analyser_unaryop.c2

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ fn IdentifierKind getInnerExprAddressOf(const Expr* e) {
260260
return getInnerExprAddressOf(c.getInner());
261261
case Range:
262262
break;
263+
case NamedArgument:
264+
NamedArgument* n = (NamedArgument*)e;
265+
return getInnerExprAddressOf(n.getInner());
263266
}
264267

265268
return IdentifierKind.Unresolved;

ast/ast_evaluator.c2

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ fn Value Evaluator.get_value(Evaluator* eval, const Expr* e) {
115115
case Range:
116116
assert(0);
117117
break;
118+
case NamedArgument:
119+
const NamedArgument* n = (NamedArgument*)e;
120+
return eval.get_value(n.getInner());
118121
}
119122
return Value.error("expression is not constant");
120123
}

ast/expr.c2

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public type ExprKind enum u8 {
4343
ExplicitCast,
4444
ImplicitCast,
4545
Range,
46+
NamedArgument,
4647
}
4748

4849
const char*[] exprKind_names = {
@@ -69,6 +70,7 @@ const char*[] exprKind_names = {
6970
"ExplicitCast",
7071
"ImplicitCast",
7172
"RangeExpr",
73+
"NamedArgument",
7274
}
7375
static_assert(elemsof(ExprKind), elemsof(exprKind_names));
7476

@@ -182,6 +184,8 @@ fn Expr* Expr.instantiate(Expr* e, Instantiator* inst) {
182184
break;
183185
case Range:
184186
return RangeExpr.instantiate((RangeExpr*)e, inst);
187+
case NamedArgument:
188+
return NamedArgument.instantiate((NamedArgument*)e, inst);
185189
}
186190
e.dump();
187191
assert(0);
@@ -273,6 +277,10 @@ public fn bool Expr.isInitlistAssignment(const Expr* e) {
273277
return e.isAssignment() && ((BinaryOperator*)e).getRHS().isInitList();
274278
}
275279

280+
public fn bool Expr.isNamedArgument(const Expr* e) {
281+
return e.getKind() == ExprKind.NamedArgument;
282+
}
283+
276284
public fn bool Expr.isCtv(const Expr* e) { return e.base.exprBits.is_ctv; }
277285

278286
public fn bool Expr.isCtc(const Expr* e) { return e.base.exprBits.is_ctc; }
@@ -382,6 +390,8 @@ public fn SrcLoc Expr.getStartLoc(const Expr* e) {
382390
return ((ImplicitCastExpr*)e).getStartLoc();
383391
case Range:
384392
return ((RangeExpr*)e).getStartLoc();
393+
case NamedArgument:
394+
return ((NamedArgument*)e).getStartLoc();
385395
}
386396
return e.base.loc;
387397
}
@@ -434,6 +444,8 @@ public fn SrcLoc Expr.getEndLoc(const Expr* e) {
434444
return ((ImplicitCastExpr*)e).getEndLoc();
435445
case Range:
436446
return ((RangeExpr*)e).getEndLoc();
447+
case NamedArgument:
448+
return ((NamedArgument*)e).getEndLoc();
437449
}
438450
return e.base.loc;
439451
}
@@ -480,6 +492,9 @@ public fn bool Expr.needsSemi(const Expr* e) {
480492
case ImplicitCast:
481493
case Range:
482494
break;
495+
case NamedArgument:
496+
const NamedArgument* n = (NamedArgument*)e;
497+
return n.getInner().needsSemi();
483498
}
484499
return true;
485500
}
@@ -563,6 +578,9 @@ fn void Expr.print(const Expr* e, string_buffer.Buf* out, u32 indent) {
563578
case Range:
564579
RangeExpr.print((RangeExpr*)e, out, indent);
565580
break;
581+
case NamedArgument:
582+
NamedArgument.print((NamedArgument*)e, out, indent);
583+
break;
566584
}
567585
}
568586

@@ -638,6 +656,9 @@ public fn void Expr.printLiteral(const Expr* e, string_buffer.Buf* out) {
638656
case Range:
639657
RangeExpr.printLiteral((RangeExpr*)e, out);
640658
return;
659+
case NamedArgument:
660+
NamedArgument.printLiteral((NamedArgument*)e, out);
661+
break;
641662
}
642663
out.print("<<kind=%d>>", e.getKind());
643664
}

ast/named_argument.c2

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* Copyright 2025 Chqrlie
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
module ast;
17+
18+
import ast_context;
19+
import string_buffer;
20+
import src_loc local;
21+
22+
public type NamedArgument struct @(opaque) {
23+
Expr base;
24+
SrcLoc loc;
25+
u32 name;
26+
Expr* inner;
27+
}
28+
29+
public fn NamedArgument* NamedArgument.create(ast_context.Context* c, SrcLoc loc, u32 name, Expr* inner) {
30+
NamedArgument* e = c.alloc(sizeof(NamedArgument));
31+
e.base.init(ExprKind.NamedArgument, loc, 0, 0, false, ValType.RValue);
32+
e.name = name;
33+
e.inner = inner;
34+
#if AstStatistics
35+
Stats.addExpr(ExprKind.NamedArgument, sizeof(NamedArgument));
36+
#endif
37+
return e;
38+
}
39+
40+
fn Expr* NamedArgument.instantiate(NamedArgument* e, Instantiator* inst) {
41+
return (Expr*)NamedArgument.create(inst.c, e.base.base.loc, e.name, e.inner.instantiate(inst));
42+
}
43+
44+
public fn u32 NamedArgument.getNameIdx(const NamedArgument* e) { return e.name; }
45+
46+
public fn Expr* NamedArgument.getInner(const NamedArgument* e) { return e.inner; }
47+
public fn Expr** NamedArgument.getInner2(NamedArgument* e) { return &e.inner; }
48+
49+
fn SrcLoc NamedArgument.getStartLoc(const NamedArgument* e) {
50+
return e.inner.getStartLoc();
51+
}
52+
53+
fn SrcLoc NamedArgument.getEndLoc(const NamedArgument* e) {
54+
return e.inner.getEndLoc();
55+
}
56+
57+
fn void NamedArgument.print(const NamedArgument* e, string_buffer.Buf* out, u32 indent) {
58+
e.base.printKind(out, indent);
59+
e.base.printTypeBits(out);
60+
out.color(col_Value);
61+
out.space();
62+
out.add("name: ");
63+
out.add(idx2name(e.name));
64+
out.newline();
65+
e.inner.print(out, indent + 1);
66+
}
67+
68+
public fn void NamedArgument.printLiteral(const NamedArgument* e, string_buffer.Buf* out) {
69+
out.add(idx2name(e.name));
70+
out.add(": ");
71+
e.inner.printLiteral(out);
72+
}
73+

generator/ast_visitor_expr.c2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ fn void Visitor.handleExpr(Visitor* v, Expr* e) {
115115
v.handleExpr(b.getLHS());
116116
v.handleExpr(b.getRHS());
117117
break;
118+
case NamedArgument:
119+
NamedArgument* n = (NamedArgument*)e;
120+
v.handleExpr(n.getInner());
121+
break;
118122
}
119123
}
120124

generator/c/c_generator_call.c2

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,25 @@ fn void Generator.emitCall(Generator* gen, string_buffer.Buf* out, Expr* e) {
162162
(u32)(string.strlen(format_text + fc.last_offset)), '"');
163163
out.add1('"');
164164
} else {
165-
Expr* arg = (call_index < call_num_args) ?
166-
args[call_index++] : func_args[func_index].getInit();
165+
Expr* arg;
166+
Decl* d = vd.asDecl();
167+
168+
if (call_index < call_num_args) {
169+
arg = args[call_index++];
170+
if (arg.isNamedArgument()) {
171+
NamedArgument* n = (NamedArgument*)arg;
172+
u32 arg_name_idx = n.getNameIdx();
173+
if (d.getNameIdx() == arg_name_idx) {
174+
arg = n.getInner();
175+
} else {
176+
call_index--;
177+
arg = vd.getInit();
178+
}
179+
}
180+
} else {
181+
arg = vd.getInit();
182+
}
167183
if (arg.isInitList()) {
168-
Decl* d = (Decl*)func_args[func_index];
169184
gen.emitCast(out, d.getType(), true);
170185
}
171186
gen.emitExpr(out, arg);

0 commit comments

Comments
 (0)