]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Remove debugging aid and left over cruft.
[xonotic/gmqcc.git] / parser.c
index b3e98823604b553363ff9db41caa7151d1551e51..faa5604e2086868f469f332440ed373454e9d471 100644 (file)
--- a/parser.c
+++ b/parser.c
  * 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 "parser.h"
 
-#include "gmqcc.h"
-#include "lexer.h"
-#include "ast.h"
-
-/* beginning of locals */
 #define PARSER_HT_LOCALS  2
-
-#define PARSER_HT_SIZE    128
-#define TYPEDEF_HT_SIZE   16
-
-typedef struct parser_s {
-    lex_file *lex;
-    int      tok;
-
-    ast_expression **globals;
-    ast_expression **fields;
-    ast_function **functions;
-    ast_value    **imm_float;
-    ast_value    **imm_string;
-    ast_value    **imm_vector;
-    size_t         translated;
-
-    ht ht_imm_string;
-
-    /* must be deleted first, they reference immediates and values */
-    ast_value    **accessors;
-
-    ast_value *imm_float_zero;
-    ast_value *imm_float_one;
-    ast_value *imm_float_neg_one;
-
-    ast_value *imm_vector_zero;
-
-    ast_value *nil;
-    ast_value *reserved_version;
-
-    size_t crc_globals;
-    size_t crc_fields;
-
-    ast_function *function;
-    ht            aliases;
-
-    /* All the labels the function defined...
-     * Should they be in ast_function instead?
-     */
-    ast_label  **labels;
-    ast_goto   **gotos;
-    const char **breaks;
-    const char **continues;
-
-    /* A list of hashtables for each scope */
-    ht *variables;
-    ht htfields;
-    ht htglobals;
-    ht *typedefs;
-
-    /* same as above but for the spelling corrector */
-    correct_trie_t  **correct_variables;
-    size_t         ***correct_variables_score;  /* vector of vector of size_t* */
-
-    /* not to be used directly, we use the hash table */
-    ast_expression **_locals;
-    size_t          *_blocklocals;
-    ast_value      **_typedefs;
-    size_t          *_blocktypedefs;
-    lex_ctx         *_block_ctx;
-
-    /* we store the '=' operator info */
-    const oper_info *assign_op;
-
-    /* magic values */
-    ast_value *const_vec[3];
-
-    /* pragma flags */
-    bool noref;
-
-    /* collected information */
-    size_t     max_param_count;
-} parser_t;
+#define PARSER_HT_SIZE    512
+#define TYPEDEF_HT_SIZE   512
 
 static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
 
@@ -142,47 +66,11 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *
     return r;
 }
 
-/**********************************************************************
- * some maths used for constant folding
- */
-
-vector vec3_add(vector a, vector b)
-{
-    vector out;
-    out.x = a.x + b.x;
-    out.y = a.y + b.y;
-    out.z = a.z + b.z;
-    return out;
-}
-
-vector vec3_sub(vector a, vector b)
-{
-    vector out;
-    out.x = a.x - b.x;
-    out.y = a.y - b.y;
-    out.z = a.z - b.z;
-    return out;
-}
-
-qcfloat vec3_mulvv(vector a, vector b)
-{
-    return (a.x * b.x + a.y * b.y + a.z * b.z);
-}
-
-vector vec3_mulvf(vector a, float b)
-{
-    vector out;
-    out.x = a.x * b;
-    out.y = a.y * b;
-    out.z = a.z * b;
-    return out;
-}
-
 /**********************************************************************
  * parsing
  */
 
-bool parser_next(parser_t *parser)
+static bool parser_next(parser_t *parser)
 {
     /* lex_do kills the previous token */
     parser->tok = lex_do(parser->lex);
@@ -197,52 +85,8 @@ bool parser_next(parser_t *parser)
 
 #define parser_tokval(p) ((p)->lex->tok.value)
 #define parser_token(p)  (&((p)->lex->tok))
-#define parser_ctx(p)    ((p)->lex->tok.ctx)
 
-static ast_value* parser_const_float(parser_t *parser, double d)
-{
-    size_t i;
-    ast_value *out;
-    lex_ctx ctx;
-    for (i = 0; i < vec_size(parser->imm_float); ++i) {
-        const double compare = parser->imm_float[i]->constval.vfloat;
-        if (memcmp((const void*)&compare, (const void *)&d, sizeof(double)) == 0)
-            return parser->imm_float[i];
-    }
-    if (parser->lex)
-        ctx = parser_ctx(parser);
-    else {
-        memset(&ctx, 0, sizeof(ctx));
-    }
-    out = ast_value_new(ctx, "#IMMEDIATE", TYPE_FLOAT);
-    out->cvq      = CV_CONST;
-    out->hasvalue = true;
-    out->constval.vfloat = d;
-    vec_push(parser->imm_float, out);
-    return out;
-}
-
-static ast_value* parser_const_float_0(parser_t *parser)
-{
-    if (!parser->imm_float_zero)
-        parser->imm_float_zero = parser_const_float(parser, 0);
-    return parser->imm_float_zero;
-}
-
-static ast_value* parser_const_float_neg1(parser_t *parser) {
-    if (!parser->imm_float_neg_one)
-        parser->imm_float_neg_one = parser_const_float(parser, -1);
-    return parser->imm_float_neg_one;
-}
-
-static ast_value* parser_const_float_1(parser_t *parser)
-{
-    if (!parser->imm_float_one)
-        parser->imm_float_one = parser_const_float(parser, 1);
-    return parser->imm_float_one;
-}
-
-static char *parser_strdup(const char *str)
+char *parser_strdup(const char *str)
 {
     if (str && !*str) {
         /* actually dup empty strings */
@@ -253,70 +97,6 @@ static char *parser_strdup(const char *str)
     return util_strdup(str);
 }
 
-static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate)
-{
-    size_t hash = util_hthash(parser->ht_imm_string, str);
-    ast_value *out;
-    if ( (out = util_htgeth(parser->ht_imm_string, str, hash)) ) {
-        if (dotranslate && out->name[0] == '#') {
-            char name[32];
-            snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
-            ast_value_set_name(out, name);
-        }
-        return out;
-    }
-    /*
-    for (i = 0; i < vec_size(parser->imm_string); ++i) {
-        if (!strcmp(parser->imm_string[i]->constval.vstring, str))
-            return parser->imm_string[i];
-    }
-    */
-    if (dotranslate) {
-        char name[32];
-        snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
-        out = ast_value_new(parser_ctx(parser), name, TYPE_STRING);
-    } else
-        out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING);
-    out->cvq      = CV_CONST;
-    out->hasvalue = true;
-    out->constval.vstring = parser_strdup(str);
-    vec_push(parser->imm_string, out);
-    util_htseth(parser->ht_imm_string, str, hash, out);
-    return out;
-}
-
-static ast_value* parser_const_vector(parser_t *parser, vector v)
-{
-    size_t i;
-    ast_value *out;
-    for (i = 0; i < vec_size(parser->imm_vector); ++i) {
-        if (!memcmp(&parser->imm_vector[i]->constval.vvec, &v, sizeof(v)))
-            return parser->imm_vector[i];
-    }
-    out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_VECTOR);
-    out->cvq      = CV_CONST;
-    out->hasvalue = true;
-    out->constval.vvec = v;
-    vec_push(parser->imm_vector, out);
-    return out;
-}
-
-static ast_value* parser_const_vector_f(parser_t *parser, float x, float y, float z)
-{
-    vector v;
-    v.x = x;
-    v.y = y;
-    v.z = z;
-    return parser_const_vector(parser, v);
-}
-
-static ast_value* parser_const_vector_0(parser_t *parser)
-{
-    if (!parser->imm_vector_zero)
-        parser->imm_vector_zero = parser_const_vector_f(parser, 0, 0, 0);
-    return parser->imm_vector_zero;
-}
-
 static ast_expression* parser_find_field(parser_t *parser, const char *name)
 {
     return ( ast_expression*)util_htget(parser->htfields, name);
@@ -403,7 +183,7 @@ typedef struct
     size_t          off;
     ast_expression *out;
     ast_block      *block; /* for commas and function calls */
-    lex_ctx ctx;
+    lex_ctx_t ctx;
 } sy_elem;
 
 enum {
@@ -421,7 +201,7 @@ typedef struct
     unsigned int   *paren;
 } shunt;
 
