Skip to content

Commit 29d7820

Browse files
committed
Compiler: add local function statements and expressions
* local functions are defined using the standard syntax in the body of a function. * they cannot be templates nor type functions * they can be named or anonymous * accept a capture list between square backets fater the parameter list (currently ignored)
1 parent e963f89 commit 29d7820

File tree

7 files changed

+105
-31
lines changed

7 files changed

+105
-31
lines changed

ast/decl.c2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ public fn u32 Decl.getNameIdx(const Decl* d) {
204204
return d.name_idx;
205205
}
206206

207+
public fn void Decl.setNameIdx(Decl* d, u32 name_idx) {
208+
d.name_idx = name_idx;
209+
}
210+
207211
// convenience function
208212
public fn const char* Decl.getModuleName(const Decl* d) {
209213
const AST* a = d.getAST();

ast/function_decl.c2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public type DefKind enum u8 {
4141
Type, // type A fn ..
4242
StructMember, // (in struct) fn void(..) member;
4343
Param, // fn void a( fn void(..) cb)
44+
Local, // local function
4445
Template, // ..
4546
}
4647

@@ -49,6 +50,7 @@ const char*[] defKind_names = {
4950
"type",
5051
"struct-member",
5152
"template",
53+
"local",
5254
"param",
5355
}
5456

@@ -269,6 +271,8 @@ public fn bool FunctionDecl.canBeNil(const FunctionDecl* d) {
269271
return true;
270272
case Template:
271273
break;
274+
case Local:
275+
return false;
272276
}
273277
return d.hasAttrWeak();
274278
}

common/ast_builder.c2

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -723,9 +723,10 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
723723
const Ref* prefix,
724724
VarDecl** params,
725725
u32 num_params,
726-
bool is_variadic)
726+
bool is_variadic,
727+
bool is_local)
727728
{
728-
is_public |= b.is_interface;
729+
is_public |= b.is_interface & !is_local;
729730
FunctionDecl* f = FunctionDecl.create(b.context,
730731
name,
731732
loc,
@@ -736,10 +737,12 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
736737
params,
737738
num_params,
738739
is_variadic,
739-
DefKind.Global);
740+
is_local ? DefKind.Local : DefKind.Global);
740741
b.ast.addFunc(f);
741-
if (!prefix) b.addSymbol(name, f.asDecl());
742-
if (b.is_interface) f.asDecl().setExternal();
742+
if (!is_local) {
743+
if (!prefix) b.addSymbol(name, f.asDecl());
744+
if (b.is_interface) f.asDecl().setExternal();
745+
}
743746
return f;
744747
}
745748

@@ -1067,7 +1070,7 @@ public fn void Builder.insertImplicitCast(Builder* b,
10671070
*e_ptr = ic;
10681071
}
10691072

