]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Merge branch 'master' of github.com:graphitemaster/gmqcc
[xonotic/gmqcc.git] / parser.c
index d0563c1ac43fbb21fd34705b8cd27798767b2cf7..8a7cf876948e06119fa1730340f5b0be22e6742b 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -21,8 +21,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <stdio.h>
-#include <stdarg.h>
+#include <string.h>
 #include <math.h>
 
 #include "gmqcc.h"
@@ -39,6 +38,8 @@ typedef struct parser_s {
     lex_file *lex;
     int      tok;
 
+    bool     ast_cleaned;
+
     ast_expression **globals;
     ast_expression **fields;
     ast_function **functions;
@@ -106,13 +107,6 @@ typedef struct parser_s {
 
     /* code generator */
     code_t     *code;
-    
-    /* vector of global return vars.
-     * for example, you can return string, float, vector, or other
-     * things, hese will be created as globals here instead of
-     * locals in a function (saves space).
-     */
-    ast_value     **returns;
 } parser_t;
 
 static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
@@ -273,6 +267,7 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do
             char name[32];
             util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
             ast_value_set_name(out, name);
+            out->expression.flags |= AST_FLAG_INCLUDE_DEF;
         }
         return out;
     }
@@ -286,6 +281,7 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do
         char name[32];
         util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
         out = ast_value_new(parser_ctx(parser), name, TYPE_STRING);
+        out->expression.flags |= AST_FLAG_INCLUDE_DEF;
     } else
         out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING);
     out->cvq      = CV_CONST;
@@ -352,29 +348,6 @@ static ast_expression* parser_find_global(parser_t *parser, const char *name)
     return (ast_expression*)util_htget(parser->htglobals, name);
 }
 
