authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-03 00:47:35-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-03 00:47:35-07:00
logf8ca6c70c74db6e6f0d4462aa763adb6f1f41c7e
tree99bc3d06bb37abb267a2a59f3ba04523c2c430eb
parentc89f77dd8e5005e60e8fb223c6c68b50566ac1ed

add labels and goto


14 files changed, 270 insertions(+), 27 deletions(-)

README.md+6-3
...@@ -45,7 +45,6 @@ make...@@ -45,7 +45,6 @@ make
45 * variable declarations and assignment expressions45 * variable declarations and assignment expressions
46 * Type checking46 * Type checking
47 * loops47 * loops
48 * labels and goto
49 * inline assembly and syscalls48 * inline assembly and syscalls
50 * conditional compilation and ability to check target platform and architecture49 * conditional compilation and ability to check target platform and architecture
51 * main function with command line arguments50 * main function with command line arguments
...@@ -110,7 +109,9 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type...@@ -110,7 +109,9 @@ PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
110109
111Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)110Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
112111
113Statement : NonBlockExpression token(Semicolon) | BlockExpression112Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression
113
114Label: token(Symbol) token(Colon)
114115
115Expression : BlockExpression | NonBlockExpression116Expression : BlockExpression | NonBlockExpression
116117
...@@ -162,7 +163,9 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)...@@ -162,7 +163,9 @@ FnCallExpression : PrimaryExpression token(LParen) list(Expression, token(Comma)
162163
163PrefixOp : token(Not) | token(Dash) | token(Tilde)164PrefixOp : token(Not) | token(Dash) | token(Tilde)
164165
165PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)166PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto
167
168Goto: token(Goto) token(Symbol)
166169
167GroupedExpression : token(LParen) Expression token(RParen)170GroupedExpression : token(LParen) Expression token(RParen)
168```171```
doc/vim/syntax/zig.vim+2-2
...@@ -1,13 +1,13 @@...@@ -1,13 +1,13 @@
1" Vim syntax file1" Vim syntax file
2" Language: Zig2" Language: Zig
3" Maintainer: Andrew Kelley3" Maintainer: Andrew Kelley
4" Latest Revision: 27 November 20154" Latest Revision: 02 December 2015
55
6if exists("b:current_syntax")6if exists("b:current_syntax")
7 finish7 finish
8endif8endif
99
10syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void10syn keyword zigKeyword fn return mut const extern unreachable export pub as use if else let void goto
11syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f12811syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128
1212
13syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell13syn region zigCommentLine start="//" end="$" contains=zigTodo,@Spell
example/hello_world/hello.zig+12-1
...@@ -6,7 +6,18 @@ extern {...@@ -6,7 +6,18 @@ extern {
6 fn exit(code: i32) -> unreachable;6 fn exit(code: i32) -> unreachable;
7}7}
88
9fn loop(a : i32) {
10 if a == 0 {
11 goto done;
12 }
13 puts("loop");
14 loop(a - 1);
15
16done:
17 return;
18}
19
9export fn _start() -> unreachable {20export fn _start() -> unreachable {
10 puts("Hello, world!");21 loop(3);
11 exit(0);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,6 +131,25 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
131 resolve_type(g, node->data.fn_proto.return_type);131 resolve_type(g, node->data.fn_proto.return_type);
132}132}
133133
134static 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
134static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) {153static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) {
135 switch (node->type) {154 switch (node->type) {
136 case NodeTypeExternBlock:155 case NodeTypeExternBlock:
...@@ -158,6 +177,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,...@@ -158,6 +177,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
158 fn_table_entry->calling_convention = LLVMCCallConv;177 fn_table_entry->calling_convention = LLVMCCallConv;
159 fn_table_entry->import_entry = import;178 fn_table_entry->import_entry = import;
160 fn_table_entry->symbol_table.init(8);179 fn_table_entry->symbol_table.init(8);
180 fn_table_entry->label_table.init(8);
161181
162 resolve_function_proto(g, fn_proto, fn_table_entry);182 resolve_function_proto(g, fn_proto, fn_table_entry);
163183
...@@ -208,6 +228,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,...@@ -208,6 +228,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
208 fn_table_entry->internal_linkage = is_internal;228 fn_table_entry->internal_linkage = is_internal;
209 fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv;229 fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv;
210 fn_table_entry->symbol_table.init(8);230 fn_table_entry->symbol_table.init(8);
231 fn_table_entry->label_table.init(8);
211232
212 g->fn_protos.append(fn_table_entry);233 g->fn_protos.append(fn_table_entry);
213 g->fn_defs.append(fn_table_entry);234 g->fn_defs.append(fn_table_entry);
...@@ -222,6 +243,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,...@@ -222,6 +243,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
222 assert(!proto_node->codegen_node);243 assert(!proto_node->codegen_node);
223 proto_node->codegen_node = allocate<CodeGenNode>(1);244 proto_node->codegen_node = allocate<CodeGenNode>(1);
224 proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;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 break;250 break;
...@@ -290,6 +313,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,...@@ -290,6 +313,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
290 case NodeTypeCastExpr:313 case NodeTypeCastExpr:
291 case NodeTypePrefixOpExpr:314 case NodeTypePrefixOpExpr:
292 case NodeTypeIfExpr:315 case NodeTypeIfExpr:
316 case NodeTypeLabel:
317 case NodeTypeGoto:
293 zig_unreachable();318 zig_unreachable();
294 }319 }
295}320}
...@@ -339,6 +364,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -339,6 +364,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
339 return_type = g->builtin_types.entry_void;364 return_type = g->builtin_types.entry_void;
340 for (int i = 0; i < node->data.block.statements.length; i += 1) {365 for (int i = 0; i < node->data.block.statements.length; i += 1) {
341 AstNode *child = node->data.block.statements.at(i);366 AstNode *child = node->data.block.statements.at(i);
367 if (child->type == NodeTypeLabel)
368 continue;
342 if (return_type == g->builtin_types.entry_unreachable) {369 if (return_type == g->builtin_types.entry_unreachable) {
343 if (child->type == NodeTypeVoid) {370 if (child->type == NodeTypeVoid) {
344 // {unreachable;void;void} is allowed.371 // {unreachable;void;void} is allowed.
...@@ -365,7 +392,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -365,7 +392,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
365392
366 if (actual_return_type == g->builtin_types.entry_unreachable) {393 if (actual_return_type == g->builtin_types.entry_unreachable) {
367 // "return exit(0)" should just be "exit(0)".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 actual_return_type = g->builtin_types.entry_invalid;396 actual_return_type = g->builtin_types.entry_invalid;
370 }397 }
371398
...@@ -373,7 +400,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -373,7 +400,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
373 return_type = g->builtin_types.entry_unreachable;400 return_type = g->builtin_types.entry_unreachable;
374 break;401 break;
375 }402 }
376
377 case NodeTypeVariableDeclaration:403 case NodeTypeVariableDeclaration:
378 {404 {
379 zig_panic("TODO: analyze variable declaration");405 zig_panic("TODO: analyze variable declaration");
...@@ -382,6 +408,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -382,6 +408,21 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
382 break;408 break;
383 }409 }
384410
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 case NodeTypeBinOpExpr:426 case NodeTypeBinOpExpr:
386 {427 {
387 switch (node->data.bin_op_expr.bin_op) {428 switch (node->data.bin_op_expr.bin_op) {
...@@ -563,8 +604,18 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -563,8 +604,18 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
563 TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,604 TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type,
564 node->data.if_expr.then_block);605 node->data.if_expr.then_block);
565606
566 check_type_compatibility(g, node, expected_type, else_type);607 TypeTableEntry *primary_type;
567 return_type = then_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 break;619 break;
569 }620 }
570 case NodeTypeDirective:621 case NodeTypeDirective:
...@@ -577,14 +628,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -577,14 +628,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
577 case NodeTypeExternBlock:628 case NodeTypeExternBlock:
578 case NodeTypeFnDef:629 case NodeTypeFnDef:
579 case NodeTypeUse:630 case NodeTypeUse:
631 case NodeTypeLabel:
580 zig_unreachable();632 zig_unreachable();
581 }633 }
582 assert(return_type);634 assert(return_type);
583 check_type_compatibility(g, node, expected_type, return_type);635 check_type_compatibility(g, node, expected_type, return_type);
584636
585 assert(!node->codegen_node);637 if (node->codegen_node) {
586 node->codegen_node = allocate<CodeGenNode>(1);638 assert(node->type == NodeTypeGoto);
587 node->codegen_node->data.expr_node.type_entry = return_type;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;
588644
589 return return_type;645 return return_type;
590}646}
...@@ -652,6 +708,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,...@@ -652,6 +708,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
652 case NodeTypeCastExpr:708 case NodeTypeCastExpr:
653 case NodeTypePrefixOpExpr:709 case NodeTypePrefixOpExpr:
654 case NodeTypeIfExpr:710 case NodeTypeIfExpr:
711 case NodeTypeLabel:
712 case NodeTypeGoto:
655 zig_unreachable();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,7 +115,7 @@ static LLVMValueRef get_variable_value(CodeGen *g, Buf *name) {
115}115}
116116
117static TypeTableEntry *get_expr_type(AstNode *node) {117static 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}
120120
121static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {121static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
...@@ -407,11 +407,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {...@@ -407,11 +407,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
407407
408 LLVMPositionBuilderAtEnd(g->builder, then_block);408 LLVMPositionBuilderAtEnd(g->builder, then_block);
409 LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block);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);
411412
412 LLVMPositionBuilderAtEnd(g->builder, else_block);413 LLVMPositionBuilderAtEnd(g->builder, else_block);
413 LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node);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);
415417
416 LLVMPositionBuilderAtEnd(g->builder, endif_block);418 LLVMPositionBuilderAtEnd(g->builder, endif_block);
417 if (use_expr_value) {419 if (use_expr_value) {
...@@ -435,7 +437,8 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {...@@ -435,7 +437,8 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) {
435437
436 LLVMPositionBuilderAtEnd(g->builder, then_block);438 LLVMPositionBuilderAtEnd(g->builder, then_block);
437 gen_expr(g, node->data.if_expr.then_block);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);
439442
440 LLVMPositionBuilderAtEnd(g->builder, endif_block);443 LLVMPositionBuilderAtEnd(g->builder, endif_block);
441 return nullptr;444 return nullptr;
...@@ -518,6 +521,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {...@@ -518,6 +521,17 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
518 }521 }
519 case NodeTypeBlock:522 case NodeTypeBlock:
520 return gen_block(g, node, nullptr);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 case NodeTypeRoot:535 case NodeTypeRoot:
522 case NodeTypeRootExportDecl:536 case NodeTypeRootExportDecl:
523 case NodeTypeFnProto:537 case NodeTypeFnProto:
...@@ -533,6 +547,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {...@@ -533,6 +547,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
533 zig_unreachable();547 zig_unreachable();
534}548}
535549
550static 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
536static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto,564static LLVMZigDISubroutineType *create_di_function_type(CodeGen *g, AstNodeFnProto *fn_proto,
537 LLVMZigDIFile *di_file)565 LLVMZigDIFile *di_file)
538{566{
...@@ -623,10 +651,13 @@ static void do_code_gen(CodeGen *g) {...@@ -623,10 +651,13 @@ static void do_code_gen(CodeGen *g) {
623 codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn));651 codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn));
624 LLVMGetParams(fn, codegen_fn_def->params);652 LLVMGetParams(fn, codegen_fn_def->params);
625653
654 build_label_blocks(g, fn_def_node->data.fn_def.body);
655
626 TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;656 TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
627 gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);657 gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
628658
629 g->block_scopes.pop();659 g->block_scopes.pop();
660
630 }661 }
631 assert(!g->errors.length);662 assert(!g->errors.length);
632663
src/main.cpp+11-2
...@@ -9,6 +9,7 @@...@@ -9,6 +9,7 @@
9#include "buffer.hpp"9#include "buffer.hpp"
10#include "codegen.hpp"10#include "codegen.hpp"
11#include "os.hpp"11#include "os.hpp"
12#include "error.hpp"
1213
13#include <stdio.h>14#include <stdio.h>
1415
...@@ -48,6 +49,8 @@ struct Build {...@@ -48,6 +49,8 @@ struct Build {
48};49};
4950
50static int build(const char *arg0, Build *b) {51static int build(const char *arg0, Build *b) {
52 int err;
53
51 if (!b->in_file)54 if (!b->in_file)
52 return usage(arg0);55 return usage(arg0);
5356
...@@ -59,11 +62,17 @@ static int build(const char *arg0, Build *b) {...@@ -59,11 +62,17 @@ static int build(const char *arg0, Build *b) {
59 Buf root_source_name = BUF_INIT;62 Buf root_source_name = BUF_INIT;
60 if (buf_eql_str(&in_file_buf, "-")) {63 if (buf_eql_str(&in_file_buf, "-")) {
61 os_get_cwd(&root_source_dir);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 buf_init_from_str(&root_source_name, "");69 buf_init_from_str(&root_source_name, "");
64 } else {70 } else {
65 os_path_split(&in_file_buf, &root_source_dir, &root_source_name);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 }
6877
69 CodeGen *g = codegen_create(&root_source_dir);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,6 +95,10 @@ const char *node_type_str(NodeType node_type) {
95 return "Void";95 return "Void";
96 case NodeTypeIfExpr:96 case NodeTypeIfExpr:
97 return "IfExpr";97 return "IfExpr";
98 case NodeTypeLabel:
99 return "Label";
100 case NodeTypeGoto:
101 return "Label";
98 }102 }
99 zig_unreachable();103 zig_unreachable();
100}104}
...@@ -260,6 +264,12 @@ void ast_print(AstNode *node, int indent) {...@@ -260,6 +264,12 @@ void ast_print(AstNode *node, int indent) {
260 if (node->data.if_expr.else_node)264 if (node->data.if_expr.else_node)
261 ast_print(node->data.if_expr.else_node, indent + 2);265 ast_print(node->data.if_expr.else_node, indent + 2);
262 break;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}
265275
...@@ -581,7 +591,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool...@@ -581,7 +591,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool
581}591}
582592
583/*593/*
584PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol)594PrimaryExpression : token(Number) | token(String) | token(Unreachable) | GroupedExpression | token(Symbol) | Goto
585*/595*/
586static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {596static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
587 Token *token = &pc->tokens->at(*token_index);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,6 +619,16 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
609 ast_buf_from_token(pc, token, &node->data.symbol);619 ast_buf_from_token(pc, token, &node->data.symbol);
610 *token_index += 1;620 *token_index += 1;
611 return node;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 }
613633
614 AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);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,7 +1201,36 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma
1181}1201}
11821202
1183/*1203/*
1184Statement : NonBlockExpression token(Semicolon) | BlockExpression1204Label: token(Symbol) token(Colon)
1205*/
1206static 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/*
1233Statement : Label | NonBlockExpression token(Semicolon) | BlockExpression
1185Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)1234Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
1186*/1235*/
1187static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {1236static 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,12 +1253,18 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
1204 // {2;} -> {2;void}1253 // {2;} -> {2;void}
1205 // {;2} -> {void;2}1254 // {;2} -> {void;2}
1206 for (;;) {1255 for (;;) {
1207 AstNode *statement_node = ast_parse_block_expr(pc, token_index, false);1256 AstNode *statement_node = ast_parse_label(pc, token_index, false);
1208 bool semicolon_expected = !statement_node;1257 bool semicolon_expected;
1209 if (!statement_node) {1258 if (statement_node) {
1210 statement_node = ast_parse_non_block_expr(pc, token_index, false);1259 semicolon_expected = false;
1260 } else {
1261 statement_node = ast_parse_block_expr(pc, token_index, false);
1262 semicolon_expected = !statement_node;
1211 if (!statement_node) {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 node->data.block.statements.append(statement_node);1270 node->data.block.statements.append(statement_node);
src/parser.hpp+12
...@@ -41,6 +41,8 @@ enum NodeType {...@@ -41,6 +41,8 @@ enum NodeType {
41 NodeTypeUse,41 NodeTypeUse,
42 NodeTypeVoid,42 NodeTypeVoid,
43 NodeTypeIfExpr,43 NodeTypeIfExpr,
44 NodeTypeLabel,
45 NodeTypeGoto,
44};46};
4547
46struct AstNodeRoot {48struct AstNodeRoot {
...@@ -182,6 +184,14 @@ struct AstNodeIfExpr {...@@ -182,6 +184,14 @@ struct AstNodeIfExpr {
182 AstNode *else_node; // null, block node, or other if expr node184 AstNode *else_node; // null, block node, or other if expr node
183};185};
184186
187struct AstNodeLabel {
188 Buf name;
189};
190
191struct AstNodeGoto {
192 Buf name;
193};
194
185struct AstNode {195struct AstNode {
186 enum NodeType type;196 enum NodeType type;
187 int line;197 int line;
...@@ -207,6 +217,8 @@ struct AstNode {...@@ -207,6 +217,8 @@ struct AstNode {
207 AstNodeFnCallExpr fn_call_expr;217 AstNodeFnCallExpr fn_call_expr;
208 AstNodeUse use;218 AstNodeUse use;
209 AstNodeIfExpr if_expr;219 AstNodeIfExpr if_expr;
220 AstNodeLabel label;
221 AstNodeGoto go_to;
210 Buf number;222 Buf number;
211 Buf string;223 Buf string;
212 Buf symbol;224 Buf symbol;
src/semantic_info.hpp+9-1
...@@ -43,6 +43,11 @@ struct SymbolTableEntry {...@@ -43,6 +43,11 @@ struct SymbolTableEntry {
43 int param_index; // only valid in the case of parameters43 int param_index; // only valid in the case of parameters
44};44};
4545
46struct LabelTableEntry {
47 AstNode *label_node;
48 LLVMBasicBlockRef basic_block;
49};
50
46struct FnTableEntry {51struct FnTableEntry {
47 LLVMValueRef fn_value;52 LLVMValueRef fn_value;
48 AstNode *proto_node;53 AstNode *proto_node;
...@@ -54,6 +59,7 @@ struct FnTableEntry {...@@ -54,6 +59,7 @@ struct FnTableEntry {
5459
55 // reminder: hash tables must be initialized before use60 // reminder: hash tables must be initialized before use
56 HashMap<Buf *, SymbolTableEntry *, buf_hash, buf_eql_buf> symbol_table;61 HashMap<Buf *, SymbolTableEntry *, buf_hash, buf_eql_buf> symbol_table;
62 HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
57};63};
5864
59struct CodeGen {65struct CodeGen {
...@@ -100,6 +106,7 @@ struct CodeGen {...@@ -100,6 +106,7 @@ struct CodeGen {
100106
101 OutType out_type;107 OutType out_type;
102 FnTableEntry *cur_fn;108 FnTableEntry *cur_fn;
109 LLVMBasicBlockRef cur_basic_block;
103 bool c_stdint_used;110 bool c_stdint_used;
104 AstNode *root_export_decl;111 AstNode *root_export_decl;
105 int version_major;112 int version_major;
...@@ -132,9 +139,10 @@ struct CodeGenNode {...@@ -132,9 +139,10 @@ struct CodeGenNode {
132 union {139 union {
133 TypeNode type_node; // for NodeTypeType140 TypeNode type_node; // for NodeTypeType
134 FnDefNode fn_def_node; // for NodeTypeFnDef141 FnDefNode fn_def_node; // for NodeTypeFnDef
135 ExprNode expr_node; // for all the expression nodes
136 FnProtoNode fn_proto_node; // for NodeTypeFnProto142 FnProtoNode fn_proto_node; // for NodeTypeFnProto
143 LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
137 } data;144 } data;
145 ExprNode expr_node; // for all the expression nodes
138};146};
139147
140static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) {148static 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,6 +189,8 @@ static void end_token(Tokenize *t) {
189 t->cur_tok->id = TokenIdKeywordIf;189 t->cur_tok->id = TokenIdKeywordIf;
190 } else if (mem_eql_str(token_mem, token_len, "else")) {190 } else if (mem_eql_str(token_mem, token_len, "else")) {
191 t->cur_tok->id = TokenIdKeywordElse;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 }
193195
194 t->cur_tok = nullptr;196 t->cur_tok = nullptr;
...@@ -586,6 +588,7 @@ static const char * token_name(Token *token) {...@@ -586,6 +588,7 @@ static const char * token_name(Token *token) {
586 case TokenIdKeywordVoid: return "Void";588 case TokenIdKeywordVoid: return "Void";
587 case TokenIdKeywordIf: return "If";589 case TokenIdKeywordIf: return "If";
588 case TokenIdKeywordElse: return "Else";590 case TokenIdKeywordElse: return "Else";
591 case TokenIdKeywordGoto: return "Goto";
589 case TokenIdLParen: return "LParen";592 case TokenIdLParen: return "LParen";
590 case TokenIdRParen: return "RParen";593 case TokenIdRParen: return "RParen";
591 case TokenIdComma: return "Comma";594 case TokenIdComma: return "Comma";
src/tokenizer.hpp+1
...@@ -27,6 +27,7 @@ enum TokenId {...@@ -27,6 +27,7 @@ enum TokenId {
27 TokenIdKeywordVoid,27 TokenIdKeywordVoid,
28 TokenIdKeywordIf,28 TokenIdKeywordIf,
29 TokenIdKeywordElse,29 TokenIdKeywordElse,
30 TokenIdKeywordGoto,
30 TokenIdLParen,31 TokenIdLParen,
31 TokenIdRParen,32 TokenIdRParen,
32 TokenIdComma,33 TokenIdComma,
src/zig_llvm.cpp+13
...@@ -255,6 +255,19 @@ void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder) {...@@ -255,6 +255,19 @@ void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder) {
255 reinterpret_cast<DIBuilder*>(dibuilder)->finalize();255 reinterpret_cast<DIBuilder*>(dibuilder)->finalize();
256}256}
257257
258LLVMZigInsertionPoint *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
264void 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
258enum FloatAbi {271enum FloatAbi {
259 FloatAbiHard,272 FloatAbiHard,
260 FloatAbiSoft,273 FloatAbiSoft,
src/zig_llvm.hpp+4
...@@ -22,6 +22,7 @@ struct LLVMZigDIFile;...@@ -22,6 +22,7 @@ struct LLVMZigDIFile;
22struct LLVMZigDILexicalBlock;22struct LLVMZigDILexicalBlock;
23struct LLVMZigDISubprogram;23struct LLVMZigDISubprogram;
24struct LLVMZigDISubroutineType;24struct LLVMZigDISubroutineType;
25struct LLVMZigInsertionPoint;
2526
26void LLVMZigInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);27void LLVMZigInitializeLoopStrengthReducePass(LLVMPassRegistryRef R);
27void LLVMZigInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);28void LLVMZigInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
...@@ -75,6 +76,9 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD...@@ -75,6 +76,9 @@ LLVMZigDISubprogram *LLVMZigCreateFunction(LLVMZigDIBuilder *dibuilder, LLVMZigD
7576
76void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder);77void LLVMZigDIBuilderFinalize(LLVMZigDIBuilder *dibuilder);
7778
79LLVMZigInsertionPoint *LLVMZigSaveInsertPoint(LLVMBuilderRef builder);
80void LLVMZigRestoreInsertPoint(LLVMBuilderRef builder, LLVMZigInsertionPoint *point);
81
7882
79/*83/*
80 * This stuff is not LLVM API but it depends on the LLVM C++ API so we put it here.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,6 +232,30 @@ static void add_compiling_test_cases(void) {
232 exit(0);232 exit(0);
233 }233 }
234 )SOURCE", "pass\n");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}
236260
237static void add_compile_failure_test_cases(void) {261static void add_compile_failure_test_cases(void) {
...@@ -305,6 +329,17 @@ fn a() {...@@ -305,6 +329,17 @@ fn a() {
305 )SOURCE", 2,329 )SOURCE", 2,
306 ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'",330 ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'",
307 ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'");331 ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'");
332
333 add_compile_fail_case("goto cause unreachable code", R"SOURCE(
334fn a() {
335 goto done;
336 b();
337done:
338 return;
339}
340fn b() {}
341 )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code");
342
308}343}
309344
310static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) {345static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) {