-static sy_elem syexp(lex_ctx ctx, ast_expression *v) {
+static sy_elem syexp(lex_ctx_t ctx, ast_expression *v) {
     sy_elem e;
     e.etype = 0;
     e.off   = 0;
@@ -432,7 +212,7 @@ static sy_elem syexp(lex_ctx ctx, ast_expression *v) {
     return e;
 }
 
-static sy_elem syblock(lex_ctx ctx, ast_block *v) {
+static sy_elem syblock(lex_ctx_t ctx, ast_block *v) {
     sy_elem e;
     e.etype = 0;
     e.off   = 0;
@@ -443,7 +223,7 @@ static sy_elem syblock(lex_ctx ctx, ast_block *v) {
     return e;
 }
 
-static sy_elem syop(lex_ctx ctx, const oper_info *op) {
+static sy_elem syop(lex_ctx_t ctx, const oper_info *op) {
     sy_elem e;
     e.etype = 1 + (op - operators);
     e.off   = 0;
@@ -454,7 +234,7 @@ static sy_elem syop(lex_ctx ctx, const oper_info *op) {
     return e;
 }
 
-static sy_elem syparen(lex_ctx ctx, size_t off) {
+static sy_elem syparen(lex_ctx_t ctx, size_t off) {
     sy_elem e;
     e.etype = 0;
     e.off   = off;
@@ -477,7 +257,7 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     ast_expression  *sub;
     ast_expression  *entity;
 
-    lex_ctx ctx = ast_ctx(*out);
+    lex_ctx_t ctx = ast_ctx(*out);
 
     if (!ast_istype(*out, ast_array_index))
         return false;
@@ -507,43 +287,17 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
-static bool immediate_is_true(lex_ctx ctx, ast_value *v)
-{
-    switch (v->expression.vtype) {
-        case TYPE_FLOAT:
-            return !!v->constval.vfloat;
-        case TYPE_INTEGER:
-            return !!v->constval.vint;
-        case TYPE_VECTOR:
-            if (OPTS_FLAG(CORRECT_LOGIC))
-                return v->constval.vvec.x &&
-                       v->constval.vvec.y &&
-                       v->constval.vvec.z;
-            else
-                return !!(v->constval.vvec.x);
-        case TYPE_STRING:
-            if (!v->constval.vstring)
-                return false;
-            if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS))
-                return true;
-            return !!v->constval.vstring[0];
-        default:
-            compile_error(ctx, "internal error: immediate_is_true on invalid type");
-            return !!v->constval.vfunc;
-    }
-}
-
 static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
-    lex_ctx ctx;
+    lex_ctx_t ctx;
     ast_expression *out = NULL;
     ast_expression *exprs[3];
     ast_block      *blocks[3];
     ast_value      *asvalue[3];
     ast_binstore   *asbinstore;
     size_t i, assignop, addop, subop;
-    qcint  generated_op = 0;
+    qcint_t  generated_op = 0;
 
     char ty1[1024];
     char ty2[1024];
@@ -579,7 +333,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         blocks[i] = sy->out[vec_size(sy->out)+i].block;
         asvalue[i] = (ast_value*)exprs[i];
 
-        if (exprs[i]->expression.vtype == TYPE_NOEXPR &&
+        if (exprs[i]->vtype == TYPE_NOEXPR &&
             !(i != 0 && op->id == opid2('?',':')) &&
             !(i == 1 && op->id == opid1('.')))
         {
@@ -597,16 +351,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     }
 
 #define NotSameType(T) \
-             (exprs[0]->expression.vtype != exprs[1]->expression.vtype || \
-              exprs[0]->expression.vtype != T)
-#define CanConstFold1(A) \
-             (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\
-              (A)->expression.vtype != TYPE_FUNCTION)
-#define CanConstFold(A, B) \
-             (CanConstFold1(A) && CanConstFold1(B))
-#define ConstV(i) (asvalue[(i)]->constval.vvec)
-#define ConstF(i) (asvalue[(i)]->constval.vfloat)
-#define ConstS(i) (asvalue[(i)]->constval.vstring)
+             (exprs[0]->vtype != exprs[1]->vtype || \
+              exprs[0]->vtype != T)
     switch (op->id)
     {
         default:
@@ -614,8 +360,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             return false;
 
         case opid1('.'):
-            if (exprs[0]->expression.vtype == TYPE_VECTOR &&
-                exprs[1]->expression.vtype == TYPE_NOEXPR)
+            if (exprs[0]->vtype == TYPE_VECTOR &&
+                exprs[1]->vtype == TYPE_NOEXPR)
             {
                 if      (exprs[1] == (ast_expression*)parser->const_vec[0])
                     out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
@@ -628,14 +374,14 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     return false;
                 }
             }
-            else if (exprs[0]->expression.vtype == TYPE_ENTITY) {
-                if (exprs[1]->expression.vtype != TYPE_FIELD) {
+            else if (exprs[0]->vtype == TYPE_ENTITY) {
+                if (exprs[1]->vtype != TYPE_FIELD) {
                     compile_error(ast_ctx(exprs[1]), "type error: right hand of member-operand should be an entity-field");
                     return false;
                 }
                 out = (ast_expression*)ast_entfield_new(ctx, exprs[0], exprs[1]);
             }
-            else if (exprs[0]->expression.vtype == TYPE_VECTOR) {
+            else if (exprs[0]->vtype == TYPE_VECTOR) {
                 compile_error(ast_ctx(exprs[1]), "vectors cannot be accessed this way");
                 return false;
             }
@@ -646,15 +392,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
 
         case opid1('['):
-            if (exprs[0]->expression.vtype != TYPE_ARRAY &&
-                !(exprs[0]->expression.vtype == TYPE_FIELD &&
-                  exprs[0]->expression.next->expression.vtype == TYPE_ARRAY))
+            if (exprs[0]->vtype != TYPE_ARRAY &&
+                !(exprs[0]->vtype == TYPE_FIELD &&
+                  exprs[0]->next->vtype == TYPE_ARRAY))
             {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "cannot index value of type %s", ty1);
                 return false;
             }
-            if (exprs[1]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[1]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[1]), "index must be of type float, not %s", ty1);
                 return false;
@@ -701,295 +447,168 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             out = exprs[0];
             break;
         case opid2('-','P'):
-            switch (exprs[0]->expression.vtype) {
-                case TYPE_FLOAT:
-                    if (CanConstFold1(exprs[0]))
-                        out = (ast_expression*)parser_const_float(parser, -ConstF(0));
-                    else
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                switch (exprs[0]->vtype) {
+                    case TYPE_FLOAT:
                         out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F,
-                                                              (ast_expression*)parser_const_float_0(parser),
-                                                              exprs[0]);
-                    break;
-                case TYPE_VECTOR:
-                    if (CanConstFold1(exprs[0]))
-                        out = (ast_expression*)parser_const_vector_f(parser,
-                            -ConstV(0).x, -ConstV(0).y, -ConstV(0).z);
-                    else
+                                                                  (ast_expression*)parser->fold->imm_float[0],
+                                                                  exprs[0]);
+                        break;
+                    case TYPE_VECTOR:
                         out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V,
-                                                              (ast_expression*)parser_const_vector_0(parser),
-                                                              exprs[0]);
-                    break;
-                default:
-                compile_error(ctx, "invalid types used in expression: cannot negate type %s",
-                              type_name[exprs[0]->expression.vtype]);
-                return false;
+                                                                  (ast_expression*)parser->fold->imm_vector[0],
+                                                                  exprs[0]);
+                        break;
+                    default:
+                    compile_error(ctx, "invalid types used in expression: cannot negate type %s",
+                                  type_name[exprs[0]->vtype]);
+                    return false;
+                }
             }
             break;
 
         case opid2('!','P'):
-            switch (exprs[0]->expression.vtype) {
-                case TYPE_FLOAT:
-                    if (CanConstFold1(exprs[0]))
-                        out = (ast_expression*)parser_const_float(parser, !ConstF(0));
-                    else
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                switch (exprs[0]->vtype) {
+                    case TYPE_FLOAT:
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]);
-                    break;
-                case TYPE_VECTOR:
-                    if (CanConstFold1(exprs[0]))
-                        out = (ast_expression*)parser_const_float(parser,
-                            (!ConstV(0).x && !ConstV(0).y && !ConstV(0).z));
-                    else
+                        break;
+                    case TYPE_VECTOR:
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]);
-                    break;
-                case TYPE_STRING:
-                    if (CanConstFold1(exprs[0])) {
-                        if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
-                            out = (ast_expression*)parser_const_float(parser, !ConstS(0));
-                        else
-                            out = (ast_expression*)parser_const_float(parser, !ConstS(0) || !*ConstS(0));
-                    } else {
+                        break;
+                    case TYPE_STRING:
                         if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
                             out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, exprs[0]);
                         else
                             out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[0]);
-                    }
-                    break;
-                /* we don't constant-fold NOT for these types */
-                case TYPE_ENTITY:
-                    out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_ENT, exprs[0]);
-                    break;
-                case TYPE_FUNCTION:
-                    out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_FNC, exprs[0]);
-                    break;
-                default:
-                compile_error(ctx, "invalid types used in expression: cannot logically negate type %s",
-                              type_name[exprs[0]->expression.vtype]);
-                return false;
+                        break;
+                    /* we don't constant-fold NOT for these types */
+                    case TYPE_ENTITY:
+                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_ENT, exprs[0]);
+                        break;
+                    case TYPE_FUNCTION:
+                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_FNC, exprs[0]);
+                        break;
+                    default:
+                    compile_error(ctx, "invalid types used in expression: cannot logically negate type %s",
+                                  type_name[exprs[0]->vtype]);
+                    return false;
+                }
             }
             break;
 
         case opid1('+'):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
-                (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+            if (exprs[0]->vtype != exprs[1]->vtype ||
+               (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
             {
                 compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
-            switch (exprs[0]->expression.vtype) {
-                case TYPE_FLOAT:
-                    if (CanConstFold(exprs[0], exprs[1]))
-                    {
-                        out = (ast_expression*)parser_const_float(parser, ConstF(0) + ConstF(1));
-                    }
-                    else
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                switch (exprs[0]->vtype) {
+                    case TYPE_FLOAT:
                         out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F, exprs[0], exprs[1]);
-                    break;
-                case TYPE_VECTOR:
-                    if (CanConstFold(exprs[0], exprs[1]))
-                        out = (ast_expression*)parser_const_vector(parser, vec3_add(ConstV(0), ConstV(1)));
-                    else
+                        break;
+                    case TYPE_VECTOR:
                         out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]);
-                    break;
-                default:
-                    compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
-                                  type_name[exprs[0]->expression.vtype],
-                                  type_name[exprs[1]->expression.vtype]);
-                    return false;
-            };
+                        break;
+                    default:
+                        compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
+                                      type_name[exprs[0]->vtype],
+                                      type_name[exprs[1]->vtype]);
+                        return false;
+                }
+            }
             break;
         case opid1('-'):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
