]> de.git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
parsing of loop labels
authorWolfgang Bumiller <blub@speed.at>
Thu, 27 Dec 2012 22:43:20 +0000 (23:43 +0100)
committerWolfgang Bumiller <blub@speed.at>
Thu, 27 Dec 2012 22:43:20 +0000 (23:43 +0100)
ast.c
ast.h
opts.def
parser.c

diff --git a/ast.c b/ast.c
index b6a8a786c7d76b55a1fb7af884e82958848255af..2f78fe52b9774797937cefa30eb3999030a5d701 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -764,12 +764,13 @@ void ast_loop_delete(ast_loop *self)
     mem_d(self);
 }
 
-ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont)
+ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels)
 {
     ast_instantiate(ast_breakcont, ctx, ast_breakcont_delete);
     ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_breakcont_codegen);
 
     self->is_continue = iscont;
+    self->levels      = levels;
 
     return self;
 }
diff --git a/ast.h b/ast.h
index bc37eea72888fb3800028997f1f9b4559567224c..39cd950a158ba8a74b5a1a850eaa18dfd2c86383 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -474,9 +474,10 @@ bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
 struct ast_breakcont_s
 {
     ast_expression_common expression;
-    bool is_continue;
+    bool         is_continue;
+    unsigned int levels;
 };
-ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont);
+ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels);
 void ast_breakcont_delete(ast_breakcont*);
 
 bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
index 5d58d976cab2a357ef50f8ae716f1176a8e87900..d9fee1321acc09d656b351b3cee786dca47a549b 100644 (file)
--- a/opts.def
+++ b/opts.def
@@ -45,6 +45,7 @@
     GMQCC_DEFINE_FLAG(FALSE_EMPTY_STRINGS)
     GMQCC_DEFINE_FLAG(UTF8)
     GMQCC_DEFINE_FLAG(BAIL_ON_WERROR)
+    GMQCC_DEFINE_FLAG(LOOP_LABELS)
 #endif
 
 /* warning flags */
index 6a9157797c4517d12a3582dce6840f74e40bb1dc..23a3ed6fc7bae059e7ac69dc696a70b936f2ba7d 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -60,8 +60,9 @@ typedef struct {
     /* All the labels the function defined...
      * Should they be in ast_function instead?
      */
-    ast_label **labels;
-    ast_goto  **gotos;
+    ast_label  **labels;
+    ast_goto   **gotos;
+    const char **loops;
 
     /* A list of hashtables for each scope */
     ht *variables;
@@ -2018,7 +2019,58 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
     return true;
 }
 
+static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out);
 static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    bool rv;
+    char *label = NULL;
+
+    /* skip the 'while' and get the body */
+    if (!parser_next(parser)) {
+        if (OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "expected loop label or 'while' condition in parenthesis");
+        else
+            parseerror(parser, "expected 'while' condition in parenthesis");
+        return false;
+    }
+
+    if (parser->tok == ':') {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            parseerror(parser, "expected loop label");
+            return false;
+        }
+        label = util_strdup(parser_tokval(parser));
+        if (!parser_next(parser)) {
+            mem_d(label);
+            parseerror(parser, "expected 'while' condition in parenthesis");
+            return false;
+        }
+    }
+
+    if (parser->tok != '(') {
+        parseerror(parser, "expected 'while' condition in parenthesis");
+        return false;
+    }
+
+    vec_push(parser->loops, label);
+
+    rv = parse_while_go(parser, block, out);
+    if (label)
+        mem_d(label);
+    if (vec_last(parser->loops) != label) {
+        parseerror(parser, "internal error: label stack corrupted");
+        rv = false;
+        ast_delete(*out);
+        *out = NULL;
+    }
+    else
+        vec_pop(parser->loops);
+    return rv;
+}
+
+static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
@@ -2070,7 +2122,53 @@ static bool parse_while(parser_t *parser, ast_block *block, ast_expression **out
     return true;
 }
 
+static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out);
 static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    bool rv;
