Skip to content

Commit e3525b0

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 e6d486a commit e3525b0

File tree

7 files changed

+103
-26
lines changed

7 files changed

+103
-26
lines changed

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
@@ -734,9 +734,10 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
734734
const Ref* prefix,
735735
VarDecl** params,
736736
u32 num_params,
737-
bool is_variadic)
737+
bool is_variadic,
738+
bool is_local)
738739
{
739-
is_public |= b.is_interface;
740+
is_public |= b.is_interface & !is_local;
740741
FunctionDecl* f = FunctionDecl.create(b.context,
741742
name,
742743
loc,
@@ -747,10 +748,12 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
747748
params,
748749
num_params,
749750
is_variadic,
750-
Global);
751+
is_local ? DefKind.Local : DefKind.Global);
751752
b.ast.addFunc(f);
752-
if (!prefix) b.addSymbol(name, f.asDecl());
753-
if (b.is_interface) f.asDecl().setExternal();
753+
if (!is_local) {
754+
if (!prefix) b.addSymbol(name, f.asDecl());
755+
if (b.is_interface) f.asDecl().setExternal();
756+
}
754757
return f;
755758
}
756759

@@ -1079,7 +1082,7 @@ public fn void Builder.insertImplicitCast(Builder* b,
10791082
*e_ptr = ic;
10801083
}
10811084

1082-
fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
1085+
public fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
10831086
Decl* old = b.mod.findSymbol(name_idx);
10841087
if (old) {
10851088
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
@@ -377,7 +377,7 @@ fn void Parser.parseTopLevel(Parser* p) {
377377
break;
378378
case KW_fn:
379379
if (p.tokenizer.c_mode) goto invalid_c;
380-
p.parseFuncDecl(is_public);
380+
p.parseFuncDecl(is_public, false);
381381
break;
382382
case KW_import:
383383
p.error("no imports allowed after declarations");
@@ -509,27 +509,73 @@ fn void Parser.applyAttributes(Parser* p, Decl* d, u32 count) {
509509
if (count) p.builder.applyAttributes(d, count);
510510
}
511511

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

515556
TypeRefHolder rtype.init();
516557
// Note: dont check arrays in this phase, but in Analyser
517558
p.parseTypeSpecifier(&rtype);
518559

519-
p.expectIdentifier();
520-
u32 func_name = p.tok.name_idx;
521-
SrcLoc func_loc = p.tok.loc;
522-
p.consumeToken();
523-
524-
p.parseFunctionDecl2(func_name, func_loc, is_public, &rtype);
560+
if (p.tok.kind == Identifier || !is_local) {
561+
p.expectIdentifier();
562+
func_name = p.tok.name_idx;
563+
func_loc = p.tok.loc;
564+
p.consumeToken();
565+
}
566+
return p.parseFunctionDecl2(func_name, func_loc, is_public, is_local, &rtype);
525567
}
526568

527-
fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
528-
bool is_public, TypeRefHolder* rtype)
569+
fn FunctionDecl* Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
570+
bool is_public, bool is_local, TypeRefHolder* rtype)
529571
{
530572
Ref prefix_ref;
531573
Ref* prefix = nil;
574+
532575
if (p.tok.kind == Dot) {
576+
if (is_local) {
577+
p.error("local functions cannot be type functions");
578+
}
533579
p.consumeToken();
534580
p.expectIdentifier();
535581

@@ -542,16 +588,27 @@ fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
542588
p.consumeToken();
543589
}
544590

545-
if (!p.checkName(func_name, p.is_interface)) {
591+
if (func_name && !p.checkName(func_name, p.is_interface)) {
546592
p.errorAt(func_loc, "a function name must start with a lower case character");
547593
}
548594

549595
DeclList params.init();
550596

551597
bool is_variadic = p.parseFunctionParams(&params, is_public, true);
552598

599+
if (p.tok.kind == LSquare && is_local) {
600+
// TODO: parse capture specification
601+
while (!p.tok.done && p.tok.kind != RSquare) {
602+
p.consumeToken();
603+
}
604+
p.expectAndConsume(RSquare);
605+
}
606+
553607
FunctionDecl* f;
554608
if (p.tok.kind == KW_template) {
609+
if (is_local) {
610+
p.error("local functions cannot be template functions");
611+
}
555612
p.consumeToken();
556613
p.expectIdentifier();
557614

@@ -577,19 +634,21 @@ fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
577634
prefix,
578635
(VarDecl**)params.getDecls(),
579636
params.size(),
580-
is_variadic);
637+
is_variadic, is_local);
581638
}
582639

583640
params.free();
584641

585-
u32 num_attr = p.parseOptionalAttributes();
586-
p.applyAttributes((Decl*)f, num_attr);
642+
if (!is_local) {
643+
u32 num_attr = p.parseOptionalAttributes();
644+
p.applyAttributes((Decl*)f, num_attr);
645+
}
587646

588647
if (p.is_interface) {
589648
if (p.tok.kind == Semicolon) {
590649
// function without body (eg. i32 printf(..); ) is allowed
591650
p.consumeToken();
592-
return;
651+
return f;
593652
}
594653

595654
if (func_name != p.main_idx) {
@@ -598,9 +657,10 @@ fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
598657
}
599658
}
600659

601-
p.stmt_list_count = 0;
660+
if (!is_local) p.stmt_list_count = 0;
602661
CompoundStmt* body = p.parseCompoundStmt();
603662
p.builder.actOnFunctionBody(f, body);
663+
return f;
604664
}
605665

606666
fn bool Parser.parseFunctionParams(Parser* p, DeclList* params, bool is_public, bool accept_default) {
@@ -775,7 +835,7 @@ fn void Parser.parseVarDecl(Parser* p, bool is_public) {
775835
if (!p.tokenizer.c_mode) {
776836
p.error("function definitions require the 'fn' keyword");
777837
}
778-
p.parseFunctionDecl2(name, loc, is_public, &ref);
838+
p.parseFunctionDecl2(name, loc, is_public, false, &ref);
779839
return;
780840
}
781841

@@ -785,7 +845,7 @@ fn void Parser.parseVarDecl(Parser* p, bool is_public) {
785845
// Either a function definition or an init call
786846
if (!p.tokenizer.c_mode)
787847
p.error("global variables cannot have an init call");
788-
p.parseFunctionDecl2(name, loc, is_public, &ref);
848+
p.parseFunctionDecl2(name, loc, is_public, false, &ref);
789849
return;
790850
}
791851
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)