-                (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+            if  (exprs[0]->vtype != exprs[1]->vtype ||
+                (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT))
             {
                 compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
-                              type_name[exprs[1]->expression.vtype],
-                              type_name[exprs[0]->expression.vtype]);
+                              type_name[exprs[1]->vtype],
+                              type_name[exprs[0]->vtype]);
                 return false;
             }
-            switch (exprs[0]->expression.vtype) {
-                case TYPE_FLOAT:
-                    if (CanConstFold(exprs[0], exprs[1]))
-                        out = (ast_expression*)parser_const_float(parser, ConstF(0) - ConstF(1));
-                    else
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                switch (exprs[0]->vtype) {
+                    case TYPE_FLOAT:
                         out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, exprs[0], exprs[1]);
-                    break;
-                case TYPE_VECTOR:
-                    if (CanConstFold(exprs[0], exprs[1]))
-                        out = (ast_expression*)parser_const_vector(parser, vec3_sub(ConstV(0), ConstV(1)));
-                    else
+                        break;
+                    case TYPE_VECTOR:
                         out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]);
-                    break;
-                default:
-                    compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
-                                  type_name[exprs[1]->expression.vtype],
-                                  type_name[exprs[0]->expression.vtype]);
-                    return false;
-            };
+                        break;
+                    default:
+                        compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
+                                      type_name[exprs[1]->vtype],
+                                      type_name[exprs[0]->vtype]);
+                        return false;
+                }
+            }
             break;
         case opid1('*'):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype &&
-                !(exprs[0]->expression.vtype == TYPE_VECTOR &&
-                  exprs[1]->expression.vtype == TYPE_FLOAT) &&
-                !(exprs[1]->expression.vtype == TYPE_VECTOR &&
-                  exprs[0]->expression.vtype == TYPE_FLOAT)
+            if (exprs[0]->vtype != exprs[1]->vtype &&
+                !(exprs[0]->vtype == TYPE_VECTOR &&
+                  exprs[1]->vtype == TYPE_FLOAT) &&
+                !(exprs[1]->vtype == TYPE_VECTOR &&
+                  exprs[0]->vtype == TYPE_FLOAT)
                 )
             {
                 compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
-                              type_name[exprs[1]->expression.vtype],
-                              type_name[exprs[0]->expression.vtype]);
+                              type_name[exprs[1]->vtype],
+                              type_name[exprs[0]->vtype]);
                 return false;
             }
-            switch (exprs[0]->expression.vtype) {
-                case TYPE_FLOAT:
-                    if (exprs[1]->expression.vtype == TYPE_VECTOR)
-                    {
-                        if (CanConstFold(exprs[0], exprs[1]))
-                            out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(1), ConstF(0)));
-                        else
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                switch (exprs[0]->vtype) {
+                    case TYPE_FLOAT:
+                        if (exprs[1]->vtype == TYPE_VECTOR)
                             out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
-                    }
-                    else
-                    {
-                        if (CanConstFold(exprs[0], exprs[1]))
-                            out = (ast_expression*)parser_const_float(parser, ConstF(0) * ConstF(1));
                         else
                             out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, exprs[0], exprs[1]);
-                    }
-                    break;
-                case TYPE_VECTOR:
-                    if (exprs[1]->expression.vtype == TYPE_FLOAT)
-                    {
-                        if (CanConstFold(exprs[0], exprs[1]))
-                            out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), ConstF(1)));
-                        else
+                        break;
+                    case TYPE_VECTOR:
+                        if (exprs[1]->vtype == TYPE_FLOAT)
                             out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], exprs[1]);
-                    }
-                    else
-                    {
-                        if (CanConstFold(exprs[0], exprs[1]))
-                            out = (ast_expression*)parser_const_float(parser, vec3_mulvv(ConstV(0), ConstV(1)));
-                        else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[0])) {
-                            vector vec = ConstV(0);
-                            if (!vec.y && !vec.z) { /* 'n 0 0' * v */
-                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
-                                out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL);
-                                out->expression.node.keep = false;
-                                ((ast_member*)out)->rvalue = true;
-                                if (vec.x != 1)
-                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out);
-                            }
-                            else if (!vec.x && !vec.z) { /* '0 n 0' * v */
-                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
-                                out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL);
-                                out->expression.node.keep = false;
-                                ((ast_member*)out)->rvalue = true;
-                                if (vec.y != 1)
-                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out);
-                            }
-                            else if (!vec.x && !vec.y) { /* '0 n 0' * v */
-                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
-                                out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL);
-                                out->expression.node.keep = false;
-                                ((ast_member*)out)->rvalue = true;
-                                if (vec.z != 1)
-                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out);
-                            }
-                            else
-                                out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
-                        }
-                        else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && CanConstFold1(exprs[1])) {
-                            vector vec = ConstV(1);
-                            if (!vec.y && !vec.z) { /* v * 'n 0 0' */
-                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
-                                out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
-                                out->expression.node.keep = false;
-                                ((ast_member*)out)->rvalue = true;
-                                if (vec.x != 1)
-                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x));
-                            }
-                            else if (!vec.x && !vec.z) { /* v * '0 n 0' */
-                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
-                                out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
-                                out->expression.node.keep = false;
-                                ((ast_member*)out)->rvalue = true;
-                                if (vec.y != 1)
-                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y));
-                            }
-                            else if (!vec.x && !vec.y) { /* v * '0 n 0' */
-                                ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
-                                out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
-                                out->expression.node.keep = false;
-                                ((ast_member*)out)->rvalue = true;
-                                if (vec.z != 1)
-                                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z));
-                            }
-                            else
-                                out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
-                        }
                         else
                             out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
-                    }
-                    break;
-                default:
-                    compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
-                                  type_name[exprs[1]->expression.vtype],
-                                  type_name[exprs[0]->expression.vtype]);
-                    return false;
-            };
+                        break;
+                    default:
+                        compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
+                                      type_name[exprs[1]->vtype],
+                                      type_name[exprs[0]->vtype]);
+                        return false;
+                }
+            }
             break;
+
         case opid1('/'):
-            if (exprs[1]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[1]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
-                compile_error(ctx, "invalid types used in expression: cannot divide tyeps %s and %s", ty1, ty2);
+                compile_error(ctx, "invalid types used in expression: cannot divide types %s and %s", ty1, ty2);
                 return false;
             }
-            if (exprs[0]->expression.vtype == TYPE_FLOAT) {
-                if (CanConstFold(exprs[0], exprs[1]))
-                    out = (ast_expression*)parser_const_float(parser, ConstF(0) / ConstF(1));
-                else
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                if (exprs[0]->vtype == TYPE_FLOAT)
                     out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
-            }
-            else if (exprs[0]->expression.vtype == TYPE_VECTOR) {
-                if (CanConstFold(exprs[0], exprs[1]))
-                    out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), 1.0/ConstF(1)));
                 else {
-                    if (CanConstFold1(exprs[1])) {
-                        out = (ast_expression*)parser_const_float(parser, 1.0 / ConstF(1));
-                    } else {
-                        out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
-                                                              (ast_expression*)parser_const_float_1(parser),
-                                                              exprs[1]);
-                    }
-                    if (!out) {
-                        compile_error(ctx, "internal error: failed to generate division");
-                        return false;
-                    }
-                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], out);
+                    ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                    ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+                    compile_error(ctx, "invalid types used in expression: cannot divide types %s and %s", ty1, ty2);
+                    return false;
                 }
             }
-            else
-            {
-                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
-                ast_type_to_string(exprs[1], ty2, sizeof(ty2));
-                compile_error(ctx, "invalid types used in expression: cannot divide tyeps %s and %s", ty1, ty2);
-                return false;
-            }
             break;
 
         case opid1('%'):
             if (NotSameType(TYPE_FLOAT)) {
                 compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s",
-                    type_name[exprs[0]->expression.vtype],
-                    type_name[exprs[1]->expression.vtype]);
+                    type_name[exprs[0]->vtype],
+                    type_name[exprs[1]->vtype]);
                 return false;
-            }
-            if (CanConstFold(exprs[0], exprs[1])) {
-                out = (ast_expression*)parser_const_float(parser,
-                            (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1))));
-            } else {
+            } else if (!(out = fold_op(parser->fold, op, exprs))) {
                 /* generate a call to __builtin_mod */
                 ast_expression *mod  = intrin_func(parser, "mod");
                 ast_call       *call = NULL;
@@ -1011,56 +630,128 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         case opid1('&'):
             if (NotSameType(TYPE_FLOAT)) {
                 compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
-            if (CanConstFold(exprs[0], exprs[1]))
-                out = (ast_expression*)parser_const_float(parser,
-                    (op->id == opid1('|') ? (float)( ((qcint)ConstF(0)) | ((qcint)ConstF(1)) ) :
-                                            (float)( ((qcint)ConstF(0)) & ((qcint)ConstF(1)) ) ));
-            else
+            if (!(out = fold_op(parser->fold, op, exprs)))
                 out = (ast_expression*)ast_binary_new(ctx,
                     (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
                     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 (!(out = fold_op(parser->fold, op, exprs))) {
+                /*
+                 * IF the first expression is float, the following will be too
+                 * since scalar ^ vector is not allowed.
+                 */
+                if (exprs[0]->vtype == TYPE_FLOAT) {
+                    ast_binary *expr = ast_binary_new(
+                        ctx,
+                        INSTR_SUB_F,
+                        (ast_expression*)parser->fold->imm_float[2],
+                        (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.
+                         */
+                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
+                        return false;
+                    } else {
+                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
+                        return false;
+                    }
+                }
+            }
+            break;
 
         case opid2('<','<'):
         case opid2('>','>'):
-            if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) {
-                if (op->id == opid2('<','<'))
-                    out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) << (int)(ConstF(1))));
-                else
-                    out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) >> (int)(ConstF(1))));
-                break;
-            }
         case opid3('<','<','='):
         case opid3('>','>','='):
