]> git.xonotic.org Git - xonotic/gmqcc.git/commitdiff
Merge remote-tracking branch 'origin/pp-unary-numbers'
authorWolfgang Bumiller <blub@speed.at>
Thu, 3 Jan 2013 13:58:02 +0000 (14:58 +0100)
committerWolfgang Bumiller <blub@speed.at>
Thu, 3 Jan 2013 13:58:02 +0000 (14:58 +0100)
12 files changed:
ast.c
ast.h
conout.c
file.c
ftepp.c
gmqcc.h
ir.c
lexer.h
parser.c
test.c
tests/goto.qc [new file with mode: 0644]
tests/goto.tmpl [new file with mode: 0644]

diff --git a/ast.c b/ast.c
index 6e14bbc2184c8d8e7805495773b997c17d8aba0f..2b4a456ebb08e5e388d79bbe9a910d49c51d1a9e 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -724,9 +724,12 @@ ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *
 
 void ast_ternary_delete(ast_ternary *self)
 {
-    ast_unref(self->cond);
-    ast_unref(self->on_true);
-    ast_unref(self->on_false);
+    /* the if()s are only there because computed-gotos can set them
+     * to NULL
+     */
+    if (self->cond)     ast_unref(self->cond);
+    if (self->on_true)  ast_unref(self->on_true);
+    if (self->on_false) ast_unref(self->on_false);
     ast_expression_delete((ast_expression*)self);
     mem_d(self);
 }
@@ -826,14 +829,17 @@ void ast_switch_delete(ast_switch *self)
     mem_d(self);
 }
 
-ast_label* ast_label_new(lex_ctx ctx, const char *name)
+ast_label* ast_label_new(lex_ctx ctx, const char *name, bool undefined)
 {
     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;
-    self->gotos   = NULL;
+    self->expression.vtype = TYPE_NOEXPR;
+
+    self->name      = util_strdup(name);
+    self->irblock   = NULL;
+    self->gotos     = NULL;
+    self->undefined = undefined;
 
     return self;
 }
@@ -2868,6 +2874,11 @@ bool ast_label_codegen(ast_label *self, ast_function *func, bool lvalue, ir_valu
     size_t i;
     ir_value *dummy;
 
+    if (self->undefined) {
+        compile_error(ast_ctx(self), "internal error: ast_label never defined");
+        return false;
+    }
+
     *out = NULL;
     if (lvalue) {
         compile_error(ast_ctx(self), "internal error: ast_label cannot be an lvalue");
diff --git a/ast.h b/ast.h
index 97a3dc0f4ef88a5c3ac07e5db29cafcfa90be254..ce69810e152fbc7ea8e338ed0fe0e00bc89b3cf0 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -525,9 +525,11 @@ struct ast_label_s
     const char *name;
     ir_block   *irblock;
     ast_goto  **gotos;
+    /* means it has not yet been defined */
+    bool        undefined;
 };
 
-ast_label* ast_label_new(lex_ctx ctx, const char *name);
+ast_label* ast_label_new(lex_ctx ctx, const char *name, bool undefined);
 void ast_label_delete(ast_label*);
 void ast_label_register_goto(ast_label*, ast_goto*);
 
index 39d02fbf8116c179c4c30bab8ff1adbc6ab175af..81cf48135433264abeffa2e659ab1d7915dc6f00 100644 (file)
--- a/conout.c
+++ b/conout.c
@@ -54,7 +54,7 @@ typedef struct {
  * Doing colored output on windows is fucking stupid.  The linux way is
  * the real way. So we emulate it on windows :)
  */
-#ifdef _MSC_VER
+#ifdef _WIN32
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
@@ -104,7 +104,7 @@ static const int ansi2win[] = {
     WWHITE
 };
 
-static int win_fputs(const char *str, FILE *h) {
+static int win_fputs(FILE *h, const char *str) {
     /* state for translate */
     int acolor;
     int wcolor;
@@ -168,7 +168,7 @@ static int win_fputs(const char *str, FILE *h) {
                 state    = -1;
             }
         } else {
-            file_putc(*str, h);
+            file_putc(h, *str);
             length ++;
         }
         str++;
@@ -212,14 +212,14 @@ static void con_enablecolor() {
  */
 static int con_write(FILE *handle, const char *fmt, va_list va) {
     int      ln;
-    #ifndef _MSC_VER
+    #ifndef _WIN32
     ln = vfprintf(handle, fmt, va);
     #else
     {
         char data[4096];
         memset(data, 0, sizeof(data));
         vsnprintf(data, sizeof(data), fmt, va);
-        ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(data, handle) : file_puts(data, handle);
+        ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(handle, data) : file_puts(handle, data);
     }
     #endif
     return ln;
diff --git a/file.c b/file.c
index fd7b84e67a67911f171866e25de4418373677145..fe6167f3a979d33533bb757f9e4eff713f7f20da 100644 (file)
--- a/file.c
+++ b/file.c
@@ -165,6 +165,11 @@ int file_seek(FILE *fp, long int off, int whence) {
     return fseek(fp, off, whence);
 }
 
+int file_putc(FILE *fp, int ch) {
+    /* Invokes file_exception on windows if fp is null */
+    return fputc(ch, fp);
+}
+
 /*
  * Implements libc getline for systems that don't have it, which is
  * assmed all.  This works the same as getline().
diff --git a/ftepp.c b/ftepp.c
index ecc36d8e1851f422aed1c31f0e2696ae71db548a..838d0de38d37f9198fbe3fa2e795340e83c09394 100644 (file)
--- a/ftepp.c
+++ b/ftepp.c
@@ -21,6 +21,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+#include <time.h>
 #include "gmqcc.h"
 #include "lexer.h"
 
@@ -69,11 +70,6 @@ typedef struct {
     char        *includename;
 } ftepp_t;
 
-typedef struct {
-    const char  *name;
-    char      *(*func)(lex_file *);
-} predef_t;
-
 /*
  * Implement the predef subsystem now.  We can do this safely with the
  * help of lexer contexts.
@@ -81,6 +77,42 @@ typedef struct {
 static uint32_t ftepp_predef_countval = 0;
 static uint32_t ftepp_predef_randval  = 0;
 
+/* __DATE__ */
+char *ftepp_predef_date(lex_file *context) {
+    struct tm *itime;
+    time_t     rtime;
+    char      *value = mem_a(82);
+    /* 82 is enough for strftime but we also have " " in our string */
+
+    (void)context;
+
+    /* get time */
+    time (&rtime);
+    itime = localtime(&rtime);
+
+    strftime(value, 82, "\"%b %d %Y\"", itime);
+
+    return value;
+}
+
+/* __TIME__ */
+char *ftepp_predef_time(lex_file *context) {
+    struct tm *itime;
+    time_t     rtime;
+    char      *value = mem_a(82);
+    /* 82 is enough for strftime but we also have " " in our string */
+
+    (void)context;
+
+    /* get time */
+    time (&rtime);
+    itime = localtime(&rtime);
+
+    strftime(value, 82, "\"%X\"", itime);
+
+    return value;
+}
+
 /* __LINE__ */
 char *ftepp_predef_line(lex_file *context) {
     char   *value;
@@ -131,13 +163,15 @@ char *ftepp_predef_randomlast(lex_file *context) {
     return value;
 }
 
-static const predef_t ftepp_predefs[] = {
+const ftepp_predef_t ftepp_predefs[FTEPP_PREDEF_COUNT] = {
     { "__LINE__",         &ftepp_predef_line        },
     { "__FILE__",         &ftepp_predef_file        },
     { "__COUNTER__",      &ftepp_predef_counter     },
     { "__COUNTER_LAST__", &ftepp_predef_counterlast },
     { "__RANDOM__",       &ftepp_predef_random      },
     { "__RANDOM_LAST__",  &ftepp_predef_randomlast  },
+    { "__DATE__",         &ftepp_predef_date        },
+    { "__TIME__",         &ftepp_predef_time        }
 };
 
 #define ftepp_tokval(f) ((f)->lex->tok.value)
diff --git a/gmqcc.h b/gmqcc.h
index 02b9bfe147f0b06535c8f14b5115e10b45272587..36a67fdf282bb6f78b7a9cb3bb437f51912ad427 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -413,6 +413,7 @@ GMQCC_INLINE int     file_error  (FILE *);
 GMQCC_INLINE int     file_getc   (FILE *);
 GMQCC_INLINE int     file_printf (FILE *, const char *, ...);
 GMQCC_INLINE int     file_puts   (FILE *, const char *);
+GMQCC_INLINE int     file_putc   (FILE *, int);
 GMQCC_INLINE int     file_seek   (FILE *, long int, int);
 
 GMQCC_INLINE size_t  file_read   (void *,        size_t, size_t, FILE *);
@@ -444,6 +445,7 @@ enum {
     TYPE_ARRAY    ,
 
     TYPE_NIL      , /* it's its own type / untyped */
+    TYPE_NOEXPR   , /* simply invalid in expressions */
 
     TYPE_COUNT
 };
@@ -961,6 +963,18 @@ void parser_cleanup       ();
 /*===================================================================*/
 /*====================== ftepp.c commandline ========================*/
 /*===================================================================*/
+struct lex_file_s;
+typedef struct {
+    const char  *name;
+    char      *(*func)(struct lex_file_s *);
+} ftepp_predef_t;
+
+/*
+ * line, file, counter, counter_last, random, random_last, date, time
+ * increment when items are added
+ */
+#define FTEPP_PREDEF_COUNT 8
+
 bool        ftepp_init             ();
 bool        ftepp_preprocess_file  (const char *filename);
 bool        ftepp_preprocess_string(const char *name, const char *str);
@@ -970,6 +984,8 @@ void        ftepp_flush            ();
 void        ftepp_add_define       (const char *source, const char *name);
 void        ftepp_add_macro        (const char *name,   const char *value);
 
+extern const ftepp_predef_t ftepp_predefs[FTEPP_PREDEF_COUNT];
+
 /*===================================================================*/
 /*======================= main.c commandline ========================*/
 /*===================================================================*/
diff --git a/ir.c b/ir.c
index 9458038dd18522ba7378b41d0957200f5fcba5c2..afe4a40d1d5258988dac2e665f43c75a37faca21 100644 (file)
--- a/ir.c
+++ b/ir.c
@@ -44,7 +44,8 @@ const char *type_name[TYPE_COUNT] = {
     "union",
     "array",
 
-    "nil"
+    "nil",
+    "<no-expression>"
 };
 
 size_t type_sizeof_[TYPE_COUNT] = {
@@ -62,6 +63,7 @@ size_t type_sizeof_[TYPE_COUNT] = {
     0, /* TYPE_UNION    */
     0, /* TYPE_ARRAY    */
     0, /* TYPE_NIL      */
+    0, /* TYPE_NOESPR   */
 };
 
 uint16_t type_store_instr[TYPE_COUNT] = {
@@ -85,6 +87,7 @@ uint16_t type_store_instr[TYPE_COUNT] = {
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
     AINSTR_END, /* nil    */
+    AINSTR_END, /* noexpr */
 };
 
 uint16_t field_store_instr[TYPE_COUNT] = {
@@ -108,6 +111,7 @@ uint16_t field_store_instr[TYPE_COUNT] = {
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
     AINSTR_END, /* nil    */
+    AINSTR_END, /* noexpr */
 };
 
 uint16_t type_storep_instr[TYPE_COUNT] = {
@@ -131,6 +135,7 @@ uint16_t type_storep_instr[TYPE_COUNT] = {
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
     AINSTR_END, /* nil    */
+    AINSTR_END, /* noexpr */
 };
 
 uint16_t type_eq_instr[TYPE_COUNT] = {
@@ -154,6 +159,7 @@ uint16_t type_eq_instr[TYPE_COUNT] = {
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
     AINSTR_END, /* nil    */
+    AINSTR_END, /* noexpr */
 };
 
 uint16_t type_ne_instr[TYPE_COUNT] = {
@@ -177,6 +183,7 @@ uint16_t type_ne_instr[TYPE_COUNT] = {
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
     AINSTR_END, /* nil    */
+    AINSTR_END, /* noexpr */
 };
 
 uint16_t type_not_instr[TYPE_COUNT] = {
@@ -200,6 +207,7 @@ uint16_t type_not_instr[TYPE_COUNT] = {
     AINSTR_END, /* union  */
     AINSTR_END, /* array  */
     AINSTR_END, /* nil    */
+    AINSTR_END, /* noexpr */
 };
 
 /* protos */
diff --git a/lexer.h b/lexer.h
index 919dbec1d10078a7c2a449701e34251cc42ad5fe..5d7b7f611bd0ef8166ed8e70ad432c7173ea6e3b 100644 (file)
--- a/lexer.h
+++ b/lexer.h
@@ -99,7 +99,7 @@ typedef struct {
     int   value;
 } frame_macro;
 
-typedef struct {
+typedef struct lex_file_s {
     FILE   *file;
     const char *open_string;
     size_t      open_string_length;
index 425668225f2ee37c21be88d8c1ab4bbf40bc17b1..229d2ec0da2c6495cad77c3b4d4f3eb1fb67db85 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);
@@ -540,6 +549,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         exprs[i]  = sy->out[vec_size(sy->out)+i].out;
         blocks[i] = sy->out[vec_size(sy->out)+i].block;
         asvalue[i] = (ast_value*)exprs[i];
+
+        if (exprs[i]->expression.vtype == TYPE_NOEXPR &&
+            !(i != 0 && op->id == opid2('?',':')))
+        {
+            if (ast_istype(exprs[i], ast_label))
+                compile_error(ast_ctx(exprs[i]), "expected expression, got an unknown identifier");
+            else
+                compile_error(ast_ctx(exprs[i]), "not an expression");
+            (void)!compile_warning(ast_ctx(exprs[i]), WARN_DEBUG, "expression %u\n", (unsigned int)i);
+        }
     }
 
     if (blocks[0] && !vec_size(blocks[0]->exprs) && op->id != opid1(',')) {
@@ -1483,7 +1502,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,14 +1599,36 @@ 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
                 {
+                    /*
+                     * sometimes people use preprocessing predefs without enabling them
+                     * i've done this thousands of times already myself.  Lets check for
+                     * it in the predef table.  And diagnose it better :)
+                     */
+                    if (!OPTS_FLAG(FTEPP_PREDEFS)) {
+                        size_t i;
+                        for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) {
+                            if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) {
+                                parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
+                                goto onerr;
+                            }
+                        }
+                    }
+
                     parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
                     goto onerr;
                 }
@@ -1908,9 +1949,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 +2101,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 +2222,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 +2337,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 +2466,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 +2483,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 +2500,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 +2557,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 +2843,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 +2907,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 +2998,81 @@ 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;
+    ast_expression *cond;
+
+    if (!*side)
+        return NULL;
+
+    if (ast_istype(*side, ast_ternary)) {
+        ast_ternary *tern = (ast_ternary*)*side;
+        on_true  = parse_goto_computed(parser, &tern->on_true);
+        on_false = parse_goto_computed(parser, &tern->on_false);
+
+        if (!on_true || !on_false) {
+            parseerror(parser, "expected label or expression in ternary");
+            if (on_true) ast_unref(on_true);
+            if (on_false) ast_unref(on_false);
+            return NULL;
+        }
+
+        cond = tern->cond;
+        tern->cond = NULL;
+        ast_delete(tern);
+        *side = NULL;
+        return (ast_expression*)ast_ifthen_new(parser_ctx(parser), 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));
+        *side = NULL;
+        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 +3307,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 +3343,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 +3504,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 +3552,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 +4218,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 +4984,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;
 
diff --git a/test.c b/test.c
index bf32885a19fb31a306f8c5b784eb99d4e0f1441b..8c8a80ec68600a432480d11711418f42f77550f2 100644 (file)
--- a/test.c
+++ b/test.c
@@ -156,7 +156,7 @@ int task_pclose(FILE **handles) {
 #    define popen  _popen
 #    define pclose _pclose
 #    include <windows.h>
-#   include <io.h>
+#    include <io.h>
 #    include <fcntl.h>
     /*
      * Bidirectional piping implementation for windows using CreatePipe and DuplicateHandle +
@@ -185,7 +185,7 @@ int task_pclose(FILE **handles) {
 #    ifdef __MINGW32__
         /* mingw32 has dirent.h */
 #        include <dirent.h>
-#    elif defined (_MSC_VER)
+#    elif defined (_WIN32)
         /* 
          * visual studio lacks dirent.h it's a posix thing
          * so we emulate it with the WinAPI.
@@ -746,6 +746,7 @@ bool task_propagate(const char *curdir) {
         if (strcmp(files->d_name + strlen(files->d_name) - 5, ".tmpl") == 0) {
             task_template_t *template = task_template_compile(files->d_name, curdir);
             char             buf[4096]; /* one page should be enough */
+            char            *qcflags = NULL;
             task_t           task;
 
             util_debug("TEST", "compiling task template: %s/%s\n", curdir, files->d_name);
@@ -761,19 +762,37 @@ bool task_propagate(const char *curdir) {
              */
             template->tempfilename = tempnam(curdir, "TMPDAT");
 
+            /*
+             * Additional QCFLAGS enviroment variable may be used
+             * to test compile flags for all tests.  This needs to be
+             * BEFORE other flags (so that the .tmpl can override them)
+             */
+            qcflags = getenv("QCFLAGS");
+
             /*
              * Generate the command required to open a pipe to a process
              * which will be refered to with a handle in the task for
              * reading the data from the pipe.
              */
-            memset  (buf,0,sizeof(buf));
-            snprintf(buf,  sizeof(buf), "%s %s/%s %s -o %s",
-                task_bins[TASK_COMPILE],
-                curdir,
-                template->sourcefile,
-                template->compileflags,
-                template->tempfilename
-            );
+            memset (buf,0,sizeof(buf));
+            if (qcflags) {
+                snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
+                    task_bins[TASK_COMPILE],
+                    curdir,
+                    template->sourcefile,
+                    qcflags,
+                    template->compileflags,
+                    template->tempfilename
+                );
+            } else {
+                snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
+                    task_bins[TASK_COMPILE],
+                    curdir,
+                    template->sourcefile,
+                    template->compileflags,
+                    template->tempfilename
+                );
+            }
 
             /*
              * The task template was compiled, now lets create a task from
@@ -821,31 +840,6 @@ bool task_propagate(const char *curdir) {
     return success;
 }
 
-/*
- * Removes all temporary 'progs.dat' files created during compilation
- * of all tests'
- */
-void task_cleanup(const char *curdir) {
-    DIR             *dir;
-    struct dirent   *files;
-    char             buffer[4096];
-
-    dir = opendir(curdir);
-
-    while ((files = readdir(dir))) {
-        memset(buffer, 0, sizeof(buffer));
-        if (strstr(files->d_name, "TMP")) {
-            snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name);
-            if (remove(buffer))
-                con_err("error removing temporary file: %s\n", buffer);
-            else
-                util_debug("TEST", "removed temporary file: %s\n", buffer);
-        }
-    }
-
-    closedir(dir);
-}
-
 /*
  * Task precleanup removes any existing temporary files or log files
  * left behind from a previous invoke of the test-suite.
@@ -874,7 +868,7 @@ void task_precleanup(const char *curdir) {
     closedir(dir);
 }
 
-void task_destroy(const char *curdir) {
+void task_destroy(void) {
     /*
      * Free all the data in the task list and finally the list itself
      * then proceed to cleanup anything else outside the program like
@@ -900,11 +894,12 @@ void task_destroy(const char *curdir) {
                 con_err("error removing stdout log file: %s\n", task_tasks[i].stdoutlogfile);
             else
                 util_debug("TEST", "removed stdout log file: %s\n", task_tasks[i].stdoutlogfile);
-
             if (remove(task_tasks[i].stderrlogfile))
                 con_err("error removing stderr log file: %s\n", task_tasks[i].stderrlogfile);
             else
                 util_debug("TEST", "removed stderr log file: %s\n", task_tasks[i].stderrlogfile);
+
+            remove(task_tasks[i].template->tempfilename);
         }
 
         /* free util_strdup data for log files */
@@ -914,11 +909,6 @@ void task_destroy(const char *curdir) {
         task_template_destroy(&task_tasks[i].template);
     }
     vec_free(task_tasks);
-
-    /*
-     * Cleanup outside stuff like temporary files.
-     */
-    task_cleanup(curdir);
 }
 
 /*
@@ -1172,7 +1162,7 @@ bool test_perform(const char *curdir) {
     task_precleanup(curdir);
     if (!task_propagate(curdir)) {
         con_err("error: failed to propagate tasks\n");
-        task_destroy(curdir);
+        task_destroy();
         return false;
     }
     /*
@@ -1183,7 +1173,7 @@ bool test_perform(const char *curdir) {
      * issues.
      */
     task_schedualize();
-    task_destroy(curdir);
+    task_destroy();
 
     return true;
 }
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