]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
parse_expression now takes a boolean flag on whether or not it should be creating...
[xonotic/gmqcc.git] / parser.c
index 425668225f2ee37c21be88d8c1ab4bbf40bc17b1..14b4c810dab20c866ebbffbb025f89078d223feb 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -111,8 +111,8 @@ static ast_block* parse_block(parser_t *parser);
 static bool parse_block_into(parser_t *parser, ast_block *block);
 static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases);
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue);
-static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
+static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
@@ -300,6 +300,15 @@ static ast_expression* parser_find_field(parser_t *parser, const char *name)
     return ( ast_expression*)util_htget(parser->htfields, name);
 }
 
+static ast_expression* parser_find_label(parser_t *parser, const char *name)
+{
+    size_t i;
+    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);
@@ -1483,7 +1492,7 @@ static void parser_reclassify_token(parser_t *parser)
     }
 }
 
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue)
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels)
 {
     ast_expression *expr = NULL;
     shunt sy;
@@ -1580,11 +1589,18 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 if (!var)
                     var = parser_find_field(parser, parser_tokval(parser));
             }
+            if (!var && with_labels) {
+                var = (ast_expression*)parser_find_label(parser, parser_tokval(parser));
+                if (!with_labels) {
+                    ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true);
+                    var = (ast_expression*)lbl;
+                    vec_push(parser->labels, lbl);
+                }
+            }
             if (!var) {
                 /* intrinsics */
                 if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
                     var = (ast_expression*)intrinsic_debug_typestring;
-
                 }
                 else
                 {
@@ -1908,9 +1924,9 @@ onerr:
     return NULL;
 }
 
-static ast_expression* parse_expression(parser_t *parser, bool stopatcomma)
+static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels)
 {
-    ast_expression *e = parse_expression_leave(parser, stopatcomma, false);
+    ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels);
     if (!e)
         return NULL;
     if (!parser_next(parser)) {
@@ -2060,7 +2076,7 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2181,7 +2197,7 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2296,7 +2312,7 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2425,7 +2441,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
     }
     else if (parser->tok != ';')
     {
-        initexpr = parse_expression_leave(parser, false, false);
+        initexpr = parse_expression_leave(parser, false, false, false);
         if (!initexpr)
             goto onerr;
     }
@@ -2442,7 +2458,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the condition */
     if (parser->tok != ';') {
-        cond = parse_expression_leave(parser, false, true);
+        cond = parse_expression_leave(parser, false, true, false);
         if (!cond)
             goto onerr;
     }
@@ -2459,7 +2475,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the incrementor */
     if (parser->tok != ')') {
-        increment = parse_expression_leave(parser, false, false);
+        increment = parse_expression_leave(parser, false, false, false);
         if (!increment)
             goto onerr;
         if (!ast_side_effects(increment)) {
@@ -2516,7 +2532,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
     }
 
     if (parser->tok != ';') {
-        exp = parse_expression(parser, false);
+        exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
 
@@ -2802,7 +2818,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
         return false;
     }
     /* parse the operand */
-    operand = parse_expression_leave(parser, false, false);
+    operand = parse_expression_leave(parser, false, false, false);
     if (!operand)
         return false;
 
@@ -2866,7 +2882,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
                 parseerror(parser, "expected expression for case");
                 return false;
             }
-            swcase.value = parse_expression_leave(parser, false, false);
+            swcase.value = parse_expression_leave(parser, false, false, false);
             if (!swcase.value) {
                 ast_delete(switchnode);
                 parseerror(parser, "expected expression for case");
@@ -2957,25 +2973,74 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
     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);
+
+        if (!on_true || !on_false) {
+            parseerror(parser, "expected label or expression in ternary");
+            if (((ast_ternary*)side)->on_false) ast_unref(((ast_ternary*)side)->on_false);
+            if (((ast_ternary*)side)->on_true)  ast_unref(((ast_ternary*)side)->on_true);
+            return NULL;
+        }
+
+        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;
-    ast_goto *gt;
+    ast_goto       *gt = NULL;
+    ast_expression *lbl;
 
-    if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
-        parseerror(parser, "expected label name after `goto`");
+    if (!parser_next(parser))
         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, true)) ||
+            !(*out = parse_goto_computed(parser, expression))) {
+            parseerror(parser, "invalid goto expression");
+            ast_unref(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) {
-        if (!strcmp(parser->labels[i]->name, parser_tokval(parser))) {
-            ast_goto_set_label(gt, parser->labels[i]);
-            break;
+    lbl = parser_find_label(parser, gt->name);
+    if (lbl) {
+        if (!ast_istype(lbl, ast_label)) {
+            parseerror(parser, "internal error: label is not an ast_label");
+            ast_delete(gt);
+            return false;
         }
+        ast_goto_set_label(gt, (ast_label*)lbl);
     }
-    if (i == vec_size(parser->labels))
+    else
         vec_push(parser->gotos, gt);
 
     if (!parser_next(parser) || parser->tok != ';') {
@@ -3210,10 +3275,18 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             parseerror(parser, "label must be an identifier");
             return false;
         }
-        label = ast_label_new(parser_ctx(parser), parser_tokval(parser));
-        if (!label)
-            return false;
-        vec_push(parser->labels, label);
+        label = (ast_label*)parser_find_label(parser, parser_tokval(parser));
+        if (label) {
+            if (!label->undefined) {
+                parseerror(parser, "label `%s` already defined", label->name);
+                return false;
+            }
+            label->undefined = false;
+        }
+        else {
+            label = ast_label_new(parser_ctx(parser), parser_tokval(parser), false);
+            vec_push(parser->labels, label);
+        }
         *out = (ast_expression*)label;
         if (!parser_next(parser)) {
             parseerror(parser, "parse error after label");
@@ -3238,7 +3311,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
     else
     {
-        ast_expression *exp = parse_expression(parser, false);
+        ast_expression *exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
         *out = exp;
@@ -3399,7 +3472,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         if (!parser_next(parser))
             return false;
 
-        framenum = parse_expression_leave(parser, true, false);
+        framenum = parse_expression_leave(parser, true, false, false);
         if (!framenum) {
             parseerror(parser, "expected a framenumber constant in[frame,think] notation");
             return false;
@@ -3447,7 +3520,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             nextthink = (ast_expression*)thinkfunc;
 
         } else {
-            nextthink = parse_expression_leave(parser, true, false);
+            nextthink = parse_expression_leave(parser, true, false, false);
             if (!nextthink) {
                 ast_unref(framenum);
                 parseerror(parser, "expected a think-function in [frame,think] notation");
@@ -4113,7 +4186,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
         return NULL;
     }
 
-    cexp = parse_expression_leave(parser, true, false);
+    cexp = parse_expression_leave(parser, true, false, false);
 
     if (!cexp || !ast_istype(cexp, ast_value)) {
         if (cexp)
@@ -4879,7 +4952,7 @@ skipvar:
             ast_expression *cexp;
             ast_value      *cval;
 
-            cexp = parse_expression_leave(parser, true, false);
+            cexp = parse_expression_leave(parser, true, false, false);
             if (!cexp)
                 break;