]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
handling TOKEN_CHARCONST - -Wmultibyte-character
[xonotic/gmqcc.git] / parser.c
index 752d62a2ad3c5358e119facce38d7aa5c6ca2a69..c840ae6e3f08bb1ea24826a1bcb31a98bf9fe60e 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -58,6 +58,12 @@ typedef struct {
 
     ast_function *function;
 
+    /* All the labels the function defined...
+     * Should they be in ast_function instead?
+     */
+    ast_label **labels;
+    ast_goto  **gotos;
+
     /* A list of hashtables for each scope */
     ht *variables;
     ht htfields;
@@ -1422,7 +1428,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
             DEBUGSHUNTDO(con_out("push %g\n", parser_token(parser)->constval.f));
         }
-        else if (parser->tok == TOKEN_INTCONST) {
+        else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
             ast_value *val;
             if (wantop) {
                 parseerror(parser, "expected operator or end of statement, got constant");
@@ -2227,6 +2233,40 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
     return true;
 }
 
+static bool parse_goto(parser_t *parser, ast_expression **out)
+{
+    size_t    i;
+    ast_goto *gt;
+
+    if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+        parseerror(parser, "expected label name after `goto`");
+        return false;
+    }
+
+    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;
+        }
+    }
+    if (i == vec_size(parser->labels))
+        vec_push(parser->gotos, gt);
+
+    if (!parser_next(parser) || parser->tok != ';') {
+        parseerror(parser, "semicolon expected after goto label");
+        return false;
+    }
+    if (!parser_next(parser)) {
+        parseerror(parser, "parse error after goto");
+        return false;
+    }
+
+    *out = (ast_expression*)gt;
+    return true;
+}
+
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
 {
     ast_value *typevar = NULL;
@@ -2253,8 +2293,11 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
-        if (!strcmp(parser_tokval(parser), "local"))
+        if (!strcmp(parser_tokval(parser), "local") ||
+            !strcmp(parser_tokval(parser), "const"))
         {
+            int cvq = parser_tokval(parser)[0] == 'c' ? CV_CONST : CV_VAR;
+
             if (!block) {
                 parseerror(parser, "cannot declare a local variable here");
                 return false;
@@ -2263,7 +2306,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
                 parseerror(parser, "expected variable declaration");
                 return false;
             }
-            if (!parse_variable(parser, block, true, CV_VAR, NULL))
+            if (!parse_variable(parser, block, true, cvq, NULL))
                 return false;
             *out = NULL;
             return true;
@@ -2313,6 +2356,10 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             }
             return true;
         }
+        else if (!strcmp(parser_tokval(parser), "goto"))
+        {
+            return parse_goto(parser, out);
+        }
         else if (!strcmp(parser_tokval(parser), "typedef"))
         {
             if (!parser_next(parser)) {
@@ -2333,6 +2380,36 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
         *out = (ast_expression*)inner;
         return true;
     }
+    else if (parser->tok == ':')
+    {
+        size_t i;
+        ast_label *label;
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected label name");
+            return false;
+        }
+        if (parser->tok != TOKEN_IDENT) {
+            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);
+        *out = (ast_expression*)label;
+        if (!parser_next(parser)) {
+            parseerror(parser, "parse error after label");
+            return false;
+        }
+        for (i = 0; i < vec_size(parser->gotos); ++i) {
+            if (!strcmp(parser->gotos[i]->name, label->name)) {
+                ast_goto_set_label(parser->gotos[i], label);
+                vec_remove(parser->gotos, i, 1);
+                --i;
+            }
+        }
+        return true;
+    }
     else if (parser->tok == ';')
     {
         if (!parser_next(parser)) {
@@ -2471,6 +2548,11 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     has_frame_think = false;
     old = parser->function;
 
+    if (vec_size(parser->gotos) || vec_size(parser->labels)) {
+        parseerror(parser, "gotos/labels leaking");
+        return false;
+    }
+
     if (var->expression.variadic) {
         if (parsewarning(parser, WARN_VARIADIC_FUNCTION,
                          "variadic function with implementation will not be able to access additional parameters"))
@@ -3839,6 +3921,7 @@ skipvar:
         }
         else if (parser->tok == '{' || parser->tok == '[')
         {
+            size_t i;
             if (localblock) {
                 parseerror(parser, "cannot declare functions within functions");
                 break;
@@ -3847,6 +3930,10 @@ skipvar:
             if (!parse_function_body(parser, var))
                 break;
             ast_delete(basetype);
+            for (i = 0; i < vec_size(parser->gotos); ++i)
+                parseerror(parser, "undefined label: `%s`", parser->gotos[i]->name);
+            vec_free(parser->gotos);
+            vec_free(parser->labels);
             return true;
         } else {
             ast_expression *cexp;
@@ -3876,7 +3963,10 @@ skipvar:
                     ast_unref(cval);
                 }
             } else {
+                bool cvq;
                 shunt sy = { NULL, NULL };
+                cvq = var->constant;
+                var->constant = false;
                 vec_push(sy.out, syexp(ast_ctx(var), (ast_expression*)var));
                 vec_push(sy.out, syexp(ast_ctx(cexp), (ast_expression*)cexp));
                 vec_push(sy.ops, syop(ast_ctx(var), parser->assign_op));
@@ -3889,6 +3979,7 @@ skipvar:
                 }
                 vec_free(sy.out);
                 vec_free(sy.ops);
+                var->constant = cvq;
             }
         }
 
@@ -4220,6 +4311,9 @@ void parser_cleanup()
     vec_free(parser->typedefs);
     vec_free(parser->_blocktypedefs);
 
+    vec_free(parser->labels);
+    vec_free(parser->gotos);
+
     mem_d(parser);
 }