From 5ceaae288c4f80fe5ce1449cd9d2efe0e541d629 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Dec 2015 15:09:35 -0700 Subject: [PATCH] add break expression --- doc/langref.md | 4 +++- src/analyze.cpp | 13 +++++++++++++ src/analyze.hpp | 1 + src/codegen.cpp | 12 ++++++++++++ src/parser.cpp | 11 ++++++++++- src/parser.hpp | 1 + test/run_tests.cpp | 15 +++++++++++++++ 7 files changed, 55 insertions(+), 2 deletions(-) diff --git a/doc/langref.md b/doc/langref.md index 42a09cdacf..98cd0e4cc4 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -146,7 +146,7 @@ ArrayAccessExpression : token(LBracket) Expression token(RBracket) PrefixOp : token(Not) | token(Dash) | token(Tilde) | (token(Ampersand) option(token(Const))) -PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | BlockExpression | token(Symbol) | StructValueExpression +PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | Break | BlockExpression | token(Symbol) | StructValueExpression StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace) @@ -154,6 +154,8 @@ StructValueExpressionField : token(Dot) token(Symbol) token(Eq) Expression Goto: token(Goto) token(Symbol) +Break: token(Break) + GroupedExpression : token(LParen) Expression token(RParen) KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) diff --git a/src/analyze.cpp b/src/analyze.cpp index bd9eaa7f2d..a5e1255126 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -48,6 +48,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeIfExpr: case NodeTypeLabel: case NodeTypeGoto: + case NodeTypeBreak: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructDecl: @@ -530,6 +531,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, case NodeTypeWhileExpr: case NodeTypeLabel: case NodeTypeGoto: + case NodeTypeBreak: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: @@ -598,6 +600,7 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { case NodeTypeWhileExpr: case NodeTypeLabel: case NodeTypeGoto: + case NodeTypeBreak: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: @@ -1360,6 +1363,12 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, return g->builtin_types.entry_void; } +static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + return g->builtin_types.entry_unreachable; +} + static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -1439,6 +1448,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, return_type = g->builtin_types.entry_unreachable; break; } + case NodeTypeBreak: + return_type = analyze_break_expr(g, import, context, expected_type, node); + break; case NodeTypeAsmExpr: { node->data.asm_expr.return_count = 0; @@ -1792,6 +1804,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, case NodeTypeWhileExpr: case NodeTypeLabel: case NodeTypeGoto: + case NodeTypeBreak: case NodeTypeAsmExpr: case NodeTypeFieldAccessExpr: case NodeTypeStructField: diff --git a/src/analyze.hpp b/src/analyze.hpp index 66f5c2eeed..fae6f0b81f 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -196,6 +196,7 @@ struct CodeGen { FnTableEntry *cur_fn; LLVMBasicBlockRef cur_basic_block; BlockContext *cur_block_context; + LLVMBasicBlockRef cur_break_block; bool c_stdint_used; AstNode *root_export_decl; int version_major; diff --git a/src/codegen.cpp b/src/codegen.cpp index 2c0ed2efed..d4870e002b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1024,13 +1024,23 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { LLVMBuildCondBr(g->builder, cond_val, body_block, end_block); LLVMPositionBuilderAtEnd(g->builder, body_block); + g->cur_break_block = end_block; gen_expr(g, node->data.while_expr.body); + g->cur_break_block = nullptr; LLVMBuildBr(g->builder, cond_block); LLVMPositionBuilderAtEnd(g->builder, end_block); return nullptr; } +static LLVMValueRef gen_break(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeBreak); + assert(g->cur_break_block); + + add_debug_source_node(g, node); + return LLVMBuildBr(g->builder, g->cur_break_block); +} + static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { switch (node->type) { case NodeTypeBinOpExpr: @@ -1160,6 +1170,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { case NodeTypeGoto: add_debug_source_node(g, node); return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); + case NodeTypeBreak: + return gen_break(g, node); case NodeTypeLabel: { LabelTableEntry *label_entry = node->codegen_node->data.label_entry; diff --git a/src/parser.cpp b/src/parser.cpp index 5a5486e713..ff6a2dd573 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -122,6 +122,8 @@ const char *node_type_str(NodeType node_type) { return "Label"; case NodeTypeGoto: return "Goto"; + case NodeTypeBreak: + return "Break"; case NodeTypeAsmExpr: return "AsmExpr"; case NodeTypeFieldAccessExpr: @@ -336,6 +338,9 @@ void ast_print(AstNode *node, int indent) { case NodeTypeGoto: fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.go_to.name)); break; + case NodeTypeBreak: + fprintf(stderr, "%s\n", node_type_str(node->type)); + break; case NodeTypeAsmExpr: fprintf(stderr, "%s\n", node_type_str(node->type)); break; @@ -1102,7 +1107,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { } /* -PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | BlockExpression | token(Symbol) | StructValueExpression +PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | Goto | Break | BlockExpression | token(Symbol) | StructValueExpression */ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1156,6 +1161,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool ast_buf_from_token(pc, dest_symbol, &node->data.go_to.name); return node; + } else if (token->id == TokenIdKeywordBreak) { + AstNode *node = ast_create_node(pc, NodeTypeBreak, token); + *token_index += 1; + return node; } AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); diff --git a/src/parser.hpp b/src/parser.hpp index af24b29131..309337bbfa 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -48,6 +48,7 @@ enum NodeType { NodeTypeWhileExpr, NodeTypeLabel, NodeTypeGoto, + NodeTypeBreak, NodeTypeAsmExpr, NodeTypeStructDecl, NodeTypeStructField, diff --git a/test/run_tests.cpp b/test/run_tests.cpp index a4a2d8ae68..91cf6591eb 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -656,6 +656,21 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { i += 1; } return 0; +} + )SOURCE", "loop\nloop\nloop\nloop\n"); + + add_simple_case("break out of while loop", R"SOURCE( +use "std.zig"; +export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { + var i : i32 = 0; + while true { + if i >= 4 { + break; + } + print_str("loop\n"); + i += 1; + } + return 0; } )SOURCE", "loop\nloop\nloop\nloop\n"); }