-static ast_value* parser_find_returnvalue(parser_t *parser, int vtype)
-{
-    ast_value *out;
-    size_t     i;
-    char      *name = NULL;
-    
-    /* find existing global for the job */
-    for (i = 0; i < vec_size(parser->returns); i++)
-        if (parser->returns[i]->expression.vtype == vtype)
-            return parser->returns[i];
-            
-    util_asprintf(&name, "#ret_%s", type_name[vtype]);
-            
-    out           = ast_value_new(parser_ctx(parser), name, vtype);
-    out->hasvalue = false;
-    out->isimm    = false;
-    
-    vec_push(parser->returns, out);
-    
-    mem_d(name);
-    return out;
-}
-
 static ast_expression* parser_find_param(parser_t *parser, const char *name)
 {
     size_t i;
@@ -1717,6 +1690,8 @@ static bool parser_close_paren(parser_t *parser, shunt *sy)
 static void parser_reclassify_token(parser_t *parser)
 {
     size_t i;
+    if (parser->tok >= TOKEN_START)
+        return;
     for (i = 0; i < operator_count; ++i) {
         if (!strcmp(parser_tokval(parser), operators[i].op)) {
             parser->tok = TOKEN_OPERATOR;
@@ -1963,7 +1938,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                  * We should also consider adding correction tables for
                  * other things as well.
                  */
-                if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) {
+                if (OPTS_OPTION_BOOL(OPTION_CORRECTION) && strlen(parser_tokval(parser)) <= 16) {
                     correction_t corr;
                     correct_init(&corr);
 
@@ -2926,7 +2901,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
     ast_expression *exp      = NULL;
     ast_expression *var      = NULL;
     ast_return     *ret      = NULL;
-    ast_expression *find     = NULL;
+    ast_value      *retval   = parser->function->return_value;
     ast_value      *expected = parser->function->vtype;
 
     lex_ctx ctx = parser_ctx(parser);
@@ -2944,34 +2919,52 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
             parseerror(parser, "return assignments not activated, try using -freturn-assigments");
             return false;
         }
-            
+
+        if (type_store_instr[expected->expression.next->vtype] == VINSTR_END) {
+            char ty1[1024];
+            ast_type_to_string(expected->expression.next, ty1, sizeof(ty1));
+            parseerror(parser, "invalid return type: `%s'", ty1);
+            return false;
+        }
+
         if (!parser_next(parser)) {
             parseerror(parser, "expected return assignment expression");
             return false;
         }
-        
+
         if (!(exp = parse_expression_leave(parser, false, false, false)))
             return false;
-            
-        if (exp->vtype != TYPE_NIL &&
-            exp->vtype != ((ast_expression*)expected)->next->vtype)
-        {
-            parseerror(parser, "return assignment with invalid expression");
+
+        /* prepare the return value */
+        if (!retval) {
+            retval = ast_value_new(ctx, "#LOCAL_RETURN", TYPE_VOID);
+            ast_type_adopt(retval, expected->expression.next);
+            parser->function->return_value = retval;
+        }
+
+        if (!ast_compare_type(exp, (ast_expression*)retval)) {
+            char ty1[1024], ty2[1024];
+            ast_type_to_string(exp, ty1, sizeof(ty1));
+            ast_type_to_string(&retval->expression, ty2, sizeof(ty2));
+            parseerror(parser, "invalid type for return value: `%s', expected `%s'", ty1, ty2);
         }
-        
+
         /* store to 'return' local variable */
         var = (ast_expression*)ast_store_new(
             ctx,
-            type_store_instr[exp->vtype],
-            (ast_expression*)parser_find_returnvalue(parser, exp->vtype),
-            (ast_expression*)exp
-        );
-        
+            type_store_instr[expected->expression.next->vtype],
+            (ast_expression*)retval, exp);
+
         if (!var) {
             ast_unref(exp);
             return false;
         }
-        
+
+        if (parser->tok != ';')
+            parseerror(parser, "missing semicolon after return assignment");
+        else if (!parser_next(parser))
+            parseerror(parser, "parse error after return assignment");
+
         *out = var;
         return true;
     }
@@ -2995,16 +2988,12 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
     } else {
         if (!parser_next(parser))
             parseerror(parser, "parse error");
-            
-        /* build expression to return */
-        if ((find = (ast_expression*)parser_find_returnvalue(parser, expected->expression.next->vtype)) && OPTS_FLAG(RETURN_ASSIGNMENTS))
-            ret = ast_return_new(ctx, find);
-            
-        else if (expected->expression.next->vtype != TYPE_VOID)
+
+        if (!retval && expected->expression.next->vtype != TYPE_VOID)
         {
             (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
-            ret = ast_return_new(ctx, NULL);
         }
+        ret = ast_return_new(ctx, (ast_expression*)retval);
     }
     *out = (ast_expression*)ret;
     return true;
@@ -3752,7 +3741,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             }
             return parse_typedef(parser);
         }
-        parseerror(parser, "Unexpected keyword");
+        parseerror(parser, "Unexpected keyword: `%s'", parser_tokval(parser));
         return false;
     }
     else if (parser->tok == '{')
@@ -4807,6 +4796,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
 
     /* for the sake of less code we parse-in in this function */
     if (!parser_next(parser)) {
+        ast_delete(var);
         parseerror(parser, "expected parameter list");
         return NULL;
     }
@@ -5074,6 +5064,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
         /* parse on */
         if (!parser_next(parser)) {
             ast_delete(var);
+            mem_d(name);
             parseerror(parser, "error after variable or field declaration");
             return NULL;
         }
@@ -5083,8 +5074,10 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     if (parser->tok == '[') {
         wasarray = true;
         var = parse_arraysize(parser, var);
-        if (!var)
+        if (!var) {
+            if (name) mem_d(name);
             return NULL;
+        }
     }
 
     /* This is the point where we can turn it into a field */
@@ -5103,8 +5096,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     while (parser->tok == '(') {
         var = parse_parameter_list(parser, var);
         if (!var) {
-            if (name)
-                mem_d((void*)name);
+            if (name) mem_d(name);
             return NULL;
         }
     }
