Commit 09f68c7c33

Andrew Kelley <superjoe30@gmail.com>
2015-11-25 07:44:41
support linker directives
now you can depend on libc in zig language instead of it being hardcoded in the compiler.
1 parent afac1a0
src/codegen.cpp
@@ -56,6 +56,7 @@ struct CodeGen {
     HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
     HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table;
     HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
+    HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
     TypeTableEntry *invalid_type_entry;
     LLVMTargetDataRef target_data_ref;
     unsigned pointer_size_bytes;
@@ -86,6 +87,7 @@ CodeGen *create_codegen(AstNode *root, Buf *in_full_path) {
     g->fn_table.init(32);
     g->str_table.init(32);
     g->type_table.init(32);
+    g->link_table.init(32);
     g->is_static = false;
     g->build_type = CodeGenBuildTypeDebug;
     g->strip_debug_symbols = false;
@@ -198,6 +200,18 @@ static void analyze_node(CodeGen *g, AstNode *node) {
             }
             break;
         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, node,
+                            buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
+                }
+            }
+
             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);
                 analyze_node(g, fn_decl);
@@ -306,6 +320,8 @@ static void analyze_node(CodeGen *g, AstNode *node) {
                 analyze_node(g, child);
             }
             break;
+        case NodeTypeDirective:
+            break;
     }
 }
 
@@ -639,6 +655,16 @@ void code_gen_link(CodeGen *g, const char *out_file) {
     args.append("-o");
     args.append(out_file);
     args.append((const char *)buf_ptr(&out_file_o));
-    args.append("-lc");
+
+    auto it = g->link_table.entry_iterator();
+    for (;;) {
+        auto *entry = it.next();
+        if (!entry)
+            break;
+
+        Buf *arg = buf_sprintf("-l%s", buf_ptr(entry->key));
+        args.append(buf_ptr(arg));
+    }
+
     os_spawn_process("ld", args, false);
 }
src/parser.cpp
@@ -48,6 +48,8 @@ const char *node_type_str(NodeType node_type) {
             return "FnCall";
         case NodeTypeExternBlock:
             return "ExternBlock";
+        case NodeTypeDirective:
+            return "Directive";
     }
     zig_unreachable();
 }
@@ -158,13 +160,23 @@ struct ParseContext {
     Buf *buf;
     AstNode *root;
     ZigList<Token> *tokens;
+    ZigList<AstNode *> *directive_list;
 };
 
