Skip to content

Commit 2153c46

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 f9fddf6 commit 2153c46

7 files changed

Lines changed: 103 additions & 26 deletions

File tree

ast/decl.c2

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

210+
public fn void Decl.setNameIdx(Decl* d, u32 name_idx) {
211+
d.name_idx = name_idx;
212+
}
213+
210214
// convenience function
211215
public fn const char* Decl.getModuleName(const Decl* d) {
212216
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] defKind_names = {
4950
"type",
5051
"struct-member",
5152
"param",
53+
"local",
5254
"template",
5355
}
5456

@@ -276,6 +278,8 @@ public fn bool FunctionDecl.canBeNil(const FunctionDecl* d) {
276278
return true;
277279
case Template:
278280
break;
281+
case Local:
282+
return false;
279283
}
280284
return d.hasAttrWeak();
281285
}

common/ast_builder.c2

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -733,9 +733,10 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
733733
const Ref* prefix,
734734
VarDecl** params,
735735
u32 num_params,
736-
bool is_variadic)
736+
bool is_variadic,
737+
bool is_local)
737738
{
738-
is_public |= b.is_interface;
739+
is_public |= b.is_interface & !is_local;
739740
FunctionDecl* f = FunctionDecl.create(b.context,
740741
name,
741742
loc,
@@ -746,10 +747,12 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
746747
params,
747748
num_params,
748749
is_variadic,
749-
Global);
750+
is_local ? DefKind.Local : DefKind.Global);
750751
b.ast.addFunc(f);
751-
if (!prefix) b.addSymbol(name, f.asDecl());
752-
if (b.is_interface) f.asDecl().setExternal();
752+
if (!is_local) {
753+
if (!prefix) b.addSymbol(name, f.asDecl());
754+
if (b.is_interface) f.asDecl().setExternal();
755+
}
753756
return f;
754757
}
755758

@@ -1077,7 +1080,7 @@ public fn void Builder.insertImplicitCast(Builder* b,
10771080
*e_ptr = ic;
10781081
}
10791082

1080-
fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
1083+
public fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
10811084
Decl* old = b.mod.findSymbol(name_idx);
10821085
if (old) {
10831086
b.diags.error(d.getLoc(), "redefinition of '%s'", idx2name(name_idx));

parser/c2_parser.c2

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ fn void Parser.parseTopLevel(Parser* p) {
364364
break;
365365
case KW_fn:
366366
if (p.tokenizer.c_mode) goto invalid_c;
367-
p.parseFuncDecl(is_public);
367+
p.parseFuncDecl(is_public, false);
368368
break;
369369
case KW_import:
370370
p.error("no imports allowed after declarations");
@@ -496,27 +496,73 @@ fn void Parser.applyAttributes(Parser* p, Decl* d, u32 count) {
496496
if (count) p.builder.applyAttributes(d, count);
497497
}
498498

499-
fn void Parser.parseFuncDecl(Parser* p, bool is_public) {
500-
p.consumeToken();
499+
fn u32 Parser.createLocalName(Parser* p, u32 name, SrcLoc loc, u32* plen) {
500+
char[128] buf;
501+
*plen = (u32)snprintf(buf, sizeof(buf), "%s__%d", p.pool.idx2str(name), loc);
502+
return p.pool.addStr(buf, true);
503+
}
504+
505+
fn Expr* Parser.parseLocalFuncExpr(Parser* p) {
506+
SrcLoc loc = p.tok.loc;
507+
FunctionDecl* f = p.parseFuncDecl(false, true);
508+
Decl* d = f.asDecl();
509+
u32 name = d.getNameIdx();
510+
u32 len;
511+
u32 local_name = p.createLocalName(name, loc, &len);
512+
d.setNameIdx(local_name);
513+
p.builder.addSymbol(local_name, d);
514+
return p.builder.actOnIdentifier(loc, local_name, len);
515+
}
516+
517+
fn Stmt* Parser.parseLocalFuncStmt(Parser* p) {
518+
SrcLoc loc = p.tok.loc;
519+
FunctionDecl* f = p.parseFuncDecl(false, true);
520+
Decl* d = f.asDecl();
521+
u32 name = d.getNameIdx();
522+
u32 len;
523+
u32 local_name = p.createLocalName(name, loc, &len);
524+
d.setNameIdx(local_name);
525+
p.builder.addSymbol(local_name, d);
526+
Expr* fexpr = p.builder.actOnIdentifier(loc, local_name, len);
527+
// TODO: use auto typing
528+
TypeRefHolder ref.init();
529+
ref.setVoid(loc);
530+
ref.addPointer();
531+
VarDecl* decl = p.builder.actOnVarDecl(name, loc, &ref, loc, fexpr, false, false);
532+
return p.builder.actOnDeclStmt(&decl, 1);
533+
}
534+
535+
fn FunctionDecl* Parser.parseFuncDecl(Parser* p, bool is_public, bool is_local) {
536+
u32 func_name = 0;
537+
SrcLoc func_loc = p.tok.loc;
538+
Ref prefix_ref;
539+
Ref* prefix = nil;
540+
541+
p.consumeToken(); // fn
501542

502543
TypeRefHolder rtype.init();
503544
// Note: dont check arrays in this phase, but in Analyser
504545
p.parseTypeSpecifier(&rtype);
505546

506-
p.expectIdentifier();
507-
u32 func_name = p.tok.name_idx;
508-
SrcLoc func_loc = p.tok.loc;
509-
p.consumeToken();
510-
511-
p.parseFunctionDecl2(func_name, func_loc, is_public, &rtype);
547+
if (p.tok.kind == Identifier || !is_local) {
548+
p.expectIdentifier();
549+
func_name = p.tok.name_idx;
550+
func_loc = p.tok.loc;
551+
p.consumeToken();
552+
}
553+
return p.parseFunctionDecl2(func_name, func_loc, is_public, is_local, &rtype);
512554
}
513555

514-
fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
515-
bool is_public, TypeRefHolder* rtype)
556+
fn FunctionDecl* Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
557+
bool is_public, bool is_local, TypeRefHolder* rtype)
516558
{
517559
Ref prefix_ref;
518560
Ref* prefix = nil;
561+
519562
if (p.tok.kind == Dot) {
563+
if (is_local) {
564+
p.error("local functions cannot be type functions");
565+
}
520566
p.consumeToken();
521567
p.expectIdentifier();
522568

@@ -529,16 +575,27 @@ fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
529575
p.consumeToken();
530576
}
531577

532-
if (!p.checkName(func_name, p.is_interface)) {
578+
if (func_name && !p.checkName(func_name, p.is_interface)) {
533579
p.errorAt(func_loc, "a function name must start with a lower case character");
534580
}
535581

536582
DeclList params.init();
537583

538584
bool is_variadic = p.parseFunctionParams(&params, is_public, true);
539585

586+
if (p.tok.kind == LSquare && is_local) {
587+
// TODO: parse capture specification
588+
while (!p.tok.done && p.tok.kind != RSquare) {
589+
p.consumeToken();
590+
}
591+
p.expectAndConsume(RSquare);
592+
}
593+
540594
FunctionDecl* f;
541595
if (p.tok.kind == KW_template) {
596+
if (is_local) {
597+
p.error("local functions cannot be template functions");
598+
}
542599
p.consumeToken();
543600
p.expectIdentifier();
544601

@@ -564,19 +621,21 @@ fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
564621
prefix,
565622
(VarDecl**)params.getDecls(),
566623
params.size(),
567-
is_variadic);
624+
is_variadic, is_local);
568625
}
569626

570627
params.free();
571628

572-
u32 num_attr = p.parseOptionalAttributes();
573-
p.applyAttributes((Decl*)f, num_attr);
629+
if (!is_local) {
630+
u32 num_attr = p.parseOptionalAttributes();
631+
p.applyAttributes((Decl*)f, num_attr);
632+
}
574633

575634
if (p.is_interface) {
576635
if (p.tok.kind == Semicolon) {
577636
// function without body (eg. i32 printf(..); ) is allowed
578637
p.consumeToken();
579-
return;
638+
return f;
580639
}
581640

582641
if (func_name != p.main_idx) {
@@ -585,9 +644,10 @@ fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
585644
}
586645
}
587646

588-
p.stmt_list_count = 0;
647+
if (!is_local) p.stmt_list_count = 0;
589648
CompoundStmt* body = p.parseCompoundStmt();
590649
p.builder.actOnFunctionBody(f, body);
650+
return f;
591651
}
592652

