diff --git a/ast/decl.c2 b/ast/decl.c2 index 7d3163ad..71a35c3a 100644 --- a/ast/decl.c2 +++ b/ast/decl.c2 @@ -204,6 +204,10 @@ public fn u32 Decl.getNameIdx(const Decl* d) { return d.name_idx; } +public fn void Decl.setNameIdx(Decl* d, u32 name_idx) { + d.name_idx = name_idx; +} + // convenience function public fn const char* Decl.getModuleName(const Decl* d) { const AST* a = d.getAST(); diff --git a/ast/function_decl.c2 b/ast/function_decl.c2 index 027a1294..7a7fb4ad 100644 --- a/ast/function_decl.c2 +++ b/ast/function_decl.c2 @@ -41,6 +41,7 @@ public type DefKind enum u8 { Type, // type A fn .. StructMember, // (in struct) fn void(..) member; Param, // fn void a( fn void(..) cb) + Local, // local function Template, // .. } @@ -49,6 +50,7 @@ const char*[] defKind_names = { "type", "struct-member", "template", + "local", "param", } @@ -268,6 +270,8 @@ public fn bool FunctionDecl.canBeNil(const FunctionDecl* d) { return true; case Template: break; + case Local: + return false; } return d.hasAttrWeak(); } diff --git a/common/ast_builder.c2 b/common/ast_builder.c2 index 827b6b40..45f0b9ca 100644 --- a/common/ast_builder.c2 +++ b/common/ast_builder.c2 @@ -731,9 +731,10 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b, const Ref* prefix, VarDecl** params, u32 num_params, - bool is_variadic) + bool is_variadic, + bool is_local) { - is_public |= b.is_interface; + is_public |= b.is_interface & !is_local; FunctionDecl* f = FunctionDecl.create(b.context, name, loc, @@ -744,10 +745,12 @@ public fn FunctionDecl* Builder.actOnFunctionDecl(Builder* b, params, num_params, is_variadic, - DefKind.Global); + is_local ? DefKind.Local : DefKind.Global); b.ast.addFunc(f); - if (!prefix) b.addSymbol(name, f.asDecl()); - if (b.is_interface) f.asDecl().setExternal(); + if (!is_local) { + if (!prefix) b.addSymbol(name, f.asDecl()); + if (b.is_interface) f.asDecl().setExternal(); + } return f; } @@ -1075,7 +1078,7 @@ public fn void Builder.insertImplicitCast(Builder* b, *e_ptr = ic; } -fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) { +public fn void Builder.addSymbol(Builder* b, u32 name_idx, Decl* d) { Decl* old = b.mod.findSymbol(name_idx); if (old) { b.diags.error(d.getLoc(), "redefinition of '%s'", idx2name(name_idx)); diff --git a/parser/c2_parser.c2 b/parser/c2_parser.c2 index 3082a6a1..ab6d116f 100644 --- a/parser/c2_parser.c2 +++ b/parser/c2_parser.c2 @@ -332,7 +332,7 @@ fn void Parser.parseTopLevel(Parser* p) { p.error("assert can only be used inside a function"); break; case KW_fn: - p.parseFuncDecl(is_public); + p.parseFuncDecl(is_public, false); break; case KW_import: p.error("no imports allowed after declarations"); @@ -453,43 +453,97 @@ fn void Parser.parseParamOptionalAttributes(Parser* p, VarDecl* d) { p.expectAndConsume(Kind.RParen); } -fn void Parser.parseFuncDecl(Parser* p, bool is_public) { +fn u32 Parser.createLocalName(Parser* p, u32 name, SrcLoc loc) { + char[128] buf; + snprintf(buf, sizeof(buf), "%s__%d", p.pool.idx2str(name), loc); + return p.pool.addStr(buf, true); +} + +fn Expr* Parser.parseLocalFuncExpr(Parser* p) { + SrcLoc loc = p.tok.loc; + FunctionDecl* f = p.parseFuncDecl(false, true); + Decl* d = f.asDecl(); + u32 name = d.getNameIdx(); + u32 local_name = p.createLocalName(name, loc); + d.setNameIdx(local_name); + p.builder.addSymbol(local_name, d); + Expr* fexpr = p.builder.actOnIdentifier(loc, local_name).asExpr(); + return fexpr; +} + +fn Stmt* Parser.parseLocalFuncStmt(Parser* p) { + SrcLoc loc = p.tok.loc; + FunctionDecl* f = p.parseFuncDecl(false, true); + Decl* d = f.asDecl(); + u32 name = d.getNameIdx(); + u32 local_name = p.createLocalName(name, loc); + d.setNameIdx(local_name); + p.builder.addSymbol(local_name, d); + Expr* fexpr = p.builder.actOnIdentifier(loc, local_name).asExpr(); + // TODO: use auto typing + TypeRefHolder ref.init(); + ref.setBuiltin(Void, loc); + ref.addPointer(); + VarDecl* decl = p.builder.actOnVarDecl(name, loc, &ref, loc, fexpr, false, false); + return p.builder.actOnDeclStmt(&decl, 1); +} + +fn FunctionDecl* Parser.parseFuncDecl(Parser* p, bool is_public, bool is_local) { + u32 func_name = 0; + SrcLoc func_loc = p.tok.loc; + Ref prefix_ref; + Ref* prefix = nil; + p.consumeToken(); TypeRefHolder rtype.init(); // Note: dont check arrays in this phase, but in Analyser p.parseTypeSpecifier(&rtype); - p.expectIdentifier(); - u32 func_name = p.tok.name_idx; - SrcLoc func_loc = p.tok.loc; - p.consumeToken(); - - Ref prefix_ref; - Ref* prefix = nil; - if (p.tok.kind == Kind.Dot) { - p.consumeToken(); + if (p.tok.kind == Kind.Identifier || !is_local) { p.expectIdentifier(); - - prefix_ref.loc = func_loc; - prefix_ref.name_idx = func_name; - prefix_ref.decl = nil; - prefix = &prefix_ref; func_name = p.tok.name_idx; func_loc = p.tok.loc; p.consumeToken(); - } - if (!p.checkName(func_name, p.is_interface)) { - p.errorAt(func_loc, "a function name must start with a lower case character"); + if (p.tok.kind == Kind.Dot) { + if (is_local) { + p.error("local functions cannot be type functions"); + } + p.consumeToken(); + p.expectIdentifier(); + + prefix_ref.loc = func_loc; + prefix_ref.name_idx = func_name; + prefix_ref.decl = nil; + prefix = &prefix_ref; + func_name = p.tok.name_idx; + func_loc = p.tok.loc; + p.consumeToken(); + } + + if (!p.checkName(func_name, p.is_interface)) { + p.errorAt(func_loc, "a function name must start with a lower case character"); + } } DeclList params.init(); bool is_variadic = p.parseFunctionParams(¶ms, is_public, true); + if (p.tok.kind == Kind.LSquare && is_local) { + // TODO: parse capture specification + while (!p.tok.done && p.tok.kind != Kind.RSquare) { + p.consumeToken(); + } + p.expectAndConsume(Kind.RSquare); + } + FunctionDecl* f; if (p.tok.kind == Kind.KW_template) { + if (is_local) { + p.error("local functions cannot be template functions"); + } p.consumeToken(); p.expectIdentifier(); @@ -515,28 +569,31 @@ fn void Parser.parseFuncDecl(Parser* p, bool is_public) { prefix, (VarDecl**)params.getDecls(), params.size(), - is_variadic); + is_variadic, is_local); } params.free(); - p.parseOptionalAttributes(); - p.builder.applyAttributes((Decl*)f); + if (!is_local) { + p.parseOptionalAttributes(); + p.builder.applyAttributes((Decl*)f); + } if (p.is_interface) { if (p.tok.kind == Kind.Semicolon) { // function without body (eg. i32 printf(..); ) is allowed p.consumeToken(); - return; + return f; } // mark functions with bodies in interface files as 'inline' f.setAttrInline(); } - p.stmt_list_count = 0; + if (!is_local) p.stmt_list_count = 0; CompoundStmt* body = p.parseCompoundStmt(); p.builder.actOnFunctionBody(f, body); + return f; } fn bool Parser.parseFunctionParams(Parser* p, DeclList* params, bool is_public, bool accept_default) { diff --git a/parser/c2_parser_expr.c2 b/parser/c2_parser_expr.c2 index 96c676f9..5171f673 100644 --- a/parser/c2_parser_expr.c2 +++ b/parser/c2_parser_expr.c2 @@ -212,6 +212,7 @@ const u8[128] CastExprTokenLookup = { [Kind.KW_usize] = 16, [Kind.KW_f32] = 16, [Kind.KW_f64] = 16, + [Kind.KW_fn] = 17, } // Note: tried lookup table, was slower.. @@ -337,6 +338,9 @@ fn Expr* Parser.parseCastExpr(Parser* p, bool /*isUnaryExpr*/, bool /*isAddrOfOp p.error("expected expression"); } break; + case 17: // KW_fn + res = p.parseLocalFuncExpr(); + break; } return p.parsePostfixExprSuffix(res, couldBeTemplateCall); } diff --git a/parser/c2_parser_stmt.c2 b/parser/c2_parser_stmt.c2 index 5816d907..68d364cb 100644 --- a/parser/c2_parser_stmt.c2 +++ b/parser/c2_parser_stmt.c2 @@ -79,6 +79,8 @@ fn Stmt* Parser.parseStmt(Parser* p) { return p.parseDeclStmt(true, true, false); case KW_while: return p.parseWhileStmt(); + case KW_fn: + return p.parseLocalFuncStmt(); default: return p.parseExprStmt(); } diff --git a/test/parser/invalid_func.c2 b/test/parser/invalid_func.c2 index 5eba96f7..294869cc 100644 --- a/test/parser/invalid_func.c2 +++ b/test/parser/invalid_func.c2 @@ -1,7 +1,7 @@ module test; public fn i32 main() { - fn void() v = foo; // @error{expected expression} + fn void() v = foo; // @error{expected '{'} return 0; }