Skip to content

Commit 7d6068f

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 1d863b3 commit 7d6068f

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
@@ -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
@@ -365,7 +365,7 @@ fn void Parser.parseTopLevel(Parser* p) {
365365
break;
366366
case KW_fn:
367367
if (p.tokenizer.c_mode) goto invalid_c;
368-
p.parseFuncDecl(is_public);
368+
p.parseFuncDecl(is_public, false);
369369
break;
370370
case KW_import:
371371
p.error("no imports allowed after declarations");
@@ -497,27 +497,73 @@ fn void Parser.applyAttributes(Parser* p, Decl* d, u32 count) {
497497
if (count) p.builder.applyAttributes(d, count);
498498
}
499499

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

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

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

515-
fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
516-
bool is_public, TypeRefHolder* rtype)
557+
fn FunctionDecl* Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
558+
bool is_public, bool is_local, TypeRefHolder* rtype)
517559
{
518560
Ref prefix_ref;
519561
Ref* prefix = nil;
562+
520563
if (p.tok.kind == Dot) {
564+
if (is_local) {
565+
p.error("local functions cannot be type functions");
566+
}
521567
p.consumeToken();
522568
p.expectIdentifier();
523569

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

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

537583
DeclList params.init();
538584

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

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

@@ -565,19 +622,21 @@ fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
565622
prefix,
566623
(VarDecl**)params.getDecls(),
567624
params.size(),
568-
is_variadic);
625+
is_variadic, is_local);
569626
}
570627

571628
params.free();
572629

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

576635
if (p.is_interface) {
577636
if (p.tok.kind == Semicolon) {
578637
// function without body (eg. i32 printf(..); ) is allowed
579638
p.consumeToken();
580-
return;
639+
return f;
581640
}
582641

583642
if (func_name != p.main_idx) {
@@ -586,9 +645,10 @@ fn void Parser.parseFunctionDecl2(Parser* p, u32 func_name, SrcLoc func_loc,
586645
}
587646
}
588647

589-
p.stmt_list_count = 0;
648+
if (!is_local) p.stmt_list_count = 0;
590649
CompoundStmt* body = p.parseCompoundStmt();
591650
p.builder.actOnFunctionBody(f, body);
651+
return f;
592652
}
593653

594654
fn bool Parser.parseFunctionParams(Parser* p, DeclList* params, bool is_public, bool accept_default) {
@@ -763,7 +823,7 @@ fn void Parser.parseVarDecl(Parser* p, bool is_public) {
763823
if (!p.tokenizer.c_mode) {
764824
p.error("function definitions require the 'fn' keyword");
765825
}
766-
p.parseFunctionDecl2(name, loc, is_public, &ref);
826+
p.parseFunctionDecl2(name, loc, is_public, false, &ref);
767827
return;
768828
}
769829

@@ -773,7 +833,7 @@ fn void Parser.parseVarDecl(Parser* p, bool is_public) {
773833
// Either a function definition or an init call
774834
if (!p.tokenizer.c_mode)
775835
p.error("global variables cannot have an init call");
776-
p.parseFunctionDecl2(name, loc, is_public, &ref);
836+
p.parseFunctionDecl2(name, loc, is_public, false, &ref);
777837
return;
778838
}
779839
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)