1070-
fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
1073+
public fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
10711074
Decl* old = b.mod.findSymbol(name_idx);
10721075
if (old) {
10731076
b.diags.error(d.getLoc(), "redefinition of '%s'", idx2name(name_idx));

parser/c2_parser.c2

Lines changed: 81 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ fn void Parser.parseTopLevel(Parser* p) {
340340
p.error("assert can only be used inside a function");
341341
break;
342342
case KW_fn:
343-
p.parseFuncDecl(is_public);
343+
p.parseFuncDecl(is_public, false);
344344
break;
345345
case KW_import:
346346
p.error("no imports allowed after declarations");
@@ -448,43 +448,97 @@ fn void Parser.applyAttributes(Parser* p, Decl* d, u32 count) {
448448
if (count) p.builder.applyAttributes(d, count);
449449
}
450450

451-
fn void Parser.parseFuncDecl(Parser* p, bool is_public) {
451+
fn u32 Parser.createLocalName(Parser* p, u32 name, SrcLoc loc) {
452+
char[128] buf;
453+
snprintf(buf, sizeof(buf), "%s__%d", p.pool.idx2str(name), loc);
454+
return p.pool.addStr(buf, true);
455+
}
456+
457+
fn Expr* Parser.parseLocalFuncExpr(Parser* p) {
458+
SrcLoc loc = p.tok.loc;
459+
FunctionDecl* f = p.parseFuncDecl(false, true);
460+
Decl* d = f.asDecl();
461+
u32 name = d.getNameIdx();
462+
u32 local_name = p.createLocalName(name, loc);
463+
d.setNameIdx(local_name);
464+
p.builder.addSymbol(local_name, d);
465+
Expr* fexpr = p.builder.actOnIdentifier(loc, local_name).asExpr();
466+
return fexpr;
467+
}
468+
469+
fn Stmt* Parser.parseLocalFuncStmt(Parser* p) {
470+
SrcLoc loc = p.tok.loc;
471+
FunctionDecl* f = p.parseFuncDecl(false, true);
472+
Decl* d = f.asDecl();
473+
u32 name = d.getNameIdx();
474+
u32 local_name = p.createLocalName(name, loc);
475+
d.setNameIdx(local_name);
476+
p.builder.addSymbol(local_name, d);
477+
Expr* fexpr = p.builder.actOnIdentifier(loc, local_name).asExpr();
478+
// TODO: use auto typing
479+
TypeRefHolder ref.init();
480+
ref.setVoid(loc);
481+
ref.addPointer();
482+
VarDecl* decl = p.builder.actOnVarDecl(name, loc, &ref, loc, fexpr, false, false);
483+
return p.builder.actOnDeclStmt(&decl, 1);
484+
}
485+
486+
fn FunctionDecl* Parser.parseFuncDecl(Parser* p, bool is_public, bool is_local) {
487+
u32 func_name = 0;
488+
SrcLoc func_loc = p.tok.loc;
489+
Ref prefix_ref;
490+
Ref* prefix = nil;
491+
452492
p.consumeToken();
453493

454494
TypeRefHolder rtype.init();
455495
// Note: dont check arrays in this phase, but in Analyser
456496
p.parseTypeSpecifier(&rtype);
457497

458-
p.expectIdentifier();
459-
u32 func_name = p.tok.name_idx;
460-
SrcLoc func_loc = p.tok.loc;
461-
p.consumeToken();
462-
463-
Ref prefix_ref;
464-
Ref* prefix = nil;
465-
if (p.tok.kind == Kind.Dot) {
466-
p.consumeToken();
498+
if (p.tok.kind == Kind.Identifier || !is_local) {
467499
p.expectIdentifier();
468-
469-
prefix_ref.loc = func_loc;
470-
prefix_ref.name_idx = func_name;
471-
prefix_ref.decl = nil;
472-
prefix = &prefix_ref;
473500
func_name = p.tok.name_idx;
474501
func_loc = p.tok.loc;
475502
p.consumeToken();
476-
}
477503

478-
if (!p.checkName(func_name, p.is_interface)) {
479-
p.errorAt(func_loc, "a function name must start with a lower case character");
504+
if (p.tok.kind == Kind.Dot) {
505+
if (is_local) {
506+
p.error("local functions cannot be type functions");
507+
}
508+
p.consumeToken();
509+
p.expectIdentifier();
510+
511+
prefix_ref.loc = func_loc;
512+
prefix_ref.name_idx = func_name;
513+
prefix_ref.decl = nil;
514+
prefix = &prefix_ref;
515+
func_name = p.tok.name_idx;
516+
func_loc = p.tok.loc;
517+
p.consumeToken();
518+
}
519+
520+
if (!p.checkName(func_name, p.is_interface)) {
521+
p.errorAt(func_loc, "a function name must start with a lower case character");
522+
}
480523
}
481524

482525
DeclList params.init();
483526

484527
bool is_variadic = p.parseFunctionParams(&params, is_public, true);
485528

529+
if (p.tok.kind == Kind.LSquare && is_local) {
530+
// TODO: parse capture specification
531+
while (!p.tok.done && p.tok.kind != Kind.RSquare) {
532+
p.consumeToken();
533+
}
534+
p.expectAndConsume(Kind.RSquare);
535+
}
536+
486537
FunctionDecl* f;
487538
if (p.tok.kind == Kind.KW_template) {
539+
if (is_local) {
540+
p.error("local functions cannot be template functions");
541+
}
488542
p.consumeToken();
489543
p.expectIdentifier();
490544

@@ -510,28 +564,31 @@ fn void Parser.parseFuncDecl(Parser* p, bool is_public) {
510564
prefix,
511565
(VarDecl**)params.getDecls(),
512566
params.size(),
513-
is_variadic);
567+
is_variadic, is_local);
514568
}
515569

516570
params.free();
517571

518-
u32 num_attr = p.parseOptionalAttributes();
519-
p.applyAttributes((Decl*)f, num_attr);
572+
if (!is_local) {
573+
u32 num_attr = p.parseOptionalAttributes();
574+
p.applyAttributes((Decl*)f, num_attr);
575+
}
520576

521577
if (p.is_interface) {
522578
if (p.tok.kind == Kind.Semicolon) {
523579
// function without body (eg. i32 printf(..); ) is allowed
524580
p.consumeToken();
525-
return;
581+
return f;
526582
}
527583

528584
// mark functions with bodies in interface files as 'inline'
529585
f.setAttrInline();
530586
}
531587

532-
p.stmt_list_count = 0;
588+
if (!is_local) p.stmt_list_count = 0;
533589
CompoundStmt* body = p.parseCompoundStmt();
534590
p.builder.actOnFunctionBody(f, body);
591+
return f;
535592
}
536593

537594
fn bool Parser.parseFunctionParams(Parser* p, DeclList* params, bool is_public, bool accept_default) {

parser/c2_parser_expr.c2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ const u8[128] CastExprTokenLookup = {
212212
[Kind.KW_usize] = 16,
213213
[Kind.KW_f32] = 16,
214214
[Kind.KW_f64] = 16,
215+
[Kind.KW_fn] = 17,
215216
}
216217

217218
// Note: tried lookup table, was slower..
@@ -337,6 +338,9 @@ fn Expr* Parser.parseCastExpr(Parser* p, bool /*isUnaryExpr*/, bool /*isAddrOfOp
337338
p.error("expected expression");
338339
}
339340
break;
341+
case 17: // KW_fn
342+
res = p.parseLocalFuncExpr();
343+
break;
340344
}
341345
return p.parsePostfixExprSuffix(res, couldBeTemplateCall);
342346
}

parser/c2_parser_stmt.c2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ fn Stmt* Parser.parseStmt(Parser* p) {
7979
return p.parseDeclStmt(true, true, false);
8080
case KW_while:
8181
return p.parseWhileStmt();
82+
case KW_fn:
83+
return p.parseLocalFuncStmt();
8284
default:
8385
return p.parseExprStmt();
8486
}

test/parser/invalid_func.c2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module test;
22

33
public fn i32 main() {
4-
fn void() v = foo; // @error{expected expression}
4+
fn void() v = foo; // @error{expected '{'}
55
return 0;
66
}
77

0 commit comments

Comments
 (0)