]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
do-while loops
[xonotic/gmqcc.git] / parser.c
index 85640e9b692b081e2b00e59879f07fe03748111c..ee5c76da4df170b18cab98fd9b2cf40c44a1ee1f 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -62,7 +62,7 @@ void parseerror(parser_t *parser, const char *fmt, ...)
 }
 
 /* returns true if it counts as an error */
-bool parsewarning(parser_t *parser, int warntype, const char *fmt, ...)
+bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *fmt, ...)
 {
        va_list ap;
        int lvl = LVL_WARNING;
@@ -1114,6 +1114,67 @@ static bool parser_parse_while(parser_t *parser, ast_block *block, ast_expressio
     return true;
 }
 
+static bool parser_parse_dowhile(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    ast_loop *aloop;
+    ast_expression *cond, *ontrue;
+
+    lex_ctx ctx = parser_ctx(parser);
+
+    /* skip the 'do' and get the body */
+    if (!parser_next(parser) || parser->tok != '(') {
+        parseerror(parser, "expected loop body");
+        return false;
+    }
+    ontrue = parser_parse_statement_or_block(parser);
+    if (!ontrue)
+        return false;
+
+    /* expect the "while" */
+    if (parser->tok != TOKEN_KEYWORD ||
+        !strcmp(parser_tokval(parser), "while"))
+    {
+        parseerror(parser, "expected 'while' and condition");
+        ast_delete(ontrue);
+        return false;
+    }
+
+    /* skip the 'while' and check for opening paren */
+    if (!parser_next(parser) || parser->tok != '(') {
+        parseerror(parser, "expected 'while' condition in parenthesis");
+        ast_delete(ontrue);
+        return false;
+    }
+    /* parse into the expression */
+    if (!parser_next(parser)) {
+        parseerror(parser, "expected 'while' condition after opening paren");
+        ast_delete(ontrue);
+        return false;
+    }
+    /* parse the condition */
+    cond = parser_expression_leave(parser);
+    if (!cond)
+        return false;
+    /* closing paren */
+    if (parser->tok != ')') {
+        parseerror(parser, "expected closing paren after 'while' condition");
+        ast_delete(ontrue);
+        ast_delete(cond);
+        return false;
+    }
+    /* parse on */
+    if (!parser_next(parser)) {
+        parseerror(parser, "parse error");
+        ast_delete(ontrue);
+        ast_delete(cond);
+        return false;
+    }
+
+    aloop = ast_loop_new(ctx, NULL, NULL, cond, NULL, ontrue);
+    *out = (ast_expression*)aloop;
+    return true;
+}
+
 static bool parser_parse_for(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_loop *aloop;
@@ -1235,8 +1296,10 @@ static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expre
             parseerror(parser, "cannot declare a variable from here");
             return false;
         }
-        if (opts_standard == COMPILER_QCC)
-            parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable");
+        if (opts_standard == COMPILER_QCC) {
+            if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
+                return false;
+        }
         if (!parser_variable(parser, block))
             return false;
         *out = NULL;
@@ -1302,6 +1365,10 @@ static bool parser_parse_statement(parser_t *parser, ast_block *block, ast_expre
         {
             return parser_parse_while(parser, block, out);
         }
+        else if (!strcmp(parser_tokval(parser), "do"))
+        {
+            return parser_parse_dowhile(parser, block, out);
+        }
         else if (!strcmp(parser_tokval(parser), "for"))
         {
             return parser_parse_for(parser, block, out);