]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Implemented computed goto + added goto test for testsuite (tests both normal and...
authorDale Weiler <killfieldengine@gmail.com>
Wed, 2 Jan 2013 21:32:57 +0000 (21:32 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Wed, 2 Jan 2013 21:32:57 +0000 (21:32 +0000)
parser.c
tests/goto.qc [new file with mode: 0644]
tests/goto.tmpl [new file with mode: 0644]

index 425668225f2ee37c21be88d8c1ab4bbf40bc17b1..8701a8fa93b0d159aee8761f87504e1b548ad0d6 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -300,6 +300,17 @@ static ast_expression* parser_find_field(parser_t *parser, const char *name)
     return ( ast_expression*)util_htget(parser->htfields, name);
 }
 
     return ( ast_expression*)util_htget(parser->htfields, name);
 }
 
+static ast_expression* parser_find_label(parser_t *parser, const char *name) {
+    size_t i;
+    if (!parser->labels)
+        return NULL;
+    for(i = 0; i < vec_size(parser->labels); i++)
+        if (!strcmp(parser->labels[i]->name, name))
+            return (ast_expression*)parser->labels[i];
+    return NULL;
+}
+
 static ast_expression* parser_find_global(parser_t *parser, const char *name)
 {
     return (ast_expression*)util_htget(parser->htglobals, name);
 static ast_expression* parser_find_global(parser_t *parser, const char *name)
 {
     return (ast_expression*)util_htget(parser->htglobals, name);
@@ -1573,18 +1584,19 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     parseerror(parser, "namespace for member not found");
                     goto onerr;
                 }
                     parseerror(parser, "namespace for member not found");
                     goto onerr;
                 }
-                else
-                    var = parser_find_var(parser, parser_tokval(parser));
+                else if (!(var = parser_find_var(parser, parser_tokval(parser))))
+                    var = (ast_expression*)parser_find_label(parser, parser_tokval(parser));
             } else {
                 var = parser_find_var(parser, parser_tokval(parser));
                 if (!var)
                     var = parser_find_field(parser, parser_tokval(parser));
             } else {
                 var = parser_find_var(parser, parser_tokval(parser));
                 if (!var)
                     var = parser_find_field(parser, parser_tokval(parser));
+                if (!var)
+                    var = parser_find_label(parser, parser_tokval(parser));
             }
             if (!var) {
                 /* intrinsics */
                 if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
                     var = (ast_expression*)intrinsic_debug_typestring;
             }
             if (!var) {
                 /* intrinsics */
                 if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
                     var = (ast_expression*)intrinsic_debug_typestring;
-
                 }
                 else
                 {
                 }
                 else
                 {
@@ -2957,16 +2969,54 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
     return true;
 }
 
     return true;
 }
 
+/* parse computed goto sides */
+static ast_expression *parse_goto_computed(parser_t *parser, ast_expression *side) {
+    ast_expression *on_true;
+    ast_expression *on_false;
+
+    if (!side)
+        return NULL;
+
+    if (ast_istype(side, ast_ternary)) {
+        on_true  = parse_goto_computed(parser, ((ast_ternary*)side)->on_true);
+        on_false = parse_goto_computed(parser, ((ast_ternary*)side)->on_false);
+
+        return (ast_expression*)ast_ifthen_new(parser_ctx(parser), ((ast_ternary*)side)->cond, on_true, on_false);
+    } else if (ast_istype(side, ast_label)) {
+        ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)side)->name);
+        ast_goto_set_label(gt, ((ast_label*)side));
+        return (ast_expression*)gt;
+    }
+    return NULL;
+}
+
 static bool parse_goto(parser_t *parser, ast_expression **out)
 {
     size_t    i;
 static bool parse_goto(parser_t *parser, ast_expression **out)
 {
     size_t    i;
-    ast_goto *gt;
+    ast_goto *gt = NULL;
 
 
-    if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
-        parseerror(parser, "expected label name after `goto`");
+    if (!parser_next(parser))
         return false;
         return false;
+
+    if (parser->tok != TOKEN_IDENT) {
+        ast_expression *expression;
+        /* could be an expression i.e computed goto :-) */
+        if (parser->tok != '(') {
+            parseerror(parser, "expected label name after `goto`");
+            return false;
+        }
+
+        /* failed to parse expression for goto */
+        if (!(expression = parse_expression(parser, false)))
+            return false;
+
+        if (!(*out = parse_goto_computed(parser, expression)))
+            return false;
+
+        return true;
     }
 
     }
 
+    /* not computed goto */
     gt = ast_goto_new(parser_ctx(parser), parser_tokval(parser));
 
     for (i = 0; i < vec_size(parser->labels); ++i) {
     gt = ast_goto_new(parser_ctx(parser), parser_tokval(parser));
 
     for (i = 0; i < vec_size(parser->labels); ++i) {
@@ -2979,7 +3029,7 @@ static bool parse_goto(parser_t *parser, ast_expression **out)
         vec_push(parser->gotos, gt);
 
     if (!parser_next(parser) || parser->tok != ';') {
         vec_push(parser->gotos, gt);
 
     if (!parser_next(parser) || parser->tok != ';') {
-        parseerror(parser, "semicolon expected after goto label");
+        parseerror(parser, "semicolon expected after goto label got");
         return false;
     }
     if (!parser_next(parser)) {
         return false;
     }
     if (!parser_next(parser)) {
diff --git a/tests/goto.qc b/tests/goto.qc
new file mode 100644 (file)
index 0000000..7c9f297
--- /dev/null
@@ -0,0 +1,34 @@
+void(string, ...) print = #1;
+
+// correct execution order:
+// label_3
+// label_2
+// label_4
+// label_3
+// label_1
+// label_5
+void main() {
+    float x = 1;
+    float y = 2;
+
+    goto label_3;
+
+    :label_1; print("label_1", "\n"); goto label_5;
+    :label_2; print("label_2", "\n"); goto label_4;
+    :label_3; print("label_3", "\n");
+
+    // will goto label_2
+    goto (x == y) ? label_1 : label_2;
+
+    :label_4; print("label_4", "\n");
+    {
+        x = 1;
+        y = 1;
+
+        // will goto label_1
+        // then goes label_5
+        goto label_3;
+    }
+
+    :label_5; print("label_5", "\n");
+}
diff --git a/tests/goto.tmpl b/tests/goto.tmpl
new file mode 100644 (file)
index 0000000..4acf179
--- /dev/null
@@ -0,0 +1,12 @@
+I: goto.qc
+D: test goto (both normal and computed)
+T: -execute
+C: -std=gmqcc
+F: goto failed
+S: goto worked
+M: label_3
+M: label_2
+M: label_4
+M: label_3
+M: label_1
+M: label_5