@@ -5113,11 +5105,12 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     if (name) {
         if (!ast_value_set_name(var, name)) {
             ast_delete(var);
+            mem_d(name);
             parseerror(parser, "internal error: failed to set name");
             return NULL;
         }
         /* free the name, ast_value_set_name duplicates */
-        mem_d((void*)name);
+        mem_d(name);
     }
 
     return var;
@@ -5703,8 +5696,16 @@ skipvar:
 
         if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) {
             if (parser->tok != '=') {
-                parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
-                break;
+                if (!strcmp(parser_tokval(parser), "break")) {
+                    if (!parser_next(parser)) {
+                        parseerror(parser, "error parsing break definition");
+                        break;
+                    }
+                    (void)!!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)");
+                } else {
+                    parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
+                    break;
+                }
             }
 
             if (!parser_next(parser)) {
@@ -6144,7 +6145,6 @@ parser_t *parser_create()
         parser->reserved_version = NULL;
     }
 
-    parser->returns = NULL;
     return parser;
 }
 
@@ -6200,9 +6200,12 @@ bool parser_compile_string(parser_t *parser, const char *name, const char *str,
     return parser_compile(parser);
 }
 
-void parser_cleanup(parser_t *parser)
+static void parser_remove_ast(parser_t *parser)
 {
     size_t i;
+    if (parser->ast_cleaned)
+        return;
+    parser->ast_cleaned = true;
     for (i = 0; i < vec_size(parser->accessors); ++i) {
         ast_delete(parser->accessors[i]->constval.vfunc);
         parser->accessors[i]->constval.vfunc = NULL;
@@ -6226,9 +6229,6 @@ void parser_cleanup(parser_t *parser)
     for (i = 0; i < vec_size(parser->globals); ++i) {
         ast_delete(parser->globals[i]);
     }
-    for (i = 0; i < vec_size(parser->returns); ++i) {
-        ast_delete(parser->returns[i]);
-    }
     vec_free(parser->accessors);
     vec_free(parser->functions);
     vec_free(parser->imm_vector);
@@ -6274,9 +6274,12 @@ void parser_cleanup(parser_t *parser)
     ast_value_delete(parser->const_vec[2]);
 
     util_htdel(parser->aliases);
-
     intrin_intrinsics_destroy(parser);
+}
 
+void parser_cleanup(parser_t *parser)
+{
+    parser_remove_ast(parser);
     code_cleanup(parser->code);
 
     mem_d(parser);
@@ -6420,13 +6423,6 @@ bool parser_finish(parser_t *parser, const char *output)
             return false;
         }
     }
-    for (i = 0; i < vec_size(parser->returns); ++i) {
-        if (!ast_global_codegen(parser->returns[i], ir, false)) {
-            con_out("internal error: failed to generate return assignment %s\n", parser->returns[i]->name);
-            ir_builder_delete(ir);
-            return false;
-        }
-    }
     if (parser->reserved_version &&
         !ast_global_codegen(parser->reserved_version, ir, false))
     {
@@ -6442,6 +6438,8 @@ bool parser_finish(parser_t *parser, const char *output)
             return false;
         }
     }
+
+    generate_checksum(parser);
     if (OPTS_OPTION_BOOL(OPTION_DUMP))
         ir_builder_dump(ir, con_out);
     for (i = 0; i < vec_size(parser->functions); ++i) {
@@ -6451,6 +6449,7 @@ bool parser_finish(parser_t *parser, const char *output)
             return false;
         }
     }
+    parser_remove_ast(parser);
 
     if (compile_Werrors) {
         con_out("*** there were warnings treated as errors\n");
@@ -6462,15 +6461,12 @@ bool parser_finish(parser_t *parser, const char *output)
         if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
             ir_builder_dump(ir, con_out);
 
-        generate_checksum(parser);
-
         if (!ir_builder_generate(parser->code, ir, output)) {
             con_out("*** failed to generate output file\n");
             ir_builder_delete(ir);
             return false;
         }
     }
-
     ir_builder_delete(ir);
     return retval;
 }