-            compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
-            return false;
+            if(!(out = fold_op(parser->fold, op, exprs))) {
+                compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
+                return false;
+            }
 
         case opid2('|','|'):
             generated_op += 1; /* INSTR_OR */
         case opid2('&','&'):
             generated_op += INSTR_AND;
-            if (CanConstFold(exprs[0], exprs[1]))
-            {
-                if (OPTS_FLAG(PERL_LOGIC)) {
-                    if (immediate_is_true(ctx, asvalue[0]))
-                        out = exprs[1];
-                }
-                else
-                    out = (ast_expression*)parser_const_float(parser,
-                          ( (generated_op == INSTR_OR)
-                            ? (immediate_is_true(ctx, asvalue[0]) || immediate_is_true(ctx, asvalue[1]))
-                            : (immediate_is_true(ctx, asvalue[0]) && immediate_is_true(ctx, asvalue[1])) )
-                          ? 1 : 0);
-            }
-            else
-            {
+            if (!(out = fold_op(parser->fold, op, exprs))) {
                 if (OPTS_FLAG(PERL_LOGIC) && !ast_compare_type(exprs[0], exprs[1])) {
                     ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
@@ -1068,7 +759,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     return false;
                 }
                 for (i = 0; i < 2; ++i) {
-                    if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->expression.vtype == TYPE_VECTOR) {
+                    if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->vtype == TYPE_VECTOR) {
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]);
                         if (!out) break;
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
@@ -1079,7 +770,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                             break;
                         }
                     }
-                    else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->expression.vtype == TYPE_STRING) {
+                    else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->vtype == TYPE_STRING) {
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]);
                         if (!out) break;
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
@@ -1107,9 +798,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 compile_error(ctx, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2);
                 return false;
             }
-            if (CanConstFold1(exprs[0]))
-                out = (immediate_is_true(ctx, asvalue[0]) ? exprs[1] : exprs[2]);
-            else
+            if (!(out = fold_op(parser->fold, op, exprs)))
                 out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
             break;
 
@@ -1119,13 +808,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                 compile_error(ctx, "invalid types used in exponentiation: %s and %s",
                     ty1, ty2);
-
                 return false;
             }
-
-            if (CanConstFold(exprs[0], exprs[1])) {
-                out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1)));
-            } else {
+            
+            if (!(out = fold_op(parser->fold, op, exprs))) {
                 ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow"));
                 vec_push(gencall->params, exprs[0]);
                 vec_push(gencall->params, exprs[1]);
@@ -1141,33 +827,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     ty1, ty2);
 
                 return false;
-            }
+            } 
 
-            if (CanConstFold(exprs[0], exprs[1])) {
-                if (ConstF(0) < ConstF(1))
-                    out = (ast_expression*)parser_const_float_neg1(parser);
-                else if (ConstF(0) == ConstF(1))
-                    out = (ast_expression*)parser_const_float_0(parser);
-                else if (ConstF(0) > ConstF(1))
-                    out = (ast_expression*)parser_const_float_1(parser);
-            } else {
+            if (!(out = fold_op(parser->fold, op, exprs))) {
                 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,
                         (ast_expression*)ast_binary_new(ctx, INSTR_LT, exprs[0], exprs[1]),
                         /* out = -1 */
-                        (ast_expression*)parser_const_float_neg1(parser),
+                        (ast_expression*)parser->fold->imm_float[2],
                     /* } else { */
                         /* if (eq) { */
                         (ast_expression*)ast_ternary_new(ctx, (ast_expression*)eq,
                             /* out = 0 */
-                            (ast_expression*)parser_const_float_0(parser),
+                            (ast_expression*)parser->fold->imm_float[0],
                         /* } else { */
                             /* out = 1 */
-                            (ast_expression*)parser_const_float_1(parser)
+                            (ast_expression*)parser->fold->imm_float[1]
                         /* } */
                         )
                     /* } */
@@ -1186,49 +865,52 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             generated_op += INSTR_LE;
             if (NotSameType(TYPE_FLOAT)) {
                 compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+            if (!(out = fold_op(parser->fold, op, exprs)))
+                out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
             break;
         case opid2('!', '='):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) {
+            if (exprs[0]->vtype != exprs[1]->vtype) {
                 compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]);
+            if (!(out = fold_op(parser->fold, op, exprs)))
+                out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
             break;
         case opid2('=', '='):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) {
+            if (exprs[0]->vtype != exprs[1]->vtype) {
                 compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]);
+            if (!(out = fold_op(parser->fold, op, exprs)))
+                out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
             break;
 
         case opid1('='):
             if (ast_istype(exprs[0], ast_entfield)) {
                 ast_expression *field = ((ast_entfield*)exprs[0])->field;
                 if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
-                    exprs[0]->expression.vtype == TYPE_FIELD &&
-                    exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+                    exprs[0]->vtype == TYPE_FIELD &&
+                    exprs[0]->next->vtype == TYPE_VECTOR)
                 {
                     assignop = type_storep_instr[TYPE_VECTOR];
                 }
                 else
-                    assignop = type_storep_instr[exprs[0]->expression.vtype];
-                if (assignop == VINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
+                    assignop = type_storep_instr[exprs[0]->vtype];
+                if (assignop == VINSTR_END || !ast_compare_type(field->next, exprs[1]))
                 {
-                    ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
+                    ast_type_to_string(field->next, ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) &&
-                        field->expression.next->expression.vtype == TYPE_FUNCTION &&
-                        exprs[1]->expression.vtype == TYPE_FUNCTION)
+                        field->next->vtype == TYPE_FUNCTION &&
+                        exprs[1]->vtype == TYPE_FUNCTION)
                     {
                         (void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES,
                                                "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
@@ -1240,13 +922,13 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             else
             {
                 if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
-                    exprs[0]->expression.vtype == TYPE_FIELD &&
-                    exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+                    exprs[0]->vtype == TYPE_FIELD &&
+                    exprs[0]->next->vtype == TYPE_VECTOR)
                 {
                     assignop = type_store_instr[TYPE_VECTOR];
                 }
                 else {
-                    assignop = type_store_instr[exprs[0]->expression.vtype];
+                    assignop = type_store_instr[exprs[0]->vtype];
                 }
 
                 if (assignop == VINSTR_END) {
@@ -1259,8 +941,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) &&
-                        exprs[0]->expression.vtype == TYPE_FUNCTION &&
-                        exprs[1]->expression.vtype == TYPE_FUNCTION)
+                        exprs[0]->vtype == TYPE_FUNCTION &&
+                        exprs[1]->vtype == TYPE_FUNCTION)
                     {
                         (void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES,
                                                "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
@@ -1277,7 +959,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         case opid3('+','+','P'):
         case opid3('-','-','P'):
             /* prefix ++ */
-            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[0]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "invalid type for prefix increment: %s", ty1);
                 return false;
@@ -1292,17 +974,17 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
                                                         exprs[0],
-                                                        (ast_expression*)parser_const_float_1(parser));
+                                                        (ast_expression*)parser->fold->imm_float[1]);
             } else {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop,
                                                         exprs[0],
-                                                        (ast_expression*)parser_const_float_1(parser));
+                                                        (ast_expression*)parser->fold->imm_float[1]);
             }
             break;
         case opid3('S','+','+'):
         case opid3('S','-','-'):
             /* prefix ++ */
-            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[0]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "invalid type for suffix increment: %s", ty1);
                 return false;
@@ -1320,22 +1002,22 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
                                                         exprs[0],
-                                                        (ast_expression*)parser_const_float_1(parser));
+                                                        (ast_expression*)parser->fold->imm_float[1]);
             } else {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop,
                                                         exprs[0],
-                                                        (ast_expression*)parser_const_float_1(parser));
+                                                        (ast_expression*)parser->fold->imm_float[1]);
             }
             if (!out)
                 return false;
             out = (ast_expression*)ast_binary_new(ctx, subop,
                                                   out,
-                                                  (ast_expression*)parser_const_float_1(parser));
+                                                  (ast_expression*)parser->fold->imm_float[1]);
             break;
         case opid2('+','='):
         case opid2('-','='):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
