From 5afe473a86c2fe32b6d7c472b71f88e70ac671c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Jan 2016 13:08:21 -0700 Subject: [PATCH] different extern syntax and simplify parsing top level decls --- doc/langref.md | 26 +- doc/vim/syntax/zig.vim | 2 +- example/hello_world/hello_libc.zig | 6 +- src/all_types.hpp | 16 +- src/analyze.cpp | 41 +-- src/codegen.cpp | 20 +- src/parser.cpp | 395 ++++++++++------------------- test/run_tests.cpp | 23 +- 8 files changed, 175 insertions(+), 354 deletions(-) diff --git a/doc/langref.md b/doc/langref.md index 4dae123f1f..20d07bc32a 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -5,31 +5,33 @@ ``` Root : many(TopLevelDecl) "EOF" -TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl +TopLevelDecl : many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl) -ErrorValueDecl : option(FnVisibleMod) "error" "Symbol" +CImportDecl : "c_import" Block -VariableDeclaration : option(FnVisibleMod) ("var" | "const") "Symbol" option(":" PrefixOpExpression) "=" Expression +ErrorValueDecl : "error" "Symbol" ";" -ContainerDecl : many(Directive) option(FnVisibleMod) ("struct" | "enum") "Symbol" "{" many(StructMember) "}" +GlobalVarDecl : VariableDeclaration ";" -StructMember: StructField | FnDecl +VariableDeclaration : ("var" | "const") "Symbol" option(":" PrefixOpExpression) "=" Expression + +ContainerDecl : ("struct" | "enum") "Symbol" "{" many(StructMember) "}" + +StructMember: many(Directive) option(VisibleMod) (StructField | FnDef) StructField : "Symbol" option(":" Expression) ",") -Import : many(Directive) "import" "String" ";" +Import : "import" "String" ";" -RootExportDecl : many(Directive) "export" "Symbol" "String" ";" +RootExportDecl : "export" "Symbol" "String" ";" -ExternBlock : many(Directive) "extern" "{" many(FnDecl) "}" +ExternDecl : "extern" FnProto ";" -FnProto : many(Directive) option(FnVisibleMod) "fn" "Symbol" ParamDeclList option("->" PrefixOpExpression) +FnProto : "fn" "Symbol" ParamDeclList option("->" PrefixOpExpression) Directive : "#" "Symbol" "(" "String" ")" -FnVisibleMod : "pub" | "export" - -FnDecl : FnProto ";" +VisibleMod : "pub" | "export" FnDef : FnProto Block diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index c534805982..28b347ba9c 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -14,7 +14,7 @@ syn keyword zigConditional if else switch syn keyword zigRepeat while for syn keyword zigConstant null undefined -syn keyword zigKeyword fn import +syn keyword zigKeyword fn import c_import syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 void unreachable type error syn keyword zigType c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index 4a2af7cf40..4dc14398ba 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -1,9 +1,7 @@ +#link("c") export executable "hello"; -#link("c") -extern { - fn printf(__format: &const u8, ...) -> c_int; -} +extern fn printf(__format: &const u8, ...) -> c_int; export fn main(argc: c_int, argv: &&u8) -> c_int { printf(c"Hello, world!\n"); diff --git a/src/all_types.hpp b/src/all_types.hpp index db0c6da156..0991faa2cb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -121,7 +121,6 @@ enum NodeType { NodeTypeFnDecl, NodeTypeParamDecl, NodeTypeBlock, - NodeTypeExternBlock, NodeTypeDirective, NodeTypeReturnExpr, NodeTypeVariableDeclaration, @@ -178,11 +177,10 @@ struct AstNodeFnProto { ZigList params; AstNode *return_type; bool is_var_args; + bool is_extern; // populated by semantic analyzer: - // the extern block this fn proto is inside. can be null. - AstNode *extern_node; // the struct decl node this fn proto is inside. can be null. AstNode *struct_node; // the function definition this fn proto is inside. can be null. @@ -247,6 +245,7 @@ struct AstNodeVariableDeclaration { // one or both of type and expr will be non null AstNode *type; AstNode *expr; + ZigList *directives; // populated by semantic analyzer TopLevelDecl top_level_decl; @@ -255,8 +254,9 @@ struct AstNodeVariableDeclaration { }; struct AstNodeErrorValueDecl { - VisibMod visib_mod; Buf name; + VisibMod visib_mod; + ZigList *directives; // populated by semantic analyzer TopLevelDecl top_level_decl; @@ -378,11 +378,6 @@ struct AstNodeFieldAccessExpr { StructValExprCodeGen resolved_struct_val_expr; // for enum values }; -struct AstNodeExternBlock { - ZigList *directives; - ZigList fn_decls; -}; - struct AstNodeDirective { Buf name; Buf param; @@ -418,6 +413,7 @@ struct AstNodePrefixOpExpr { struct AstNodeUse { Buf path; ZigList *directives; + VisibMod visib_mod; // populated by semantic analyzer ImportTableEntry *import; @@ -559,6 +555,7 @@ struct AstNodeStructField { Buf name; AstNode *type; ZigList *directives; + VisibMod visib_mod; }; struct AstNodeStringLiteral { @@ -697,7 +694,6 @@ struct AstNode { AstNodeErrorValueDecl error_value_decl; AstNodeBinOpExpr bin_op_expr; AstNodeUnwrapErrorExpr unwrap_err_expr; - AstNodeExternBlock extern_block; AstNodeDirective directive; AstNodePrefixOpExpr prefix_op_expr; AstNodeFnCallExpr fn_call_expr; diff --git a/src/analyze.cpp b/src/analyze.cpp index bac27ffcc4..665fb3185f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -45,7 +45,6 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeBlock: - case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeReturnExpr: case NodeTypeVariableDeclaration: @@ -897,8 +896,8 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *proto_node) { AstNode *fn_def_node = proto_node->data.fn_proto.fn_def_node; - AstNode *extern_node = proto_node->data.fn_proto.extern_node; AstNode *struct_node = proto_node->data.fn_proto.struct_node; + bool is_extern = proto_node->data.fn_proto.is_extern; TypeTableEntry *struct_type; if (struct_node) { assert(struct_node->type == NodeTypeStructDecl); @@ -914,7 +913,7 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, auto entry = fn_table->maybe_get(proto_name); bool skip = false; bool is_internal = (proto_node->data.fn_proto.visib_mod != VisibModExport); - bool is_c_compat = !is_internal || extern_node; + bool is_c_compat = !is_internal || is_extern; bool is_pub = (proto_node->data.fn_proto.visib_mod != VisibModPrivate); if (entry) { add_node_error(g, proto_node, @@ -922,7 +921,7 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, proto_node->data.fn_proto.skip = true; skip = true; } - if (!extern_node && proto_node->data.fn_proto.is_var_args) { + if (!is_extern && proto_node->data.fn_proto.is_var_args) { add_node_error(g, proto_node, buf_sprintf("variadic arguments only allowed in extern functions")); } @@ -935,7 +934,7 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, fn_table_entry->proto_node = proto_node; fn_table_entry->fn_def_node = fn_def_node; fn_table_entry->internal_linkage = !is_c_compat; - fn_table_entry->is_extern = extern_node; + fn_table_entry->is_extern = is_extern; fn_table_entry->label_table.init(8); fn_table_entry->member_of_struct = struct_type; @@ -950,7 +949,7 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, g->fn_protos.append(fn_table_entry); - if (!extern_node) { + if (!is_extern) { g->fn_defs.append(fn_table_entry); } @@ -1021,19 +1020,6 @@ static void resolve_error_value_decl(CodeGen *g, ImportTableEntry *import, AstNo static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { switch (node->type) { - case NodeTypeExternBlock: - for (int i = 0; i < node->data.extern_block.directives->length; i += 1) { - AstNode *directive_node = node->data.extern_block.directives->at(i); - Buf *name = &directive_node->data.directive.name; - Buf *param = &directive_node->data.directive.param; - if (buf_eql_str(name, "link")) { - g->link_table.put(param, true); - } else { - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); - } - } - break; case NodeTypeFnProto: preview_fn_proto(g, import, node); break; @@ -4051,7 +4037,6 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeParamDecl: case NodeTypeRoot: case NodeTypeRootExportDecl: - case NodeTypeExternBlock: case NodeTypeFnDef: case NodeTypeUse: case NodeTypeLabel: @@ -4155,15 +4140,14 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode break; } case NodeTypeRootExportDecl: - case NodeTypeExternBlock: case NodeTypeUse: case NodeTypeVariableDeclaration: case NodeTypeErrorValueDecl: + case NodeTypeFnProto: // already took care of these break; case NodeTypeDirective: case NodeTypeParamDecl: - case NodeTypeFnProto: case NodeTypeFnDecl: case NodeTypeReturnExpr: case NodeTypeRoot: @@ -4350,7 +4334,6 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode break; case NodeTypeVariableDeclaration: case NodeTypeFnProto: - case NodeTypeExternBlock: case NodeTypeRootExportDecl: case NodeTypeFnDef: case NodeTypeRoot: @@ -4453,16 +4436,6 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast break; } - case NodeTypeExternBlock: - for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) { - AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i); - assert(fn_decl->type == NodeTypeFnDecl); - AstNode *fn_proto = fn_decl->data.fn_decl.fn_proto; - fn_proto->data.fn_proto.extern_node = node; - detect_top_level_decl_deps(g, import, fn_proto); - } - resolve_top_level_decl(g, import, node); - break; case NodeTypeFnDef: node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = node; detect_top_level_decl_deps(g, import, node->data.fn_def.fn_proto); @@ -4786,7 +4759,6 @@ Expr *get_resolved_expr(AstNode *node) { case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeUse: case NodeTypeStructDecl: @@ -4832,7 +4804,6 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeBlock: - case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeStringLiteral: case NodeTypeCharLiteral: diff --git a/src/codegen.cpp b/src/codegen.cpp index cf76c6ce15..0c91f496db 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2249,7 +2249,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeFnDef: case NodeTypeFnDecl: case NodeTypeParamDecl: - case NodeTypeExternBlock: case NodeTypeDirective: case NodeTypeUse: case NodeTypeStructDecl: @@ -3001,18 +3000,6 @@ static void init(CodeGen *g, Buf *source_path) { } -static bool directives_contains_link_libc(ZigList *directives) { - for (int i = 0; i < directives->length; i += 1) { - AstNode *directive_node = directives->at(i); - if (buf_eql_str(&directive_node->data.directive.name, "link") && - buf_eql_str(&directive_node->data.directive.param, "c")) - { - return true; - } - } - return false; -} - static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) { char *dot1 = strstr(buf_ptr(buf), "."); if (!dot1) @@ -3114,6 +3101,11 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, Buf *param = &directive_node->data.directive.param; if (buf_eql_str(name, "version")) { set_root_export_version(g, param, directive_node); + } else if (buf_eql_str(name, "link")) { + g->link_table.put(param, true); + if (buf_eql_str(param, "c")) { + g->link_libc = true; + } } else { add_node_error(g, directive_node, buf_sprintf("invalid directive: '%s'", buf_ptr(name))); @@ -3204,8 +3196,6 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, if (buf_eql_str(proto_name, "main") && !is_private) { g->have_exported_main = true; } - } else if (top_level_decl->type == NodeTypeExternBlock) { - g->link_libc = directives_contains_link_libc(top_level_decl->data.extern_block.directives); } } diff --git a/src/parser.cpp b/src/parser.cpp index 3c0812fbe0..2b6b030d06 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -105,8 +105,6 @@ const char *node_type_str(NodeType node_type) { return "ArrayAccessExpr"; case NodeTypeSliceExpr: return "SliceExpr"; - case NodeTypeExternBlock: - return "ExternBlock"; case NodeTypeDirective: return "Directive"; case NodeTypeReturnExpr: @@ -258,15 +256,6 @@ void ast_print(AstNode *node, int indent) { fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf)); break; } - case NodeTypeExternBlock: - { - fprintf(stderr, "%s\n", node_type_str(node->type)); - for (int i = 0; i < node->data.extern_block.fn_decls.length; i += 1) { - AstNode *child = node->data.extern_block.fn_decls.at(i); - ast_print(child, indent + 2); - } - break; - } case NodeTypeFnDecl: fprintf(stderr, "%s\n", node_type_str(node->type)); ast_print(node->data.fn_decl.fn_proto, indent + 2); @@ -482,9 +471,9 @@ struct ParseContext { Buf *buf; AstNode *root; ZigList *tokens; - ZigList *directive_list; ImportTableEntry *owner; ErrColor err_color; + bool parsed_root_export; uint32_t *next_node_index; }; @@ -2146,56 +2135,32 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m } /* -VariableDeclaration : option(FnVisibleMod) ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) +VariableDeclaration : ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) */ -static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { +static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory, + ZigList *directives, VisibMod visib_mod) +{ Token *first_token = &pc->tokens->at(*token_index); - VisibMod visib_mod; bool is_const; - if (first_token->id == TokenIdKeywordPub) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdKeywordVar || - next_token->id == TokenIdKeywordConst) - { - visib_mod = VisibModPub; - is_const = (next_token->id == TokenIdKeywordConst); - *token_index += 2; - } else if (mandatory) { - ast_invalid_token_error(pc, next_token); - } else { - return nullptr; - } - } else if (first_token->id == TokenIdKeywordExport) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdKeywordVar || - next_token->id == TokenIdKeywordConst) - { - visib_mod = VisibModExport; - is_const = (next_token->id == TokenIdKeywordConst); - *token_index += 2; - } else if (mandatory) { - ast_invalid_token_error(pc, next_token); - } else { - return nullptr; - } - } else if (first_token->id == TokenIdKeywordVar || - first_token->id == TokenIdKeywordConst) - { - visib_mod = VisibModPrivate; - is_const = (first_token->id == TokenIdKeywordConst); - *token_index += 1; + if (first_token->id == TokenIdKeywordVar) { + is_const = false; + } else if (first_token->id == TokenIdKeywordConst) { + is_const = true; } else if (mandatory) { ast_invalid_token_error(pc, first_token); } else { return nullptr; } + *token_index += 1; + AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, first_token); node->data.variable_declaration.is_const = is_const; node->data.variable_declaration.visib_mod = visib_mod; + node->data.variable_declaration.directives = directives; Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol); @@ -2643,7 +2608,8 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato if (statement_node) { semicolon_expected = false; } else { - statement_node = ast_parse_variable_declaration_expr(pc, token_index, false); + statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, + nullptr, VisibModPrivate); if (statement_node) { semicolon_expected = true; } else { @@ -2677,47 +2643,25 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato } /* -FnProto : many(Directive) option(FnVisibleMod) "fn" "Symbol" ParamDeclList option("->" PrefixOpExpression) +FnProto : "fn" "Symbol" ParamDeclList option("->" PrefixOpExpression) */ -static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory) { +static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory, + ZigList *directives, VisibMod visib_mod) +{ Token *first_token = &pc->tokens->at(*token_index); - VisibMod visib_mod; - - if (first_token->id == TokenIdKeywordPub) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdKeywordFn) { - visib_mod = VisibModPub; - *token_index += 2; - } else if (mandatory) { - ast_invalid_token_error(pc, first_token); + if (first_token->id != TokenIdKeywordFn) { + if (mandatory) { + ast_expect_token(pc, first_token, TokenIdKeywordFn); } else { return nullptr; } - } else if (first_token->id == TokenIdKeywordExport) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdKeywordFn) { - visib_mod = VisibModExport; - *token_index += 2; - } else if (mandatory) { - ast_invalid_token_error(pc, first_token); - } else { - return nullptr; - } - } else if (first_token->id == TokenIdKeywordFn) { - visib_mod = VisibModPrivate; - *token_index += 1; - } else if (mandatory) { - ast_invalid_token_error(pc, first_token); - } else { - return nullptr; } + *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeFnProto, first_token); node->data.fn_proto.visib_mod = visib_mod; - node->data.fn_proto.directives = pc->directive_list; - pc->directive_list = nullptr; - + node->data.fn_proto.directives = directives; Token *fn_name = &pc->tokens->at(*token_index); *token_index += 1; @@ -2743,107 +2687,59 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand /* FnDef : FnProto Block */ -static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandatory) { - AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory); +static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandatory, + ZigList *directives, VisibMod visib_mod) +{ + AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, directives, visib_mod); if (!fn_proto) return nullptr; AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDef, fn_proto); node->data.fn_def.fn_proto = fn_proto; node->data.fn_def.body = ast_parse_block(pc, token_index, true); - normalize_parent_ptrs(node); return node; } /* -FnDecl : FnProto token(Semicolon) +ExternDecl : "extern" FnProto ";" */ -static AstNode *ast_parse_fn_decl(ParseContext *pc, int *token_index) { - AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, true); - AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDecl, fn_proto); - - node->data.fn_decl.fn_proto = fn_proto; - - Token *semicolon = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, semicolon, TokenIdSemicolon); - - normalize_parent_ptrs(node); - return node; -} - -/* -Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen) -*/ -/* -ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace) -*/ -static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool mandatory) { +static AstNode *ast_parse_extern_decl(ParseContext *pc, int *token_index, bool mandatory, + ZigList *directives, VisibMod visib_mod) +{ Token *extern_kw = &pc->tokens->at(*token_index); if (extern_kw->id != TokenIdKeywordExtern) { - if (mandatory) + if (mandatory) { ast_invalid_token_error(pc, extern_kw); - else - return nullptr; - } - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeExternBlock, extern_kw); - - node->data.extern_block.directives = pc->directive_list; - pc->directive_list = nullptr; - - Token *l_brace = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, l_brace, TokenIdLBrace); - - for (;;) { - Token *directive_token = &pc->tokens->at(*token_index); - assert(!pc->directive_list); - pc->directive_list = allocate>(1); - ast_parse_directives(pc, token_index, pc->directive_list); - - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRBrace) { - if (pc->directive_list->length > 0) { - ast_error(pc, directive_token, "invalid directive"); - } - pc->directive_list = nullptr; - - *token_index += 1; - - normalize_parent_ptrs(node); - return node; } else { - AstNode *child = ast_parse_fn_decl(pc, token_index); - node->data.extern_block.fn_decls.append(child); + return nullptr; } } + *token_index += 1; + AstNode *node = ast_parse_fn_proto(pc, token_index, true, directives, visib_mod); - zig_unreachable(); + ast_eat_token(pc, token_index, TokenIdSemicolon); + + node->data.fn_proto.is_extern = true; + normalize_parent_ptrs(node); + return node; } /* -RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token(Semicolon) +RootExportDecl : "export" "Symbol" "String" ";" */ -static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, bool mandatory) { - assert(mandatory == false); - - Token *export_kw = &pc->tokens->at(*token_index); - if (export_kw->id != TokenIdKeywordExport) - return nullptr; - - Token *export_type = &pc->tokens->at(*token_index + 1); +static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, + ZigList *directives) +{ + Token *export_type = &pc->tokens->at(*token_index); if (export_type->id != TokenIdSymbol) return nullptr; - *token_index += 2; + *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeRootExportDecl, export_kw); - node->data.root_export_decl.directives = pc->directive_list; - pc->directive_list = nullptr; + AstNode *node = ast_create_node(pc, NodeTypeRootExportDecl, export_type); + node->data.root_export_decl.directives = directives; ast_buf_from_token(pc, export_type, &node->data.root_export_decl.type); @@ -2862,11 +2758,13 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b } /* -Use : many(Directive) token(Use) token(String) token(Semicolon) +Import : "import" "String" ";" */ -static AstNode *ast_parse_use(ParseContext *pc, int *token_index) { - Token *use_kw = &pc->tokens->at(*token_index); - if (use_kw->id != TokenIdKeywordImport) +static AstNode *ast_parse_import(ParseContext *pc, int *token_index, + ZigList *directives, VisibMod visib_mod) +{ + Token *import_kw = &pc->tokens->at(*token_index); + if (import_kw->id != TokenIdKeywordImport) return nullptr; *token_index += 1; @@ -2878,59 +2776,35 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index) { *token_index += 1; ast_expect_token(pc, semicolon, TokenIdSemicolon); - AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw); + AstNode *node = ast_create_node(pc, NodeTypeUse, import_kw); + node->data.use.visib_mod = visib_mod; + node->data.use.directives = directives; parse_string_literal(pc, use_name, &node->data.use.path, nullptr, nullptr); - - node->data.use.directives = pc->directive_list; - pc->directive_list = nullptr; - normalize_parent_ptrs(node); return node; } /* -ContainerDecl : many(Directive) option(FnVisibleMod) (token(Struct) | token(Enum)) token(Symbol) token(LBrace) many(StructMember) token(RBrace) -StructMember: StructField | FnDecl -StructField : token(Symbol) option(token(Colon) Expression) token(Comma)) +ContainerDecl : ("struct" | "enum") "Symbol" "{" many(StructMember) "}" +StructMember: many(Directive) option(VisibleMod) (StructField | FnDef) +StructField : "Symbol" option(":" Expression) ",") */ -static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { +static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index, + ZigList *directives, VisibMod visib_mod) +{ Token *first_token = &pc->tokens->at(*token_index); - VisibMod visib_mod; ContainerKind kind; - if (first_token->id == TokenIdKeywordPub) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdKeywordStruct || - next_token->id == TokenIdKeywordEnum) - { - visib_mod = VisibModPub; - kind = (next_token->id == TokenIdKeywordStruct) ? ContainerKindStruct : ContainerKindEnum; - *token_index += 2; - } else { - return nullptr; - } - } else if (first_token->id == TokenIdKeywordExport) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdKeywordStruct || - next_token->id == TokenIdKeywordEnum) - { - visib_mod = VisibModExport; - kind = (next_token->id == TokenIdKeywordStruct) ? ContainerKindStruct : ContainerKindEnum; - *token_index += 2; - } else { - return nullptr; - } - } else if (first_token->id == TokenIdKeywordStruct || - first_token->id == TokenIdKeywordEnum) - { - visib_mod = VisibModPrivate; - kind = (first_token->id == TokenIdKeywordStruct) ? ContainerKindStruct : ContainerKindEnum; - *token_index += 1; + if (first_token->id == TokenIdKeywordStruct) { + kind = ContainerKindStruct; + } else if (first_token->id == TokenIdKeywordEnum) { + kind = ContainerKindEnum; } else { return nullptr; } + *token_index += 1; Token *struct_name = ast_eat_token(pc, token_index, TokenIdSymbol); @@ -2938,19 +2812,28 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { node->data.struct_decl.kind = kind; ast_buf_from_token(pc, struct_name, &node->data.struct_decl.name); node->data.struct_decl.visib_mod = visib_mod; + node->data.struct_decl.directives = directives; ast_eat_token(pc, token_index, TokenIdLBrace); - node->data.struct_decl.directives = pc->directive_list; - pc->directive_list = nullptr; - for (;;) { - assert(!pc->directive_list); - pc->directive_list = allocate>(1); Token *directive_token = &pc->tokens->at(*token_index); - ast_parse_directives(pc, token_index, pc->directive_list); + ZigList *directive_list = allocate>(1); + ast_parse_directives(pc, token_index, directive_list); - AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false); + Token *visib_tok = &pc->tokens->at(*token_index); + VisibMod visib_mod; + if (visib_tok->id == TokenIdKeywordPub) { + *token_index += 1; + visib_mod = VisibModPub; + } else if (visib_tok->id == TokenIdKeywordExport) { + *token_index += 1; + visib_mod = VisibModExport; + } else { + visib_mod = VisibModPrivate; + } + + AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, directive_list, visib_mod); if (fn_def_node) { node->data.struct_decl.fns.append(fn_def_node); continue; @@ -2959,10 +2842,9 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdRBrace) { - if (pc->directive_list->length > 0) { + if (directive_list->length > 0) { ast_error(pc, directive_token, "invalid directive"); } - pc->directive_list = nullptr; *token_index += 1; break; @@ -2970,8 +2852,8 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token); *token_index += 1; - field_node->data.struct_field.directives = pc->directive_list; - pc->directive_list = nullptr; + field_node->data.struct_field.visib_mod = visib_mod; + field_node->data.struct_field.directives = directive_list; ast_buf_from_token(pc, token, &field_node->data.struct_field.name); @@ -2997,47 +2879,24 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { } /* -ErrorValueDecl : option(FnVisibleMod) "error" "Symbol" +ErrorValueDecl : "error" "Symbol" ";" */ -static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, bool mandatory) { +static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, + ZigList *directives, VisibMod visib_mod) +{ Token *first_token = &pc->tokens->at(*token_index); - VisibMod visib_mod; - - if (first_token->id == TokenIdKeywordPub) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdKeywordError) { - visib_mod = VisibModPub; - *token_index += 2; - } else if (mandatory) { - ast_invalid_token_error(pc, next_token); - } else { - return nullptr; - } - } else if (first_token->id == TokenIdKeywordExport) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdKeywordError) { - visib_mod = VisibModExport; - *token_index += 2; - } else if (mandatory) { - ast_invalid_token_error(pc, next_token); - } else { - return nullptr; - } - } else if (first_token->id == TokenIdKeywordError) { - visib_mod = VisibModPrivate; - *token_index += 1; - } else if (mandatory) { - ast_invalid_token_error(pc, first_token); - } else { + if (first_token->id != TokenIdKeywordError) { return nullptr; } + *token_index += 1; Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); ast_eat_token(pc, token_index, TokenIdSemicolon); AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token); node->data.error_value_decl.visib_mod = visib_mod; + node->data.error_value_decl.directives = directives; ast_buf_from_token(pc, name_tok, &node->data.error_value_decl.name); normalize_parent_ptrs(node); @@ -3045,63 +2904,79 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, b } /* -TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl +TopLevelDecl : many(Directive) option(FnVisibleMod) (FnDef | ExternFnProto | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl | CImportDecl) */ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList *top_level_decls) { for (;;) { Token *directive_token = &pc->tokens->at(*token_index); - assert(!pc->directive_list); - pc->directive_list = allocate>(1); - ast_parse_directives(pc, token_index, pc->directive_list); + ZigList *directives = allocate>(1); + ast_parse_directives(pc, token_index, directives); - AstNode *root_export_decl_node = ast_parse_root_export_decl(pc, token_index, false); - if (root_export_decl_node) { - top_level_decls->append(root_export_decl_node); - continue; + Token *visib_tok = &pc->tokens->at(*token_index); + VisibMod visib_mod; + if (visib_tok->id == TokenIdKeywordPub) { + *token_index += 1; + visib_mod = VisibModPub; + } else if (visib_tok->id == TokenIdKeywordExport) { + *token_index += 1; + visib_mod = VisibModExport; + } else { + visib_mod = VisibModPrivate; } - AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false); + bool try_to_parse_root_export = (visib_mod == VisibModExport && !pc->parsed_root_export); + pc->parsed_root_export = true; + + if (try_to_parse_root_export) { + AstNode *root_export_decl_node = ast_parse_root_export_decl(pc, token_index, directives); + if (root_export_decl_node) { + top_level_decls->append(root_export_decl_node); + continue; + } + } + + AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, directives, visib_mod); if (fn_def_node) { top_level_decls->append(fn_def_node); continue; } - AstNode *extern_node = ast_parse_extern_block(pc, token_index, false); - if (extern_node) { - top_level_decls->append(extern_node); + AstNode *fn_proto_node = ast_parse_extern_decl(pc, token_index, false, directives, visib_mod); + if (fn_proto_node) { + top_level_decls->append(fn_proto_node); continue; } - AstNode *use_node = ast_parse_use(pc, token_index); - if (use_node) { - top_level_decls->append(use_node); + AstNode *import_node = ast_parse_import(pc, token_index, directives, visib_mod); + if (import_node) { + top_level_decls->append(import_node); continue; } - AstNode *struct_node = ast_parse_struct_decl(pc, token_index); + AstNode *struct_node = ast_parse_struct_decl(pc, token_index, directives, visib_mod); if (struct_node) { top_level_decls->append(struct_node); continue; } - if (pc->directive_list->length > 0) { - ast_error(pc, directive_token, "invalid directive"); - } - pc->directive_list = nullptr; - - AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false); + AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, + directives, visib_mod); if (var_decl_node) { ast_eat_token(pc, token_index, TokenIdSemicolon); top_level_decls->append(var_decl_node); continue; } - AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index, false); + AstNode *error_value_node = ast_parse_error_value_decl(pc, token_index, directives, visib_mod); if (error_value_node) { top_level_decls->append(error_value_node); continue; } + if (directives->length > 0) { + ast_error(pc, directive_token, "invalid directive"); + } + return; } zig_unreachable(); @@ -3175,10 +3050,6 @@ void normalize_parent_ptrs(AstNode *node) { case NodeTypeBlock: set_list_fields(&node->data.block.statements); break; - case NodeTypeExternBlock: - set_list_fields(node->data.extern_block.directives); - set_list_fields(&node->data.extern_block.fn_decls); - break; case NodeTypeDirective: // none break; diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 86539a9c23..5cd32f6299 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -97,10 +97,8 @@ static TestCase *add_compile_fail_case(const char *case_name, const char *source static void add_compiling_test_cases(void) { add_simple_case("hello world with libc", R"SOURCE( #link("c") -extern { - fn puts(s: &const u8) -> c_int; -} - +export executable "test"; +extern fn puts(s: &const u8) -> c_int; export fn main(argc: c_int, argv: &&u8) -> c_int { puts(c"Hello, world!"); return 0; @@ -482,9 +480,8 @@ pub fn main(args: [][]u8) -> %void { add_simple_case("number literals", R"SOURCE( #link("c") -extern { - fn printf(__format: &const u8, ...) -> c_int; -} +export executable "test"; +extern fn printf(__format: &const u8, ...) -> c_int; export fn main(argc: c_int, argv: &&u8) -> c_int { printf(c"\n"); @@ -1321,13 +1318,11 @@ fn a() {} add_compile_fail_case("bad directive", R"SOURCE( #bogus1("") -extern { - fn b(); -} +extern fn b(); #bogus2("") fn a() {} )SOURCE", 2, ".tmp_source.zig:2:1: error: invalid directive: 'bogus1'", - ".tmp_source.zig:6:1: error: invalid directive: 'bogus2'"); + ".tmp_source.zig:4:1: error: invalid directive: 'bogus2'"); add_compile_fail_case("unreachable with return", R"SOURCE( fn a() -> unreachable {return;} @@ -1668,11 +1663,9 @@ fn f() { )SOURCE", 1, ".tmp_source.zig:6:9: error: multiple else prongs in switch expression"); add_compile_fail_case("global variable initializer must be constant expression", R"SOURCE( -extern { - fn foo() -> i32; -} +extern fn foo() -> i32; const x = foo(); - )SOURCE", 1, ".tmp_source.zig:5:11: error: global variable initializer requires constant expression"); + )SOURCE", 1, ".tmp_source.zig:3:11: error: global variable initializer requires constant expression"); add_compile_fail_case("non compile time string concatenation", R"SOURCE( fn f(s: []u8) -> []u8 {