Commit cb4773ce29

Andrew Kelley <superjoe30@gmail.com>
2015-11-28 05:24:11
add root export declaration which is overridable by command line options
1 parent 4cc9517
doc/vim/syntax/zig.vim
@@ -10,7 +10,22 @@ endif
 syn keyword zigKeyword fn return mut const extern unreachable export pub
 syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
 
+syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
+syn region zigCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=zigTodo,@Spell
+syn region zigCommentBlock matchgroup=zigCommentBlock start="/\*\%(!\|\*[*/]\@!\)\@!" end="\*/" contains=zigTodo,zigCommentBlockNest,@Spell
+syn region zigCommentBlockDoc matchgroup=zigCommentBlockDoc start="/\*\%(!\|\*[*/]\@!\)" end="\*/" contains=zigTodo,zigCommentBlockDocNest,@Spell
+syn region zigCommentBlockNest matchgroup=zigCommentBlock start="/\*" end="\*/" contains=zigTodo,zigCommentBlockNest,@Spell contained transparent
+syn region zigCommentBlockDocNest matchgroup=zigCommentBlockDoc start="/\*" end="\*/" contains=zigTodo,zigCommentBlockDocNest,@Spell contained transparent
+
+syn keyword zigTodo contained TODO XXX
+
 let b:current_syntax = "zig"
 
 hi def link zigKeyword Keyword
 hi def link zigType Type
+hi def link zigCommentLine Comment
+hi def link zigCommentLineDoc SpecialComment
+hi def link zigCommentBlock zigCommentLine
+hi def link zigCommentBlockDoc zigCommentLineDoc
+hi def link zigTodo Todo
+
example/hello.zig
@@ -1,3 +1,5 @@
+export executable "hello";
+
 #link("c")
 extern {
     fn puts(s: *mut u8) -> i32;
example/math.zig
@@ -0,0 +1,6 @@
+export library "math";
+
+export fn add(a: i32, b: i32) -> i32 {
+    return a + b;
+}
+
src/codegen.cpp
@@ -75,6 +75,8 @@ struct CodeGen {
     ZigList<llvm::DIScope *> block_scopes;
     llvm::DIFile *di_file;
     ZigList<FnTableEntry *> fn_defs;
+    Buf *out_name;
+    OutType out_type;
 };
 
 struct TypeNode {
@@ -103,6 +105,8 @@ CodeGen *create_codegen(AstNode *root, Buf *in_full_path) {
     g->is_static = false;
     g->build_type = CodeGenBuildTypeDebug;
     g->strip_debug_symbols = false;
+    g->out_name = nullptr;
+    g->out_type = OutTypeUnknown;
 
     os_path_split(in_full_path, &g->in_dir, &g->in_file);
     return g;
@@ -120,6 +124,14 @@ void codegen_set_strip(CodeGen *g, bool strip) {
     g->strip_debug_symbols = strip;
 }
 
+void codegen_set_out_type(CodeGen *g, OutType out_type) {
+    g->out_type = out_type;
+}
+
+void codegen_set_out_name(CodeGen *g, Buf *out_name) {
+    g->out_name = out_name;
+}
+
 static void add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
     g->errors.add_one();
     ErrorMsg *last_msg = &g->errors.last();
@@ -294,6 +306,7 @@ static void find_declarations(CodeGen *g, AstNode *node) {
         case NodeTypeBlock:
         case NodeTypeExpression:
         case NodeTypeFnCall:
+        case NodeTypeRootExportDecl:
             zig_unreachable();
     }
 }
@@ -355,15 +368,50 @@ static void check_fn_def_control_flow(CodeGen *g, AstNode *node) {
 static void analyze_node(CodeGen *g, AstNode *node) {
     switch (node->type) {
         case NodeTypeRoot:
-            // 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);
-                find_declarations(g, child);
-            }
-            for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
-                AstNode *child = node->data.root.top_level_decls.at(i);
-                analyze_node(g, child);
+            {
+                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);
+                    find_declarations(g, child);
+                }
+                for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
+                    AstNode *child = node->data.root.top_level_decls.at(i);
+                    analyze_node(g, child);
+                }
+                break;
             }
+        case NodeTypeRootExportDecl:
+            // handled in parent
             break;
         case NodeTypeExternBlock:
             for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) {
@@ -674,6 +722,7 @@ static void gen_block(CodeGen *g, AstNode *block_node, bool add_implicit_return)
             case NodeTypeFnCall:
             case NodeTypeExternBlock:
             case NodeTypeDirective:
+            case NodeTypeRootExportDecl:
                 zig_unreachable();
         }
     }