-                (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+            if (exprs[0]->vtype != exprs[1]->vtype ||
+                (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
             {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
@@ -1347,10 +1029,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                assignop = type_storep_instr[exprs[0]->vtype];
             else
-                assignop = type_store_instr[exprs[0]->expression.vtype];
-            switch (exprs[0]->expression.vtype) {
+                assignop = type_store_instr[exprs[0]->vtype];
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
                     out = (ast_expression*)ast_binstore_new(ctx, assignop,
                                                             (op->id == opid2('+','=') ? INSTR_ADD_F : INSTR_SUB_F),
@@ -1363,16 +1045,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                     compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s",
-                                  type_name[exprs[0]->expression.vtype],
-                                  type_name[exprs[1]->expression.vtype]);
+                                  type_name[exprs[0]->vtype],
+                                  type_name[exprs[1]->vtype]);
                     return false;
             };
             break;
         case opid2('*','='):
         case opid2('/','='):
-            if (exprs[1]->expression.vtype != TYPE_FLOAT ||
-                !(exprs[0]->expression.vtype == TYPE_FLOAT ||
-                  exprs[0]->expression.vtype == TYPE_VECTOR))
+            if (exprs[1]->vtype != TYPE_FLOAT ||
+                !(exprs[0]->vtype == TYPE_FLOAT ||
+                  exprs[0]->vtype == TYPE_VECTOR))
             {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
@@ -1384,10 +1066,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                assignop = type_storep_instr[exprs[0]->vtype];
             else
-                assignop = type_store_instr[exprs[0]->expression.vtype];
-            switch (exprs[0]->expression.vtype) {
+                assignop = type_store_instr[exprs[0]->vtype];
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
                     out = (ast_expression*)ast_binstore_new(ctx, assignop,
                                                             (op->id == opid2('*','=') ? INSTR_MUL_F : INSTR_DIV_F),
@@ -1398,14 +1080,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         out = (ast_expression*)ast_binstore_new(ctx, assignop, INSTR_MUL_VF,
                                                                 exprs[0], exprs[1]);
                     } else {
-                        /* there's no DIV_VF */
-                        if (CanConstFold1(exprs[1])) {
-                            out = (ast_expression*)parser_const_float(parser, 1.0 / ConstF(1));
-                        } else {
-                            out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
-                                                                  (ast_expression*)parser_const_float_1(parser),
+                        out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
+                                                                  (ast_expression*)parser->fold->imm_float[1],
                                                                   exprs[1]);
-                        }
                         if (!out) {
                             compile_error(ctx, "internal error: failed to generate division");
                             return false;
@@ -1416,8 +1093,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                     compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s",
-                                  type_name[exprs[0]->expression.vtype],
-                                  type_name[exprs[1]->expression.vtype]);
+                                  type_name[exprs[0]->vtype],
+                                  type_name[exprs[1]->vtype]);
                     return false;
             };
             break;
@@ -1434,9 +1111,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                assignop = type_storep_instr[exprs[0]->vtype];
             else
-                assignop = type_store_instr[exprs[0]->expression.vtype];
+                assignop = type_store_instr[exprs[0]->vtype];
             out = (ast_expression*)ast_binstore_new(ctx, assignop,
                                                     (op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
                                                     exprs[0], exprs[1]);
@@ -1454,9 +1131,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 return false;
             }
             if (ast_istype(exprs[0], ast_entfield))
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                assignop = type_storep_instr[exprs[0]->vtype];
             else
-                assignop = type_store_instr[exprs[0]->expression.vtype];
+                assignop = type_store_instr[exprs[0]->vtype];
             out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
             if (!out)
                 return false;
@@ -1469,21 +1146,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
 
         case opid2('~', 'P'):
-            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[0]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
                 return false;
             }
-
-            if(CanConstFold1(exprs[0]))
-                out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0));
-            else
-                out = (ast_expression*)
-                    ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
+            if (!(out = fold_op(parser->fold, op, exprs)))
+                out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
             break;
     }
 #undef NotSameType
-
     if (!out) {
         compile_error(ctx, "failed to apply operator %s", op->op);
         return false;
@@ -1538,7 +1210,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
         ast_type_to_string(vec_last(sy->out).out, ty, sizeof(ty));
         ast_unref(vec_last(sy->out).out);
         sy->out[fid] = syexp(ast_ctx(vec_last(sy->out).out),
-                             (ast_expression*)parser_const_string(parser, ty, false));
+                             (ast_expression*)fold_constgen_string(parser->fold, ty, false));
         vec_shrinkby(sy->out, 1);
         return true;
     }
@@ -1559,34 +1231,34 @@ 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;
 
     if (ast_istype(fun, ast_value)) {
         funval = (ast_value*)fun;
-        if ((fun->expression.flags & AST_FLAG_VARIADIC) &&
+        if ((fun->flags & AST_FLAG_VARIADIC) &&
             !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
         {
-            call->va_count = (ast_expression*)parser_const_float(parser, (double)paramcount);
+            call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount);
         }
     }
 
     /* overwrite fid, the function, with a call */
     sy->out[fid] = syexp(call->expression.node.context, (ast_expression*)call);
 
-    if (fun->expression.vtype != TYPE_FUNCTION) {
-        parseerror(parser, "not a function (%s)", type_name[fun->expression.vtype]);
+    if (fun->vtype != TYPE_FUNCTION) {
+        parseerror(parser, "not a function (%s)", type_name[fun->vtype]);
         return false;
     }
 
-    if (!fun->expression.next) {
+    if (!fun->next) {
         parseerror(parser, "could not determine function return type");
         return false;
     } else {
         ast_value *fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL);
 
-        if (fun->expression.flags & AST_FLAG_DEPRECATED) {
+        if (fun->flags & AST_FLAG_DEPRECATED) {
             if (!fval) {
                 return !parsewarning(parser, WARN_DEPRECATED,
                         "call to function (which is marked deprecated)\n",
@@ -1606,22 +1278,22 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
                     ast_ctx(fun).line);
         }
 
-        if (vec_size(fun->expression.params) != paramcount &&
-            !((fun->expression.flags & AST_FLAG_VARIADIC) &&
-              vec_size(fun->expression.params) < paramcount))
+        if (vec_size(fun->params) != paramcount &&
+            !((fun->flags & AST_FLAG_VARIADIC) &&
+              vec_size(fun->params) < paramcount))
         {
-            const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many";
+            const char *fewmany = (vec_size(fun->params) > paramcount) ? "few" : "many";
             if (fval)
                 return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
                                      "too %s parameters for call to %s: expected %i, got %i\n"
                                      " -> `%s` has been declared here: %s:%i",
-                                     fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
+                                     fewmany, fval->name, (int)vec_size(fun->params), (int)paramcount,
                                      fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
             else
                 return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
                                      "too %s parameters for function call: expected %i, got %i\n"
                                      " -> it has been declared here: %s:%i",
-                                     fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+                                     fewmany, (int)vec_size(fun->params), (int)paramcount,
                                      ast_ctx(fun).file, (int)ast_ctx(fun).line);
         }
     }
@@ -1681,6 +1353,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;
@@ -1694,8 +1368,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_t         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");
@@ -1711,9 +1389,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)) {
@@ -1735,22 +1418,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))
     {
@@ -1782,6 +1449,9 @@ static ast_expression* parse_vararg(parser_t *parser)
     return out;
 }
 
+/* not to be exposed */
+extern bool ftepp_predef_exists(const char *name);
+
 static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
 {
     if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
@@ -1801,7 +1471,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
             parseerror(parser, "expected a constant string in translatable-string extension");
             return false;
         }
-        val = parser_const_string(parser, parser_tokval(parser), true);
+        val = (ast_value*)fold_constgen_string(parser->fold, parser_tokval(parser), true);
         if (!val)
             return false;
         vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
@@ -1826,35 +1496,31 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
         return true;
     }
     else if (parser->tok == TOKEN_FLOATCONST) {
-        ast_value *val;
-        val = parser_const_float(parser, (parser_token(parser)->constval.f));
+        ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f));
         if (!val)
             return false;
-        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
-        ast_value *val;
-        val = parser_const_float(parser, (double)(parser_token(parser)->constval.i));
+        ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i));
         if (!val)
             return false;
-        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_STRINGCONST) {
-        ast_value *val;
-        val = parser_const_string(parser, parser_tokval(parser), false);
+        ast_expression *val = fold_constgen_string(parser->fold, parser_tokval(parser), false);
         if (!val)
             return false;
-        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_VECTORCONST) {
-        ast_value *val;
-        val = parser_const_vector(parser, parser_token(parser)->constval.v);
+        ast_expression *val = fold_constgen_vector(parser->fold, parser_token(parser)->constval.v);
         if (!val)
             return false;
-        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_IDENT)
@@ -1872,7 +1538,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
             /* When adding more intrinsics, fix the above condition */
             prev = NULL;
         }
-        if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
+        if (prev && prev->vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
         {
             var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
         } else {
@@ -1888,6 +1554,8 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                 vec_push(parser->labels, lbl);
             }
         }
+        if (!var && !strcmp(parser_tokval(parser), "__FUNC__"))
+            var = (ast_expression*)fold_constgen_string(parser->fold, parser->function->name, false);
         if (!var) {
             /* intrinsics */
             if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
@@ -1899,8 +1567,6 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
              */
             else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
                 var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */);
-            } else {
-                var = intrin_func(parser, parser_tokval(parser));
             }
 
             if (!var) {
@@ -1912,13 +1578,9 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                  * 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)) {
-                    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));
-                            return false;
-                        }
-                    }
+                if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) {
+                    parseerror(parser, "unexpected identifier: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
+                    return false;
                 }
 
                 /*
@@ -1928,7 +1590,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);
 
@@ -1936,7 +1598,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                         correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser));
                         if (strcmp(correct, parser_tokval(parser))) {
                             break;
-                        } else if (correct) {
+                        } else  {
                             mem_d(correct);
                             correct = NULL;
                         }
@@ -1944,12 +1606,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;
             }
         }
@@ -1996,7 +1658,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;
         }
 
@@ -2182,8 +1844,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             wantop = true;
         }
         else {
-            parseerror(parser, "expected operator or end of statement");
-            goto onerr;
+            /* in this case we might want to allow constant string concatenation */
+            bool concatenated = false;
+            if (parser->tok == TOKEN_STRINGCONST && vec_size(sy.out)) {
+                ast_expression *lexpr = vec_last(sy.out).out;
+                if (ast_istype(lexpr, ast_value)) {
+                    ast_value *last = (ast_value*)lexpr;
+                    if (last->isimm == true && last->cvq == CV_CONST &&
+                        last->hasvalue && last->expression.vtype == TYPE_STRING)
+                    {
+                        char *newstr = NULL;
+                        util_asprintf(&newstr, "%s%s", last->constval.vstring, parser_tokval(parser));
+                        vec_last(sy.out).out = (ast_expression*)fold_constgen_string(parser->fold, newstr, false);
+                        mem_d(newstr);
+                        concatenated = true;
+                    }
+                }
+            }
+            if (!concatenated) {
+                parseerror(parser, "expected operator or end of statement");
+                goto onerr;
+            }
         }
 
         if (!parser_next(parser)) {
@@ -2203,8 +1884,8 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
     }
 
     parser->lex->flags.noops = true;
-    if (!vec_size(sy.out)) {
-        parseerror(parser, "empty expression");
+    if (vec_size(sy.out) != 1) {
+        parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out));
         expr = NULL;
     } else
         expr = sy.out[0].out;
@@ -2339,13 +2020,13 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond,
     ast_unary *unary;
     ast_expression *prev;
 
-    if (cond->expression.vtype == TYPE_VOID || cond->expression.vtype >= TYPE_VARIANT) {
+    if (cond->vtype == TYPE_VOID || cond->vtype >= TYPE_VARIANT) {
         char ty[1024];
         ast_type_to_string(cond, ty, sizeof(ty));
         compile_error(ast_ctx(cond), "invalid type for if() condition: %s", ty);
     }
 
-    if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING)
+    if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->vtype == TYPE_STRING)
     {
         prev = cond;
         cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond);
@@ -2356,7 +2037,7 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond,
         }
         ifnot = !ifnot;
     }
-    else if (OPTS_FLAG(CORRECT_LOGIC) && cond->expression.vtype == TYPE_VECTOR)
+    else if (OPTS_FLAG(CORRECT_LOGIC) && cond->vtype == TYPE_VECTOR)
     {
         /* vector types need to be cast to true booleans */
         ast_binary *bin = (ast_binary*)cond;
@@ -2375,7 +2056,8 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond,
     }
 
     unary = (ast_unary*)cond;
-    while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
+    /* ast_istype dereferences cond, should test here for safety */
+    while (cond && ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F)
     {
         cond = unary->operand;
         unary->operand = NULL;
@@ -2397,7 +2079,7 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
     ast_expression *cond, *ontrue = NULL, *onfalse = NULL;
     bool ifnot = false;
 
-    lex_ctx ctx = parser_ctx(parser);
+    lex_ctx_t ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
 
@@ -2429,17 +2111,17 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
     /* closing paren */
     if (parser->tok != ')') {
         parseerror(parser, "expected closing paren after 'if' condition");
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     /* parse into the 'then' branch */
     if (!parser_next(parser)) {
         parseerror(parser, "expected statement for on-true branch of 'if'");
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     if (!parse_statement_or_block(parser, &ontrue)) {
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     if (!ontrue)
@@ -2450,12 +2132,12 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         if (!parser_next(parser)) {
             parseerror(parser, "expected on-false branch after 'else'");
             ast_delete(ontrue);
-            ast_delete(cond);
+            ast_unref(cond);
             return false;
         }
         if (!parse_statement_or_block(parser, &onfalse)) {
             ast_delete(ontrue);
-            ast_delete(cond);
+            ast_unref(cond);
             return false;
         }
     }
@@ -2536,7 +2218,7 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **
 
     bool ifnot = false;
 
-    lex_ctx ctx = parser_ctx(parser);
+    lex_ctx_t ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
 
@@ -2552,23 +2234,23 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **
     /* closing paren */
     if (parser->tok != ')') {
         parseerror(parser, "expected closing paren after 'while' condition");
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     /* parse into the 'then' branch */
     if (!parser_next(parser)) {
         parseerror(parser, "expected while-loop body");
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     if (!parse_statement_or_block(parser, &ontrue)) {
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
 
     cond = process_condition(parser, cond, &ifnot);
     if (!cond) {
-        ast_delete(ontrue);
+        ast_unref(ontrue);
         return false;
     }
     aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue);
@@ -2615,7 +2297,12 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
     if (vec_last(parser->breaks) != label || vec_last(parser->continues) != label) {
         parseerror(parser, "internal error: label stack corrupted");
         rv = false;
-        ast_delete(*out);
+        /*
+         * Test for NULL otherwise ast_delete dereferences null pointer
+         * and boom.
+         */
+        if (*out)
+            ast_delete(*out);
         *out = NULL;
     }
     else {
@@ -2632,7 +2319,7 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression
 
     bool ifnot = false;
 
-    lex_ctx ctx = parser_ctx(parser);
+    lex_ctx_t ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
 
@@ -2668,21 +2355,21 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression
     if (parser->tok != ')') {
         parseerror(parser, "expected closing paren after 'while' condition");
         ast_delete(ontrue);
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     /* parse on */
     if (!parser_next(parser) || parser->tok != ';') {
         parseerror(parser, "expected semicolon after condition");
         ast_delete(ontrue);
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
 
     if (!parser_next(parser)) {
         parseerror(parser, "parse error");
         ast_delete(ontrue);
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
 
@@ -2755,10 +2442,9 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
     ast_expression *initexpr, *cond, *increment, *ontrue;
     ast_value      *typevar;
 
-    bool retval = true;
     bool ifnot  = false;
 
-    lex_ctx ctx = parser_ctx(parser);
+    lex_ctx_t ctx = parser_ctx(parser);
 
     parser_enterblock(parser);
 
@@ -2824,7 +2510,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the incrementor */
     if (parser->tok != ')') {
-        lex_ctx condctx = parser_ctx(parser);
+        lex_ctx_t condctx = parser_ctx(parser);
         increment = parse_expression_leave(parser, false, false, false);
         if (!increment)
             goto onerr;
@@ -2855,24 +2541,28 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
     aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue);
     *out = (ast_expression*)aloop;
 
-    if (!parser_leaveblock(parser))
-        retval = false;
-    return retval;
+    if (!parser_leaveblock(parser)) {
+        ast_delete(aloop);
+        return false;
+    }
+    return true;
 onerr:
-    if (initexpr)  ast_delete(initexpr);
-    if (cond)      ast_delete(cond);
-    if (increment) ast_delete(increment);
+    if (initexpr)  ast_unref(initexpr);
+    if (cond)      ast_unref(cond);
+    if (increment) ast_unref(increment);
     (void)!parser_leaveblock(parser);
     return false;
 }
 
 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);
+    lex_ctx_t ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
 
@@ -2881,29 +2571,87 @@ 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)
             return false;
 
-        if (exp->expression.vtype != TYPE_NIL &&
-            exp->expression.vtype != expected->expression.next->expression.vtype)
+        if (exp->vtype != TYPE_NIL &&
+            exp->vtype != ((ast_expression*)expected)->next->vtype)
         {
             parseerror(parser, "return with invalid expression");
         }
 
         ret = ast_return_new(ctx, exp);
         if (!ret) {
-            ast_delete(exp);
+            ast_unref(exp);
             return false;
         }
     } else {
         if (!parser_next(parser))
             parseerror(parser, "parse error");
-        if (expected->expression.next->expression.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;
@@ -2913,7 +2661,7 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
 {
     size_t       i;
     unsigned int levels = 0;
-    lex_ctx      ctx = parser_ctx(parser);
+    lex_ctx_t      ctx = parser_ctx(parser);
     const char **loops = (is_continue ? parser->continues : parser->breaks);
 
     (void)block; /* not touching */
@@ -3200,7 +2948,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
     bool noref, is_static;
     uint32_t qflags = 0;
 
-    lex_ctx ctx = parser_ctx(parser);
+    lex_ctx_t ctx = parser_ctx(parser);
 
     (void)block; /* not touching */
     (void)opval;
@@ -3422,7 +3170,8 @@ static bool parse_goto(parser_t *parser, ast_expression **out)
         if (!(expression = parse_expression(parser, false, true)) ||
             !(*out = parse_goto_computed(parser, &expression))) {
             parseerror(parser, "invalid goto expression");
-            ast_unref(expression);
+            if(expression)
+                ast_unref(expression);
             return false;
         }
 
@@ -3651,7 +3400,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 == '{')
@@ -3711,7 +3460,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
     else
     {
-        lex_ctx ctx = parser_ctx(parser);
+        lex_ctx_t ctx = parser_ctx(parser);
         ast_expression *exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
@@ -3728,7 +3477,7 @@ static bool parse_enum(parser_t *parser)
 {
     bool        flag = false;
     bool        reverse = false;
-    qcfloat     num = 0;
+    qcfloat_t     num = 0;
     ast_value **values = NULL;
     ast_value  *var = NULL;
     ast_value  *asvalue;
@@ -4042,9 +3791,9 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             /* qc allows the use of not-yet-declared functions here
              * - this automatically creates a prototype */
             ast_value      *thinkfunc;
-            ast_expression *functype = fld_think->expression.next;
+            ast_expression *functype = fld_think->next;
 
-            thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->expression.vtype);
+            thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->vtype);
             if (!thinkfunc) { /* || !ast_type_adopt(thinkfunc, functype)*/
                 ast_unref(framenum);
                 parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser));
@@ -4110,7 +3859,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     }
 
     if (has_frame_think) {
-        lex_ctx ctx;
+        lex_ctx_t ctx;
         ast_expression *self_frame;
         ast_expression *self_nextthink;
         ast_expression *self_think;
@@ -4125,7 +3874,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         self_think     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think);
 
         time_plus_1    = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F,
-                         gbl_time, (ast_expression*)parser_const_float(parser, 0.1));
+                         gbl_time, (ast_expression*)fold_constgen_float(parser->fold, 0.1));
 
         if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
             if (self_frame)     ast_delete(self_frame);
@@ -4199,7 +3948,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
 
         if (param->expression.vtype != TYPE_VECTOR &&
             (param->expression.vtype != TYPE_FIELD ||
-             param->expression.next->expression.vtype != TYPE_VECTOR))
+             param->expression.next->vtype != TYPE_VECTOR))
         {
             continue;
         }
@@ -4227,21 +3976,20 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         varargs->expression.flags |= AST_FLAG_IS_VARARG;
         varargs->expression.next = (ast_expression*)ast_value_new(ast_ctx(var), NULL, TYPE_VECTOR);
         varargs->expression.count = 0;
-        snprintf(name, sizeof(name), "%s##va##SET", var->name);
+        util_snprintf(name, sizeof(name), "%s##va##SET", var->name);
         if (!parser_create_array_setter_proto(parser, varargs, name)) {
             ast_delete(varargs);
             ast_block_delete(block);
             goto enderrfn;
         }
-        snprintf(name, sizeof(name), "%s##va##GET", var->name);
+        util_snprintf(name, sizeof(name), "%s##va##GET", var->name);
         if (!parser_create_array_getter_proto(parser, varargs, varargs->expression.next, name)) {
             ast_delete(varargs);
             ast_block_delete(block);
             goto enderrfn;
         }
-        func->varargs = varargs;
-
-        func->fixedparams = parser_const_float(parser, vec_size(var->expression.params));
+        func->varargs     = varargs;
+        func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params));
     }
 
     parser->function = func;
@@ -4252,6 +4000,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;
@@ -4289,7 +4038,7 @@ static ast_expression *array_accessor_split(
     ast_ifthen *ifthen;
     ast_binary *cmp;
 
-    lex_ctx ctx = ast_ctx(array);
+    lex_ctx_t ctx = ast_ctx(array);
 
     if (!left || !right) {
         if (left)  ast_delete(left);
@@ -4299,7 +4048,7 @@ static ast_expression *array_accessor_split(
 
     cmp = ast_binary_new(ctx, INSTR_LT,
                          (ast_expression*)index,
-                         (ast_expression*)parser_const_float(parser, middle));
+                         (ast_expression*)fold_constgen_float(parser->fold, middle));
     if (!cmp) {
         ast_delete(left);
         ast_delete(right);
@@ -4319,7 +4068,7 @@ static ast_expression *array_accessor_split(
 
 static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast_value *index, ast_value *value, size_t from, size_t afterend)
 {
-    lex_ctx ctx = ast_ctx(array);
+    lex_ctx_t ctx = ast_ctx(array);
 
     if (from+1 == afterend) {
         /* set this value */
@@ -4329,10 +4078,10 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
         ast_store       *st;
         int assignop = type_store_instr[value->expression.vtype];
 
-        if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+        if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
             assignop = INSTR_STORE_V;
 
-        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
         if (!subscript)
             return NULL;
 
@@ -4384,7 +4133,7 @@ static ast_expression *array_field_setter_node(
     size_t     from,
     size_t     afterend)
 {
-    lex_ctx ctx = ast_ctx(array);
+    lex_ctx_t ctx = ast_ctx(array);
 
     if (from+1 == afterend) {
         /* set this value */
@@ -4395,10 +4144,10 @@ static ast_expression *array_field_setter_node(
         ast_store       *st;
         int assignop = type_storep_instr[value->expression.vtype];
 
-        if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+        if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
             assignop = INSTR_STOREP_V;
 
-        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
         if (!subscript)
             return NULL;
 
@@ -4455,13 +4204,13 @@ static ast_expression *array_field_setter_node(
 
 static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast_value *index, size_t from, size_t afterend)
 {
-    lex_ctx ctx = ast_ctx(array);
+    lex_ctx_t ctx = ast_ctx(array);
 
     if (from+1 == afterend) {
         ast_return      *ret;
         ast_array_index *subscript;
 
-        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
         if (!subscript)
             return NULL;
 
@@ -4691,7 +4440,7 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
 static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
 static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
 {
-    lex_ctx     ctx;
+    lex_ctx_t     ctx;
     size_t      i;
     ast_value **params;
     ast_value  *param;
@@ -4705,6 +4454,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;
     }
@@ -4817,7 +4567,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
 {
     ast_expression *cexp;
     ast_value      *cval, *tmp;
-    lex_ctx ctx;
+    lex_ctx_t ctx;
 
     ctx = parser_ctx(parser);
 
@@ -4827,32 +4577,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);
@@ -4884,7 +4646,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
 static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef)
 {
     ast_value *var, *tmp;
-    lex_ctx    ctx;
+    lex_ctx_t    ctx;
 
     const char *name = NULL;
     bool        isfield  = false;
@@ -4972,6 +4734,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;
         }
@@ -4981,8 +4744,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 */
@@ -5001,8 +4766,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;
         }
     }
@@ -5011,11 +4775,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;
@@ -5099,6 +4864,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;
@@ -5114,7 +4948,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
     bool      cleanvar  = true;
     bool      wasarray  = false;
 
-    ast_member *me[3];
+    ast_member *me[3] = { NULL, NULL, NULL };
 
     if (!localblock && is_static)
         parseerror(parser, "`static` qualifier is not supported in global scope");
@@ -5246,7 +5080,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             {
                 /* deal with other globals */
                 old = parser_find_global(parser, var->name);
-                if (old && var->expression.vtype == TYPE_FUNCTION && old->expression.vtype == TYPE_FUNCTION)
+                if (old && var->expression.vtype == TYPE_FUNCTION && old->vtype == TYPE_FUNCTION)
                 {
                     /* This is a function which had a prototype */
                     if (!ast_istype(old, ast_value)) {
@@ -5354,7 +5188,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             if (var->expression.vtype == TYPE_VECTOR)
                 isvector = true;
             else if (var->expression.vtype == TYPE_FIELD &&
-                     var->expression.next->expression.vtype == TYPE_VECTOR)
+                     var->expression.next->vtype == TYPE_VECTOR)
                 isvector = true;
 
             if (isvector) {
@@ -5393,7 +5227,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             return false;
                         }
 
-                        if (var->expression.vtype != find->expression.vtype) {
+                        if (var->expression.vtype != find->vtype) {
                             char ty1[1024];
                             char ty2[1024];
 
@@ -5532,17 +5366,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
          * deal with arrays
          */
         if (var->expression.vtype == TYPE_ARRAY) {
-            char name[1024];
-            snprintf(name, sizeof(name), "%s##SET", var->name);
-            if (!parser_create_array_setter(parser, var, name))
-                goto cleanup;
-            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 &&
-                 var->expression.next->expression.vtype == TYPE_ARRAY)
+                 var->expression.next->vtype == TYPE_ARRAY)
         {
             char name[1024];
             ast_expression *telem;
@@ -5554,14 +5385,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 goto cleanup;
             }
 
-            snprintf(name, sizeof(name), "%s##SETF", var->name);
+            util_snprintf(name, sizeof(name), "%s##SETF", var->name);
             if (!parser_create_array_field_setter(parser, array, name))
                 goto cleanup;
 
             telem = ast_type_copy(ast_ctx(var), array->expression.next);
             tfield = ast_value_new(ast_ctx(var), "<.type>", TYPE_FIELD);
             tfield->expression.next = telem;
-            snprintf(name, sizeof(name), "%s##GETFP", var->name);
+            util_snprintf(name, sizeof(name), "%s##GETFP", var->name);
             if (!parser_create_array_getter(parser, array, (ast_expression*)tfield, name)) {
                 ast_delete(tfield);
                 goto cleanup;
@@ -5599,10 +5430,18 @@ skipvar:
             }
         }
 
-        if (parser->tok != '{') {
+        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)) {
@@ -5615,7 +5454,11 @@ skipvar:
         }
 
         if (parser->tok == '#') {
-            ast_function *func = NULL;
+            ast_function *func   = NULL;
+            ast_value    *number = NULL;
+            float         fractional;
+            float         integral;
+            int           builtin_num;
 
             if (localblock) {
                 parseerror(parser, "cannot declare builtins within functions");
@@ -5629,12 +5472,42 @@ skipvar:
                 parseerror(parser, "expected builtin number");
                 break;
             }
-            if (parser->tok != TOKEN_INTCONST) {
-                parseerror(parser, "builtin number must be an integer constant");
-                break;
-            }
-            if (parser_token(parser)->constval.i < 0) {
-                parseerror(parser, "builtin number must be an integer greater than zero");
+
+            if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS)) {
+                number = (ast_value*)parse_expression_leave(parser, true, false, false);
+                if (!number) {
+                    parseerror(parser, "builtin number expected");
+                    break;
+                }
+                if (!ast_istype(number, ast_value) || !number->hasvalue || number->cvq != CV_CONST)
+                {
+                    ast_unref(number);
+                    parseerror(parser, "builtin number must be a compile time constant");
+                    break;
+                }
+                if (number->expression.vtype == TYPE_INTEGER)
+                    builtin_num = number->constval.vint;
+                else if (number->expression.vtype == TYPE_FLOAT)
+                    builtin_num = number->constval.vfloat;
+                else {
+                    ast_unref(number);
+                    parseerror(parser, "builtin number must be an integer constant");
+                    break;
+                }
+                ast_unref(number);
+
+                fractional = modff(builtin_num, &integral);
+                if (builtin_num < 0 || fractional != 0) {
+                    parseerror(parser, "builtin number must be an integer greater than zero");
+                    break;
+                }
+
+                /* we only want the integral part anyways */
+                builtin_num = integral;
+            } else if (parser->tok == TOKEN_INTCONST) {
+                builtin_num = parser_token(parser)->constval.i;
+            } else {
+                parseerror(parser, "builtin number must be a compile time constant");
                 break;
             }
 
@@ -5653,10 +5526,13 @@ skipvar:
                 }
                 vec_push(parser->functions, func);
 
-                func->builtin = -parser_token(parser)->constval.i-1;
+                func->builtin = -builtin_num-1;
             }
 
-            if (!parser_next(parser)) {
+            if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS)
+                    ? (parser->tok != ',' && parser->tok != ';')
+                    : (!parser_next(parser)))
+            {
                 parseerror(parser, "expected comma or semicolon");
                 if (func)
                     ast_function_delete(func);
@@ -5664,7 +5540,21 @@ skipvar:
                 break;
             }
         }
-        else if (parser->tok == '{' || parser->tok == '[')
+        else if (var->expression.vtype == TYPE_ARRAY && parser->tok == '{')
+        {
+            if (localblock) {
+                /* Note that fteqcc and most others don't even *have*
+                 * local arrays, so this is not a high priority.
+                 */
+                parseerror(parser, "TODO: initializers for local arrays");
+                break;
+            }
+
+            var->hasvalue = true;
+            if (!parse_array(parser, var))
+                break;
+        }
+        else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '['))
         {
             if (localblock) {
                 parseerror(parser, "cannot declare functions within functions");
@@ -5737,6 +5627,7 @@ skipvar:
                 }
                 vec_free(sy.out);
                 vec_free(sy.ops);
+                vec_free(sy.argc);
                 var->cvq = cvq;
             }
         }
@@ -5866,7 +5757,7 @@ static uint16_t progdefs_crc_both(uint16_t old, const char *str)
     return old;
 }
 
-static void generate_checksum(parser_t *parser)
+static void generate_checksum(parser_t *parser, ir_builder *ir)
 {
     uint16_t   crc = 0xFFFF;
     size_t     i;
@@ -5907,7 +5798,7 @@ static void generate_checksum(parser_t *parser)
         if (!ast_istype(parser->fields[i], ast_value))
             continue;
         value = (ast_value*)(parser->fields[i]);
-        switch (value->expression.next->expression.vtype) {
+        switch (value->expression.next->vtype) {
             case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
             case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
             case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
@@ -5920,14 +5811,13 @@ static void generate_checksum(parser_t *parser)
         crc = progdefs_crc_both(crc, ";\n");
     }
     crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
-
-    code_crc = crc;
+    ir->code->crc = crc;
 }
 
 parser_t *parser_create()
 {
     parser_t *parser;
-    lex_ctx empty_ctx;
+    lex_ctx_t empty_ctx;
     size_t i;
 
     parser = (parser_t*)mem_a(sizeof(parser_t));
@@ -5955,14 +5845,13 @@ parser_t *parser_create()
 
     parser->aliases = util_htnew(PARSER_HT_SIZE);
 
-    parser->ht_imm_string = util_htnew(512);
-
     /* corrector */
     vec_push(parser->correct_variables, correct_trie_new());
     vec_push(parser->correct_variables_score, NULL);
 
-    empty_ctx.file = "<internal>";
-    empty_ctx.line = 0;
+    empty_ctx.file   = "<internal>";
+    empty_ctx.line   = 0;
+    empty_ctx.column = 0;
     parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL);
     parser->nil->cvq = CV_CONST;
     if (OPTS_FLAG(UNTYPED_NIL))
@@ -5984,10 +5873,11 @@ parser_t *parser_create()
         parser->reserved_version = NULL;
     }
 
+    parser->fold = fold_init(parser);
     return parser;
 }
 
-bool parser_compile(parser_t *parser)
+static bool parser_compile(parser_t *parser)
 {
     /* initial lexer/parser state */
     parser->lex->flags.noops = true;
@@ -5998,7 +5888,7 @@ 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);
@@ -6039,9 +5929,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;
@@ -6050,15 +5943,6 @@ void parser_cleanup(parser_t *parser)
     for (i = 0; i < vec_size(parser->functions); ++i) {
         ast_delete(parser->functions[i]);
     }
-    for (i = 0; i < vec_size(parser->imm_vector); ++i) {
-        ast_delete(parser->imm_vector[i]);
-    }
-    for (i = 0; i < vec_size(parser->imm_string); ++i) {
-        ast_delete(parser->imm_string[i]);
-    }
-    for (i = 0; i < vec_size(parser->imm_float); ++i) {
-        ast_delete(parser->imm_float[i]);
-    }
     for (i = 0; i < vec_size(parser->fields); ++i) {
         ast_delete(parser->fields[i]);
     }
@@ -6067,10 +5951,6 @@ void parser_cleanup(parser_t *parser)
     }
     vec_free(parser->accessors);
     vec_free(parser->functions);
-    vec_free(parser->imm_vector);
-    vec_free(parser->imm_string);
-    util_htdel(parser->ht_imm_string);
-    vec_free(parser->imm_float);
     vec_free(parser->globals);
     vec_free(parser->fields);
 
@@ -6087,7 +5967,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);
@@ -6109,10 +5988,17 @@ void parser_cleanup(parser_t *parser)
     ast_value_delete(parser->const_vec[1]);
     ast_value_delete(parser->const_vec[2]);
 
-    util_htdel(parser->aliases);
+    if (parser->reserved_version)
+        ast_value_delete(parser->reserved_version);
 
+    util_htdel(parser->aliases);
     intrin_intrinsics_destroy(parser);
+    fold_cleanup(parser->fold);
+}
 
+void parser_cleanup(parser_t *parser)
+{
+    parser_remove_ast(parser);
     mem_d(parser);
 }
 
@@ -6151,11 +6037,11 @@ bool parser_finish(parser_t *parser, const char *output)
             ast_expression *subtype;
             field->hasvalue = true;
             subtype = field->expression.next;
-            ifld = ir_builder_create_field(ir, field->name, subtype->expression.vtype);
-            if (subtype->expression.vtype == TYPE_FIELD)
-                ifld->fieldtype = subtype->expression.next->expression.vtype;
-            else if (subtype->expression.vtype == TYPE_FUNCTION)
-                ifld->outtype = subtype->expression.next->expression.vtype;
+            ifld = ir_builder_create_field(ir, field->name, subtype->vtype);
+            if (subtype->vtype == TYPE_FIELD)
+                ifld->fieldtype = subtype->next->vtype;
+            else if (subtype->vtype == TYPE_FUNCTION)
+                ifld->outtype = subtype->next->vtype;
             (void)!ir_value_set_field(field->ir_v, ifld);
         }
     }
@@ -6199,27 +6085,9 @@ bool parser_finish(parser_t *parser, const char *output)
         }
     }
     /* Now we can generate immediates */
-    for (i = 0; i < vec_size(parser->imm_float); ++i) {
-        if (!ast_global_codegen(parser->imm_float[i], ir, false)) {
-            con_out("failed to generate global %s\n", parser->imm_float[i]->name);
-            ir_builder_delete(ir);
-            return false;
-        }
-    }
-    for (i = 0; i < vec_size(parser->imm_string); ++i) {
-        if (!ast_global_codegen(parser->imm_string[i], ir, false)) {
-            con_out("failed to generate global %s\n", parser->imm_string[i]->name);
-            ir_builder_delete(ir);
-            return false;
-        }
-    }
-    for (i = 0; i < vec_size(parser->imm_vector); ++i) {
-        if (!ast_global_codegen(parser->imm_vector[i], ir, false)) {
-            con_out("failed to generate global %s\n", parser->imm_vector[i]->name);
-            ir_builder_delete(ir);
-            return false;
-        }
-    }
+    if (!fold_generate(parser->fold, ir))
+        return false;
+
     for (i = 0; i < vec_size(parser->globals); ++i) {
         ast_value *asvalue;
         if (!ast_istype(parser->globals[i], ast_value))
@@ -6243,7 +6111,7 @@ bool parser_finish(parser_t *parser, const char *output)
     }
     for (i = 0; i < vec_size(parser->fields); ++i) {
         ast_value *asvalue;
-        asvalue = (ast_value*)(parser->fields[i]->expression.next);
+        asvalue = (ast_value*)(parser->fields[i]->next);
 
         if (!ast_istype((ast_expression*)asvalue, ast_value))
             continue;
@@ -6269,6 +6137,9 @@ bool parser_finish(parser_t *parser, const char *output)
             return false;
         }
     }
+
+    generate_checksum(parser, ir);
+
     if (OPTS_OPTION_BOOL(OPTION_DUMP))
         ir_builder_dump(ir, con_out);
     for (i = 0; i < vec_size(parser->functions); ++i) {
@@ -6278,6 +6149,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");
@@ -6289,15 +6161,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(ir, output)) {
             con_out("*** failed to generate output file\n");
             ir_builder_delete(ir);
             return false;
         }
     }
-
     ir_builder_delete(ir);
     return retval;
 }