]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Uhh forgot to save file
[xonotic/gmqcc.git] / parser.c
index 482410677becfaeb2749bd79bfbdf81c389b1366..c0a7f3bb6593d4363d8937bbf5354ed5dddbf010 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;
@@ -266,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;
     }
@@ -279,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;
@@ -1031,8 +1034,128 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     exprs[0], exprs[1]);
             break;
         case opid1('^'):
-            compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^");
-            return false;
+            /*
+             * Okay lets designate what the hell is an acceptable use
+             * of the ^ operator. In many vector processing units, XOR
+             * is allowed to be used on vectors, but only if the first
+             * operand is a vector, the second operand can be a float
+             * or vector. It's never legal for the first operand to be
+             * a float, and then the following operand to be a vector.
+             * Further more, the only time it is legal to do XOR otherwise
+             * is when both operand are floats. This nicely crafted if
+             * statement catches them all.
+             * 
+             * In the event that the first operand is a vector, two
+             * possible situations can arise, thus, each element of
+             * vector A (operand A) is exclusive-ORed with the corresponding
+             * element of vector B (operand B), If B is scalar, the
+             * scalar value is first replicated for each element.
+             * 
+             * The QCVM itself lacks a BITXOR instruction. Thus emulating
+             * the mathematics of it is required. The following equation
+             * is used: (LHS | RHS) & ~(LHS & RHS). However, due to the
+             * QCVM also lacking a BITNEG instruction, we need to emulate
+             * ~FOO with -1 - FOO, the whole process becoming this nicely
+             * crafted expression: (LHS | RHS) & (-1 - (LHS & RHS)).
+             * 
+             * When A is not scalar, this process is repeated for all
+             * components of vector A with the value in operand B,
+             * only if operand B is scalar. When A is not scalar, and B
+             * is also not scalar, this process is repeated for all
+             * components of the vector A with the components of vector B.
+             * Finally when A is scalar and B is scalar, this process is
+             * simply used once for A and B being LHS and RHS respectfully.
+             * 
+             * Yes the semantics are a bit strange (no pun intended).
+             * But then again BITXOR is strange itself, consdering it's
+             * commutative, assocative, and elements of the BITXOR operation
+             * are their own inverse.
+             */
+            if ( !(exprs[0]->vtype == TYPE_FLOAT  && exprs[1]->vtype == TYPE_FLOAT) &&
+                 !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_FLOAT) &&
+                 !(exprs[0]->vtype == TYPE_VECTOR && exprs[1]->vtype == TYPE_VECTOR))
+            {
+                compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s",
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
+                return false;
+            }
+
+            /*
+             * IF the first expression is float, the following will be too
+             * since scalar ^ vector is not allowed.
+             */
+            if (exprs[0]->vtype == TYPE_FLOAT) {
+                if(CanConstFold(exprs[0], exprs[1])) {
+                    out = (ast_expression*)parser_const_float(parser, (float)((qcint)(ConstF(0)) ^ ((qcint)(ConstF(1)))));
+                } else {
+                    ast_binary *expr = ast_binary_new(
+                        ctx,
+                        INSTR_SUB_F,
+                        (ast_expression*)parser_const_float_neg1(parser),
+                        (ast_expression*)ast_binary_new(
+                            ctx,
+                            INSTR_BITAND,
+                            exprs[0],
+                            exprs[1]
+                        )
+                    );
+                    expr->refs = AST_REF_NONE;
+                    
+                    out = (ast_expression*)
+                        ast_binary_new(
+                            ctx,
+                            INSTR_BITAND,
+                            (ast_expression*)ast_binary_new(
+                                ctx,
+                                INSTR_BITOR,
+                                exprs[0],
+                                exprs[1]
+                            ),
+                            (ast_expression*)expr
+                        );
+                }
+            } else {
+                /*
+                 * The first is a vector: vector is allowed to xor with vector and
+                 * with scalar, branch here for the second operand.
+                 */
+                if (exprs[1]->vtype == TYPE_VECTOR) {
+                    /*
+                     * Xor all the values of the vector components against the
+                     * vectors components in question.
+                     */
+                    if (CanConstFold(exprs[0], exprs[1])) {
+                        out = (ast_expression*)parser_const_vector_f(
+                            parser,
+                            (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstV(1).x))),
+                            (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstV(1).y))),
+                            (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstV(1).z)))
+                        );
+                    } else {
+                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
+                        return false;
+                    }
+                } else {
+                    /*
+                     * Xor all the values of the vector components against the
+                     * scalar in question.
+                     */
+                    if (CanConstFold(exprs[0], exprs[1])) {
+                        out = (ast_expression*)parser_const_vector_f(
+                            parser,
+                            (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstF(1)))),
+                            (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstF(1)))),
+                            (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstF(1))))
+                        );
+                    } else {
+                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
+                        return false;
+                    }
+                }
+            }
+                
+            break;
 
         case opid2('<','<'):
         case opid2('>','>'):
@@ -1159,7 +1282,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             } else {
                 ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
 
-                eq->refs = (ast_binary_ref)false; /* references nothing */
+                eq->refs = AST_REF_NONE;
 
                     /* if (lt) { */
                 out = (ast_expression*)ast_ternary_new(ctx,
@@ -1565,7 +1688,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
     for (i = 0; i < paramcount; ++i)
         vec_push(call->params, sy->out[fid+1 + i].out);
     vec_shrinkby(sy->out, paramcount);
-    (void)!ast_call_check_types(call);
+    (void)!ast_call_check_types(call, parser->function->vtype->expression.varparam);
     if (parser->max_param_count < paramcount)
         parser->max_param_count = paramcount;
 
@@ -1700,8 +1823,12 @@ static ast_expression* parse_vararg_do(parser_t *parser)
     ast_expression *idx, *out;
     ast_value      *typevar;
     ast_value      *funtype = parser->function->vtype;
+    lex_ctx         ctx     = parser_ctx(parser);
 
-    lex_ctx ctx = parser_ctx(parser);
+    if (!parser->function->varargs) {
+        parseerror(parser, "function has no variable argument list");
+        return NULL;
+    }
 
     if (!parser_next(parser) || parser->tok != '(') {
         parseerror(parser, "expected parameter index and type in parenthesis");
@@ -1717,9 +1844,14 @@ static ast_expression* parse_vararg_do(parser_t *parser)
         return NULL;
 
     if (parser->tok != ',') {
-        ast_unref(idx);
-        parseerror(parser, "expected comma after parameter index");
-        return NULL;
+        if (parser->tok != ')') {
+            ast_unref(idx);
+            parseerror(parser, "expected comma after parameter index");
+            return NULL;
+        }
+        /* vararg piping: ...(start) */
+        out = (ast_expression*)ast_argpipe_new(ctx, idx);
+        return out;
     }
 
     if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) {
@@ -1741,22 +1873,6 @@ static ast_expression* parse_vararg_do(parser_t *parser)
         return NULL;
     }
 
-#if 0
-    if (!parser_next(parser)) {
-        ast_unref(idx);
-        ast_delete(typevar);
-        parseerror(parser, "parse error after vararg");
-        return NULL;
-    }
-#endif
-
-    if (!parser->function->varargs) {
-        ast_unref(idx);
-        ast_delete(typevar);
-        parseerror(parser, "function has no variable argument list");
-        return NULL;
-    }
-
     if (funtype->expression.varparam &&
         !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam))
     {
@@ -1922,7 +2038,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                  * it in the predef table.  And diagnose it better :)
                  */
                 if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) {
-                    parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
+                    parseerror(parser, "unexpected identifier: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
                     return false;
                 }
 
@@ -1933,7 +2049,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);
 
@@ -1949,12 +2065,12 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                     correct_free(&corr);
 
                     if (correct) {
-                        parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+                        parseerror(parser, "unexpected identifier: %s (did you mean %s?)", parser_tokval(parser), correct);
                         mem_d(correct);
                         return false;
                     }
                 }
-                parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
+                parseerror(parser, "unexpected identifier: %s", parser_tokval(parser));
                 return false;
             }
         }
@@ -2001,7 +2117,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
     while (true)
     {
         if (parser->tok == TOKEN_TYPENAME) {
-            parseerror(parser, "unexpected typename");
+            parseerror(parser, "unexpected typename `%s`", parser_tokval(parser));
             goto onerr;
         }
 
@@ -2893,8 +3009,10 @@ onerr:
 
 static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out)
 {
-    ast_expression *exp = NULL;
-    ast_return     *ret = NULL;
+    ast_expression *exp      = NULL;
+    ast_expression *var      = NULL;
+    ast_return     *ret      = NULL;
+    ast_value      *retval   = parser->function->return_value;
     ast_value      *expected = parser->function->vtype;
 
     lex_ctx ctx = parser_ctx(parser);
@@ -2906,6 +3024,62 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
         return false;
     }
 
+    /* return assignments */
+    if (parser->tok == '=') {
+        if (!OPTS_FLAG(RETURN_ASSIGNMENTS)) {
+            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;
+
+        /* 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[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;
+    }
+
     if (parser->tok != ';') {
         exp = parse_expression(parser, false, false);
         if (!exp)
@@ -2925,10 +3099,12 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
     } else {
         if (!parser_next(parser))
             parseerror(parser, "parse error");
-        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;
@@ -3676,7 +3852,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 == '{')
@@ -4277,6 +4453,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
 
     vec_push(func->blocks, block);
 
+
     parser->function = old;
     if (!parser_leaveblock(parser))
         retval = false;
@@ -4730,6 +4907,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;
     }
@@ -4852,32 +5030,44 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
         return NULL;
     }
 
-    cexp = parse_expression_leave(parser, true, false, false);
+    if (parser->tok != ']') {
+        cexp = parse_expression_leave(parser, true, false, false);
 
-    if (!cexp || !ast_istype(cexp, ast_value)) {
-        if (cexp)
-            ast_unref(cexp);
-        ast_delete(var);
-        parseerror(parser, "expected array-size as constant positive integer");
-        return NULL;
+        if (!cexp || !ast_istype(cexp, ast_value)) {
+            if (cexp)
+                ast_unref(cexp);
+            ast_delete(var);
+            parseerror(parser, "expected array-size as constant positive integer");
+            return NULL;
+        }
+        cval = (ast_value*)cexp;
+    }
+    else {
+        cexp = NULL;
+        cval = NULL;
     }
-    cval = (ast_value*)cexp;
 
     tmp = ast_value_new(ctx, "<type[]>", TYPE_ARRAY);
     tmp->expression.next = (ast_expression*)var;
     var = tmp;
 
-    if (cval->expression.vtype == TYPE_INTEGER)
-        tmp->expression.count = cval->constval.vint;
-    else if (cval->expression.vtype == TYPE_FLOAT)
-        tmp->expression.count = cval->constval.vfloat;
-    else {
+    if (cval) {
+        if (cval->expression.vtype == TYPE_INTEGER)
+            tmp->expression.count = cval->constval.vint;
+        else if (cval->expression.vtype == TYPE_FLOAT)
+            tmp->expression.count = cval->constval.vfloat;
+        else {
+            ast_unref(cexp);
+            ast_delete(var);
+            parseerror(parser, "array-size must be a positive integer constant");
+            return NULL;
+        }
+
         ast_unref(cexp);
-        ast_delete(var);
-        parseerror(parser, "array-size must be a positive integer constant");
-        return NULL;
+    } else {
+        var->expression.count = -1;
+        var->expression.flags |= AST_FLAG_ARRAY_INIT;
     }
-    ast_unref(cexp);
 
     if (parser->tok != ']') {
         ast_delete(var);
@@ -4997,6 +5187,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;
         }
@@ -5006,8 +5197,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 */
@@ -5026,8 +5219,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;
         }
     }
@@ -5036,11 +5228,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;
@@ -5124,6 +5317,75 @@ static bool parser_check_qualifiers(parser_t *parser, const ast_value *var, cons
     return true;
 }
 
+static bool create_array_accessors(parser_t *parser, ast_value *var)
+{
+    char name[1024];
+    util_snprintf(name, sizeof(name), "%s##SET", var->name);
+    if (!parser_create_array_setter(parser, var, name))
+        return false;
+    util_snprintf(name, sizeof(name), "%s##GET", var->name);
+    if (!parser_create_array_getter(parser, var, var->expression.next, name))
+        return false;
+    return true;
+}
+
+static bool parse_array(parser_t *parser, ast_value *array)
+{
+    size_t i;
+    if (array->initlist) {
+        parseerror(parser, "array already initialized elsewhere");
+        return false;
+    }
+    if (!parser_next(parser)) {
+        parseerror(parser, "parse error in array initializer");
+        return false;
+    }
+    i = 0;
+    while (parser->tok != '}') {
+        ast_value *v = (ast_value*)parse_expression_leave(parser, true, false, false);
+        if (!v)
+            return false;
+        if (!ast_istype(v, ast_value) || !v->hasvalue || v->cvq != CV_CONST) {
+            ast_unref(v);
+            parseerror(parser, "initializing element must be a compile time constant");
+            return false;
+        }
+        vec_push(array->initlist, v->constval);
+        if (v->expression.vtype == TYPE_STRING) {
+            array->initlist[i].vstring = util_strdupe(array->initlist[i].vstring);
+            ++i;
+        }
+        ast_unref(v);
+        if (parser->tok == '}')
+            break;
+        if (parser->tok != ',' || !parser_next(parser)) {
+            parseerror(parser, "expected comma or '}' in element list");
+            return false;
+        }
+    }
+    if (!parser_next(parser) || parser->tok != ';') {
+        parseerror(parser, "expected semicolon after initializer, got %s");
+        return false;
+    }
+    /*
+    if (!parser_next(parser)) {
+        parseerror(parser, "parse error after initializer");
+        return false;
+    }
+    */
+
+    if (array->expression.flags & AST_FLAG_ARRAY_INIT) {
+        if (array->expression.count != (size_t)-1) {
+            parseerror(parser, "array `%s' has already been initialized with %u elements",
+                       array->name, (unsigned)array->expression.count);
+        }
+        array->expression.count = vec_size(array->initlist);
+        if (!create_array_accessors(parser, array))
+            return false;
+    }
+    return true;
+}
+
 static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring)
 {
     ast_value *var;
@@ -5557,13 +5819,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
          * deal with arrays
          */
         if (var->expression.vtype == TYPE_ARRAY) {
-            char name[1024];
-            util_snprintf(name, sizeof(name), "%s##SET", var->name);
-            if (!parser_create_array_setter(parser, var, name))
-                goto cleanup;
-            util_snprintf(name, sizeof(name), "%s##GET", var->name);
-            if (!parser_create_array_getter(parser, var, var->expression.next, name))
-                goto cleanup;
+            if (var->expression.count != (size_t)-1) {
+                if (!create_array_accessors(parser, var))
+                    goto cleanup;
+            }
         }
         else if (!localblock && !nofields &&
                  var->expression.vtype == TYPE_FIELD &&
@@ -5735,11 +5994,10 @@ skipvar:
                 parseerror(parser, "TODO: initializers for local arrays");
                 break;
             }
-            /*
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
-*/
-            parseerror(parser, "TODO: initializing global arrays is not supported yet!");
-            break;
+
+            var->hasvalue = true;
+            if (!parse_array(parser, var))
+                break;
         }
         else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '['))
         {
@@ -6081,7 +6339,7 @@ static bool parser_compile(parser_t *parser)
         {
             if (!parser_global_statement(parser)) {
                 if (parser->tok == TOKEN_EOF)
-                    parseerror(parser, "unexpected eof");
+                    parseerror(parser, "unexpected end of file");
                 else if (compile_errors)
                     parseerror(parser, "there have been errors, bailing out");
                 lex_close(parser->lex);
@@ -6122,9 +6380,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;
@@ -6170,7 +6431,6 @@ void parser_cleanup(parser_t *parser)
     vec_free(parser->correct_variables);
     vec_free(parser->correct_variables_score);
 
-
     for (i = 0; i < vec_size(parser->_typedefs); ++i)
         ast_delete(parser->_typedefs[i]);
     vec_free(parser->_typedefs);
@@ -6191,11 +6451,17 @@ void parser_cleanup(parser_t *parser)
     ast_value_delete(parser->const_vec[0]);
     ast_value_delete(parser->const_vec[1]);
     ast_value_delete(parser->const_vec[2]);
+    
+    if (parser->reserved_version)
+        ast_value_delete(parser->reserved_version);
 
     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);
@@ -6354,6 +6620,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) {
@@ -6363,6 +6631,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");
@@ -6374,15 +6643,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;
 }