+    char *label = NULL;
+
+    /* skip the 'do' and get the body */
+    if (!parser_next(parser)) {
+        if (OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "expected loop label or body");
+        else
+            parseerror(parser, "expected loop body");
+        return false;
+    }
+
+    if (parser->tok == ':') {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            parseerror(parser, "expected loop label");
+            return false;
+        }
+        label = util_strdup(parser_tokval(parser));
+        if (!parser_next(parser)) {
+            mem_d(label);
+            parseerror(parser, "expected loop body");
+            return false;
+        }
+    }
+
+    vec_push(parser->loops, label);
+
+    rv = parse_dowhile_go(parser, block, out);
+    if (label)
+        mem_d(label);
+    if (vec_last(parser->loops) != label) {
+        parseerror(parser, "internal error: label stack corrupted");
+        rv = false;
+        ast_delete(*out);
+        *out = NULL;
+    }
+    else
+        vec_pop(parser->loops);
+    return rv;
+}
+
+static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_loop *aloop;
     ast_expression *cond, *ontrue;
@@ -2081,11 +2179,6 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
 
     (void)block; /* not touching */
 
-    /* skip the 'do' and get the body */
-    if (!parser_next(parser)) {
-        parseerror(parser, "expected loop body");
-        return false;
-    }
     if (!parse_statement_or_block(parser, &ontrue))
         return false;
 
@@ -2146,7 +2239,57 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
     return true;
 }
 
+static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out);
 static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
+{
+    bool rv;
+    char *label = NULL;
+
+    /* skip the 'for' and check for opening paren */
+    if (!parser_next(parser)) {
+        if (OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "expected loop label or 'for' expressions in parenthesis");
+        else
+            parseerror(parser, "expected 'for' expressions in parenthesis");
+        return false;
+    }
+
+    if (parser->tok == ':') {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            parseerror(parser, "expected loop label");
+            return false;
+        }
+        label = util_strdup(parser_tokval(parser));
+        if (!parser_next(parser)) {
+            mem_d(label);
+            parseerror(parser, "expected 'for' expressions in parenthesis");
+            return false;
+        }
+    }
+
+    if (parser->tok != '(') {
+        parseerror(parser, "expected 'for' expressions in parenthesis");
+        return false;
+    }
+
+    vec_push(parser->loops, label);
+
+    rv = parse_for_go(parser, block, out);
+    if (label)
+        mem_d(label);
+    if (vec_last(parser->loops) != label) {
+        parseerror(parser, "internal error: label stack corrupted");
+        rv = false;
+        ast_delete(*out);
+        *out = NULL;
+    }
+    else
+        vec_pop(parser->loops);
+    return rv;
+}
+static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **out)
 {
     ast_loop       *aloop;
     ast_expression *initexpr, *cond, *increment, *ontrue;
@@ -2164,11 +2307,6 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
     increment = NULL;
     ontrue    = NULL;
 
-    /* skip the 'while' and check for opening paren */
-    if (!parser_next(parser) || parser->tok != '(') {
-        parseerror(parser, "expected 'for' expressions in parenthesis");
-        goto onerr;
-    }
     /* parse into the expression */
     if (!parser_next(parser)) {
         parseerror(parser, "expected 'for' initializer after opening paren");
@@ -2311,11 +2449,38 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
 
 static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue)
 {
+    size_t i;
+    unsigned int levels = 0;
     lex_ctx ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
+    if (!parser_next(parser)) {
+        parseerror(parser, "expected semicolon or loop label");
+        return false;
+    }
 
-    if (!parser_next(parser) || parser->tok != ';') {
+    if (parser->tok == TOKEN_IDENT) {
+        if (!OPTS_FLAG(LOOP_LABELS))
+            parseerror(parser, "labeled loops not activated, try using -floop-labels");
+        i = vec_size(parser->loops);
+        while (i--) {
+            if (parser->loops[i] && !strcmp(parser->loops[i], parser_tokval(parser)))
+                break;
+            if (!i) {
+                parseerror(parser, "no such loop to %s: `%s`",
+                           (is_continue ? "continue" : "break out of"),
+                           parser_tokval(parser));
+                return false;
+            }
+            ++levels;
+        }
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected semicolon");
+            return false;
+        }
+    }
+
+    if (parser->tok != ';') {
         parseerror(parser, "expected semicolon");
         return false;
     }
@@ -2323,7 +2488,7 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
     if (!parser_next(parser))
         parseerror(parser, "parse error");
 
-    *out = (ast_expression*)ast_breakcont_new(ctx, is_continue);
+    *out = (ast_expression*)ast_breakcont_new(ctx, is_continue, levels);
     return true;
 }