| author | |
| committer | |
| log | dfda85e870df1b0620c418940db3b946fdd3d620 |
| tree | 94d4d7e158f62ee2cd153b002165bc3ef7aa7c47 |
| parent | 4eff5f114b463ddd887665129f2e1d29b0f13b7f |
10 files changed, 121 insertions(+), 29 deletions(-)
README.md+4-2| ... | ... | @@ -58,7 +58,6 @@ compromises backward compatibility. |
| 58 | 58 | * structs |
| 59 | 59 | * loops |
| 60 | 60 | * enums |
| 61 | * calling external variadic functions and exporting variadic functions | |
| 62 | 61 | * inline assembly and syscalls |
| 63 | 62 | * conditional compilation and ability to check target platform and architecture |
| 64 | 63 | * main function with command line arguments |
| ... | ... | @@ -69,6 +68,9 @@ compromises backward compatibility. |
| 69 | 68 | * static initializers |
| 70 | 69 | * assert |
| 71 | 70 | * function pointers |
| 71 | * hex literal, binary literal, float literal, hex float literal | |
| 72 | * += and -= operators | |
| 73 | * fix a + b + c | |
| 72 | 74 | * running code at compile time |
| 73 | 75 | * standard library print functions |
| 74 | 76 | * panic! macro or statement that prints a stack trace to stderr in debug mode |
| ... | ... | @@ -144,7 +146,7 @@ FnDef : FnProto Block |
| 144 | 146 | |
| 145 | 147 | ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen) |
| 146 | 148 | |
| 147 | ParamDecl : token(Symbol) token(Colon) Type | |
| 149 | ParamDecl : token(Symbol) token(Colon) Type | token(Ellipse) | |
| 148 | 150 | |
| 149 | 151 | Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType |
| 150 | 152 |
example/hello_world/hello.zig+3-3| ... | ... | @@ -2,11 +2,11 @@ export executable "hello"; |
| 2 | 2 | |
| 3 | 3 | #link("c") |
| 4 | 4 | extern { |
| 5 | fn puts(s: *const u8) -> i32; | |
| 6 | fn exit(code: i32) -> unreachable; | |
| 5 | fn printf(__format: *const u8, ...) -> i32; | |
| 6 | fn exit(__status: i32) -> unreachable; | |
| 7 | 7 | } |
| 8 | 8 | |
| 9 | 9 | export fn _start() -> unreachable { |
| 10 | puts("Hello, world!"); | |
| 10 | printf("Hello, world!\n"); | |
| 11 | 11 | exit(0); |
| 12 | 12 | } |
src/analyze.cpp+14-1| ... | ... | @@ -163,9 +163,12 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { |
| 163 | 163 | { |
| 164 | 164 | resolve_type(g, node->data.type.child_type); |
| 165 | 165 | TypeTableEntry *child_type = node->data.type.child_type->codegen_node->data.type_node.entry; |
| 166 | assert(child_type); | |
| 166 | 167 | if (child_type == g->builtin_types.entry_unreachable) { |
| 167 | 168 | add_node_error(g, node, |
| 168 | 169 | buf_create_from_str("pointer to unreachable not allowed")); |
| 170 | } else if (child_type->id == TypeTableEntryIdInvalid) { | |
| 171 | return child_type; | |
| 169 | 172 | } |
| 170 | 173 | type_node->entry = get_pointer_to_type(g, child_type, node->data.type.is_const); |
| 171 | 174 | return type_node->entry; |
| ... | ... | @@ -312,6 +315,10 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 312 | 315 | skip = true; |
| 313 | 316 | } |
| 314 | 317 | } |
| 318 | if (proto_node->data.fn_proto.is_var_args) { | |
| 319 | add_node_error(g, node, | |
| 320 | buf_sprintf("variadic arguments only allowed in extern functions")); | |
| 321 | } | |
| 315 | 322 | if (!skip) { |
| 316 | 323 | FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1); |
| 317 | 324 | fn_table_entry->import_entry = import; |
| ... | ... | @@ -743,7 +750,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 743 | 750 | // count parameters |
| 744 | 751 | int expected_param_count = fn_proto->params.length; |
| 745 | 752 | int actual_param_count = node->data.fn_call_expr.params.length; |
| 746 | if (expected_param_count != actual_param_count) { | |
| 753 | if (fn_proto->is_var_args) { | |
| 754 | if (actual_param_count < expected_param_count) { | |
| 755 | add_node_error(g, node, | |
| 756 | buf_sprintf("wrong number of arguments. Expected at least %d, got %d.", | |
| 757 | expected_param_count, actual_param_count)); | |
| 758 | } | |
| 759 | } else if (expected_param_count != actual_param_count) { | |
| 747 | 760 | add_node_error(g, node, |
| 748 | 761 | buf_sprintf("wrong number of arguments. Expected %d, got %d.", |
| 749 | 762 | expected_param_count, actual_param_count)); |
src/codegen.cpp+19-6| ... | ... | @@ -138,19 +138,32 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 138 | 138 | fn_table_entry = g->fn_table.get(name); |
| 139 | 139 | |
| 140 | 140 | assert(fn_table_entry->proto_node->type == NodeTypeFnProto); |
| 141 | int expected_param_count = fn_table_entry->proto_node->data.fn_proto.params.length; | |
| 141 | AstNodeFnProto *fn_proto_data = &fn_table_entry->proto_node->data.fn_proto; | |
| 142 | ||
| 143 | int expected_param_count = fn_proto_data->params.length; | |
| 142 | 144 | int actual_param_count = node->data.fn_call_expr.params.length; |
| 143 | assert(expected_param_count == actual_param_count); | |
| 145 | bool is_var_args = fn_proto_data->is_var_args; | |
| 146 | assert((is_var_args && actual_param_count >= expected_param_count) || | |
| 147 | actual_param_count == expected_param_count); | |
| 144 | 148 | |
| 145 | 149 | // don't really include void values |
| 146 | int gen_param_count = count_non_void_params(g, &fn_table_entry->proto_node->data.fn_proto.params); | |
| 150 | int gen_param_count; | |
| 151 | if (is_var_args) { | |
| 152 | gen_param_count = actual_param_count; | |
| 153 | } else { | |
| 154 | gen_param_count = count_non_void_params(g, &fn_table_entry->proto_node->data.fn_proto.params); | |
| 155 | } | |
| 147 | 156 | LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(gen_param_count); |
| 148 | 157 | |
| 158 | int loop_end = max(gen_param_count, actual_param_count); | |
| 159 | ||
| 149 | 160 | int gen_param_index = 0; |
| 150 | for (int i = 0; i < actual_param_count; i += 1) { | |
| 161 | for (int i = 0; i < loop_end; i += 1) { | |
| 151 | 162 | AstNode *expr_node = node->data.fn_call_expr.params.at(i); |
| 152 | 163 | LLVMValueRef param_value = gen_expr(g, expr_node); |
| 153 | if (!is_param_decl_type_void(g, fn_table_entry->proto_node->data.fn_proto.params.at(i))) { | |
| 164 | if (is_var_args || | |
| 165 | !is_param_decl_type_void(g, fn_table_entry->proto_node->data.fn_proto.params.at(i))) | |
| 166 | { | |
| 154 | 167 | gen_param_values[gen_param_index] = param_value; |
| 155 | 168 | gen_param_index += 1; |
| 156 | 169 | } |
| ... | ... | @@ -773,7 +786,7 @@ static void do_code_gen(CodeGen *g) { |
| 773 | 786 | param_types[gen_param_index] = to_llvm_type(type_node); |
| 774 | 787 | gen_param_index += 1; |
| 775 | 788 | } |
| 776 | LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, 0); | |
| 789 | LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, param_count, fn_proto->is_var_args); | |
| 777 | 790 | LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_proto->name), function_type); |
| 778 | 791 | |
| 779 | 792 | LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage); |
src/parseh.cpp+7-3| ... | ... | @@ -132,10 +132,10 @@ static bool resolves_to_void(ParseH *p, CXType raw_type) { |
| 132 | 132 | static Buf *to_zig_type(ParseH *p, CXType raw_type) { |
| 133 | 133 | if (raw_type.kind == CXType_Unexposed) { |
| 134 | 134 | CXType canonical = clang_getCanonicalType(raw_type); |
| 135 | if (canonical.kind != CXType_Unexposed) | |
| 136 | return to_zig_type(p, canonical); | |
| 137 | else | |
| 135 | if (canonical.kind == CXType_Unexposed) | |
| 138 | 136 | zig_panic("clang C api insufficient"); |
| 137 | else | |
| 138 | return to_zig_type(p, canonical); | |
| 139 | 139 | } |
| 140 | 140 | switch (raw_type.kind) { |
| 141 | 141 | case CXType_Invalid: |
| ... | ... | @@ -453,6 +453,10 @@ static enum CXChildVisitResult fn_visitor(CXCursor cursor, CXCursor parent, CXCl |
| 453 | 453 | } else if (underlying_type.kind == CXType_Record) { |
| 454 | 454 | CXCursor decl_cursor = clang_getTypeDeclaration(underlying_type); |
| 455 | 455 | skip_typedef = handle_struct_cursor(p, decl_cursor, clang_getCString(name), false); |
| 456 | } else if (underlying_type.kind == CXType_Invalid) { | |
| 457 | fprintf(stderr, "warning: invalid type\n"); | |
| 458 | print_location(p); | |
| 459 | skip_typedef = true; | |
| 456 | 460 | } else { |
| 457 | 461 | skip_typedef = false; |
| 458 | 462 | } |
src/parser.cpp+30-14| ... | ... | @@ -517,32 +517,39 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token |
| 517 | 517 | } |
| 518 | 518 | |
| 519 | 519 | /* |
| 520 | ParamDecl : token(Symbol) token(Colon) Type | |
| 520 | ParamDecl : token(Symbol) token(Colon) Type | token(Ellipse) | |
| 521 | 521 | */ |
| 522 | 522 | static AstNode *ast_parse_param_decl(ParseContext *pc, int token_index, int *new_token_index) { |
| 523 | 523 | Token *param_name = &pc->tokens->at(token_index); |
| 524 | 524 | token_index += 1; |
| 525 | ast_expect_token(pc, param_name, TokenIdSymbol); | |
| 526 | 525 | |
| 527 | AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name); | |
| 526 | if (param_name->id == TokenIdSymbol) { | |
| 527 | AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name); | |
| 528 | 528 | |
| 529 | ast_buf_from_token(pc, param_name, &node->data.param_decl.name); | |
| 529 | 530 | |
| 530 | ast_buf_from_token(pc, param_name, &node->data.param_decl.name); | |
| 531 | ||
| 532 | Token *colon = &pc->tokens->at(token_index); | |
| 533 | token_index += 1; | |
| 534 | ast_expect_token(pc, colon, TokenIdColon); | |
| 531 | Token *colon = &pc->tokens->at(token_index); | |
| 532 | token_index += 1; | |
| 533 | ast_expect_token(pc, colon, TokenIdColon); | |
| 535 | 534 | |
| 536 | node->data.param_decl.type = ast_parse_type(pc, token_index, &token_index); | |
| 535 | node->data.param_decl.type = ast_parse_type(pc, token_index, &token_index); | |
| 537 | 536 | |
| 538 | *new_token_index = token_index; | |
| 539 | return node; | |
| 537 | *new_token_index = token_index; | |
| 538 | return node; | |
| 539 | } else if (param_name->id == TokenIdEllipse) { | |
| 540 | *new_token_index = token_index; | |
| 541 | return nullptr; | |
| 542 | } else { | |
| 543 | ast_invalid_token_error(pc, param_name); | |
| 544 | } | |
| 540 | 545 | } |
| 541 | 546 | |
| 542 | 547 | |
| 543 | 548 | static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *new_token_index, |
| 544 | ZigList<AstNode *> *params) | |
| 549 | ZigList<AstNode *> *params, bool *is_var_args) | |
| 545 | 550 | { |
| 551 | *is_var_args = false; | |
| 552 | ||
| 546 | 553 | Token *l_paren = &pc->tokens->at(token_index); |
| 547 | 554 | token_index += 1; |
| 548 | 555 | ast_expect_token(pc, l_paren, TokenIdLParen); |
| ... | ... | @@ -556,13 +563,21 @@ static void ast_parse_param_decl_list(ParseContext *pc, int token_index, int *ne |
| 556 | 563 | |
| 557 | 564 | for (;;) { |
| 558 | 565 | AstNode *param_decl_node = ast_parse_param_decl(pc, token_index, &token_index); |
| 559 | params->append(param_decl_node); | |
| 566 | bool expect_end = false; | |
| 567 | if (param_decl_node) { | |
| 568 | params->append(param_decl_node); | |
| 569 | } else { | |
| 570 | *is_var_args = true; | |
| 571 | expect_end = true; | |
| 572 | } | |
| 560 | 573 | |
| 561 | 574 | Token *token = &pc->tokens->at(token_index); |
| 562 | 575 | token_index += 1; |
| 563 | 576 | if (token->id == TokenIdRParen) { |
| 564 | 577 | *new_token_index = token_index; |
| 565 | 578 | return; |
| 579 | } else if (expect_end) { | |
| 580 | ast_invalid_token_error(pc, token); | |
| 566 | 581 | } else { |
| 567 | 582 | ast_expect_token(pc, token, TokenIdComma); |
| 568 | 583 | } |
| ... | ... | @@ -1421,7 +1436,8 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand |
| 1421 | 1436 | ast_buf_from_token(pc, fn_name, &node->data.fn_proto.name); |
| 1422 | 1437 | |
| 1423 | 1438 | |
| 1424 | ast_parse_param_decl_list(pc, *token_index, token_index, &node->data.fn_proto.params); | |
| 1439 | ast_parse_param_decl_list(pc, *token_index, token_index, | |
| 1440 | &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); | |
| 1425 | 1441 | |
| 1426 | 1442 | Token *arrow = &pc->tokens->at(*token_index); |
| 1427 | 1443 | if (arrow->id == TokenIdArrow) { |
src/parser.hpp+1| ... | ... | @@ -63,6 +63,7 @@ struct AstNodeFnProto { |
| 63 | 63 | Buf name; |
| 64 | 64 | ZigList<AstNode *> params; |
| 65 | 65 | AstNode *return_type; |
| 66 | bool is_var_args; | |
| 66 | 67 | }; |
| 67 | 68 | |
| 68 | 69 | struct AstNodeFnDef { |
src/tokenizer.cpp+36| ... | ... | @@ -104,6 +104,8 @@ enum TokenizeState { |
| 104 | 104 | TokenizeStateBang, |
| 105 | 105 | TokenizeStateLessThan, |
| 106 | 106 | TokenizeStateGreaterThan, |
| 107 | TokenizeStateDot, | |
| 108 | TokenizeStateDotDot, | |
| 107 | 109 | TokenizeStateError, |
| 108 | 110 | }; |
| 109 | 111 | |
| ... | ... | @@ -323,10 +325,40 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 323 | 325 | begin_token(&t, TokenIdCmpGreaterThan); |
| 324 | 326 | t.state = TokenizeStateGreaterThan; |
| 325 | 327 | break; |
| 328 | case '.': | |
| 329 | begin_token(&t, TokenIdDot); | |
| 330 | t.state = TokenizeStateDot; | |
| 331 | break; | |
| 326 | 332 | default: |
| 327 | 333 | tokenize_error(&t, "invalid character: '%c'", c); |
| 328 | 334 | } |
| 329 | 335 | break; |
| 336 | case TokenizeStateDot: | |
| 337 | switch (c) { | |
| 338 | case '.': | |
| 339 | t.state = TokenizeStateDotDot; | |
| 340 | t.cur_tok->id = TokenIdEllipse; | |
| 341 | break; | |
| 342 | default: | |
| 343 | t.pos -= 1; | |
| 344 | end_token(&t); | |
| 345 | t.state = TokenizeStateStart; | |
| 346 | continue; | |
| 347 | } | |
| 348 | break; | |
| 349 | case TokenizeStateDotDot: | |
| 350 | switch (c) { | |
| 351 | case '.': | |
| 352 | t.state = TokenizeStateStart; | |
| 353 | end_token(&t); | |
| 354 | break; | |
| 355 | default: | |
| 356 | t.pos -= 1; | |
| 357 | end_token(&t); | |
| 358 | t.state = TokenizeStateStart; | |
| 359 | continue; | |
| 360 | } | |
| 361 | break; | |
| 330 | 362 | case TokenizeStateGreaterThan: |
| 331 | 363 | switch (c) { |
| 332 | 364 | case '=': |
| ... | ... | @@ -561,9 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 561 | 593 | case TokenizeStateBang: |
| 562 | 594 | case TokenizeStateLessThan: |
| 563 | 595 | case TokenizeStateGreaterThan: |
| 596 | case TokenizeStateDot: | |
| 564 | 597 | end_token(&t); |
| 565 | 598 | break; |
| 566 | 599 | case TokenizeStateSawSlash: |
| 600 | case TokenizeStateDotDot: | |
| 567 | 601 | tokenize_error(&t, "unexpected EOF"); |
| 568 | 602 | break; |
| 569 | 603 | case TokenizeStateLineComment: |
| ... | ... | @@ -637,6 +671,8 @@ static const char * token_name(Token *token) { |
| 637 | 671 | case TokenIdBitShiftRight: return "BitShiftRight"; |
| 638 | 672 | case TokenIdSlash: return "Slash"; |
| 639 | 673 | case TokenIdPercent: return "Percent"; |
| 674 | case TokenIdDot: return "Dot"; | |
| 675 | case TokenIdEllipse: return "Ellipse"; | |
| 640 | 676 | } |
| 641 | 677 | return "(invalid token)"; |
| 642 | 678 | } |
src/tokenizer.hpp+2| ... | ... | @@ -64,6 +64,8 @@ enum TokenId { |
| 64 | 64 | TokenIdBitShiftRight, |
| 65 | 65 | TokenIdSlash, |
| 66 | 66 | TokenIdPercent, |
| 67 | TokenIdDot, | |
| 68 | TokenIdEllipse, | |
| 67 | 69 | }; |
| 68 | 70 | |
| 69 | 71 | struct Token { |
test/run_tests.cpp+5| ... | ... | @@ -577,6 +577,11 @@ fn f() { |
| 577 | 577 | ".tmp_source.zig:5:8: error: array subscripts must be integers", |
| 578 | 578 | ".tmp_source.zig:5:19: error: array access of non-array", |
| 579 | 579 | ".tmp_source.zig:5:19: error: array subscripts must be integers"); |
| 580 | ||
| 581 | add_compile_fail_case("variadic functions only allowed in extern", R"SOURCE( | |
| 582 | fn f(...) {} | |
| 583 | )SOURCE", 1, ".tmp_source.zig:2:1: error: variadic arguments only allowed in extern functions"); | |
| 584 | ||
| 580 | 585 | } |
| 581 | 586 | |
| 582 | 587 | static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { |