| author | |
| committer | |
| log | 5ceaae288c4f80fe5ce1449cd9d2efe0e541d629 |
| tree | 7cf0b1b188c9f9ca8df0d4f379b47a6348adf6fd |
| parent | ffc593b8082eb0fa83b1a3d1b63c840379911cd5 |
7 files changed, 55 insertions(+), 2 deletions(-)
doc/langref.md+3-1| ... | @@ -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 | BlockExpression | token(Symbol) | StructValueExpression | 149 | PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | Break | 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,6 +154,8 @@ StructValueExpressionField : token(Dot) token(Symbol) token(Eq) Expression | ... | @@ -154,6 +154,8 @@ 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 | |||
| 157 | GroupedExpression : token(LParen) Expression token(RParen) | 159 | GroupedExpression : token(LParen) Expression token(RParen) |
| 158 | 160 | ||
| 159 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | 161 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) |
src/analyze.cpp+13| ... | @@ -48,6 +48,7 @@ static AstNode *first_executing_node(AstNode *node) { | ... | @@ -48,6 +48,7 @@ static AstNode *first_executing_node(AstNode *node) { |
| 48 | case NodeTypeIfExpr: | 48 | case NodeTypeIfExpr: |
| 49 | case NodeTypeLabel: | 49 | case NodeTypeLabel: |
| 50 | case NodeTypeGoto: | 50 | case NodeTypeGoto: |
| 51 | case NodeTypeBreak: | ||
| 51 | case NodeTypeAsmExpr: | 52 | case NodeTypeAsmExpr: |
| 52 | case NodeTypeFieldAccessExpr: | 53 | case NodeTypeFieldAccessExpr: |
| 53 | case NodeTypeStructDecl: | 54 | case NodeTypeStructDecl: |
| ... | @@ -530,6 +531,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -530,6 +531,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 530 | case NodeTypeWhileExpr: | 531 | case NodeTypeWhileExpr: |
| 531 | case NodeTypeLabel: | 532 | case NodeTypeLabel: |
| 532 | case NodeTypeGoto: | 533 | case NodeTypeGoto: |
| 534 | case NodeTypeBreak: | ||
| 533 | case NodeTypeAsmExpr: | 535 | case NodeTypeAsmExpr: |
| 534 | case NodeTypeFieldAccessExpr: | 536 | case NodeTypeFieldAccessExpr: |
| 535 | case NodeTypeStructField: | 537 | case NodeTypeStructField: |
| ... | @@ -598,6 +600,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { | ... | @@ -598,6 +600,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 598 | case NodeTypeWhileExpr: | 600 | case NodeTypeWhileExpr: |
| 599 | case NodeTypeLabel: | 601 | case NodeTypeLabel: |
| 600 | case NodeTypeGoto: | 602 | case NodeTypeGoto: |
| 603 | case NodeTypeBreak: | ||
| 601 | case NodeTypeAsmExpr: | 604 | case NodeTypeAsmExpr: |
| 602 | case NodeTypeFieldAccessExpr: | 605 | case NodeTypeFieldAccessExpr: |
| 603 | case NodeTypeStructField: | 606 | case NodeTypeStructField: |
| ... | @@ -1360,6 +1363,12 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -1360,6 +1363,12 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, |
| 1360 | return g->builtin_types.entry_void; | 1363 | return g->builtin_types.entry_void; |
| 1361 | } | 1364 | } |
| 1362 | 1365 | ||
| 1366 | static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | ||
| 1367 | TypeTableEntry *expected_type, AstNode *node) | ||
| 1368 | { | ||
| 1369 | return g->builtin_types.entry_unreachable; | ||
| 1370 | } | ||
| 1371 | |||
| 1363 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 1372 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 1364 | TypeTableEntry *expected_type, AstNode *node) | 1373 | TypeTableEntry *expected_type, AstNode *node) |
| 1365 | { | 1374 | { |
| ... | @@ -1439,6 +1448,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -1439,6 +1448,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1439 | return_type = g->builtin_types.entry_unreachable; | 1448 | return_type = g->builtin_types.entry_unreachable; |
| 1440 | break; | 1449 | break; |
| 1441 | } | 1450 | } |
| 1451 | case NodeTypeBreak: | ||
| 1452 | return_type = analyze_break_expr(g, import, context, expected_type, node); | ||
| 1453 | break; | ||
| 1442 | case NodeTypeAsmExpr: | 1454 | case NodeTypeAsmExpr: |
| 1443 | { | 1455 | { |
| 1444 | node->data.asm_expr.return_count = 0; | 1456 | node->data.asm_expr.return_count = 0; |
| ... | @@ -1792,6 +1804,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, | ... | @@ -1792,6 +1804,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 1792 | case NodeTypeWhileExpr: | 1804 | case NodeTypeWhileExpr: |
| 1793 | case NodeTypeLabel: | 1805 | case NodeTypeLabel: |
| 1794 | case NodeTypeGoto: | 1806 | case NodeTypeGoto: |
| 1807 | case NodeTypeBreak: | ||
| 1795 | case NodeTypeAsmExpr: | 1808 | case NodeTypeAsmExpr: |
| 1796 | case NodeTypeFieldAccessExpr: | 1809 | case NodeTypeFieldAccessExpr: |
| 1797 | case NodeTypeStructField: | 1810 | case NodeTypeStructField: |
src/analyze.hpp+1| ... | @@ -196,6 +196,7 @@ struct CodeGen { | ... | @@ -196,6 +196,7 @@ struct CodeGen { |
| 196 | FnTableEntry *cur_fn; | 196 | FnTableEntry *cur_fn; |
| 197 | LLVMBasicBlockRef cur_basic_block; | 197 | LLVMBasicBlockRef cur_basic_block; |
| 198 | BlockContext *cur_block_context; | 198 | BlockContext *cur_block_context; |
| 199 | LLVMBasicBlockRef cur_break_block; | ||
| 199 | bool c_stdint_used; | 200 | bool c_stdint_used; |
| 200 | AstNode *root_export_decl; | 201 | AstNode *root_export_decl; |
| 201 | int version_major; | 202 | int version_major; |
src/codegen.cpp+12| ... | @@ -1024,13 +1024,23 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { | ... | @@ -1024,13 +1024,23 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { |
| 1024 | LLVMBuildCondBr(g->builder, cond_val, body_block, end_block); | 1024 | LLVMBuildCondBr(g->builder, cond_val, body_block, end_block); |
| 1025 | 1025 | ||
| 1026 | LLVMPositionBuilderAtEnd(g->builder, body_block); | 1026 | LLVMPositionBuilderAtEnd(g->builder, body_block); |
| 1027 | g->cur_break_block = end_block; | ||
| 1027 | gen_expr(g, node->data.while_expr.body); | 1028 | gen_expr(g, node->data.while_expr.body); |
| 1029 | g->cur_break_block = nullptr; | ||
| 1028 | LLVMBuildBr(g->builder, cond_block); | 1030 | LLVMBuildBr(g->builder, cond_block); |
| 1029 | 1031 | ||
| 1030 | LLVMPositionBuilderAtEnd(g->builder, end_block); | 1032 | LLVMPositionBuilderAtEnd(g->builder, end_block); |
| 1031 | return nullptr; | 1033 | return nullptr; |
| 1032 | } | 1034 | } |
| 1033 | 1035 | ||
| 1036 | static LLVMValueRef gen_break(CodeGen *g, AstNode *node) { | ||
| 1037 | assert(node->type == NodeTypeBreak); | ||
| 1038 | assert(g->cur_break_block); | ||
| 1039 | |||
| 1040 | add_debug_source_node(g, node); | ||
| 1041 | return LLVMBuildBr(g->builder, g->cur_break_block); | ||
| 1042 | } | ||
| 1043 | |||
| 1034 | static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { | 1044 | static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1035 | switch (node->type) { | 1045 | switch (node->type) { |
| 1036 | case NodeTypeBinOpExpr: | 1046 | case NodeTypeBinOpExpr: |
| ... | @@ -1160,6 +1170,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { | ... | @@ -1160,6 +1170,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1160 | case NodeTypeGoto: | 1170 | case NodeTypeGoto: |
| 1161 | add_debug_source_node(g, node); | 1171 | add_debug_source_node(g, node); |
| 1162 | return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); | 1172 | return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); |
| 1173 | case NodeTypeBreak: | ||
| 1174 | return gen_break(g, node); | ||
| 1163 | case NodeTypeLabel: | 1175 | case NodeTypeLabel: |
| 1164 | { | 1176 | { |
| 1165 | LabelTableEntry *label_entry = node->codegen_node->data.label_entry; | 1177 | LabelTableEntry *label_entry = node->codegen_node->data.label_entry; |
src/parser.cpp+10-1| ... | @@ -122,6 +122,8 @@ const char *node_type_str(NodeType node_type) { | ... | @@ -122,6 +122,8 @@ const char *node_type_str(NodeType node_type) { |
| 122 | return "Label"; | 122 | return "Label"; |
| 123 | case NodeTypeGoto: | 123 | case NodeTypeGoto: |
| 124 | return "Goto"; | 124 | return "Goto"; |
| 125 | case NodeTypeBreak: | ||
| 126 | return "Break"; | ||
| 125 | case NodeTypeAsmExpr: | 127 | case NodeTypeAsmExpr: |
| 126 | return "AsmExpr"; | 128 | return "AsmExpr"; |
| 127 | case NodeTypeFieldAccessExpr: | 129 | case NodeTypeFieldAccessExpr: |
| ... | @@ -336,6 +338,9 @@ void ast_print(AstNode *node, int indent) { | ... | @@ -336,6 +338,9 @@ void ast_print(AstNode *node, int indent) { |
| 336 | case NodeTypeGoto: | 338 | case NodeTypeGoto: |
| 337 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name)); | 339 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name)); |
| 338 | break; | 340 | break; |
| 341 | case NodeTypeBreak: | ||
| 342 | fprintf(stderr, "%s\n", node_type_str(node->type)); | ||
| 343 | break; | ||
| 339 | case NodeTypeAsmExpr: | 344 | case NodeTypeAsmExpr: |
| 340 | fprintf(stderr, "%s\n", node_type_str(node->type)); | 345 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 341 | break; | 346 | break; |
| ... | @@ -1102,7 +1107,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { | ... | @@ -1102,7 +1107,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { |
| 1102 | } | 1107 | } |
| 1103 | 1108 | ||
| 1104 | /* | 1109 | /* |
| 1105 | PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | BlockExpression | token(Symbol) | StructValueExpression | 1110 | PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | Break | BlockExpression | token(Symbol) | StructValueExpression |
| 1106 | */ | 1111 | */ |
| 1107 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { | 1112 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1108 | Token *token = &pc->tokens->at(*token_index); | 1113 | Token *token = &pc->tokens->at(*token_index); |
| ... | @@ -1156,6 +1161,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool | ... | @@ -1156,6 +1161,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1156 | 1161 | ||
| 1157 | ast_buf_from_token(pc, dest_symbol, &node->data.go_to.name); | 1162 | ast_buf_from_token(pc, dest_symbol, &node->data.go_to.name); |
| 1158 | return node; | 1163 | return node; |
| 1164 | } else if (token->id == TokenIdKeywordBreak) { | ||
| 1165 | AstNode *node = ast_create_node(pc, NodeTypeBreak, token); | ||
| 1166 | *token_index += 1; | ||
| 1167 | return node; | ||
| 1159 | } | 1168 | } |
| 1160 | 1169 | ||
| 1161 | AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); | 1170 | AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); |
src/parser.hpp+1| ... | @@ -48,6 +48,7 @@ enum NodeType { | ... | @@ -48,6 +48,7 @@ enum NodeType { |
| 48 | NodeTypeWhileExpr, | 48 | NodeTypeWhileExpr, |
| 49 | NodeTypeLabel, | 49 | NodeTypeLabel, |
| 50 | NodeTypeGoto, | 50 | NodeTypeGoto, |
| 51 | NodeTypeBreak, | ||
| 51 | NodeTypeAsmExpr, | 52 | NodeTypeAsmExpr, |
| 52 | NodeTypeStructDecl, | 53 | NodeTypeStructDecl, |
| 53 | NodeTypeStructField, | 54 | NodeTypeStructField, |
test/run_tests.cpp+15| ... | @@ -656,6 +656,21 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { | ... | @@ -656,6 +656,21 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 656 | i += 1; | 656 | i += 1; |
| 657 | } | 657 | } |
| 658 | return 0; | 658 | return 0; |
| 659 | } | ||
| 660 | )SOURCE", "loop\nloop\nloop\nloop\n"); | ||
| 661 | |||
| 662 | add_simple_case("break out of while loop", R"SOURCE( | ||
| 663 | use "std.zig"; | ||
| 664 | export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { | ||
| 665 | var i : i32 = 0; | ||
| 666 | while true { | ||
| 667 | if i >= 4 { | ||
| 668 | break; | ||
| 669 | } | ||
| 670 | print_str("loop\n"); | ||
| 671 | i += 1; | ||
| 672 | } | ||
| 673 | return 0; | ||
| 659 | } | 674 | } |
| 660 | )SOURCE", "loop\nloop\nloop\nloop\n"); | 675 | )SOURCE", "loop\nloop\nloop\nloop\n"); |
| 661 | } | 676 | } |