-static AstNode *ast_create_node(NodeType type, Token *first_token) {
+static AstNode *ast_create_node_no_line_info(NodeType type) {
     AstNode *node = allocate<AstNode>(1);
     node->type = type;
+    return node;
+}
+
+static void ast_update_node_line_info(AstNode *node, Token *first_token) {
     node->line = first_token->start_line;
     node->column = first_token->start_column;
+}
+
+static AstNode *ast_create_node(NodeType type, Token *first_token) {
+    AstNode *node = ast_create_node_no_line_info(type);
+    ast_update_node_line_info(node, first_token);
     return node;
 }
 
@@ -536,7 +548,57 @@ static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_to
 }
 
 /*
-ExternBlock : token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace)
+Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
+*/
+static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_token_index) {
+    Token *number_sign = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, number_sign, TokenIdNumberSign);
+
+    AstNode *node = ast_create_node(NodeTypeDirective, number_sign);
+
+    Token *name_symbol = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, name_symbol, TokenIdSymbol);
+
+    ast_buf_from_token(pc, name_symbol, &node->data.directive.name);
+
+    Token *l_paren = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, l_paren, TokenIdLParen);
+
+    Token *param_str = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, param_str, TokenIdStringLiteral);
+
+    parse_string_literal(pc, param_str, &node->data.directive.param);
+
+    Token *r_paren = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, r_paren, TokenIdRParen);
+
+    *new_token_index = token_index;
+    return node;
+}
+
+static void ast_parse_directives(ParseContext *pc, int token_index, int *new_token_index,
+        ZigList<AstNode *> *directives)
+{
+    for (;;) {
+        Token *token = &pc->tokens->at(token_index);
+        if (token->id == TokenIdNumberSign) {
+            AstNode *directive_node = ast_parse_directive(pc, token_index, &token_index);
+            directives->append(directive_node);
+        } else {
+            *new_token_index = token_index;
+            return;
+        }
+    }
+    zig_unreachable();
+}
+
+/*
+ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace)
 */
 static AstNode *ast_parse_extern_block(ParseContext *pc, int token_index, int *new_token_index) {
     Token *extern_kw = &pc->tokens->at(token_index);
@@ -545,6 +607,9 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int token_index, int *n
 
     AstNode *node = ast_create_node(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);
@@ -570,7 +635,11 @@ static void ast_parse_top_level_decls(ParseContext *pc, int token_index, int *ne
 {
     for (;;) {
         Token *token = &pc->tokens->at(token_index);
-        if (token->id == TokenIdKeywordFn) {
+        if (token->id == TokenIdNumberSign) {
+            assert(!pc->directive_list);
+            pc->directive_list = allocate<ZigList<AstNode*>>(1);
+            ast_parse_directives(pc, token_index, &token_index, pc->directive_list);
+        } else if (token->id == TokenIdKeywordFn) {
             AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, &token_index);
             top_level_decls->append(fn_decl_node);
         } else if (token->id == TokenIdKeywordExtern) {
src/parser.hpp
@@ -27,6 +27,7 @@ enum NodeType {
     NodeTypeExpression,
     NodeTypeFnCall,
     NodeTypeExternBlock,
+    NodeTypeDirective,
 };
 
 struct AstNodeRoot {
@@ -112,9 +113,15 @@ struct AstNodeFnCall {
 };
 
 struct AstNodeExternBlock {
+    ZigList<AstNode *> *directives;
     ZigList<AstNode *> fn_decls;
 };
 
+struct AstNodeDirective {
+    Buf name;
+    Buf param;
+};
+
 struct AstNode {
     enum NodeType type;
     AstNode *parent;
@@ -133,6 +140,7 @@ struct AstNode {
         AstNodeExpression expression;
         AstNodeFnCall fn_call;
         AstNodeExternBlock extern_block;
+        AstNodeDirective directive;
     } data;
 };
 
src/tokenizer.cpp
@@ -218,6 +218,10 @@ ZigList<Token> *tokenize(Buf *buf) {
                         begin_token(&t, TokenIdDash);
                         t.state = TokenizeStateSawDash;
                         break;
+                    case '#':
+                        begin_token(&t, TokenIdNumberSign);
+                        end_token(&t);
+                        break;
                     default:
                         tokenize_error(&t, "invalid character: '%c'", c);
                 }
@@ -321,6 +325,7 @@ static const char * token_name(Token *token) {
         case TokenIdColon: return "Colon";
         case TokenIdArrow: return "Arrow";
         case TokenIdDash: return "Dash";
+        case TokenIdNumberSign: return "NumberSign";
     }
     return "(invalid token)";
 }
src/tokenizer.hpp
@@ -32,6 +32,7 @@ enum TokenId {
     TokenIdColon,
     TokenIdArrow,
     TokenIdDash,
+    TokenIdNumberSign,
 };
 
 struct Token {
test/hello.zig
@@ -1,3 +1,4 @@
+#link("c")
 extern {
     fn puts(s: *mut u8) -> i32;
     fn exit(code: i32) -> unreachable;
README.md
@@ -40,12 +40,13 @@ readable, safe, optimal, and concise code to solve any computing problem.
 
 ## Roadmap
 
- * don't hardcode the link against libc
  * C style comments.
  * Unit tests.
  * Simple .so library
  * Multiple files
  * figure out integers
+ * inline assembly and syscalls
+ * running code at compile time
  * implement a simple game using SDL2
  * How should the Widget use case be solved? In Genesis I'm using C++ and inheritance.
 
@@ -74,13 +75,13 @@ Root : many(TopLevelDecl) token(EOF)
 
 TopLevelDecl : FnDef | ExternBlock
 
-ExternBlock : token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace)
+ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace)
 
 FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
 
 FnDecl : FnProto token(Semicolon)
 
-FnDef : FnProto Block
+FnDef : many(Directive) FnProto Block
 
 ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen)
 
@@ -101,4 +102,6 @@ ReturnStatement : token(Return) Expression token(Semicolon)
 Expression : token(Number) | token(String) | token(Unreachable) | FnCall
 
 FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen)
+
+Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
 ```