]> git.rm.cloudns.org Git - xonotic/gmqcc.git/commitdiff
ast_label, and labels later used for goto
authorWolfgang (Blub) Bumiller <blub@speed.at>
Sun, 25 Nov 2012 20:27:14 +0000 (21:27 +0100)
committerWolfgang (Blub) Bumiller <blub@speed.at>
Sun, 25 Nov 2012 20:27:14 +0000 (21:27 +0100)
ast.c
ast.h
parser.c

diff --git a/ast.c b/ast.c
index 9aca0e986b135e814993bf57677f47adf70def57..16141b849ecd6e16d198edfc0a6b4a496197ae2a 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -800,6 +800,24 @@ void ast_switch_delete(ast_switch *self)
     mem_d(self);
 }
 
+ast_label* ast_label_new(lex_ctx ctx, const char *name)
+{
+    ast_instantiate(ast_label, ctx, ast_label_delete);
+    ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_label_codegen);
+
+    self->name    = util_strdup(name);
+    self->irblock = NULL;
+
+    return self;
+}
+
+void ast_label_delete(ast_label *self)
+{
+    mem_d((void*)self->name);
+    ast_expression_delete((ast_expression*)self);
+    mem_d(self);
+}
+
 ast_call* ast_call_new(lex_ctx ctx,
                        ast_expression *funcexpr)
 {
@@ -2635,6 +2653,30 @@ bool ast_switch_codegen(ast_switch *self, ast_function *func, bool lvalue, ir_va
     return true;
 }
 
+bool ast_label_codegen(ast_label *self, ast_function *func, bool lvalue, ir_value **out)
+{
+    *out = NULL;
+    if (lvalue) {
+        asterror(ast_ctx(self), "internal error: ast_label cannot be an lvalue");
+        return false;
+    }
+
+    /* simply create a new block and jump to it */
+    self->irblock = ir_function_create_block(func->ir_func, self->name);
+    if (!self->irblock) {
+        asterror(ast_ctx(self), "failed to allocate label block `%s`", self->name);
+        return false;
+    }
+    if (!func->curblock->final) {
+        if (!ir_block_create_jump(func->curblock, self->irblock))
+            return false;
+    }
+
+    /* enter the new block */
+    func->curblock = self->irblock;
+    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 4b889554ea1fa0c09891b7e600ea999739523326..62287fe88e05eb6dc39cba1e20ff141221a1c0d3 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -48,6 +48,7 @@ typedef struct ast_member_s      ast_member;
 typedef struct ast_array_index_s ast_array_index;
 typedef struct ast_breakcont_s   ast_breakcont;
 typedef struct ast_switch_s      ast_switch;
+typedef struct ast_label_s       ast_label;
 
 enum {
     TYPE_ast_node,
@@ -68,7 +69,8 @@ enum {
     TYPE_ast_member,
     TYPE_ast_array_index,
     TYPE_ast_breakcont,
-    TYPE_ast_switch
+    TYPE_ast_switch,
+    TYPE_ast_label
 };
 
 #define ast_istype(x, t) ( ((ast_node_common*)x)->nodetype == (TYPE_##t) )
@@ -489,6 +491,22 @@ void ast_switch_delete(ast_switch*);
 
 bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**);
 
+/* Label nodes
+ *
+ * Introduce a label which can be used together with 'goto'
+ */
+struct ast_label_s
+{
+    ast_expression_common expression;
+    const char *name;
+    ir_block   *irblock;
+};
+
+ast_label* ast_label_new(lex_ctx ctx, const char *name);
+void ast_label_delete(ast_label*);
+
+bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**);
+
 /* CALL node
  *
  * Contains an ast_expression as target, rather than an ast_function/value.
index 945ddbf76c8ee0b4ff513e3f339b1ea4a6ddd9ee..1d752c409688c4f1270aa7a7cc1c3cd0b6832254 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -2336,6 +2336,27 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
         *out = (ast_expression*)inner;
         return true;
     }
+    else if (parser->tok == ':')
+    {
+        ast_label *label;
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected label name");
+            return false;
+        }
+        if (parser->tok != TOKEN_IDENT) {
+            parseerror(parser, "label must be an identifier");
+            return false;
+        }
+        label = ast_label_new(parser_ctx(parser), parser_tokval(parser));
+        if (!label)
+            return false;
+        *out = (ast_expression*)label;
+        if (!parser_next(parser)) {
+            parseerror(parser, "parse error after label");
+            return false;
+        }
+        return true;
+    }
     else if (parser->tok == ';')
     {
         if (!parser_next(parser)) {