From: Wolfgang (Blub) Bumiller Date: Mon, 19 Nov 2012 18:39:52 +0000 (+0100) Subject: break and continue support X-Git-Tag: 0.1.9~399 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=ea75003cf4e38c28e51d05e9f0e77af58be9852f;p=xonotic%2Fgmqcc.git break and continue support --- diff --git a/ast.c b/ast.c index 9e59ca9..8de6e85 100644 --- a/ast.c +++ b/ast.c @@ -700,6 +700,22 @@ void ast_loop_delete(ast_loop *self) mem_d(self); } +ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont) +{ + ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete); + ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen); + + self->is_continue = iscont; + + return self; +} + +void ast_breakcont_delete(ast_breakcont *self) +{ + ast_expression_delete((ast_expression*)self); + mem_d(self); +} + ast_call* ast_call_new(lex_ctx ctx, ast_expression *funcexpr) { @@ -2184,6 +2200,31 @@ bool ast_loop_codegen(ast_loop *self, ast_function *func, bool lvalue, ir_value return true; } +bool ast_breakcont_codegen(ast_breakcont *self, ast_function *func, bool lvalue, ir_value **out) +{ + ir_block *target; + + if (lvalue) { + asterror(ast_ctx(self), "break/continue expression is not an l-value"); + return false; + } + + if (self->expression.outr) { + asterror(ast_ctx(self), "internal error: ast_breakcont cannot be reused!"); + return false; + } + self->expression.outr = (ir_value*)1; + + if (self->is_continue) + target = func->continueblock; + else + target = func->breakblock; + + if (!ir_block_create_jump(func->curblock, target)) + return false; + return true; +} + bool ast_call_codegen(ast_call *self, ast_function *func, bool lvalue, ir_value **out) { ast_expression_codegen *cgen; diff --git a/ast.h b/ast.h index 16f9807..f9f60af 100644 --- a/ast.h +++ b/ast.h @@ -46,6 +46,7 @@ typedef struct ast_unary_s ast_unary; typedef struct ast_return_s ast_return; typedef struct ast_member_s ast_member; typedef struct ast_array_index_s ast_array_index; +typedef struct ast_breakcont_s ast_breakcont; enum { TYPE_ast_node, @@ -64,7 +65,8 @@ enum { TYPE_ast_unary, TYPE_ast_return, TYPE_ast_member, - TYPE_ast_array_index + TYPE_ast_array_index, + TYPE_ast_breakcont }; #define ast_istype(x, t) ( ((ast_node_common*)x)->nodetype == (TYPE_##t) ) @@ -445,6 +447,18 @@ void ast_loop_delete(ast_loop*); bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**); +/* Break/Continue + */ +struct ast_breakcont_s +{ + ast_expression_common expression; + bool is_continue; +}; +ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont); +void ast_breakcont_delete(ast_breakcont*); + +bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**); + /* CALL node * * Contains an ast_expression as target, rather than an ast_function/value. diff --git a/ir.c b/ir.c index 9907b35..51d03da 100644 --- a/ir.c +++ b/ir.c @@ -1133,7 +1133,12 @@ bool ir_values_overlap(const ir_value *a, const ir_value *b) bool ir_block_create_store_op(ir_block *self, int op, ir_value *target, ir_value *what) { - ir_instr *in = ir_instr_new(self, op); + ir_instr *in; + if (self->final) { + irerror(self->context, "unreachable statement (%s)", self->label); + return false; + } + in = ir_instr_new(self, op); if (!in) return false; @@ -1206,7 +1211,7 @@ bool ir_block_create_return(ir_block *self, ir_value *v) { ir_instr *in; if (self->final) { - irerror(self->context, "block already ended (%s)", self->label); + irerror(self->context, "unreachable statement (%s)", self->label); return false; } self->final = true; @@ -1227,7 +1232,7 @@ bool ir_block_create_if(ir_block *self, ir_value *v, { ir_instr *in; if (self->final) { - irerror(self->context, "block already ended (%s)", self->label); + irerror(self->context, "unreachable statement (%s)", self->label); return false; } self->final = true; @@ -1257,7 +1262,7 @@ bool ir_block_create_jump(ir_block *self, ir_block *to) { ir_instr *in; if (self->final) { - irerror(self->context, "block already ended (%s)", self->label); + irerror(self->context, "unreachable statement (%s)", self->label); return false; } self->final = true; @@ -1277,7 +1282,7 @@ bool ir_block_create_goto(ir_block *self, ir_block *to) { ir_instr *in; if (self->final) { - irerror(self->context, "block already ended (%s)", self->label); + irerror(self->context, "unreachable statement (%s)", self->label); return false; } self->final = true; diff --git a/parser.c b/parser.c index c21bbc7..6178a1d 100644 --- a/parser.c +++ b/parser.c @@ -1718,6 +1718,25 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou return true; } +static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue) +{ + ast_expression *exp = NULL; + ast_return *ret = NULL; + + lex_ctx ctx = parser_ctx(parser); + + if (!parser_next(parser) || parser->tok != ';') { + parseerror(parser, "expected semicolon"); + return false; + } + + if (!parser_next(parser)) + parseerror(parser, "parse error"); + + *out = ast_breakcont_new(ctx, is_continue); + return true; +} + static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out) { if (parser->tok == TOKEN_TYPENAME || parser->tok == '.') @@ -1777,6 +1796,14 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } return parse_for(parser, block, out); } + else if (!strcmp(parser_tokval(parser), "break")) + { + return parse_break_continue(parser, block, out, false); + } + else if (!strcmp(parser_tokval(parser), "continue")) + { + return parse_break_continue(parser, block, out, true); + } parseerror(parser, "Unexpected keyword"); return false; }