| author | |
| committer | |
| log | f8ca6c70c74db6e6f0d4462aa763adb6f1f41c7e |
| tree | 99bc3d06bb37abb267a2a59f3ba04523c2c430eb |
| parent | c89f77dd8e5005e60e8fb223c6c68b50566ac1ed |
14 files changed, 270 insertions(+), 27 deletions(-)
README.md+6-3| ... | ... | @@ -45,7 +45,6 @@ make |
| 45 | 45 | * variable declarations and assignment expressions |
| 46 | 46 | * Type checking |
| 47 | 47 | * loops |
| 48 | * labels and goto | |
| 49 | 48 | * inline assembly and syscalls |
| 50 | 49 | * conditional compilation and ability to check target platform and architecture |
| 51 | 50 | * main function with command line arguments |
| ... | ... | @@ -110,7 +109,9 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type |
| 110 | 109 | |
| 111 | 110 | Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) |
| 112 | 111 | |
| 113 | Statement : NonBlockExpression token(Semicolon) | BlockExpression | |
| 112 | Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression | |
| 113 | ||
| 114 | Label: token(Symbol) token(Colon) | |
| 114 | 115 | |
| 115 | 116 | Expression : BlockExpression | NonBlockExpression |
| 116 | 117 | |
| ... | ... | @@ -162,7 +163,9 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma) |
| 162 | 163 | |
| 163 | 164 | PrefixOp : token(Not) | token(Dash) | token(Tilde) |
| 164 | 165 | |
| 165 | PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | |
| 166 | PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto | |
| 167 | ||
| 168 | Goto: token(Goto) token(Symbol) | |
| 166 | 169 | |
| 167 | 170 | GroupedExpression : token(LParen) Expression token(RParen) |
| 168 | 171 | ``` |
doc/vim/syntax/zig.vim+2-2| ... | ... | @@ -1,13 +1,13 @@ |
| 1 | 1 | " Vim syntax file |
| 2 | 2 | " Language: Zig |
| 3 | 3 | " Maintainer: Andrew Kelley |
| 4 | " Latest Revision: 27 November 2015 | |
| 4 | " Latest Revision: 02 December 2015 | |
| 5 | 5 | |
| 6 | 6 | if exists("b:current_syntax") |
| 7 | 7 | finish |
| 8 | 8 | endif |
| 9 | 9 | |
| 10 | syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void | |
| 10 | syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void goto | |
| 11 | 11 | syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 |
| 12 | 12 | |
| 13 | 13 | syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell |
example/hello_world/hello.zig+12-1| ... | ... | @@ -6,7 +6,18 @@ extern { |
| 6 | 6 | fn exit(code: i32) -> unreachable; |
| 7 | 7 | } |
| 8 | 8 | |
| 9 | fn loop(a : i32) { | |
| 10 | if a == 0 { | |
| 11 | goto done; | |
| 12 | } | |
| 13 | puts("loop"); | |
| 14 | loop(a - 1); | |
| 15 | ||
| 16 | done: | |
| 17 | return; | |
| 18 | } | |
| 19 | ||
| 9 | 20 | export fn _start() -> unreachable { |
| 10 | puts("Hello, world!"); | |
| 21 | loop(3); | |
| 11 | 22 | exit(0); |
| 12 | 23 | } |
src/analyze.cpp+65-7| ... | ... | @@ -131,6 +131,25 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 131 | 131 | resolve_type(g, node->data.fn_proto.return_type); |
| 132 | 132 | } |
| 133 | 133 | |
| 134 | static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) { | |
| 135 | assert(node->type == NodeTypeBlock); | |
| 136 | ||
| 137 | for (int i = 0; i < node->data.block.statements.length; i += 1) { | |
| 138 | AstNode *label_node = node->data.block.statements.at(i); | |
| 139 | if (label_node->type != NodeTypeLabel) | |
| 140 | continue; | |
| 141 | ||
| 142 | LabelTableEntry *label_entry = allocate<LabelTableEntry>(1); | |
| 143 | label_entry->label_node = label_node; | |
| 144 | Buf *name = &label_node->data.label.name; | |
| 145 | fn_table_entry->label_table.put(name, label_entry); | |
| 146 | ||
| 147 | assert(!label_node->codegen_node); | |
| 148 | label_node->codegen_node = allocate<CodeGenNode>(1); | |
| 149 | label_node->codegen_node->data.label_entry = label_entry; | |
| 150 | } | |
| 151 | } | |
| 152 | ||
| 134 | 153 | static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 135 | 154 | switch (node->type) { |
| 136 | 155 | case NodeTypeExternBlock: |
| ... | ... | @@ -158,6 +177,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 158 | 177 | fn_table_entry->calling_convention = LLVMCCallConv; |
| 159 | 178 | fn_table_entry->import_entry = import; |
| 160 | 179 | fn_table_entry->symbol_table.init(8); |
| 180 | fn_table_entry->label_table.init(8); | |
| 161 | 181 | |
| 162 | 182 | resolve_function_proto(g, fn_proto, fn_table_entry); |
| 163 | 183 | |
| ... | ... | @@ -208,6 +228,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 208 | 228 | fn_table_entry->internal_linkage = is_internal; |
| 209 | 229 | fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv; |
| 210 | 230 | fn_table_entry->symbol_table.init(8); |
| 231 | fn_table_entry->label_table.init(8); | |
| 211 | 232 | |
| 212 | 233 | g->fn_protos.append(fn_table_entry); |
| 213 | 234 | g->fn_defs.append(fn_table_entry); |
| ... | ... | @@ -222,6 +243,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 222 | 243 | assert(!proto_node->codegen_node); |
| 223 | 244 | proto_node->codegen_node = allocate<CodeGenNode>(1); |
| 224 | 245 | proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry; |
| 246 | ||
| 247 | preview_function_labels(g, node->data.fn_def.body, fn_table_entry); | |
| 225 | 248 | } |
| 226 | 249 | } |
| 227 | 250 | break; |
| ... | ... | @@ -290,6 +313,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 290 | 313 | case NodeTypeCastExpr: |
| 291 | 314 | case NodeTypePrefixOpExpr: |
| 292 | 315 | case NodeTypeIfExpr: |
| 316 | case NodeTypeLabel: | |
| 317 | case NodeTypeGoto: | |
| 293 | 318 | zig_unreachable(); |
| 294 | 319 | } |
| 295 | 320 | } |
| ... | ... | @@ -339,6 +364,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 339 | 364 | return_type = g->builtin_types.entry_void; |
| 340 | 365 | for (int i = 0; i < node->data.block.statements.length; i += 1) { |
| 341 | 366 | AstNode *child = node->data.block.statements.at(i); |
| 367 | if (child->type == NodeTypeLabel) | |
| 368 | continue; | |
| 342 | 369 | if (return_type == g->builtin_types.entry_unreachable) { |
| 343 | 370 | if (child->type == NodeTypeVoid) { |
| 344 | 371 | // {unreachable;void;void} is allowed. |
| ... | ... | @@ -365,7 +392,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 365 | 392 | |
| 366 | 393 | if (actual_return_type == g->builtin_types.entry_unreachable) { |
| 367 | 394 | // "return exit(0)" should just be "exit(0)". |
| 368 | add_node_error(g, node, buf_sprintf("returning is unreachable.")); | |
| 395 | add_node_error(g, node, buf_sprintf("returning is unreachable")); | |
| 369 | 396 | actual_return_type = g->builtin_types.entry_invalid; |
| 370 | 397 | } |
| 371 | 398 | |
| ... | ... | @@ -373,7 +400,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 373 | 400 | return_type = g->builtin_types.entry_unreachable; |
| 374 | 401 | break; |
| 375 | 402 | } |
| 376 | ||
| 377 | 403 | case NodeTypeVariableDeclaration: |
| 378 | 404 | { |
| 379 | 405 | zig_panic("TODO: analyze variable declaration"); |
| ... | ... | @@ -382,6 +408,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 382 | 408 | break; |
| 383 | 409 | } |
| 384 | 410 | |
| 411 | case NodeTypeGoto: | |
| 412 | { | |
| 413 | FnTableEntry *fn_table_entry = get_context_fn_entry(context); | |
| 414 | auto table_entry = fn_table_entry->label_table.maybe_get(&node->data.go_to.name); | |
| 415 | if (table_entry) { | |
| 416 | assert(!node->codegen_node); | |
| 417 | node->codegen_node = allocate<CodeGenNode>(1); | |
| 418 | node->codegen_node->data.label_entry = table_entry->value; | |
| 419 | } else { | |
| 420 | add_node_error(g, node, | |
| 421 | buf_sprintf("use of undeclared label '%s'", buf_ptr(&node->data.go_to.name))); | |
| 422 | } | |
| 423 | return_type = g->builtin_types.entry_unreachable; | |
| 424 | break; | |
| 425 | } | |
| 385 | 426 | case NodeTypeBinOpExpr: |
| 386 | 427 | { |
| 387 | 428 | switch (node->data.bin_op_expr.bin_op) { |
| ... | ... | @@ -563,8 +604,18 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 563 | 604 | TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, |
| 564 | 605 | node->data.if_expr.then_block); |
| 565 | 606 | |
| 566 | check_type_compatibility(g, node, expected_type, else_type); | |
| 567 | return_type = then_type; | |
| 607 | TypeTableEntry *primary_type; | |
| 608 | TypeTableEntry *other_type; | |
| 609 | if (then_type == g->builtin_types.entry_unreachable) { | |
| 610 | primary_type = else_type; | |
| 611 | other_type = then_type; | |
| 612 | } else { | |
| 613 | primary_type = then_type; | |
| 614 | other_type = else_type; | |
| 615 | } | |
| 616 | ||
| 617 | check_type_compatibility(g, node, expected_type, other_type); | |
| 618 | return_type = primary_type; | |
| 568 | 619 | break; |
| 569 | 620 | } |
| 570 | 621 | case NodeTypeDirective: |
| ... | ... | @@ -577,14 +628,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 577 | 628 | case NodeTypeExternBlock: |
| 578 | 629 | case NodeTypeFnDef: |
| 579 | 630 | case NodeTypeUse: |
| 631 | case NodeTypeLabel: | |
| 580 | 632 | zig_unreachable(); |
| 581 | 633 | } |
| 582 | 634 | assert(return_type); |
| 583 | 635 | check_type_compatibility(g, node, expected_type, return_type); |
| 584 | 636 | |
| 585 | assert(!node->codegen_node); | |
| 586 | node->codegen_node = allocate<CodeGenNode>(1); | |
| 587 | node->codegen_node->data.expr_node.type_entry = return_type; | |
| 637 | if (node->codegen_node) { | |
| 638 | assert(node->type == NodeTypeGoto); | |
| 639 | } else { | |
| 640 | assert(node->type != NodeTypeGoto); | |
| 641 | node->codegen_node = allocate<CodeGenNode>(1); | |
| 642 | } | |
| 643 | node->codegen_node->expr_node.type_entry = return_type; | |
| 588 | 644 | |
| 589 | 645 | return return_type; |
| 590 | 646 | } |
| ... | ... | @@ -652,6 +708,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 652 | 708 | case NodeTypeCastExpr: |
| 653 | 709 | case NodeTypePrefixOpExpr: |
| 654 | 710 | case NodeTypeIfExpr: |
| 711 | case NodeTypeLabel: | |
| 712 | case NodeTypeGoto: | |
| 655 | 713 | zig_unreachable(); |
| 656 | 714 | } |
| 657 | 715 | } |
src/codegen.cpp+35-4| ... | ... | @@ -115,7 +115,7 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) { |
| 115 | 115 | } |
| 116 | 116 | |
| 117 | 117 | static TypeTableEntry *get_expr_type(AstNode *node) { |
| 118 | return node->codegen_node->data.expr_node.type_entry; | |
| 118 | return node->codegen_node->expr_node.type_entry; | |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | 121 | static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| ... | ... | @@ -407,11 +407,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { |
| 407 | 407 | |
| 408 | 408 | LLVMPositionBuilderAtEnd(g->builder, then_block); |
| 409 | 409 | LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block); |
| 410 | LLVMBuildBr(g->builder, endif_block); | |
| 410 | if (get_expr_type(node->data.if_expr.then_block) != g->builtin_types.entry_unreachable) | |
| 411 | LLVMBuildBr(g->builder, endif_block); | |
| 411 | 412 | |
| 412 | 413 | LLVMPositionBuilderAtEnd(g->builder, else_block); |
| 413 | 414 | LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node); |
| 414 | LLVMBuildBr(g->builder, endif_block); | |
| 415 | if (get_expr_type(node->data.if_expr.else_node) != g->builtin_types.entry_unreachable) | |
| 416 | LLVMBuildBr(g->builder, endif_block); | |
| 415 | 417 | |
| 416 | 418 | LLVMPositionBuilderAtEnd(g->builder, endif_block); |
| 417 | 419 | if (use_expr_value) { |
| ... | ... | @@ -435,7 +437,8 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { |
| 435 | 437 | |
| 436 | 438 | LLVMPositionBuilderAtEnd(g->builder, then_block); |
| 437 | 439 | gen_expr(g, node->data.if_expr.then_block); |
| 438 | LLVMBuildBr(g->builder, endif_block); | |
| 440 | if (get_expr_type(node->data.if_expr.then_block) != g->builtin_types.entry_unreachable) | |
| 441 | LLVMBuildBr(g->builder, endif_block); | |
| 439 | 442 | |
| 440 | 443 | LLVMPositionBuilderAtEnd(g->builder, endif_block); |
| 441 | 444 | return nullptr; |
| ... | ... | @@ -518,6 +521,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 518 | 521 | } |
| 519 | 522 | case NodeTypeBlock: |
| 520 | 523 | return gen_block(g, node, nullptr); |
| 524 | case NodeTypeGoto: | |
| 525 | add_debug_source_node(g, node); | |
| 526 | return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); | |
| 527 | case NodeTypeLabel: | |
| 528 | { | |
| 529 | LLVMBasicBlockRef basic_block = node->codegen_node->data.label_entry->basic_block; | |
| 530 | add_debug_source_node(g, node); | |
| 531 | LLVMValueRef result = LLVMBuildBr(g->builder, basic_block); | |
| 532 | LLVMPositionBuilderAtEnd(g->builder, basic_block); | |
| 533 | return result; | |
| 534 | } | |
| 521 | 535 | case NodeTypeRoot: |
| 522 | 536 | case NodeTypeRootExportDecl: |
| 523 | 537 | case NodeTypeFnProto: |
| ... | ... | @@ -533,6 +547,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 533 | 547 | zig_unreachable(); |
| 534 | 548 | } |
| 535 | 549 | |
| 550 | static void build_label_blocks(CodeGen *g, AstNode *block_node) { | |
| 551 | assert(block_node->type == NodeTypeBlock); | |
| 552 | for (int i = 0; i < block_node->data.block.statements.length; i += 1) { | |
| 553 | AstNode *label_node = block_node->data.block.statements.at(i); | |
| 554 | if (label_node->type != NodeTypeLabel) | |
| 555 | continue; | |
| 556 | ||
| 557 | Buf *name = &label_node->data.label.name; | |
| 558 | label_node->codegen_node->data.label_entry->basic_block = LLVMAppendBasicBlock( | |
| 559 | g->cur_fn->fn_value, buf_ptr(name)); | |
| 560 | } | |
| 561 | ||
| 562 | } | |
| 563 | ||
| 536 | 564 | static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto, |
| 537 | 565 | LLVMZigDIFile *di_file) |
| 538 | 566 | { |
| ... | ... | @@ -623,10 +651,13 @@ static void do_code_gen(CodeGen *g) { |
| 623 | 651 | codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn)); |
| 624 | 652 | LLVMGetParams(fn, codegen_fn_def->params); |
| 625 | 653 | |
| 654 | build_label_blocks(g, fn_def_node->data.fn_def.body); | |
| 655 | ||
| 626 | 656 | TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type; |
| 627 | 657 | gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type); |
| 628 | 658 | |
| 629 | 659 | g->block_scopes.pop(); |
| 660 | ||
| 630 | 661 | } |
| 631 | 662 | assert(!g->errors.length); |
| 632 | 663 |
src/main.cpp+11-2| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | #include "buffer.hpp" |
| 10 | 10 | #include "codegen.hpp" |
| 11 | 11 | #include "os.hpp" |
| 12 | #include "error.hpp" | |
| 12 | 13 | |
| 13 | 14 | #include <stdio.h> |
| 14 | 15 | |
| ... | ... | @@ -48,6 +49,8 @@ struct Build { |
| 48 | 49 | }; |
| 49 | 50 | |
| 50 | 51 | static int build(const char *arg0, Build *b) { |
| 52 | int err; | |
| 53 | ||
| 51 | 54 | if (!b->in_file) |
| 52 | 55 | return usage(arg0); |
| 53 | 56 | |
| ... | ... | @@ -59,11 +62,17 @@ static int build(const char *arg0, Build *b) { |
| 59 | 62 | Buf root_source_name = BUF_INIT; |
| 60 | 63 | if (buf_eql_str(&in_file_buf, "-")) { |
| 61 | 64 | os_get_cwd(&root_source_dir); |
| 62 | os_fetch_file(stdin, &root_source_code); | |
| 65 | if ((err = os_fetch_file(stdin, &root_source_code))) { | |
| 66 | fprintf(stderr, "unable to read stdin: %s\n", err_str(err)); | |
| 67 | return 1; | |
| 68 | } | |
| 63 | 69 | buf_init_from_str(&root_source_name, ""); |
| 64 | 70 | } else { |
| 65 | 71 | os_path_split(&in_file_buf, &root_source_dir, &root_source_name); |
| 66 | os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code); | |
| 72 | if ((err = os_fetch_file_path(buf_create_from_str(b->in_file), &root_source_code))) { | |
| 73 | fprintf(stderr, "unable to open '%s': %s\n", b->in_file, err_str(err)); | |
| 74 | return 1; | |
| 75 | } | |
| 67 | 76 | } |
| 68 | 77 | |
| 69 | 78 | CodeGen *g = codegen_create(&root_source_dir); |
src/parser.cpp+62-7| ... | ... | @@ -95,6 +95,10 @@ const char *node_type_str(NodeType node_type) { |
| 95 | 95 | return "Void"; |
| 96 | 96 | case NodeTypeIfExpr: |
| 97 | 97 | return "IfExpr"; |
| 98 | case NodeTypeLabel: | |
| 99 | return "Label"; | |
| 100 | case NodeTypeGoto: | |
| 101 | return "Label"; | |
| 98 | 102 | } |
| 99 | 103 | zig_unreachable(); |
| 100 | 104 | } |
| ... | ... | @@ -260,6 +264,12 @@ void ast_print(AstNode *node, int indent) { |
| 260 | 264 | if (node->data.if_expr.else_node) |
| 261 | 265 | ast_print(node->data.if_expr.else_node, indent + 2); |
| 262 | 266 | break; |
| 267 | case NodeTypeLabel: | |
| 268 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name)); | |
| 269 | break; | |
| 270 | case NodeTypeGoto: | |
| 271 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name)); | |
| 272 | break; | |
| 263 | 273 | } |
| 264 | 274 | } |
| 265 | 275 | |
| ... | ... | @@ -581,7 +591,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool |
| 581 | 591 | } |
| 582 | 592 | |
| 583 | 593 | /* |
| 584 | PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | |
| 594 | PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto | |
| 585 | 595 | */ |
| 586 | 596 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 587 | 597 | Token *token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -609,6 +619,16 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 609 | 619 | ast_buf_from_token(pc, token, &node->data.symbol); |
| 610 | 620 | *token_index += 1; |
| 611 | 621 | return node; |
| 622 | } else if (token->id == TokenIdKeywordGoto) { | |
| 623 | AstNode *node = ast_create_node(pc, NodeTypeGoto, token); | |
| 624 | *token_index += 1; | |
| 625 | ||
| 626 | Token *dest_symbol = &pc->tokens->at(*token_index); | |
| 627 | *token_index += 1; | |
| 628 | ast_expect_token(pc, dest_symbol, TokenIdSymbol); | |
| 629 | ||
| 630 | ast_buf_from_token(pc, dest_symbol, &node->data.go_to.name); | |
| 631 | return node; | |
| 612 | 632 | } |
| 613 | 633 | |
| 614 | 634 | AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); |
| ... | ... | @@ -1181,7 +1201,36 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma |
| 1181 | 1201 | } |
| 1182 | 1202 | |
| 1183 | 1203 | /* |
| 1184 | Statement : NonBlockExpression token(Semicolon) | BlockExpression | |
| 1204 | Label: token(Symbol) token(Colon) | |
| 1205 | */ | |
| 1206 | static AstNode *ast_parse_label(ParseContext *pc, int *token_index, bool mandatory) { | |
| 1207 | Token *symbol_token = &pc->tokens->at(*token_index); | |
| 1208 | if (symbol_token->id != TokenIdSymbol) { | |
| 1209 | if (mandatory) { | |
| 1210 | ast_invalid_token_error(pc, symbol_token); | |
| 1211 | } else { | |
| 1212 | return nullptr; | |
| 1213 | } | |
| 1214 | } | |
| 1215 | ||
| 1216 | Token *colon_token = &pc->tokens->at(*token_index + 1); | |
| 1217 | if (colon_token->id != TokenIdColon) { | |
| 1218 | if (mandatory) { | |
| 1219 | ast_invalid_token_error(pc, colon_token); | |
| 1220 | } else { | |
| 1221 | return nullptr; | |
| 1222 | } | |
| 1223 | } | |
| 1224 | ||
| 1225 | *token_index += 2; | |
| 1226 | ||
| 1227 | AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token); | |
| 1228 | ast_buf_from_token(pc, symbol_token, &node->data.label.name); | |
| 1229 | return node; | |
| 1230 | } | |
| 1231 | ||
| 1232 | /* | |
| 1233 | Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression | |
| 1185 | 1234 | Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) |
| 1186 | 1235 | */ |
| 1187 | 1236 | static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) { |
| ... | ... | @@ -1204,12 +1253,18 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato |
| 1204 | 1253 | // {2;} -> {2;void} |
| 1205 | 1254 | // {;2} -> {void;2} |
| 1206 | 1255 | for (;;) { |
| 1207 | AstNode *statement_node = ast_parse_block_expr(pc, token_index, false); | |
| 1208 | bool semicolon_expected = !statement_node; | |
| 1209 | if (!statement_node) { | |
| 1210 | statement_node = ast_parse_non_block_expr(pc, token_index, false); | |
| 1256 | AstNode *statement_node = ast_parse_label(pc, token_index, false); | |
| 1257 | bool semicolon_expected; | |
| 1258 | if (statement_node) { | |
| 1259 | semicolon_expected = false; | |
| 1260 | } else { | |
| 1261 | statement_node = ast_parse_block_expr(pc, token_index, false); | |
| 1262 | semicolon_expected = !statement_node; | |
| 1211 | 1263 | if (!statement_node) { |
| 1212 | statement_node = ast_create_node(pc, NodeTypeVoid, last_token); | |
| 1264 | statement_node = ast_parse_non_block_expr(pc, token_index, false); | |
| 1265 | if (!statement_node) { | |
| 1266 | statement_node = ast_create_node(pc, NodeTypeVoid, last_token); | |
| 1267 | } | |
| 1213 | 1268 | } |
| 1214 | 1269 | } |
| 1215 | 1270 | node->data.block.statements.append(statement_node); |
src/parser.hpp+12| ... | ... | @@ -41,6 +41,8 @@ enum NodeType { |
| 41 | 41 | NodeTypeUse, |
| 42 | 42 | NodeTypeVoid, |
| 43 | 43 | NodeTypeIfExpr, |
| 44 | NodeTypeLabel, | |
| 45 | NodeTypeGoto, | |
| 44 | 46 | }; |
| 45 | 47 | |
| 46 | 48 | struct AstNodeRoot { |
| ... | ... | @@ -182,6 +184,14 @@ struct AstNodeIfExpr { |
| 182 | 184 | AstNode *else_node; // null, block node, or other if expr node |
| 183 | 185 | }; |
| 184 | 186 | |
| 187 | struct AstNodeLabel { | |
| 188 | Buf name; | |
| 189 | }; | |
| 190 | ||
| 191 | struct AstNodeGoto { | |
| 192 | Buf name; | |
| 193 | }; | |
| 194 | ||
| 185 | 195 | struct AstNode { |
| 186 | 196 | enum NodeType type; |
| 187 | 197 | int line; |
| ... | ... | @@ -207,6 +217,8 @@ struct AstNode { |
| 207 | 217 | AstNodeFnCallExpr fn_call_expr; |
| 208 | 218 | AstNodeUse use; |
| 209 | 219 | AstNodeIfExpr if_expr; |
| 220 | AstNodeLabel label; | |
| 221 | AstNodeGoto go_to; | |
| 210 | 222 | Buf number; |
| 211 | 223 | Buf string; |
| 212 | 224 | Buf symbol; |
src/semantic_info.hpp+9-1| ... | ... | @@ -43,6 +43,11 @@ struct SymbolTableEntry { |
| 43 | 43 | int param_index; // only valid in the case of parameters |
| 44 | 44 | }; |
| 45 | 45 | |
| 46 | struct LabelTableEntry { | |
| 47 | AstNode *label_node; | |
| 48 | LLVMBasicBlockRef basic_block; | |
| 49 | }; | |
| 50 | ||
| 46 | 51 | struct FnTableEntry { |
| 47 | 52 | LLVMValueRef fn_value; |
| 48 | 53 | AstNode *proto_node; |
| ... | ... | @@ -54,6 +59,7 @@ struct FnTableEntry { |
| 54 | 59 | |
| 55 | 60 | // reminder: hash tables must be initialized before use |
| 56 | 61 | HashMap<Buf *, SymbolTableEntry *, buf_hash, buf_eql_buf> symbol_table; |
| 62 | HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table; | |
| 57 | 63 | }; |
| 58 | 64 | |
| 59 | 65 | struct CodeGen { |
| ... | ... | @@ -100,6 +106,7 @@ struct CodeGen { |
| 100 | 106 | |
| 101 | 107 | OutType out_type; |
| 102 | 108 | FnTableEntry *cur_fn; |
| 109 | LLVMBasicBlockRef cur_basic_block; | |
| 103 | 110 | bool c_stdint_used; |
| 104 | 111 | AstNode *root_export_decl; |
| 105 | 112 | int version_major; |
| ... | ... | @@ -132,9 +139,10 @@ struct CodeGenNode { |
| 132 | 139 | union { |
| 133 | 140 | TypeNode type_node; // for NodeTypeType |
| 134 | 141 | FnDefNode fn_def_node; // for NodeTypeFnDef |
| 135 | ExprNode expr_node; // for all the expression nodes | |
| 136 | 142 | FnProtoNode fn_proto_node; // for NodeTypeFnProto |
| 143 | LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel | |
| 137 | 144 | } data; |
| 145 | ExprNode expr_node; // for all the expression nodes | |
| 138 | 146 | }; |
| 139 | 147 | |
| 140 | 148 | static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) { |
src/tokenizer.cpp+3| ... | ... | @@ -189,6 +189,8 @@ static void end_token(Tokenize *t) { |
| 189 | 189 | t->cur_tok->id = TokenIdKeywordIf; |
| 190 | 190 | } else if (mem_eql_str(token_mem, token_len, "else")) { |
| 191 | 191 | t->cur_tok->id = TokenIdKeywordElse; |
| 192 | } else if (mem_eql_str(token_mem, token_len, "goto")) { | |
| 193 | t->cur_tok->id = TokenIdKeywordGoto; | |
| 192 | 194 | } |
| 193 | 195 | |
| 194 | 196 | t->cur_tok = nullptr; |
| ... | ... | @@ -586,6 +588,7 @@ static const char * token_name(Token *token) { |
| 586 | 588 | case TokenIdKeywordVoid: return "Void"; |
| 587 | 589 | case TokenIdKeywordIf: return "If"; |
| 588 | 590 | case TokenIdKeywordElse: return "Else"; |
| 591 | case TokenIdKeywordGoto: return "Goto"; | |
| 589 | 592 | case TokenIdLParen: return "LParen"; |
| 590 | 593 | case TokenIdRParen: return "RParen"; |
| 591 | 594 | case TokenIdComma: return "Comma"; |
src/tokenizer.hpp+1| ... | ... | @@ -27,6 +27,7 @@ enum TokenId { |
| 27 | 27 | TokenIdKeywordVoid, |
| 28 | 28 | TokenIdKeywordIf, |
| 29 | 29 | TokenIdKeywordElse, |
| 30 | TokenIdKeywordGoto, | |
| 30 | 31 | TokenIdLParen, |
| 31 | 32 | TokenIdRParen, |
| 32 | 33 | TokenIdComma, |
src/zig_llvm.cpp+13| ... | ... | @@ -255,6 +255,19 @@ void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder) { |
| 255 | 255 | reinterpret_cast<DIBuilder*>(dibuilder)->finalize(); |
| 256 | 256 | } |
| 257 | 257 | |
| 258 | LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder_wrapped) { | |
| 259 | IRBuilderBase::InsertPoint *ip = new IRBuilderBase::InsertPoint(); | |
| 260 | *ip = unwrap(builder_wrapped)->saveIP(); | |
| 261 | return reinterpret_cast<LLVMZigInsertionPoint*>(ip); | |
| 262 | } | |
| 263 | ||
| 264 | void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *ip_wrapped) { | |
| 265 | IRBuilderBase::InsertPoint *ip = reinterpret_cast<IRBuilderBase::InsertPoint*>(ip_wrapped); | |
| 266 | unwrap(builder)->restoreIP(*ip); | |
| 267 | } | |
| 268 | ||
| 269 | //------------------------------------ | |
| 270 | ||
| 258 | 271 | enum FloatAbi { |
| 259 | 272 | FloatAbiHard, |
| 260 | 273 | FloatAbiSoft, |
src/zig_llvm.hpp+4| ... | ... | @@ -22,6 +22,7 @@ struct LLVMZigDIFile; |
| 22 | 22 | struct LLVMZigDILexicalBlock; |
| 23 | 23 | struct LLVMZigDISubprogram; |
| 24 | 24 | struct LLVMZigDISubroutineType; |
| 25 | struct LLVMZigInsertionPoint; | |
| 25 | 26 | |
| 26 | 27 | void LLVMZigInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); |
| 27 | 28 | void LLVMZigInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); |
| ... | ... | @@ -75,6 +76,9 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD |
| 75 | 76 | |
| 76 | 77 | void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder); |
| 77 | 78 | |
| 79 | LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder); | |
| 80 | void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *point); | |
| 81 | ||
| 78 | 82 | |
| 79 | 83 | /* |
| 80 | 84 | * This stuff is not LLVM API but it depends on the LLVM C++ API so we put it here. |
test/run_tests.cpp+35| ... | ... | @@ -232,6 +232,30 @@ static void add_compiling_test_cases(void) { |
| 232 | 232 | exit(0); |
| 233 | 233 | } |
| 234 | 234 | )SOURCE", "pass\n"); |
| 235 | ||
| 236 | add_simple_case("goto", R"SOURCE( | |
| 237 | #link("c") | |
| 238 | extern { | |
| 239 | fn puts(s: *const u8) -> i32; | |
| 240 | fn exit(code: i32) -> unreachable; | |
| 241 | } | |
| 242 | ||
| 243 | fn loop(a : i32) { | |
| 244 | if a == 0 { | |
| 245 | goto done; | |
| 246 | } | |
| 247 | puts("loop"); | |
| 248 | loop(a - 1); | |
| 249 | ||
| 250 | done: | |
| 251 | return; | |
| 252 | } | |
| 253 | ||
| 254 | export fn _start() -> unreachable { | |
| 255 | loop(3); | |
| 256 | exit(0); | |
| 257 | } | |
| 258 | )SOURCE", "loop\nloop\nloop\n"); | |
| 235 | 259 | } |
| 236 | 260 | |
| 237 | 261 | static void add_compile_failure_test_cases(void) { |
| ... | ... | @@ -305,6 +329,17 @@ fn a() { |
| 305 | 329 | )SOURCE", 2, |
| 306 | 330 | ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'", |
| 307 | 331 | ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'"); |
| 332 | ||
| 333 | add_compile_fail_case("goto cause unreachable code", R"SOURCE( | |
| 334 | fn a() { | |
| 335 | goto done; | |
| 336 | b(); | |
| 337 | done: | |
| 338 | return; | |
| 339 | } | |
| 340 | fn b() {} | |
| 341 | )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code"); | |
| 342 | ||
| 308 | 343 | } |
| 309 | 344 | |
| 310 | 345 | static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) { |