]> de.git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
break and continue support
authorWolfgang (Blub) Bumiller <blub@speed.at>
Mon, 19 Nov 2012 18:39:52 +0000 (19:39 +0100)
committerWolfgang (Blub) Bumiller <blub@speed.at>
Mon, 19 Nov 2012 18:39:52 +0000 (19:39 +0100)
ast.c
ast.h
ir.c
parser.c

diff --git a/ast.c b/ast.c
index 9e59ca917d73c9ed561b10f33bcf8087f01b3c28..8de6e85b73e214ba4ecc86e301fb9f024a55ad4f 100644 (file)
--- 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 16f980709d518f3eddf66b7120b90d2f71b928d6..f9f60afc13d6cb55ec67ea5e568e05ae69cadcc1 100644 (file)
--- 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 9907b3585284a5770961786259003a608ecc9e73..51d03dac660dbd76205ed6990f34faf2c0b5deef 100644 (file)
--- 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;
index c21bbc7c0c65a9f1a424a2413922c73e09b1128b..6178a1d6d42b7e690b7a3554a3cedd3ed54bf29b 100644 (file)
--- 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;
     }