Skip to content

Commit d75a858

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 9deb2a9 commit d75a858

File tree

7 files changed

+109
-33
lines changed

7 files changed

+109
-33
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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ static_assert(elemsof(CallKind), elemsof(callKind_names));
3838
type FunctionDeclBits struct {
3939
u32 : NumDeclBits;
4040
u32 is_variadic : 1;
41+
u32 is_local : 1;
4142
u32 has_prefix : 1; // is a (static) type-function
4243
u32 call_kind : 2; // CallKind
4344
u32 has_return : 1; // if it returns something, set during analysis
@@ -90,6 +91,7 @@ public fn FunctionDecl* FunctionDecl.create(ast_context.Context* c,
9091
VarDecl** params,
9192
u32 num_params,
9293
bool is_variadic,
94+
bool is_local,
9395
bool is_type)
9496
{
9597
u32 size = sizeof(FunctionDecl) + num_params * sizeof(VarDecl*) + rtype.getExtraSize();
@@ -100,6 +102,7 @@ public fn FunctionDecl* FunctionDecl.create(ast_context.Context* c,
100102
d.base.functionDeclBits.is_variadic = is_variadic;
101103
d.base.functionDeclBits.call_kind = prefix ? CallKind.StaticTypeFunc : CallKind.Normal;
102104
d.base.functionDeclBits.is_type = is_type;
105+
d.base.functionDeclBits.is_local = is_local;
103106
d.body = nil;
104107
d.rt = QualType_Invalid;
105108
d.num_params = (u8)num_params;

parser/ast_builder.c2

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ public fn Decl* Builder.actOnFunctionTypeDecl(Builder* b,
182182
params,
183183
num_params,
184184
is_variadic,
185-
true);
185+
false, // is_local
186+
true); // is_type
186187

187188
FunctionTypeDecl* d = FunctionTypeDecl.create(b.context, fd);
188189
b.ast.addTypeDecl(d.asDecl());
@@ -209,7 +210,8 @@ public fn Decl* Builder.actOnStructMemberType(Builder* b,
209210
params,
210211
num_params,
211212
is_variadic,
212-
false);
213+
false, // is_local
214+
false); // is_type
213215
fd.setMemberType();
214216
return (Decl*)fd;
215217
}
@@ -729,9 +731,10 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
729731
const Ref* prefix,
730732
VarDecl** params,
731733
u32 num_params,
732-
bool is_variadic)
734+
bool is_variadic,
735+
bool is_local)
733736
{
734-
is_public |= b.is_interface;
737+
is_public |= b.is_interface & !is_local;
735738
FunctionDecl* f = FunctionDecl.create(b.context,
736739
name,
737740
loc,
@@ -742,10 +745,13 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b,
742745
params,
743746
num_params,
744747
is_variadic,
745-
false);
748+
is_local,
749+
false); // is_type
746750
b.ast.addFunc(f);
747-
if (!prefix) b.addSymbol(name, f.asDecl());
748-
if (b.is_interface) f.asDecl().setExternal();
751+
if (!is_local) {
752+
if (!prefix) b.addSymbol(name, f.asDecl());
753+
if (b.is_interface) f.asDecl().setExternal();
754+
}
749755
return f;
750756
}
751757

@@ -1074,7 +1080,7 @@ public fn void Builder.insertImplicitCast(Builder* b,
10741080
*e_ptr = ic;
10751081
}
10761082