@@ -929,6 +978,15 @@ static Buf *get_dynamic_linker(CodeGen *g) {
     }
 }
 
+/*
+
+# static link into libfoo.a
+ar cq libfoo.a foo1.o foo2.o 
+
+# dynamic link into libfoo.so
+gcc -fPIC -g -Werror -pedantic  -shared -Wl,-soname,libsoundio.so.1 -o libsoundio.so.1.0.3 foo1.o foo2.o -ljack -lpulse -lasound -lpthread 
+
+*/
 void code_gen_link(CodeGen *g, const char *out_file) {
     LLVMPassRegistryRef registry = LLVMGetGlobalPassRegistry();
     LLVMInitializeCore(registry);
@@ -937,6 +995,10 @@ void code_gen_link(CodeGen *g, const char *out_file) {
     LLVMZigInitializeLowerIntrinsicsPass(registry);
     LLVMZigInitializeUnreachableBlockElimPass(registry);
 
+    if (!out_file) {
+        out_file = buf_ptr(g->out_name);
+    }
+
     Buf out_file_o = BUF_INIT;
     buf_init_from_str(&out_file_o, out_file);
     buf_append_str(&out_file_o, ".o");
src/codegen.hpp
@@ -12,6 +12,14 @@
 
 struct CodeGen;
 
+enum OutType {
+    OutTypeUnknown,
+    OutTypeExe,
+    OutTypeLib,
+    OutTypeObj,
+};
+
+
 struct ErrorMsg {
     int line_start;
     int column_start;
@@ -30,6 +38,8 @@ enum CodeGenBuildType {
 void codegen_set_build_type(CodeGen *codegen, CodeGenBuildType build_type);
 void codegen_set_is_static(CodeGen *codegen, bool is_static);
 void codegen_set_strip(CodeGen *codegen, bool strip);
+void codegen_set_out_type(CodeGen *codegen, OutType out_type);
+void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
 
 void semantic_analyze(CodeGen *g);
 
src/main.cpp
@@ -28,18 +28,24 @@
 static int usage(const char *arg0) {
     fprintf(stderr, "Usage: %s [command] [options] target\n"
         "Commands:\n"
-        "  build          create an executable from target\n"
-        "Options:\n"
-        "  --output       output file\n"
-        "  --version      print version number and exit\n"
-        "  -Ipath         add path to header include path\n"
-        "  --release      build with optimizations on\n"
-        "  --strip        exclude debug symbols\n"
-        "  --static       build a static executable\n"
+        "  build                  create executable, object, or library from target\n"
+        "  version                print version number and exit\n"
+        "Optional Options:\n"
+        "  --release              build with optimizations on and debug protection off\n"
+        "  --static               output will be statically linked\n"
+        "  --strip                exclude debug symbols\n"
+        "  --export [exe|lib|obj] override output type\n"
+        "  --name [name]          override output name\n"
+        "  --output [file]        override destination path\n"
     , arg0);
     return EXIT_FAILURE;
 }
 
+static int version(void) {
+    printf("%s\n", ZIG_VERSION_STRING);
+    return EXIT_SUCCESS;
+}
+
 static Buf *fetch_file(FILE *f) {
     int fd = fileno(f);
     struct stat st;
@@ -58,12 +64,12 @@ static Buf *fetch_file(FILE *f) {
     return buf;
 }
 
-static int build(const char *arg0, const char *in_file, const char *out_file,
-        ZigList<char *> *include_paths, bool release, bool strip, bool is_static)
+static int build(const char *arg0, const char *in_file, const char *out_file, bool release,
+        bool strip, bool is_static, OutType out_type, char *out_name)
 {
     static char cur_dir[1024];
 
-    if (!in_file || !out_file)
+    if (!in_file)
         return usage(arg0);
 
     FILE *in_f;
@@ -100,6 +106,10 @@ static int build(const char *arg0, const char *in_file, const char *out_file,
     codegen_set_build_type(codegen, release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug);
     codegen_set_strip(codegen, strip);
     codegen_set_is_static(codegen, is_static);
+    if (out_type != OutTypeUnknown)
+        codegen_set_out_type(codegen, out_type);
+    if (out_name)
+        codegen_set_out_name(codegen, buf_create_from_str(out_name));
     semantic_analyze(codegen);
     ZigList<ErrorMsg> *errors = codegen_error_messages(codegen);
     if (errors->length == 0) {
@@ -135,25 +145,25 @@ static int build(const char *arg0, const char *in_file, const char *out_file,
 enum Cmd {
     CmdNone,
     CmdBuild,
+    CmdVersion,
 };
 
 int main(int argc, char **argv) {
     char *arg0 = argv[0];
     char *in_file = NULL;
     char *out_file = NULL;
-    ZigList<char *> include_paths = {0};
     bool release = false;
     bool strip = false;
     bool is_static = false;
 
+    OutType out_type = OutTypeUnknown;
+    char *out_name = NULL;
+
     Cmd cmd = CmdNone;
     for (int i = 1; i < argc; i += 1) {
         char *arg = argv[i];
         if (arg[0] == '-' && arg[1] == '-') {
-            if (strcmp(arg, "--version") == 0) {
-                printf("%s\n", ZIG_VERSION_STRING);
-                return EXIT_SUCCESS;
-            } else if (strcmp(arg, "--release") == 0) {
+            if (strcmp(arg, "--release") == 0) {
                 release = true;
             } else if (strcmp(arg, "--strip") == 0) {
                 strip = true;
@@ -165,15 +175,27 @@ int main(int argc, char **argv) {
                 i += 1;
                 if (strcmp(arg, "--output") == 0) {
                     out_file = argv[i];
+                } else if (strcmp(arg, "--export") == 0) {
+                    if (strcmp(argv[i], "exe") == 0) {
+                        out_type = OutTypeExe;
+                    } else if (strcmp(argv[i], "lib") == 0) {
+                        out_type = OutTypeLib;
+                    } else if (strcmp(argv[i], "obj") == 0) {
+                        out_type = OutTypeObj;
+                    } else {
+                        return usage(arg0);
+                    }
+                } else if (strcmp(arg, "--name") == 0) {
+                    out_name = argv[i];
                 } else {
                     return usage(arg0);
                 }
             }
-        } else if (arg[0] == '-' && arg[1] == 'I') {
-            include_paths.append(arg + 2);
         } else if (cmd == CmdNone) {
             if (strcmp(arg, "build") == 0) {
                 cmd = CmdBuild;
+            } else if (strcmp(arg, "version") == 0) {
+                cmd = CmdVersion;
             } else {
                 fprintf(stderr, "Unrecognized command: %s\n", arg);
                 return usage(arg0);
@@ -189,6 +211,8 @@ int main(int argc, char **argv) {
                         return usage(arg0);
                     }
                     break;
+                case CmdVersion:
+                    return usage(arg0);
             }
         }
     }
@@ -197,9 +221,10 @@ int main(int argc, char **argv) {
         case CmdNone:
             return usage(arg0);
         case CmdBuild:
-            return build(arg0, in_file, out_file, &include_paths, release, strip, is_static);
+            return build(arg0, in_file, out_file, release, strip, is_static, out_type, out_name);
+        case CmdVersion:
+            return version();
     }
 
     zig_unreachable();
 }
-
src/parser.cpp
@@ -29,6 +29,8 @@ const char *node_type_str(NodeType node_type) {
     switch (node_type) {
         case NodeTypeRoot:
             return "Root";
+        case NodeTypeRootExportDecl:
+            return "RootExportDecl";
         case NodeTypeFnDef:
             return "FnDef";
         case NodeTypeFnDecl:
@@ -68,6 +70,11 @@ void ast_print(AstNode *node, int indent) {
                 ast_print(child, indent + 2);
             }
             break;
+        case NodeTypeRootExportDecl:
+            fprintf(stderr, "%s %s '%s'\n", node_type_str(node->type),
+                    buf_ptr(&node->data.root_export_decl.type),
+                    buf_ptr(&node->data.root_export_decl.name));
+            break;
         case NodeTypeFnDef:
             {
                 fprintf(stderr, "%s\n", node_type_str(node->type));
@@ -714,6 +721,36 @@ 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)
+ */
 AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens) {
     ParseContext pc = {0};
     pc.buf = buf;
@@ -721,6 +758,9 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens) {
     pc.tokens = tokens;
 
     int token_index = 0;
+
+    pc.root->data.root.root_export_decl = ast_parse_root_export_decl(&pc, &token_index);
+
     ast_parse_top_level_decls(&pc, &token_index, &pc.root->data.root.top_level_decls);
 
     if (token_index != tokens->length - 1) {
src/parser.hpp
@@ -17,6 +17,7 @@ struct CodeGenNode;
 
 enum NodeType {
     NodeTypeRoot,
+    NodeTypeRootExportDecl,
     NodeTypeFnProto,
     NodeTypeFnDef,
     NodeTypeFnDecl,
@@ -31,6 +32,7 @@ enum NodeType {
 };
 
 struct AstNodeRoot {
+    AstNode *root_export_decl;
     ZigList<AstNode *> top_level_decls;
 };
 
@@ -113,6 +115,11 @@ struct AstNodeDirective {
     Buf param;
 };
 
+struct AstNodeRootExportDecl {
+    Buf type;
+    Buf name;
+};
+
 struct AstNode {
     enum NodeType type;
     AstNode *parent;
@@ -121,6 +128,7 @@ struct AstNode {
     CodeGenNode *codegen_node;
     union {
         AstNodeRoot root;
+        AstNodeRootExportDecl root_export_decl;
         AstNodeFnDef fn_def;
         AstNodeFnDecl fn_decl;
         AstNodeFnProto fn_proto;
test/run_tests.cpp
@@ -39,6 +39,10 @@ static void add_simple_case(const char *case_name, const char *source, const cha
 
     test_case->compiler_args.append("build");
     test_case->compiler_args.append(tmp_source_path);
+    test_case->compiler_args.append("--export");
+    test_case->compiler_args.append("exe");
+    test_case->compiler_args.append("--name");
+    test_case->compiler_args.append("test");
     test_case->compiler_args.append("--output");
     test_case->compiler_args.append(tmp_exe_path);
     test_case->compiler_args.append("--release");
README.md
@@ -32,7 +32,9 @@ readable, safe, optimal, and concise code to solve any computing problem.
 
 ## Roadmap
 
- * Simple .so library
+ * Math expression
+ * Export .so library
+ * Export .o file
  * Multiple files
  * inline assembly and syscalls
  * running code at compile time
@@ -66,7 +68,9 @@ zig    | C equivalent | Description
 ### Grammar
 
 ```
-Root : many(TopLevelDecl) token(EOF)
+Root : RootExportDecl many(TopLevelDecl) token(EOF)
+
+RootExportDecl : token(Export) token(Symbol) token(String) token(Semicolon)
 
 TopLevelDecl : FnDef | ExternBlock