| author | |
| committer | |
| log | 09f68c7c339075183c67c56149620c6970b6842a |
| tree | 55afa659954e143035792d44bf4edf992d26fd18 |
| parent | afac1a0123cb574e92fbc37e99e6778c51b46683 |
now you can depend on libc in zig language instead of it being
hardcoded in the compiler.7 files changed, 120 insertions(+), 7 deletions(-)
README.md+6-3| ... | ... | @@ -40,12 +40,13 @@ readable, safe, optimal, and concise code to solve any computing problem. |
| 40 | 40 | |
| 41 | 41 | ## Roadmap |
| 42 | 42 | |
| 43 | * don't hardcode the link against libc | |
| 44 | 43 | * C style comments. |
| 45 | 44 | * Unit tests. |
| 46 | 45 | * Simple .so library |
| 47 | 46 | * Multiple files |
| 48 | 47 | * figure out integers |
| 48 | * inline assembly and syscalls | |
| 49 | * running code at compile time | |
| 49 | 50 | * implement a simple game using SDL2 |
| 50 | 51 | * How should the Widget use case be solved? In Genesis I'm using C++ and inheritance. |
| 51 | 52 | |
| ... | ... | @@ -74,13 +75,13 @@ Root : many(TopLevelDecl) token(EOF) |
| 74 | 75 | |
| 75 | 76 | TopLevelDecl : FnDef | ExternBlock |
| 76 | 77 | |
| 77 | ExternBlock : token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace) | |
| 78 | ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace) | |
| 78 | 79 | |
| 79 | 80 | FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type) |
| 80 | 81 | |
| 81 | 82 | FnDecl : FnProto token(Semicolon) |
| 82 | 83 | |
| 83 | FnDef : FnProto Block | |
| 84 | FnDef : many(Directive) FnProto Block | |
| 84 | 85 | |
| 85 | 86 | ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen) |
| 86 | 87 | |
| ... | ... | @@ -101,4 +102,6 @@ ReturnStatement : token(Return) Expression token(Semicolon) |
| 101 | 102 | Expression : token(Number) | token(String) | token(Unreachable) | FnCall |
| 102 | 103 | |
| 103 | 104 | FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen) |
| 105 | ||
| 106 | Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen) | |
| 104 | 107 | ``` |
src/codegen.cpp+27-1| ... | ... | @@ -56,6 +56,7 @@ struct CodeGen { |
| 56 | 56 | HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table; |
| 57 | 57 | HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table; |
| 58 | 58 | HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table; |
| 59 | HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table; | |
| 59 | 60 | TypeTableEntry *invalid_type_entry; |
| 60 | 61 | LLVMTargetDataRef target_data_ref; |
| 61 | 62 | unsigned pointer_size_bytes; |
| ... | ... | @@ -86,6 +87,7 @@ CodeGen *create_codegen(AstNode *root, Buf *in_full_path) { |
| 86 | 87 | g->fn_table.init(32); |
| 87 | 88 | g->str_table.init(32); |
| 88 | 89 | g->type_table.init(32); |
| 90 | g->link_table.init(32); | |
| 89 | 91 | g->is_static = false; |
| 90 | 92 | g->build_type = CodeGenBuildTypeDebug; |
| 91 | 93 | g->strip_debug_symbols = false; |
| ... | ... | @@ -198,6 +200,18 @@ static void analyze_node(CodeGen *g, AstNode *node) { |
| 198 | 200 | } |
| 199 | 201 | break; |
| 200 | 202 | case NodeTypeExternBlock: |
| 203 | for (int i = 0; i < node->data.extern_block.directives->length; i += 1) { | |
| 204 | AstNode *directive_node = node->data.extern_block.directives->at(i); | |
| 205 | Buf *name = &directive_node->data.directive.name; | |
| 206 | Buf *param = &directive_node->data.directive.param; | |
| 207 | if (buf_eql_str(name, "link")) { | |
| 208 | g->link_table.put(param, true); | |
| 209 | } else { | |
| 210 | add_node_error(g, node, | |
| 211 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 212 | } | |
| 213 | } | |
| 214 | ||
| 201 | 215 | for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) { |
| 202 | 216 | AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i); |
| 203 | 217 | analyze_node(g, fn_decl); |
| ... | ... | @@ -306,6 +320,8 @@ static void analyze_node(CodeGen *g, AstNode *node) { |
| 306 | 320 | analyze_node(g, child); |
| 307 | 321 | } |
| 308 | 322 | break; |
| 323 | case NodeTypeDirective: | |
| 324 | break; | |
| 309 | 325 | } |
| 310 | 326 | } |
| 311 | 327 | |
| ... | ... | @@ -639,6 +655,16 @@ void code_gen_link(CodeGen *g, const char *out_file) { |
| 639 | 655 | args.append("-o"); |
| 640 | 656 | args.append(out_file); |
| 641 | 657 | args.append((const char *)buf_ptr(&out_file_o)); |
| 642 | args.append("-lc"); | |
| 658 | ||
| 659 | auto it = g->link_table.entry_iterator(); | |
| 660 | for (;;) { | |
| 661 | auto *entry = it.next(); | |
| 662 | if (!entry) | |
| 663 | break; | |
| 664 | ||
| 665 | Buf *arg = buf_sprintf("-l%s", buf_ptr(entry->key)); | |
| 666 | args.append(buf_ptr(arg)); | |
| 667 | } | |
| 668 | ||
| 643 | 669 | os_spawn_process("ld", args, false); |
| 644 | 670 | } |
src/parser.cpp+72-3| ... | ... | @@ -48,6 +48,8 @@ const char *node_type_str(NodeType node_type) { |
| 48 | 48 | return "FnCall"; |
| 49 | 49 | case NodeTypeExternBlock: |
| 50 | 50 | return "ExternBlock"; |
| 51 | case NodeTypeDirective: | |
| 52 | return "Directive"; | |
| 51 | 53 | } |
| 52 | 54 | zig_unreachable(); |
| 53 | 55 | } |
| ... | ... | @@ -158,13 +160,23 @@ struct ParseContext { |
| 158 | 160 | Buf *buf; |
| 159 | 161 | AstNode *root; |
| 160 | 162 | ZigList<Token> *tokens; |
| 163 | ZigList<AstNode *> *directive_list; | |
| 161 | 164 | }; |
| 162 | 165 | |
| 163 | static AstNode *ast_create_node(NodeType type, Token *first_token) { | |
| 166 | static AstNode *ast_create_node_no_line_info(NodeType type) { | |
| 164 | 167 | AstNode *node = allocate<AstNode>(1); |
| 165 | 168 | node->type = type; |
| 169 | return node; | |
| 170 | } | |
| 171 | ||
| 172 | static void ast_update_node_line_info(AstNode *node, Token *first_token) { | |
| 166 | 173 | node->line = first_token->start_line; |
| 167 | 174 | node->column = first_token->start_column; |
| 175 | } | |
| 176 | ||
| 177 | static AstNode *ast_create_node(NodeType type, Token *first_token) { | |
| 178 | AstNode *node = ast_create_node_no_line_info(type); | |
| 179 | ast_update_node_line_info(node, first_token); | |
| 168 | 180 | return node; |
| 169 | 181 | } |
| 170 | 182 | |
| ... | ... | @@ -536,7 +548,57 @@ static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_to |
| 536 | 548 | } |
| 537 | 549 | |
| 538 | 550 | /* |
| 539 | ExternBlock : token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace) | |
| 551 | Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen) | |
| 552 | */ | |
| 553 | static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_token_index) { | |
| 554 | Token *number_sign = &pc->tokens->at(token_index); | |
| 555 | token_index += 1; | |
| 556 | ast_expect_token(pc, number_sign, TokenIdNumberSign); | |
| 557 | ||
| 558 | AstNode *node = ast_create_node(NodeTypeDirective, number_sign); | |
| 559 | ||
| 560 | Token *name_symbol = &pc->tokens->at(token_index); | |
| 561 | token_index += 1; | |
| 562 | ast_expect_token(pc, name_symbol, TokenIdSymbol); | |
| 563 | ||
| 564 | ast_buf_from_token(pc, name_symbol, &node->data.directive.name); | |
| 565 | ||
| 566 | Token *l_paren = &pc->tokens->at(token_index); | |
| 567 | token_index += 1; | |
| 568 | ast_expect_token(pc, l_paren, TokenIdLParen); | |
| 569 | ||
| 570 | Token *param_str = &pc->tokens->at(token_index); | |
| 571 | token_index += 1; | |
| 572 | ast_expect_token(pc, param_str, TokenIdStringLiteral); | |
| 573 | ||
| 574 | parse_string_literal(pc, param_str, &node->data.directive.param); | |
| 575 | ||
| 576 | Token *r_paren = &pc->tokens->at(token_index); | |
| 577 | token_index += 1; | |
| 578 | ast_expect_token(pc, r_paren, TokenIdRParen); | |
| 579 | ||
| 580 | *new_token_index = token_index; | |
| 581 | return node; | |
| 582 | } | |
| 583 | ||
| 584 | static void ast_parse_directives(ParseContext *pc, int token_index, int *new_token_index, | |
| 585 | ZigList<AstNode *> *directives) | |
| 586 | { | |
| 587 | for (;;) { | |
| 588 | Token *token = &pc->tokens->at(token_index); | |
| 589 | if (token->id == TokenIdNumberSign) { | |
| 590 | AstNode *directive_node = ast_parse_directive(pc, token_index, &token_index); | |
| 591 | directives->append(directive_node); | |
| 592 | } else { | |
| 593 | *new_token_index = token_index; | |
| 594 | return; | |
| 595 | } | |
| 596 | } | |
| 597 | zig_unreachable(); | |
| 598 | } | |
| 599 | ||
| 600 | /* | |
| 601 | ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace) | |
| 540 | 602 | */ |
| 541 | 603 | static AstNode *ast_parse_extern_block(ParseContext *pc, int token_index, int *new_token_index) { |
| 542 | 604 | 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 |
| 545 | 607 | |
| 546 | 608 | AstNode *node = ast_create_node(NodeTypeExternBlock, extern_kw); |
| 547 | 609 | |
| 610 | node->data.extern_block.directives = pc->directive_list; | |
| 611 | pc->directive_list = nullptr; | |
| 612 | ||
| 548 | 613 | Token *l_brace = &pc->tokens->at(token_index); |
| 549 | 614 | token_index += 1; |
| 550 | 615 | 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 |
| 570 | 635 | { |
| 571 | 636 | for (;;) { |
| 572 | 637 | Token *token = &pc->tokens->at(token_index); |
| 573 | if (token->id == TokenIdKeywordFn) { | |
| 638 | if (token->id == TokenIdNumberSign) { | |
| 639 | assert(!pc->directive_list); | |
| 640 | pc->directive_list = allocate<ZigList<AstNode*>>(1); | |
| 641 | ast_parse_directives(pc, token_index, &token_index, pc->directive_list); | |
| 642 | } else if (token->id == TokenIdKeywordFn) { | |
| 574 | 643 | AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, &token_index); |
| 575 | 644 | top_level_decls->append(fn_decl_node); |
| 576 | 645 | } else if (token->id == TokenIdKeywordExtern) { |
src/parser.hpp+8| ... | ... | @@ -27,6 +27,7 @@ enum NodeType { |
| 27 | 27 | NodeTypeExpression, |
| 28 | 28 | NodeTypeFnCall, |
| 29 | 29 | NodeTypeExternBlock, |
| 30 | NodeTypeDirective, | |
| 30 | 31 | }; |
| 31 | 32 | |
| 32 | 33 | struct AstNodeRoot { |
| ... | ... | @@ -112,9 +113,15 @@ struct AstNodeFnCall { |
| 112 | 113 | }; |
| 113 | 114 | |
| 114 | 115 | struct AstNodeExternBlock { |
| 116 | ZigList<AstNode *> *directives; | |
| 115 | 117 | ZigList<AstNode *> fn_decls; |
| 116 | 118 | }; |
| 117 | 119 | |
| 120 | struct AstNodeDirective { | |
| 121 | Buf name; | |
| 122 | Buf param; | |
| 123 | }; | |
| 124 | ||
| 118 | 125 | struct AstNode { |
| 119 | 126 | enum NodeType type; |
| 120 | 127 | AstNode *parent; |
| ... | ... | @@ -133,6 +140,7 @@ struct AstNode { |
| 133 | 140 | AstNodeExpression expression; |
| 134 | 141 | AstNodeFnCall fn_call; |
| 135 | 142 | AstNodeExternBlock extern_block; |
| 143 | AstNodeDirective directive; | |
| 136 | 144 | } data; |
| 137 | 145 | }; |
| 138 | 146 |
src/tokenizer.cpp+5| ... | ... | @@ -218,6 +218,10 @@ ZigList<Token> *tokenize(Buf *buf) { |
| 218 | 218 | begin_token(&t, TokenIdDash); |
| 219 | 219 | t.state = TokenizeStateSawDash; |
| 220 | 220 | break; |
| 221 | case '#': | |
| 222 | begin_token(&t, TokenIdNumberSign); | |
| 223 | end_token(&t); | |
| 224 | break; | |
| 221 | 225 | default: |
| 222 | 226 | tokenize_error(&t, "invalid character: '%c'", c); |
| 223 | 227 | } |
| ... | ... | @@ -321,6 +325,7 @@ static const char * token_name(Token *token) { |
| 321 | 325 | case TokenIdColon: return "Colon"; |
| 322 | 326 | case TokenIdArrow: return "Arrow"; |
| 323 | 327 | case TokenIdDash: return "Dash"; |
| 328 | case TokenIdNumberSign: return "NumberSign"; | |
| 324 | 329 | } |
| 325 | 330 | return "(invalid token)"; |
| 326 | 331 | } |
src/tokenizer.hpp+1| ... | ... | @@ -32,6 +32,7 @@ enum TokenId { |
| 32 | 32 | TokenIdColon, |
| 33 | 33 | TokenIdArrow, |
| 34 | 34 | TokenIdDash, |
| 35 | TokenIdNumberSign, | |
| 35 | 36 | }; |
| 36 | 37 | |
| 37 | 38 | struct Token { |
test/hello.zig+1| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | #link("c") | |
| 1 | 2 | extern { |
| 2 | 3 | fn puts(s: *mut u8) -> i32; |
| 3 | 4 | fn exit(code: i32) -> unreachable; |