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