1077-
fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
1083+
public fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) {
10781084
Decl* old = b.mod.findSymbol(name_idx);
10791085
if (old) {
10801086
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
@@ -332,7 +332,7 @@ fn void Parser.parseTopLevel(Parser* p) {
332332
p.error("assert can only be used inside a function");
333333
break;
334334
case KW_fn:
335-
p.parseFuncDecl(is_public);
335+
p.parseFuncDecl(is_public, false);
336336
break;
337337
case KW_import:
338338
p.error("no imports allowed after declarations");
@@ -453,43 +453,97 @@ fn void Parser.parseParamOptionalAttributes(Parser* p, VarDecl* d) {
453453
p.expectAndConsume(Kind.RParen);
454454
}
455455

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

459499
TypeRefHolder rtype.init();
460500
// Note: dont check arrays in this phase, but in Analyser
461501
p.parseTypeSpecifier(&rtype);
462502

463-
p.expectIdentifier();
464-
u32 func_name = p.tok.name_idx;
465-
SrcLoc func_loc = p.tok.loc;
466-
p.consumeToken();
467-
468-
Ref prefix_ref;
469-
Ref* prefix = nil;
470-
if (p.tok.kind == Kind.Dot) {
471-
p.consumeToken();
503+
if (p.tok.kind == Kind.Identifier || !is_local) {
472504
p.expectIdentifier();
473-
474-
prefix_ref.loc = func_loc;
475-
prefix_ref.name_idx = func_name;
476-
prefix_ref.decl = nil;
477-
prefix = &prefix_ref;
478505
func_name = p.tok.name_idx;
479506
func_loc = p.tok.loc;
480507
p.consumeToken();
481-
}
482508

483-
if (!p.checkName(func_name, p.is_interface)) {
484-
p.errorAt(func_loc, "a function name must start with a lower case character");
509+
if (p.tok.kind == Kind.Dot) {
510+
if (is_local) {
511+
p.error("local functions cannot be type functions");
512+
}
513+
p.consumeToken();
514+
p.expectIdentifier();
515+
516+
prefix_ref.loc = func_loc;
517+
prefix_ref.name_idx = func_name;
518+
prefix_ref.decl = nil;
519+
prefix = &prefix_ref;
520+
func_name = p.tok.name_idx;
521+
func_loc = p.tok.loc;
522+
p.consumeToken();
523+
}
524+
525+
if (!p.checkName(func_name, p.is_interface)) {
526+
p.errorAt(func_loc, "a function name must start with a lower case character");
527+
}
485528
}
486529

487530
DeclList params.init();
488531

489532
bool is_variadic = p.parseFunctionParams(&params, is_public, true);
490533

534+
if (p.tok.kind == Kind.LSquare && is_local) {
535+
// TODO: parse capture specification
536+
while (!p.tok.done && p.tok.kind != Kind.RSquare) {
537+
p.consumeToken();
538+
}
539+
p.expectAndConsume(Kind.RSquare);
540+
}
541+
491542
FunctionDecl* f;
492543
if (p.tok.kind == Kind.KW_template) {
544+
if (is_local) {
545+
p.error("local functions cannot be template functions");
546+
}
493547
p.consumeToken();
494548
p.expectIdentifier();
495549

@@ -515,28 +569,31 @@ fn void Parser.parseFuncDecl(Parser* p, bool is_public) {
515569
prefix,
516570
(VarDecl**)params.getDecls(),
517571
params.size(),
518-
is_variadic);
572+
is_variadic, is_local);
519573
}
520574

521575
params.free();
522576

523-
p.parseOptionalAttributes();
524-
p.builder.applyAttributes((Decl*)f);
577+
if (!is_local) {
578+
p.parseOptionalAttributes();
579+
p.builder.applyAttributes((Decl*)f);
580+
}
525581

526582
if (p.is_interface) {
527583
if (p.tok.kind == Kind.Semicolon) {
528584
// function without body (eg. i32 printf(..); ) is allowed
529585
p.consumeToken();
530-
return;
586+
return f;
531587
}
532588

533589
// mark functions with bodies in interface files as 'inline'
534590
f.setAttrInline();
535591
}
536592

537-
p.stmt_list_count = 0;
593+
if (!is_local) p.stmt_list_count = 0;
538594
CompoundStmt* body = p.parseCompoundStmt();
539595
p.builder.actOnFunctionBody(f, body);
596+
return f;
540597
}
541598

542599
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..
@@ -324,6 +325,9 @@ fn Expr* Parser.parseCastExpr(Parser* p, bool /*isUnaryExpr*/, bool /*isAddrOfOp
324325
p.error("expected expression");
325326
}
326327
break;
328+
case 17: // KW_fn
329+
res = p.parseLocalFuncExpr();
330+
break;
327331
}
328332
return p.parsePostfixExprSuffix(res, couldBeTemplateCall);
329333
}

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)