Commit 8f0f318c39

Andrew Kelley <superjoe30@gmail.com>
2015-11-30 10:11:31
add directive to specify root export version
1 parent ac0c5a3
example/mathtest.zig
@@ -1,3 +1,4 @@
+#version("2.0.0")
 export library "mathtest";
 
 export fn add(a: i32, b: i32) -> i32 {
src/codegen.cpp
@@ -10,6 +10,7 @@
 #include "zig_llvm.hpp"
 #include "os.hpp"
 #include "config.h"
+#include "error.hpp"
 
 #include <stdio.h>
 
@@ -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));
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";
     }
src/error.hpp
@@ -9,6 +9,7 @@
 #define ERROR_HPP
 
 enum Error {
+    ErrorNone,
     ErrorNoMem,
     ErrorInvalidFormat,
 };
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<AstNode *> *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<ZigList<AstNode*>>(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) {
src/parser.hpp
@@ -38,7 +38,6 @@ enum NodeType {
 };
 
 struct AstNodeRoot {
-    AstNode *root_export_decl;
     ZigList<AstNode *> top_level_decls;
 };
 
@@ -138,6 +137,7 @@ struct AstNodeDirective {
 struct AstNodeRootExportDecl {
     Buf type;
     Buf name;
+    ZigList<AstNode *> *directives;
 };
 
 struct AstNodeCastExpr {
src/tokenizer.cpp
@@ -608,4 +608,3 @@ void print_tokens(Buf *buf, ZigList<Token> *tokens) {
         fprintf(stderr, "\n");
     }
 }
-
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)