diff --git a/README.md b/README.md index b8dfbc4f92..1fa201e0c6 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,6 @@ make ## Roadmap - * ability to specify version - * cli ability to override library export locations - * add test for building library * variables and parameters * Export .so library * Multiple files @@ -81,11 +78,11 @@ zig | C equivalent | Description ### Grammar ``` -Root : RootExportDecl many(TopLevelDecl) token(EOF) +Root : many(TopLevelDecl) token(EOF) -RootExportDecl : token(Export) token(Symbol) token(String) token(Semicolon) +TopLevelDecl : FnDef | ExternBlock | RootExportDecl -TopLevelDecl : FnDef | ExternBlock +RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token(Semicolon) ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace) diff --git a/example/mathtest.zig b/example/mathtest.zig index 2736f8b26d..35ec901523 100644 --- a/example/mathtest.zig +++ b/example/mathtest.zig @@ -1,3 +1,4 @@ +#version("2.0.0") export library "mathtest"; export fn add(a: i32, b: i32) -> i32 { diff --git a/src/codegen.cpp b/src/codegen.cpp index 74ca4a322c..a768d52122 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -10,6 +10,7 @@ #include "zig_llvm.hpp" #include "os.hpp" #include "config.h" +#include "error.hpp" #include @@ -79,6 +80,10 @@ struct CodeGen { OutType out_type; FnTableEntry *cur_fn; bool c_stdint_used; + AstNode *root_export_decl; + int version_major; + int version_minor; + int version_patch; }; struct TypeNode { @@ -169,6 +174,30 @@ static bool type_is_unreachable(AstNode *type_node) { return type_node->codegen_node->data.type_node.entry->id == TypeIdUnreachable; } + +static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) { + char *dot1 = strstr(buf_ptr(buf), "."); + if (!dot1) + return ErrorInvalidFormat; + char *dot2 = strstr(dot1 + 1, "."); + if (!dot2) + return ErrorInvalidFormat; + + *major = (int)strtol(buf_ptr(buf), nullptr, 10); + *minor = (int)strtol(dot1 + 1, nullptr, 10); + *patch = (int)strtol(dot2 + 1, nullptr, 10); + + return ErrorNone; +} + +static void set_root_export_version(CodeGen *g, Buf *version_buf, AstNode *node) { + int err; + if ((err = parse_version_string(version_buf, &g->version_major, &g->version_minor, &g->version_patch))) { + add_node_error(g, node, + buf_sprintf("invalid version string")); + } +} + static void find_declarations(CodeGen *g, AstNode *node); static void resolve_type_and_recurse(CodeGen *g, AstNode *node) { @@ -303,13 +332,25 @@ static void find_declarations(CodeGen *g, AstNode *node) { case NodeTypeDirective: // we handled directives in the parent function break; + case NodeTypeRootExportDecl: + for (int i = 0; i < node->data.root_export_decl.directives->length; i += 1) { + AstNode *directive_node = node->data.root_export_decl.directives->at(i); + Buf *name = &directive_node->data.directive.name; + Buf *param = &directive_node->data.directive.param; + if (buf_eql_str(name, "version")) { + set_root_export_version(g, param, directive_node); + } else { + add_node_error(g, directive_node, + buf_sprintf("invalid directive: '%s'", buf_ptr(name))); + } + } + break; case NodeTypeFnDecl: case NodeTypeReturnExpr: case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: case NodeTypeFnCallExpr: - case NodeTypeRootExportDecl: case NodeTypeNumberLiteral: case NodeTypeStringLiteral: case NodeTypeUnreachable: @@ -385,36 +426,6 @@ static void analyze_node(CodeGen *g, AstNode *node) { switch (node->type) { case NodeTypeRoot: { - AstNode *root_export_decl_node = node->data.root.root_export_decl; - if (root_export_decl_node) { - assert(root_export_decl_node->type == NodeTypeRootExportDecl); - if (!g->out_name) - g->out_name = &root_export_decl_node->data.root_export_decl.name; - - Buf *out_type = &root_export_decl_node->data.root_export_decl.type; - OutType export_out_type; - if (buf_eql_str(out_type, "executable")) { - export_out_type = OutTypeExe; - } else if (buf_eql_str(out_type, "library")) { - export_out_type = OutTypeLib; - } else if (buf_eql_str(out_type, "object")) { - export_out_type = OutTypeObj; - } else { - add_node_error(g, root_export_decl_node, - buf_sprintf("invalid export type: '%s'", buf_ptr(out_type))); - } - if (g->out_type == OutTypeUnknown) - g->out_type = export_out_type; - } else { - if (!g->out_name) { - add_node_error(g, node, - buf_sprintf("missing export declaration and output name not provided")); - } else if (g->out_type == OutTypeUnknown) { - add_node_error(g, node, - buf_sprintf("missing export declaration and export type not provided")); - } - } - // Iterate once over the top level declarations to build the function table for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) { AstNode *child = node->data.root.top_level_decls.at(i); @@ -424,10 +435,40 @@ static void analyze_node(CodeGen *g, AstNode *node) { AstNode *child = node->data.root.top_level_decls.at(i); analyze_node(g, child); } + if (!g->out_name) { + add_node_error(g, node, + buf_sprintf("missing export declaration and output name not provided")); + } else if (g->out_type == OutTypeUnknown) { + add_node_error(g, node, + buf_sprintf("missing export declaration and export type not provided")); + } break; } case NodeTypeRootExportDecl: - // handled in parent + if (g->root_export_decl) { + add_node_error(g, node, + buf_sprintf("only one root export declaration allowed")); + } else { + g->root_export_decl = node; + + if (!g->out_name) + g->out_name = &node->data.root_export_decl.name; + + Buf *out_type = &node->data.root_export_decl.type; + OutType export_out_type; + if (buf_eql_str(out_type, "executable")) { + export_out_type = OutTypeExe; + } else if (buf_eql_str(out_type, "library")) { + export_out_type = OutTypeLib; + } else if (buf_eql_str(out_type, "object")) { + export_out_type = OutTypeObj; + } else { + add_node_error(g, node, + buf_sprintf("invalid export type: '%s'", buf_ptr(out_type))); + } + if (g->out_type == OutTypeUnknown) + g->out_type = export_out_type; + } break; case NodeTypeExternBlock: for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) { @@ -1401,11 +1442,9 @@ void code_gen_link(CodeGen *g, const char *out_file) { } if (g->out_type == OutTypeLib) { - int major = 1; - int minor = 0; - int patch = 0; - Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d", buf_ptr(g->out_name), major, minor, patch); - Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->out_name), major); + Buf *out_lib_so = buf_sprintf("lib%s.so.%d.%d.%d", + buf_ptr(g->out_name), g->version_major, g->version_minor, g->version_patch); + Buf *soname = buf_sprintf("lib%s.so.%d", buf_ptr(g->out_name), g->version_major); args.append("-shared"); args.append("-soname"); args.append(buf_ptr(soname)); diff --git a/src/error.cpp b/src/error.cpp index 19f9330ea5..e7dab68147 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -2,6 +2,7 @@ const char *err_str(int err) { switch ((enum Error)err) { + case ErrorNone: return "(no error)"; case ErrorNoMem: return "out of memory"; case ErrorInvalidFormat: return "invalid format"; } diff --git a/src/error.hpp b/src/error.hpp index d308b418ab..3a975f2177 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -9,6 +9,7 @@ #define ERROR_HPP enum Error { + ErrorNone, ErrorNoMem, ErrorInvalidFormat, }; diff --git a/src/parser.cpp b/src/parser.cpp index a571b66f9f..d1f8e6e7bd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1195,6 +1195,44 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool zig_unreachable(); } +/* +RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token(Semicolon) +*/ +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); + if (export_type->id != TokenIdSymbol) + return nullptr; + + *token_index += 2; + + AstNode *node = ast_create_node(NodeTypeRootExportDecl, export_kw); + node->data.root_export_decl.directives = pc->directive_list; + pc->directive_list = nullptr; + + ast_buf_from_token(pc, export_type, &node->data.root_export_decl.type); + + Token *export_name = &pc->tokens->at(*token_index); + *token_index += 1; + ast_expect_token(pc, export_name, TokenIdStringLiteral); + + parse_string_literal(pc, export_name, &node->data.root_export_decl.name); + + Token *semicolon = &pc->tokens->at(*token_index); + *token_index += 1; + ast_expect_token(pc, semicolon, TokenIdSemicolon); + + return node; +} + +/* +TopLevelDecl : FnDef | ExternBlock | RootExportDecl +*/ 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); @@ -1202,6 +1240,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis pc->directive_list = allocate>(1); ast_parse_directives(pc, token_index, pc->directive_list); + 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; + } + AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, false); if (fn_decl_node) { top_level_decls->append(fn_decl_node); @@ -1224,41 +1268,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis zig_unreachable(); } -static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index) { - Token *export_kw = &pc->tokens->at(*token_index); - if (export_kw->id != TokenIdKeywordExport) - return nullptr; - *token_index += 1; - - AstNode *node = ast_create_node(NodeTypeRootExportDecl, export_kw); - - Token *export_type = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, export_type, TokenIdSymbol); - - ast_buf_from_token(pc, export_type, &node->data.root_export_decl.type); - - Token *export_name = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, export_name, TokenIdStringLiteral); - - parse_string_literal(pc, export_name, &node->data.root_export_decl.name); - - Token *semicolon = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, semicolon, TokenIdSemicolon); - - return node; -} - /* -Root : RootExportDecl many(TopLevelDecl) token(EOF) +Root : many(TopLevelDecl) token(EOF) */ static AstNode *ast_parse_root(ParseContext *pc, int *token_index) { AstNode *node = ast_create_node(NodeTypeRoot, &pc->tokens->at(*token_index)); - node->data.root.root_export_decl = ast_parse_root_export_decl(pc, token_index); - ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls); if (*token_index != pc->tokens->length - 1) { diff --git a/src/parser.hpp b/src/parser.hpp index 86b29870e5..6d99dfbc4c 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -38,7 +38,6 @@ enum NodeType { }; struct AstNodeRoot { - AstNode *root_export_decl; ZigList top_level_decls; }; @@ -138,6 +137,7 @@ struct AstNodeDirective { struct AstNodeRootExportDecl { Buf type; Buf name; + ZigList *directives; }; struct AstNodeCastExpr { diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 3cfab3288a..3e8a201f52 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -608,4 +608,3 @@ void print_tokens(Buf *buf, ZigList *tokens) { fprintf(stderr, "\n"); } } -