593653
fn bool Parser.parseFunctionParams(Parser* p, DeclList* params, bool is_public, bool accept_default) {
@@ -762,7 +822,7 @@ fn void Parser.parseVarDecl(Parser* p, bool is_public) {
762822
if (!p.tokenizer.c_mode) {
763823
p.error("function definitions require the 'fn' keyword");
764824
}
765-
p.parseFunctionDecl2(name, loc, is_public, &ref);
825+
p.parseFunctionDecl2(name, loc, is_public, false, &ref);
766826
return;
767827
}
768828

@@ -772,7 +832,7 @@ fn void Parser.parseVarDecl(Parser* p, bool is_public) {
772832
// Either a function definition or an init call
773833
if (!p.tokenizer.c_mode)
774834
p.error("global variables cannot have an init call");
775-
p.parseFunctionDecl2(name, loc, is_public, &ref);
835+
p.parseFunctionDecl2(name, loc, is_public, false, &ref);
776836
return;
777837
}
778838
p.error("global type variables not supported");

parser/c2_parser_expr.c2

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ const u8[Kind] CastExprTokenLookup = {
210210
[KW_usize] = 16,
211211
[KW_f32] = 16,
212212
[KW_f64] = 16,
213+
[KW_fn] = 17,
213214
}
214215

215216
// Note: tried lookup table, was slower..
@@ -330,6 +331,9 @@ fn Expr* Parser.parseCastExpr(Parser* p, bool /*isUnaryExpr*/, bool /*isAddrOfOp
330331
p.error("expected expression");
331332
}
332333
break;
334+
case 17: // KW_fn
335+
res = p.parseLocalFuncExpr();
336+
break;
333337
}
334338
return p.parsePostfixExprSuffix(res, couldBeTemplateCall);
335339
}

parser/c2_parser_stmt.c2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ fn Stmt* Parser.parseStmt(Parser* p) {
6363
return p.parseDeclStmt(true, true, false);
6464
case KW_while:
6565
return p.parseWhileStmt();
66+
case KW_fn:
67+
return p.parseLocalFuncStmt();
6668
default:
6769
return p.parseExprStmt();
6870
}

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)