| author | |
| committer | |
| log | 5943f995106f9fc2c7c5b416c8d4a2ebba2b8315 |
| tree | e7c67f1b85260de9b54e22a85de8eb3212788d86 |
| parent | 44ca5e19dc174383b1b0490e636398092711306d |
closes #67 files changed, 55 insertions(+), 11 deletions(-)
doc/langref.md+1-3| ... | ... | @@ -146,7 +146,7 @@ ArrayAccessExpression : token(LBracket) Expression token(RBracket) |
| 146 | 146 | |
| 147 | 147 | PrefixOp : token(Not) | token(Dash) | token(Tilde) | (token(Ampersand) option(token(Const))) |
| 148 | 148 | |
| 149 | PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | Break | BlockExpression | token(Symbol) | StructValueExpression | |
| 149 | PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | |
| 150 | 150 | |
| 151 | 151 | StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace) |
| 152 | 152 | |
| ... | ... | @@ -154,8 +154,6 @@ StructValueExpressionField : token(Dot) token(Symbol) token(Eq) Expression |
| 154 | 154 | |
| 155 | 155 | Goto: token(Goto) token(Symbol) |
| 156 | 156 | |
| 157 | Break: token(Break) | |
| 158 | ||
| 159 | 157 | GroupedExpression : token(LParen) Expression token(RParen) |
| 160 | 158 | |
| 161 | 159 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) |
src/analyze.cpp+17| ... | ... | @@ -49,6 +49,7 @@ static AstNode *first_executing_node(AstNode *node) { |
| 49 | 49 | case NodeTypeLabel: |
| 50 | 50 | case NodeTypeGoto: |
| 51 | 51 | case NodeTypeBreak: |
| 52 | case NodeTypeContinue: | |
| 52 | 53 | case NodeTypeAsmExpr: |
| 53 | 54 | case NodeTypeFieldAccessExpr: |
| 54 | 55 | case NodeTypeStructDecl: |
| ... | ... | @@ -532,6 +533,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 532 | 533 | case NodeTypeLabel: |
| 533 | 534 | case NodeTypeGoto: |
| 534 | 535 | case NodeTypeBreak: |
| 536 | case NodeTypeContinue: | |
| 535 | 537 | case NodeTypeAsmExpr: |
| 536 | 538 | case NodeTypeFieldAccessExpr: |
| 537 | 539 | case NodeTypeStructField: |
| ... | ... | @@ -601,6 +603,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 601 | 603 | case NodeTypeLabel: |
| 602 | 604 | case NodeTypeGoto: |
| 603 | 605 | case NodeTypeBreak: |
| 606 | case NodeTypeContinue: | |
| 604 | 607 | case NodeTypeAsmExpr: |
| 605 | 608 | case NodeTypeFieldAccessExpr: |
| 606 | 609 | case NodeTypeStructField: |
| ... | ... | @@ -1381,6 +1384,16 @@ static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, |
| 1381 | 1384 | return g->builtin_types.entry_unreachable; |
| 1382 | 1385 | } |
| 1383 | 1386 | |
| 1387 | static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 1388 | TypeTableEntry *expected_type, AstNode *node) | |
| 1389 | { | |
| 1390 | if (!context->break_allowed) { | |
| 1391 | add_node_error(g, node, | |
| 1392 | buf_sprintf("'continue' expression not in loop")); | |
| 1393 | } | |
| 1394 | return g->builtin_types.entry_unreachable; | |
| 1395 | } | |
| 1396 | ||
| 1384 | 1397 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 1385 | 1398 | TypeTableEntry *expected_type, AstNode *node) |
| 1386 | 1399 | { |
| ... | ... | @@ -1463,6 +1476,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1463 | 1476 | case NodeTypeBreak: |
| 1464 | 1477 | return_type = analyze_break_expr(g, import, context, expected_type, node); |
| 1465 | 1478 | break; |
| 1479 | case NodeTypeContinue: | |
| 1480 | return_type = analyze_continue_expr(g, import, context, expected_type, node); | |
| 1481 | break; | |
| 1466 | 1482 | case NodeTypeAsmExpr: |
| 1467 | 1483 | { |
| 1468 | 1484 | node->data.asm_expr.return_count = 0; |
| ... | ... | @@ -1817,6 +1833,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 1817 | 1833 | case NodeTypeLabel: |
| 1818 | 1834 | case NodeTypeGoto: |
| 1819 | 1835 | case NodeTypeBreak: |
| 1836 | case NodeTypeContinue: | |
| 1820 | 1837 | case NodeTypeAsmExpr: |
| 1821 | 1838 | case NodeTypeFieldAccessExpr: |
| 1822 | 1839 | case NodeTypeStructField: |
src/analyze.hpp+1| ... | ... | @@ -197,6 +197,7 @@ struct CodeGen { |
| 197 | 197 | LLVMBasicBlockRef cur_basic_block; |
| 198 | 198 | BlockContext *cur_block_context; |
| 199 | 199 | ZigList<LLVMBasicBlockRef> break_block_stack; |
| 200 | ZigList<LLVMBasicBlockRef> continue_block_stack; | |
| 200 | 201 | bool c_stdint_used; |
| 201 | 202 | AstNode *root_export_decl; |
| 202 | 203 | int version_major; |
src/codegen.cpp+12| ... | ... | @@ -1025,8 +1025,10 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { |
| 1025 | 1025 | |
| 1026 | 1026 | LLVMPositionBuilderAtEnd(g->builder, body_block); |
| 1027 | 1027 | g->break_block_stack.append(end_block); |
| 1028 | g->continue_block_stack.append(cond_block); | |
| 1028 | 1029 | gen_expr(g, node->data.while_expr.body); |
| 1029 | 1030 | g->break_block_stack.pop(); |
| 1031 | g->continue_block_stack.pop(); | |
| 1030 | 1032 | if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) { |
| 1031 | 1033 | LLVMBuildBr(g->builder, cond_block); |
| 1032 | 1034 | } |
| ... | ... | @@ -1043,6 +1045,14 @@ static LLVMValueRef gen_break(CodeGen *g, AstNode *node) { |
| 1043 | 1045 | return LLVMBuildBr(g->builder, dest_block); |
| 1044 | 1046 | } |
| 1045 | 1047 | |
| 1048 | static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) { | |
| 1049 | assert(node->type == NodeTypeContinue); | |
| 1050 | LLVMBasicBlockRef dest_block = g->continue_block_stack.last(); | |
| 1051 | ||
| 1052 | add_debug_source_node(g, node); | |
| 1053 | return LLVMBuildBr(g->builder, dest_block); | |
| 1054 | } | |
| 1055 | ||
| 1046 | 1056 | static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1047 | 1057 | switch (node->type) { |
| 1048 | 1058 | case NodeTypeBinOpExpr: |
| ... | ... | @@ -1174,6 +1184,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1174 | 1184 | return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); |
| 1175 | 1185 | case NodeTypeBreak: |
| 1176 | 1186 | return gen_break(g, node); |
| 1187 | case NodeTypeContinue: | |
| 1188 | return gen_continue(g, node); | |
| 1177 | 1189 | case NodeTypeLabel: |
| 1178 | 1190 | { |
| 1179 | 1191 | LabelTableEntry *label_entry = node->codegen_node->data.label_entry; |
src/parser.cpp+10-1| ... | ... | @@ -124,6 +124,8 @@ const char *node_type_str(NodeType node_type) { |
| 124 | 124 | return "Goto"; |
| 125 | 125 | case NodeTypeBreak: |
| 126 | 126 | return "Break"; |
| 127 | case NodeTypeContinue: | |
| 128 | return "Continue"; | |
| 127 | 129 | case NodeTypeAsmExpr: |
| 128 | 130 | return "AsmExpr"; |
| 129 | 131 | case NodeTypeFieldAccessExpr: |
| ... | ... | @@ -341,6 +343,9 @@ void ast_print(AstNode *node, int indent) { |
| 341 | 343 | case NodeTypeBreak: |
| 342 | 344 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 343 | 345 | break; |
| 346 | case NodeTypeContinue: | |
| 347 | fprintf(stderr, "%s\n", node_type_str(node->type)); | |
| 348 | break; | |
| 344 | 349 | case NodeTypeAsmExpr: |
| 345 | 350 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 346 | 351 | break; |
| ... | ... | @@ -1107,7 +1112,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { |
| 1107 | 1112 | } |
| 1108 | 1113 | |
| 1109 | 1114 | /* |
| 1110 | PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | Break | BlockExpression | token(Symbol) | StructValueExpression | |
| 1115 | PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | |
| 1111 | 1116 | */ |
| 1112 | 1117 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1113 | 1118 | Token *token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -1165,6 +1170,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1165 | 1170 | AstNode *node = ast_create_node(pc, NodeTypeBreak, token); |
| 1166 | 1171 | *token_index += 1; |
| 1167 | 1172 | return node; |
| 1173 | } else if (token->id == TokenIdKeywordContinue) { | |
| 1174 | AstNode *node = ast_create_node(pc, NodeTypeContinue, token); | |
| 1175 | *token_index += 1; | |
| 1176 | return node; | |
| 1168 | 1177 | } |
| 1169 | 1178 | |
| 1170 | 1179 | AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); |
src/parser.hpp+1| ... | ... | @@ -49,6 +49,7 @@ enum NodeType { |
| 49 | 49 | NodeTypeLabel, |
| 50 | 50 | NodeTypeGoto, |
| 51 | 51 | NodeTypeBreak, |
| 52 | NodeTypeContinue, | |
| 52 | 53 | NodeTypeAsmExpr, |
| 53 | 54 | NodeTypeStructDecl, |
| 54 | 55 | NodeTypeStructField, |
test/run_tests.cpp+13-7| ... | ... | @@ -659,17 +659,15 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 659 | 659 | } |
| 660 | 660 | )SOURCE", "loop\nloop\nloop\nloop\n"); |
| 661 | 661 | |
| 662 | add_simple_case("break out of while loop", R"SOURCE( | |
| 662 | add_simple_case("continue and break", R"SOURCE( | |
| 663 | 663 | use "std.zig"; |
| 664 | 664 | export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 665 | 665 | var i : i32 = 0; |
| 666 | 666 | while true { |
| 667 | while true { | |
| 668 | if i >= 4 { | |
| 669 | break; | |
| 670 | } | |
| 671 | print_str("loop\n"); | |
| 672 | i += 1; | |
| 667 | print_str("loop\n"); | |
| 668 | i += 1; | |
| 669 | if i < 4 { | |
| 670 | continue; | |
| 673 | 671 | } |
| 674 | 672 | break; |
| 675 | 673 | } |
| ... | ... | @@ -678,6 +676,8 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 678 | 676 | )SOURCE", "loop\nloop\nloop\nloop\n"); |
| 679 | 677 | } |
| 680 | 678 | |
| 679 | //////////////////////////////////////////////////////////////////////////////////// | |
| 680 | ||
| 681 | 681 | static void add_compile_failure_test_cases(void) { |
| 682 | 682 | add_compile_fail_case("multiple function definitions", R"SOURCE( |
| 683 | 683 | fn a() {} |
| ... | ... | @@ -958,6 +958,12 @@ fn f() { |
| 958 | 958 | break; |
| 959 | 959 | } |
| 960 | 960 | )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression not in loop"); |
| 961 | ||
| 962 | add_compile_fail_case("invalid continue expression", R"SOURCE( | |
| 963 | fn f() { | |
| 964 | continue; | |
| 965 | } | |
| 966 | )SOURCE", 1, ".tmp_source.zig:3:5: error: 'continue' expression not in loop"); | |
| 961 | 967 | } |
| 962 | 968 | |
| 963 | 969 | static void print_compiler_invocation(TestCase *test_case) { |