]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
For now
[xonotic/gmqcc.git] / parser.c
index 159c7c93dc35185b6a5b73391ff2b5fe75fb410e..0b7833717c099605211b8f485537f0a3b5dece19 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013
+ * Copyright (C) 2012, 2013, 2014, 2015
  *     Wolfgang Bumiller
  *     Dale Weiler
  *
 #include <string.h>
 #include <math.h>
 
-#include "gmqcc.h"
-#include "lexer.h"
-#include "ast.h"
+#include "parser.h"
 
-/* beginning of locals */
 #define PARSER_HT_LOCALS  2
-
 #define PARSER_HT_SIZE    512
 #define TYPEDEF_HT_SIZE   512
 
-typedef struct parser_s {
-    lex_file *lex;
-    int      tok;
-
-    bool     ast_cleaned;
-
-    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;
-    ht ht_imm_string_dotranslate;
-
-    /* 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;
-
-static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
-
 static void parser_enterblock(parser_t *parser);
 static bool parser_leaveblock(parser_t *parser);
 static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
@@ -123,7 +44,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
 static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname);
 static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
@@ -144,42 +65,6 @@ 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
  */
@@ -199,53 +84,8 @@ static 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->isimm    = 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 */
@@ -256,74 +96,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)
-{
-    ht ht_string = (dotranslate)
-        ? parser->ht_imm_string_dotranslate
-        : parser->ht_imm_string;
-
-    ast_value *out;
-    size_t     hash = util_hthash(ht_string, str);
-
-    if ((out = (ast_value*)util_htgeth(ht_string, str, hash)))
-        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];
-        util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
-        out = ast_value_new(parser_ctx(parser), name, TYPE_STRING);
-        out->expression.flags |= AST_FLAG_INCLUDE_DEF;
-    } else
-        out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING);
-
-    out->cvq      = CV_CONST;
-    out->hasvalue = true;
-    out->isimm    = true;
-    out->constval.vstring = parser_strdup(str);
-    vec_push(parser->imm_string, out);
-    util_htseth(ht_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->isimm    = 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);
@@ -338,7 +110,7 @@ static ast_expression* parser_find_label(parser_t *parser, const char *name)
     return NULL;
 }
 
-static ast_expression* parser_find_global(parser_t *parser, const char *name)
+ast_expression* parser_find_global(parser_t *parser, const char *name)
 {
     ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser));
     if (var)
@@ -400,9 +172,6 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t
     return NULL;
 }
 
-/* include intrinsics */
-#include "intrin.h"
-
 typedef struct
 {
     size_t etype; /* 0 = expression, others are operators */
@@ -410,7 +179,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 {
@@ -428,7 +197,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;
@@ -439,7 +208,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;
@@ -450,7 +219,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;
@@ -461,7 +230,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;
@@ -484,7 +253,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;
@@ -514,43 +283,39 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
-static bool immediate_is_true(lex_ctx ctx, ast_value *v)
+static bool check_write_to(lex_ctx_t ctx, ast_expression *expr)
 {
-    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)
+    if (ast_istype(expr, ast_value)) {
+        ast_value *val = (ast_value*)expr;
+        if (val->cvq == CV_CONST) {
+            if (val->name[0] == '#') {
+                compile_error(ctx, "invalid assignment to a literal constant");
                 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;
+            }
+            /*
+             * To work around quakeworld we must elide the error and make it
+             * a warning instead.
+             */
+            if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC)
+                compile_error(ctx, "assignment to constant `%s`", val->name);
+            else
+                (void)!compile_warning(ctx, WARN_CONST_OVERWRITE, "assignment to constant `%s`", val->name);
+            return false;
+        }
     }
+    return true;
 }
 
 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];
@@ -569,8 +334,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     ctx = vec_last(sy->ops).ctx;
 
     if (vec_size(sy->out) < op->operands) {
-        compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out),
-                      op->op, (int)op->id);
+        if (op->flags & OP_PREFIX)
+            compile_error(ctx, "expected expression after unary operator `%s`", op->op, (int)op->id);
+        else /* this should have errored previously already */
+            compile_error(ctx, "expected expression after operator `%s`", op->op, (int)op->id);
         return false;
     }
 
@@ -584,7 +351,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     for (i = 0; i < op->operands; ++i) {
         exprs[i]  = sy->out[vec_size(sy->out)+i].out;
         blocks[i] = sy->out[vec_size(sy->out)+i].block;
-        asvalue[i] = (ast_value*)exprs[i];
 
         if (exprs[i]->vtype == TYPE_NOEXPR &&
             !(i != 0 && op->id == opid2('?',':')) &&
@@ -606,14 +372,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 #define NotSameType(T) \
              (exprs[0]->vtype != exprs[1]->vtype || \
               exprs[0]->vtype != T)
-#define CanConstFold1(A) \
-             (ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\
-              (A)->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)
+
     switch (op->id)
     {
         default:
@@ -708,132 +467,100 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             out = exprs[0];
             break;
         case opid2('-','P'):
-            switch (exprs[0]->vtype) {
-                case TYPE_FLOAT:
-                    if (CanConstFold1(exprs[0]))
-                        out = (ast_expression*)parser_const_float(parser, -ConstF(0));
-                    else
-                        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
-                        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]->vtype]);
+            if ((out = fold_op(parser->fold, op, exprs)))
+                break;
+
+            if (exprs[0]->vtype != TYPE_FLOAT &&
+                exprs[0]->vtype != TYPE_VECTOR) {
+                    compile_error(ctx, "invalid types used in unary expression: cannot negate type %s",
+                                  type_name[exprs[0]->vtype]);
                 return false;
             }
+            if (exprs[0]->vtype == TYPE_FLOAT)
+                out = (ast_expression*)ast_unary_new(ctx, VINSTR_NEG_F, exprs[0]);
+            else
+                out = (ast_expression*)ast_unary_new(ctx, VINSTR_NEG_V, exprs[0]);
             break;
 
         case opid2('!','P'):
-            switch (exprs[0]->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]->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]->vtype != exprs[1]->vtype ||
-                (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
+               (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]->vtype],
                               type_name[exprs[1]->vtype]);
                 return false;
             }
-            switch (exprs[0]->vtype) {
-                case TYPE_FLOAT:
-                    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_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
-                        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]->vtype],
-                                  type_name[exprs[1]->vtype]);
-                    return false;
-            };
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                switch (exprs[0]->vtype) {
+                    case TYPE_FLOAT:
+                        out = fold_binary(ctx, INSTR_ADD_F, exprs[0], exprs[1]);
+                        break;
+                    case TYPE_VECTOR:
+                        out = fold_binary(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]->vtype],
+                                      type_name[exprs[1]->vtype]);
+                        return false;
+                }
+            }
             break;
         case opid1('-'):
-            if (exprs[0]->vtype != exprs[1]->vtype ||
-                (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->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]->vtype],
                               type_name[exprs[0]->vtype]);
                 return false;
             }
-            switch (exprs[0]->vtype) {
-                case TYPE_FLOAT:
-                    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_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
-                        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]->vtype],
-                                  type_name[exprs[0]->vtype]);
-                    return false;
-            };
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                switch (exprs[0]->vtype) {
+                    case TYPE_FLOAT:
+                        out = fold_binary(ctx, INSTR_SUB_F, exprs[0], exprs[1]);
+                        break;
+                    case TYPE_VECTOR:
+                        out = fold_binary(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]->vtype],
+                                      type_name[exprs[0]->vtype]);
+                        return false;
+                }
+            }
             break;
         case opid1('*'):
             if (exprs[0]->vtype != exprs[1]->vtype &&
@@ -848,104 +575,29 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               type_name[exprs[0]->vtype]);
                 return false;
             }
-            switch (exprs[0]->vtype) {
-                case TYPE_FLOAT:
-                    if (exprs[1]->vtype == TYPE_VECTOR)
-                    {
-                        if (CanConstFold(exprs[0], exprs[1]))
-                            out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(1), ConstF(0)));
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                switch (exprs[0]->vtype) {
+                    case TYPE_FLOAT:
+                        if (exprs[1]->vtype == TYPE_VECTOR)
+                            out = fold_binary(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
                         else
-                            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]->vtype == TYPE_FLOAT)
-                    {
-                        if (CanConstFold(exprs[0], exprs[1]))
-                            out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), ConstF(1)));
-                        else
-                            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->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->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->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->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->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->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]);
-                        }
+                            out = fold_binary(ctx, INSTR_MUL_F, exprs[0], exprs[1]);
+                        break;
+                    case TYPE_VECTOR:
+                        if (exprs[1]->vtype == TYPE_FLOAT)
+                            out = fold_binary(ctx, INSTR_MUL_VF, 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]->vtype],
-                                  type_name[exprs[0]->vtype]);
-                    return false;
-            };
+                            out = fold_binary(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]->vtype],
+                                      type_name[exprs[0]->vtype]);
+                        return false;
+                }
+            }
             break;
+
         case opid1('/'):
             if (exprs[1]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
@@ -953,37 +605,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 compile_error(ctx, "invalid types used in expression: cannot divide types %s and %s", ty1, ty2);
                 return false;
             }
-            if (exprs[0]->vtype == TYPE_FLOAT) {
-                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_DIV_F, exprs[0], exprs[1]);
-            }
-            else if (exprs[0]->vtype == TYPE_VECTOR) {
-                if (CanConstFold(exprs[0], exprs[1]))
-                    out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), 1.0/ConstF(1)));
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                if (exprs[0]->vtype == TYPE_FLOAT)
+                    out = fold_binary(ctx, INSTR_DIV_F, exprs[0], exprs[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 types %s and %s", ty1, ty2);
-                return false;
-            }
             break;
 
         case opid1('%'):
@@ -992,13 +623,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     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_expression *mod  = intrin_func(parser->intrin, "mod");
                 ast_call       *call = NULL;
                 if (!mod) return false; /* can return null for missing floor */
 
@@ -1016,59 +643,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         case opid1('|'):
         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]->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
-                out = (ast_expression*)ast_binary_new(ctx,
-                    (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
-                    exprs[0], exprs[1]);
-            break;
         case opid1('^'):
-            /*
-             * 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))
@@ -1079,115 +654,84 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 return false;
             }
 
-            /*
-             * IF the first expression is float, the following will be too
-             * since scalar ^ vector is not allowed.
-             */
-            if (exprs[0]->vtype == TYPE_FLOAT) {
-                if(CanConstFold(exprs[0], exprs[1])) {
-                    out = (ast_expression*)parser_const_float(parser, (float)((qcint)(ConstF(0)) ^ ((qcint)(ConstF(1)))));
-                } else {
-                    ast_binary *expr = ast_binary_new(
-                        ctx,
-                        INSTR_SUB_F,
-                        (ast_expression*)parser_const_float_neg1(parser),
-                        (ast_expression*)ast_binary_new(
-                            ctx,
-                            INSTR_BITAND,
-                            exprs[0],
-                            exprs[1]
-                        )
-                    );
-                    expr->refs = AST_REF_NONE;
-                    
-                    out = (ast_expression*)
-                        ast_binary_new(
-                            ctx,
-                            INSTR_BITAND,
-                            (ast_expression*)ast_binary_new(
-                                ctx,
-                                INSTR_BITOR,
-                                exprs[0],
-                                exprs[1]
-                            ),
-                            (ast_expression*)expr
-                        );
-                }
-            } else {
+            if (!(out = fold_op(parser->fold, op, exprs))) {
                 /*
-                 * The first is a vector: vector is allowed to xor with vector and
-                 * with scalar, branch here for the second operand.
+                 * IF the first expression is float, the following will be too
+                 * since scalar ^ vector is not allowed.
                  */
-                if (exprs[1]->vtype == TYPE_VECTOR) {
-                    /*
-                     * Xor all the values of the vector components against the
-                     * vectors components in question.
-                     */
-                    if (CanConstFold(exprs[0], exprs[1])) {
-                        out = (ast_expression*)parser_const_vector_f(
-                            parser,
-                            (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstV(1).x))),
-                            (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstV(1).y))),
-                            (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstV(1).z)))
-                        );
-                    } else {
-                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
-                        return false;
-                    }
+                if (exprs[0]->vtype == TYPE_FLOAT) {
+                    out = fold_binary(ctx,
+                        (op->id == opid1('^') ? VINSTR_BITXOR : op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
+                        exprs[0], exprs[1]);
                 } else {
                     /*
-                     * Xor all the values of the vector components against the
-                     * scalar in question.
+                     * The first is a vector: vector is allowed to bitop with vector and
+                     * with scalar, branch here for the second operand.
                      */
-                    if (CanConstFold(exprs[0], exprs[1])) {
-                        out = (ast_expression*)parser_const_vector_f(
-                            parser,
-                            (float)(((qcint)(ConstV(0).x)) ^ ((qcint)(ConstF(1)))),
-                            (float)(((qcint)(ConstV(0).y)) ^ ((qcint)(ConstF(1)))),
-                            (float)(((qcint)(ConstV(0).z)) ^ ((qcint)(ConstF(1))))
-                        );
+                    if (exprs[1]->vtype == TYPE_VECTOR) {
+                        /*
+                         * Bitop all the values of the vector components against the
+                         * vectors components in question.
+                         */
+                        out = fold_binary(ctx,
+                            (op->id == opid1('^') ? VINSTR_BITXOR_V : op->id == opid1('|') ? VINSTR_BITOR_V : VINSTR_BITAND_V),
+                            exprs[0], exprs[1]);
                     } else {
-                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
-                        return false;
+                        out = fold_binary(ctx,
+                            (op->id == opid1('^') ? VINSTR_BITXOR_VF : op->id == opid1('|') ? VINSTR_BITOR_VF : VINSTR_BITAND_VF),
+                            exprs[0], exprs[1]);
                     }
                 }
             }
-                
             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)((unsigned int)(ConstF(0)) << (unsigned int)(ConstF(1))));
-                else
-                    out = (ast_expression*)parser_const_float(parser, (double)((unsigned int)(ConstF(0)) >> (unsigned int)(ConstF(1))));
-                break;
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform shift 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))) {
+                ast_expression *shift = intrin_func(parser->intrin, (op->id == opid2('<','<')) ? "__builtin_lshift" : "__builtin_rshift");
+                ast_call       *call  = ast_call_new(parser_ctx(parser), shift);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+                out = (ast_expression*)call;
             }
+            break;
+
         case opid3('<','<','='):
         case opid3('>','>','='):
-            compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
-            return false;
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform shift operation 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))) {
+                ast_expression *shift = intrin_func(parser->intrin, (op->id == opid3('<','<','=')) ? "__builtin_lshift" : "__builtin_rshift");
+                ast_call       *call  = ast_call_new(parser_ctx(parser), shift);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+                out = (ast_expression*)ast_store_new(
+                    parser_ctx(parser),
+                    INSTR_STORE_F,
+                    exprs[0],
+                    (ast_expression*)call
+                );
+            }
+
+            break;
 
         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));
@@ -1218,7 +762,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         }
                     }
                 }
-                out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
+                out = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
             }
             break;
 
@@ -1234,9 +778,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;
 
@@ -1246,20 +788,37 @@ 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 {
-                ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow"));
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser->intrin, "pow"));
                 vec_push(gencall->params, exprs[0]);
                 vec_push(gencall->params, exprs[1]);
                 out = (ast_expression*)gencall;
             }
             break;
 
+        case opid2('>', '<'):
+            if (NotSameType(TYPE_VECTOR)) {
+                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 cross product: %s and %s",
+                    ty1, ty2);
+                return false;
+            }
+
+            if (!(out = fold_op(parser->fold, op, exprs))) {
+                out = fold_binary(
+                        parser_ctx(parser),
+                        VINSTR_CROSS,
+                        exprs[0],
+                        exprs[1]
+                );
+            }
+
+            break;
+
         case opid3('<','=','>'): /* -1, 0, or 1 */
             if (NotSameType(TYPE_FLOAT)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
@@ -1270,14 +829,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 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))) {
+                /* This whole block is NOT fold_binary safe */
                 ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
 
                 eq->refs = AST_REF_NONE;
@@ -1286,15 +839,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 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]
                         /* } */
                         )
                     /* } */
@@ -1317,7 +870,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               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 = fold_binary(ctx, generated_op, exprs[0], exprs[1]);
             break;
         case opid2('!', '='):
             if (exprs[0]->vtype != exprs[1]->vtype) {
@@ -1326,7 +880,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+            if (!(out = fold_op(parser->fold, op, exprs)))
+                out = fold_binary(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
             break;
         case opid2('=', '='):
             if (exprs[0]->vtype != exprs[1]->vtype) {
@@ -1335,7 +890,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+            if (!(out = fold_op(parser->fold, op, exprs)))
+                out = fold_binary(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
             break;
 
         case opid1('='):
@@ -1396,9 +952,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
+            /* When we're a vector of part of an entity field we use STOREP */
+            if (ast_istype(exprs[0], ast_member) && ast_istype(((ast_member*)exprs[0])->owner, ast_entfield))
+                assignop = INSTR_STOREP_F;
             out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]);
             break;
         case opid3('+','+','P'):
@@ -1413,17 +970,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 addop = INSTR_ADD_F;
             else
                 addop = INSTR_SUB_F;
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ast_ctx(exprs[0]), exprs[0]);
             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','+','+'):
@@ -1441,23 +996,22 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 addop = INSTR_SUB_F;
                 subop = INSTR_ADD_F;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ast_ctx(exprs[0]), exprs[0]);
             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));
+            out = fold_binary(ctx, subop,
+                              out,
+                              (ast_expression*)parser->fold->imm_float[1]);
+
             break;
         case opid2('+','='):
         case opid2('-','='):
@@ -1470,9 +1024,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
@@ -1507,9 +1059,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
@@ -1525,14 +1075,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),
-                                                                  exprs[1]);
-                        }
+                        out = fold_binary(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;
@@ -1550,30 +1095,34 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
         case opid2('&','='):
         case opid2('|','='):
-            if (NotSameType(TYPE_FLOAT)) {
+        case opid2('^','='):
+            if (NotSameType(TYPE_FLOAT) && NotSameType(TYPE_VECTOR)) {
                 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: %s and %s",
                               ty1, ty2);
                 return false;
             }
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
+            (void)check_write_to(ctx, exprs[0]);
             if (ast_istype(exprs[0], ast_entfield))
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
                 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]);
+            if (exprs[0]->vtype == TYPE_FLOAT)
+                out = (ast_expression*)ast_binstore_new(ctx, assignop,
+                                                        (op->id == opid2('^','=') ? VINSTR_BITXOR : op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
+                                                        exprs[0], exprs[1]);
+            else
+                out = (ast_expression*)ast_binstore_new(ctx, assignop,
+                                                        (op->id == opid2('^','=') ? VINSTR_BITXOR_V : op->id == opid2('&','=') ? VINSTR_BITAND_V : VINSTR_BITOR_V),
+                                                        exprs[0], exprs[1]);
             break;
         case opid3('&','~','='):
             /* This is like: a &= ~(b);
              * But QC has no bitwise-not, so we implement it as
              * a -= a & (b);
              */
-            if (NotSameType(TYPE_FLOAT)) {
+            if (NotSameType(TYPE_FLOAT) && NotSameType(TYPE_VECTOR)) {
                 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: %s and %s",
@@ -1584,33 +1133,53 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 assignop = type_storep_instr[exprs[0]->vtype];
             else
                 assignop = type_store_instr[exprs[0]->vtype];
-            out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+            if (exprs[0]->vtype == TYPE_FLOAT)
+                out = fold_binary(ctx, INSTR_BITAND, exprs[0], exprs[1]);
+            else
+                out = fold_binary(ctx, VINSTR_BITAND_V, exprs[0], exprs[1]);
             if (!out)
                 return false;
-            if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
-            }
-            asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
+            (void)check_write_to(ctx, exprs[0]);
+            if (exprs[0]->vtype == TYPE_FLOAT)
+                asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
+            else
+                asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_V, exprs[0], out);
             asbinstore->keep_dest = true;
             out = (ast_expression*)asbinstore;
             break;
 
+        case opid3('l', 'e', 'n'):
+            if (exprs[0]->vtype != TYPE_STRING && exprs[0]->vtype != TYPE_ARRAY) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                compile_error(ast_ctx(exprs[0]), "invalid type for length operator: %s", ty1);
+                return false;
+            }
+            /* strings must be const, arrays are statically sized */
+            if (exprs[0]->vtype == TYPE_STRING &&
+                !(((ast_value*)exprs[0])->hasvalue && ((ast_value*)exprs[0])->cvq == CV_CONST))
+            {
+                compile_error(ast_ctx(exprs[0]), "operand of length operator not a valid constant expression");
+                return false;
+            }
+            out = fold_op(parser->fold, op, exprs);
+            break;
+
         case opid2('~', 'P'):
-            if (exprs[0]->vtype != TYPE_FLOAT) {
+            if (exprs[0]->vtype != TYPE_FLOAT && exprs[0]->vtype != TYPE_VECTOR) {
                 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))) {
+                if (exprs[0]->vtype == TYPE_FLOAT) {
+                    out = fold_binary(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
+                } else {
+                    out = fold_binary(ctx, INSTR_SUB_V, (ast_expression*)parser->fold->imm_vector[1], exprs[0]);
+                }
+            }
             break;
     }
 #undef NotSameType
-
     if (!out) {
         compile_error(ctx, "failed to apply operator %s", op->op);
         return false;
@@ -1629,6 +1198,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
 
     size_t          fid;
     size_t          paramcount, i;
+    bool            fold = true;
 
     fid = vec_last(sy->ops).off;
     vec_shrinkby(sy->ops, 1);
@@ -1652,9 +1222,11 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
         return false;
     }
 
-    fun = sy->out[fid].out;
-
-    if (fun == intrinsic_debug_typestring) {
+    /*
+     * TODO handle this at the intrinsic level with an ast_intrinsic
+     * node and codegen.
+     */
+    if ((fun = sy->out[fid].out) == intrin_debug_typestring(parser->intrin)) {
         char ty[1024];
         if (fid+2 != vec_size(sy->out) ||
             vec_last(sy->out).block)
@@ -1665,18 +1237,63 @@ 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;
     }
 
+    /*
+     * Now we need to determine if the function that is being called is
+     * an intrinsic so we can evaluate if the arguments to it are constant
+     * and than fruitfully fold them.
+     */
+#define fold_can_1(X)  \
+    (ast_istype(((ast_expression*)(X)), ast_value) && (X)->hasvalue && ((X)->cvq == CV_CONST) && \
+                ((ast_expression*)(X))->vtype != TYPE_FUNCTION)
+
+    if (fid + 1 < vec_size(sy->out))
+        ++paramcount;
+
+    for (i = 0; i < paramcount; ++i) {
+        if (!fold_can_1((ast_value*)sy->out[fid + 1 + i].out)) {
+            fold = false;
+            break;
+        }
+    }
+
+    /*
+     * All is well which ends well, if we make it into here we can ignore the
+     * intrinsic call and just evaluate it i.e constant fold it.
+     */
+    if (fold && ast_istype(fun, ast_value) && ((ast_value*)fun)->intrinsic) {
+        ast_expression **exprs  = NULL;
+        ast_expression *foldval = NULL;
+
+        for (i = 0; i < paramcount; i++)
+            vec_push(exprs, sy->out[fid+1 + i].out);
+
+        if (!(foldval = intrin_fold(parser->intrin, (ast_value*)fun, exprs))) {
+            vec_free(exprs);
+            goto fold_leave;
+        }
+
+        /*
+         * Blub: what sorts of unreffing and resizing of
+         * sy->out should I be doing here?
+         */
+        sy->out[fid] = syexp(foldval->node.context, foldval);
+        vec_shrinkby(sy->out, paramcount);
+        vec_free(exprs);
+
+        return true;
+    }
+
+    fold_leave:
     call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun);
+
     if (!call)
         return false;
 
-    if (fid+1 < vec_size(sy->out))
-        ++paramcount;
-
     if (fid+1 + paramcount != vec_size(sy->out)) {
         parseerror(parser, "internal error: parameter count mismatch: (%lu+1+%lu), %lu",
                    (unsigned long)fid, (unsigned long)paramcount, (unsigned long)vec_size(sy->out));
@@ -1695,7 +1312,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
         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, false);
         }
     }
 
@@ -1823,7 +1440,7 @@ static ast_expression* parse_vararg_do(parser_t *parser)
     ast_expression *idx, *out;
     ast_value      *typevar;
     ast_value      *funtype = parser->function->vtype;
-    lex_ctx         ctx     = parser_ctx(parser);
+    lex_ctx_t         ctx     = parser_ctx(parser);
 
     if (!parser->function->varargs) {
         parseerror(parser, "function has no variable argument list");
@@ -1860,7 +1477,7 @@ static ast_expression* parse_vararg_do(parser_t *parser)
         return NULL;
     }
 
-    typevar = parse_typename(parser, NULL, NULL);
+    typevar = parse_typename(parser, NULL, NULL, NULL);
     if (!typevar) {
         ast_unref(idx);
         return NULL;
@@ -1905,8 +1522,7 @@ static ast_expression* parse_vararg(parser_t *parser)
 }
 
 /* not to be exposed */
-extern bool ftepp_predef_exists(const char *name);
-
+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) &&
@@ -1926,7 +1542,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));
@@ -1951,35 +1567,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), 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_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), 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_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)
@@ -1990,9 +1602,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
         /* a_vector.{x,y,z} */
         if (!vec_size(sy->ops) ||
             !vec_last(sy->ops).etype ||
-            operators[vec_last(sy->ops).etype-1].id != opid1('.') ||
-            (prev >= intrinsic_debug_typestring &&
-             prev <= intrinsic_debug_typestring))
+            operators[vec_last(sy->ops).etype-1].id != opid1('.'))
         {
             /* When adding more intrinsics, fix the above condition */
             prev = NULL;
@@ -2014,20 +1624,34 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
             }
         }
         if (!var && !strcmp(parser_tokval(parser), "__FUNC__"))
-            var = (ast_expression*)parser_const_string(parser, parser->function->name, false);
+            var = (ast_expression*)fold_constgen_string(parser->fold, parser->function->name, false);
         if (!var) {
-            /* intrinsics */
-            if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
-                var = (ast_expression*)intrinsic_debug_typestring;
-            }
-            /* now we try for the real intrinsic hashtable. If the string
+            /*
+             * now we try for the real intrinsic hashtable. If the string
              * begins with __builtin, we simply skip past it, otherwise we
              * use the identifier as is.
              */
-            else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
-                var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */);
+            if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
+                var = intrin_func(parser->intrin, parser_tokval(parser));
             }
 
+            /*
+             * Try it again, intrin_func deals with the alias method as well
+             * the first one masks for __builtin though, we emit warning here.
+             */
+            if (!var) {
+                if ((var = intrin_func(parser->intrin, parser_tokval(parser)))) {
+                    (void)!compile_warning(
+                        parser_ctx(parser),
+                        WARN_BUILTINS,
+                        "using implicitly defined builtin `__builtin_%s' for `%s'",
+                        parser_tokval(parser),
+                        parser_tokval(parser)
+                    );
+                }
+            }
+
+
             if (!var) {
                 char *correct = NULL;
                 size_t i;
@@ -2101,7 +1725,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
     /* only warn once about an assignment in a truth value because the current code
      * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
      */
-    bool warn_truthvalue = true;
+    bool warn_parenthesis = true;
 
     /* count the parens because an if starts with one, so the
      * end of a condition is an unmatched closing paren
@@ -2136,7 +1760,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 }
             }
             if (o == operator_count) {
-                compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser));
+                compile_error(parser_ctx(parser), "unexpected operator: %s", parser_tokval(parser));
                 goto onerr;
             }
             /* found an operator */
@@ -2164,6 +1788,17 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
                 olast = &operators[vec_last(sy.ops).etype-1];
 
+            /* first only apply higher precedences, assoc_left+equal comes after we warn about precedence rules */
+            while (olast && op->prec < olast->prec)
+            {
+                if (!parser_sy_apply_operator(parser, &sy))
+                    goto onerr;
+                if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
+                    olast = &operators[vec_last(sy.ops).etype-1];
+                else
+                    olast = NULL;
+            }
+
 #define IsAssignOp(x) (\
                 (x) == opid1('=') || \
                 (x) == opid2('+','=') || \
@@ -2175,14 +1810,29 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 (x) == opid2('|','=') || \
                 (x) == opid3('&','~','=') \
                 )
-            if (warn_truthvalue) {
+            if (warn_parenthesis) {
                 if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
                      (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
                      (truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id))
                    )
                 {
                     (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
-                    warn_truthvalue = false;
+                    warn_parenthesis = false;
+                }
+
+                if (olast && olast->id != op->id) {
+                    if ((op->id    == opid1('&') || op->id    == opid1('|') || op->id    == opid1('^')) &&
+                        (olast->id == opid1('&') || olast->id == opid1('|') || olast->id == opid1('^')))
+                    {
+                        (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around bitwise operations");
+                        warn_parenthesis = false;
+                    }
+                    else if ((op->id    == opid2('&','&') || op->id    == opid2('|','|')) &&
+                             (olast->id == opid2('&','&') || olast->id == opid2('|','|')))
+                    {
+                        (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around logical operations");
+                        warn_parenthesis = false;
+                    }
                 }
             }
 
@@ -2314,7 +1964,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     {
                         char *newstr = NULL;
                         util_asprintf(&newstr, "%s%s", last->constval.vstring, parser_tokval(parser));
-                        vec_last(sy.out).out = (ast_expression*)parser_const_string(parser, newstr, false);
+                        vec_last(sy.out).out = (ast_expression*)fold_constgen_string(parser->fold, newstr, false);
                         mem_d(newstr);
                         concatenated = true;
                     }
@@ -2344,7 +1994,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 
     parser->lex->flags.noops = true;
     if (vec_size(sy.out) != 1) {
-        parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out));
+        parseerror(parser, "expression expected");
         expr = NULL;
     } else
         expr = sy.out[0].out;
@@ -2538,7 +2188,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 */
 
@@ -2677,7 +2327,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 */
 
@@ -2778,7 +2428,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 */
 
@@ -2903,7 +2553,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     bool ifnot  = false;
 
-    lex_ctx ctx = parser_ctx(parser);
+    lex_ctx_t ctx = parser_ctx(parser);
 
     parser_enterblock(parser);
 
@@ -2938,16 +2588,17 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
         initexpr = parse_expression_leave(parser, false, false, false);
         if (!initexpr)
             goto onerr;
-    }
 
-    /* move on to condition */
-    if (parser->tok != ';') {
-        parseerror(parser, "expected semicolon after for-loop initializer");
-        goto onerr;
-    }
-    if (!parser_next(parser)) {
-        parseerror(parser, "expected for-loop condition");
-        goto onerr;
+        /* move on to condition */
+        if (parser->tok != ';') {
+            parseerror(parser, "expected semicolon after for-loop initializer");
+            goto onerr;
+        }
+
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected for-loop condition");
+            goto onerr;
+        }
     }
 
     /* parse the condition */
@@ -2969,7 +2620,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;
@@ -3021,7 +2672,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
     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 */
 
@@ -3120,7 +2771,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 */
@@ -3172,6 +2823,11 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
 /* returns true when it was a variable qualifier, false otherwise!
  * on error, cvq is set to CV_WRONG
  */
+typedef struct {
+    const char *name;
+    size_t      flag;
+} attribute_t;
+
 static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message)
 {
     bool had_const    = false;
@@ -3181,8 +2837,18 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
     bool had_static   = false;
     uint32_t flags    = 0;
 
-    *cvq = CV_NONE;
+    static attribute_t attributes[] = {
+        { "noreturn",   AST_FLAG_NORETURN   },
+        { "inline",     AST_FLAG_INLINE     },
+        { "eraseable",  AST_FLAG_ERASEABLE  },
+        { "accumulate", AST_FLAG_ACCUMULATE },
+        { "last",       AST_FLAG_FINAL_DECL }
+    };
+
+   *cvq = CV_NONE;
+
     for (;;) {
+        size_t i;
         if (parser->tok == TOKEN_ATTRIBUTE_OPEN) {
             had_attrib = true;
             /* parse an attribute */
@@ -3191,15 +2857,25 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                 *cvq = CV_WRONG;
                 return false;
             }
-            if (!strcmp(parser_tokval(parser), "noreturn")) {
-                flags |= AST_FLAG_NORETURN;
-                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
-                    parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`");
-                    *cvq = CV_WRONG;
-                    return false;
+
+            for (i = 0; i < GMQCC_ARRAY_COUNT(attributes); i++) {
+                if (!strcmp(parser_tokval(parser), attributes[i].name)) {
+                    flags |= attributes[i].flag;
+                    if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                        parseerror(parser, "`%s` attribute has no parameters, expected `]]`",
+                            attributes[i].name);
+                        *cvq = CV_WRONG;
+                        return false;
+                    }
+                    break;
                 }
             }
-            else if (!strcmp(parser_tokval(parser), "noref")) {
+
+            if (i != GMQCC_ARRAY_COUNT(attributes))
+                goto leave;
+
+
+            if (!strcmp(parser_tokval(parser), "noref")) {
                 had_noref = true;
                 if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
                     parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
@@ -3207,14 +2883,6 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
-            else if (!strcmp(parser_tokval(parser), "inline")) {
-                flags |= AST_FLAG_INLINE;
-                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
-                    parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
-                    *cvq = CV_WRONG;
-                    return false;
-                }
-            }
             else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
                 flags   |= AST_FLAG_ALIAS;
                 *message = NULL;
@@ -3296,6 +2964,47 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "coverage") && !(flags & AST_FLAG_COVERAGE)) {
+                flags |= AST_FLAG_COVERAGE;
+                if (!parser_next(parser)) {
+                    error_in_coverage:
+                    parseerror(parser, "parse error in coverage attribute");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+                if (parser->tok == '(') {
+                    if (!parser_next(parser)) {
+                        bad_coverage_arg:
+                        parseerror(parser, "invalid parameter for coverage() attribute\n"
+                                           "valid are: block");
+                        *cvq = CV_WRONG;
+                        return false;
+                    }
+                    if (parser->tok != ')') {
+                        do {
+                            if (parser->tok != TOKEN_IDENT)
+                                goto bad_coverage_arg;
+                            if (!strcmp(parser_tokval(parser), "block"))
+                                flags |= AST_FLAG_BLOCK_COVERAGE;
+                            else if (!strcmp(parser_tokval(parser), "none"))
+                                flags &= ~(AST_FLAG_COVERAGE_MASK);
+                            else
+                                goto bad_coverage_arg;
+                            if (!parser_next(parser))
+                                goto error_in_coverage;
+                            if (parser->tok == ',') {
+                                if (!parser_next(parser))
+                                    goto error_in_coverage;
+                            }
+                        } while (parser->tok != ')');
+                    }
+                    if (parser->tok != ')' || !parser_next(parser))
+                        goto error_in_coverage;
+                } else {
+                    /* without parameter [[coverage]] equals [[coverage(block)]] */
+                    flags |= AST_FLAG_BLOCK_COVERAGE;
+                }
+            }
             else
             {
                 /* Skip tokens until we hit a ]] */
@@ -3324,6 +3033,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
         }
         else
             break;
+
+        leave:
         if (!parser_next(parser))
             goto onerr;
     }
@@ -3407,7 +3118,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;
@@ -3451,7 +3162,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
         if (parser->tok == TOKEN_IDENT)
             typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
         if (typevar || parser->tok == TOKEN_TYPENAME) {
-            if (!parse_variable(parser, block, false, CV_NONE, typevar, false, false, 0, NULL)) {
+            if (!parse_variable(parser, block, true, CV_NONE, typevar, false, false, 0, NULL)) {
                 ast_delete(switchnode);
                 return false;
             }
@@ -3463,7 +3174,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
                 ast_delete(switchnode);
                 return false;
             }
-            if (!parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, NULL)) {
+            if (!parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, NULL)) {
                 ast_delete(switchnode);
                 return false;
             }
@@ -3708,7 +3419,13 @@ static bool parse_pragma_do(parser_t *parser)
     else
     {
         (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser));
-        return false;
+
+        /* skip to eol */
+        while (!parse_eol(parser)) {
+            parser_next(parser);
+        }
+
+        return true;
     }
 
     return true;
@@ -3746,7 +3463,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     if (parser->tok == TOKEN_IDENT)
         typevar = parser_find_typedef(parser, parser_tokval(parser), 0);
 
-    if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
+    if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS)
     {
         /* local variable */
         if (!block) {
@@ -3765,7 +3482,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, block, true, cvq, NULL, noref, is_static, qflags, vstring);
+        return parse_variable(parser, block, false, cvq, NULL, noref, is_static, qflags, vstring);
     }
     else if (parser->tok == TOKEN_KEYWORD)
     {
@@ -3919,7 +3636,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;
@@ -3936,7 +3653,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;
@@ -4318,85 +4035,109 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     }
 
     if (has_frame_think) {
-        lex_ctx ctx;
-        ast_expression *self_frame;
-        ast_expression *self_nextthink;
-        ast_expression *self_think;
-        ast_expression *time_plus_1;
-        ast_store *store_frame;
-        ast_store *store_nextthink;
-        ast_store *store_think;
-
-        ctx = parser_ctx(parser);
-        self_frame     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_frame);
-        self_nextthink = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_nextthink);
-        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));
-
-        if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
-            if (self_frame)     ast_delete(self_frame);
-            if (self_nextthink) ast_delete(self_nextthink);
-            if (self_think)     ast_delete(self_think);
-            if (time_plus_1)    ast_delete(time_plus_1);
-            retval = false;
-        }
-
-        if (retval)
-        {
-            store_frame     = ast_store_new(ctx, INSTR_STOREP_F,   self_frame,     framenum);
-            store_nextthink = ast_store_new(ctx, INSTR_STOREP_F,   self_nextthink, time_plus_1);
-            store_think     = ast_store_new(ctx, INSTR_STOREP_FNC, self_think,     nextthink);
-
-            if (!store_frame) {
-                ast_delete(self_frame);
-                retval = false;
-            }
-            if (!store_nextthink) {
-                ast_delete(self_nextthink);
-                retval = false;
-            }
-            if (!store_think) {
-                ast_delete(self_think);
-                retval = false;
+        if (!OPTS_FLAG(EMULATE_STATE)) {
+            ast_state *state_op = ast_state_new(parser_ctx(parser), framenum, nextthink);
+            if (!ast_block_add_expr(block, (ast_expression*)state_op)) {
+                parseerror(parser, "failed to generate state op for [frame,think]");
+                ast_unref(nextthink);
+                ast_unref(framenum);
+                ast_delete(block);
+                return false;
             }
-            if (!retval) {
-                if (store_frame)     ast_delete(store_frame);
-                if (store_nextthink) ast_delete(store_nextthink);
-                if (store_think)     ast_delete(store_think);
+        } else {
+            /* emulate OP_STATE in code: */
+            lex_ctx_t ctx;
+            ast_expression *self_frame;
+            ast_expression *self_nextthink;
+            ast_expression *self_think;
+            ast_expression *time_plus_1;
+            ast_store *store_frame;
+            ast_store *store_nextthink;
+            ast_store *store_think;
+
+            float frame_delta = 1.0f / (float)OPTS_OPTION_U32(OPTION_STATE_FPS);
+
+            ctx = parser_ctx(parser);
+            self_frame     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_frame);
+            self_nextthink = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_nextthink);
+            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*)fold_constgen_float(parser->fold, frame_delta, false));
+
+            if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
+                if (self_frame)     ast_delete(self_frame);
+                if (self_nextthink) ast_delete(self_nextthink);
+                if (self_think)     ast_delete(self_think);
+                if (time_plus_1)    ast_delete(time_plus_1);
                 retval = false;
             }
-            if (!ast_block_add_expr(block, (ast_expression*)store_frame) ||
-                !ast_block_add_expr(block, (ast_expression*)store_nextthink) ||
-                !ast_block_add_expr(block, (ast_expression*)store_think))
+
+            if (retval)
             {
-                retval = false;
+                store_frame     = ast_store_new(ctx, INSTR_STOREP_F,   self_frame,     framenum);
+                store_nextthink = ast_store_new(ctx, INSTR_STOREP_F,   self_nextthink, time_plus_1);
+                store_think     = ast_store_new(ctx, INSTR_STOREP_FNC, self_think,     nextthink);
+
+                if (!store_frame) {
+                    ast_delete(self_frame);
+                    retval = false;
+                }
+                if (!store_nextthink) {
+                    ast_delete(self_nextthink);
+                    retval = false;
+                }
+                if (!store_think) {
+                    ast_delete(self_think);
+                    retval = false;
+                }
+                if (!retval) {
+                    if (store_frame)     ast_delete(store_frame);
+                    if (store_nextthink) ast_delete(store_nextthink);
+                    if (store_think)     ast_delete(store_think);
+                    retval = false;
+                }
+                if (!ast_block_add_expr(block, (ast_expression*)store_frame) ||
+                    !ast_block_add_expr(block, (ast_expression*)store_nextthink) ||
+                    !ast_block_add_expr(block, (ast_expression*)store_think))
+                {
+                    retval = false;
+                }
             }
-        }
 
-        if (!retval) {
-            parseerror(parser, "failed to generate code for [frame,think]");
-            ast_unref(nextthink);
-            ast_unref(framenum);
-            ast_delete(block);
-            return false;
+            if (!retval) {
+                parseerror(parser, "failed to generate code for [frame,think]");
+                ast_unref(nextthink);
+                ast_unref(framenum);
+                ast_delete(block);
+                return false;
+            }
         }
     }
 
     if (var->hasvalue) {
-        parseerror(parser, "function `%s` declared with multiple bodies", var->name);
-        ast_block_delete(block);
-        goto enderr;
-    }
+        if (!(var->expression.flags & AST_FLAG_ACCUMULATE)) {
+            parseerror(parser, "function `%s` declared with multiple bodies", var->name);
+            ast_block_delete(block);
+            goto enderr;
+        }
+        func = var->constval.vfunc;
 
-    func = ast_function_new(ast_ctx(var), var->name, var);
-    if (!func) {
-        parseerror(parser, "failed to allocate function for `%s`", var->name);
-        ast_block_delete(block);
-        goto enderr;
+        if (!func) {
+            parseerror(parser, "internal error: NULL function: `%s`", var->name);
+            ast_block_delete(block);
+            goto enderr;
+        }
+    } else {
+        func = ast_function_new(ast_ctx(var), var->name, var);
+
+        if (!func) {
+            parseerror(parser, "failed to allocate function for `%s`", var->name);
+            ast_block_delete(block);
+            goto enderr;
+        }
+        vec_push(parser->functions, func);
     }
-    vec_push(parser->functions, func);
 
     parser_enterblock(parser);
 
@@ -4423,13 +4164,13 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         }
     }
 
-    if (var->argcounter) {
+    if (var->argcounter && !func->argc) {
         ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT);
         parser_addlocal(parser, argc->name, (ast_expression*)argc);
         func->argc = argc;
     }
 
-    if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) {
+    if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC && !func->varargs) {
         char name[1024];
         ast_value *varargs = ast_value_new(ast_ctx(var), "reserved:va_args", TYPE_ARRAY);
         varargs->expression.flags |= AST_FLAG_IS_VARARG;
@@ -4447,9 +4188,8 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             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), false);
     }
 
     parser->function = func;
@@ -4460,7 +4200,6 @@ 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;
@@ -4498,7 +4237,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);
@@ -4508,7 +4247,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, false));
     if (!cmp) {
         ast_delete(left);
         ast_delete(right);
@@ -4528,7 +4267,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 */
@@ -4541,7 +4280,7 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
         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, false));
         if (!subscript)
             return NULL;
 
@@ -4593,7 +4332,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 */
@@ -4607,7 +4346,7 @@ static ast_expression *array_field_setter_node(
         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, false));
         if (!subscript)
             return NULL;
 
@@ -4664,13 +4403,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, false));
         if (!subscript)
             return NULL;
 
@@ -4702,6 +4441,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con
         parseerror(parser, "failed to create accessor function value");
         return false;
     }
+    fval->expression.flags &= ~(AST_FLAG_COVERAGE_MASK);
 
     func = ast_function_new(ast_ctx(array), funcname, fval);
     if (!func) {
@@ -4897,10 +4637,9 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
     return parser_create_array_getter_impl(parser, array);
 }
 
-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;
@@ -4923,6 +4662,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
 
     /* parse variables until we hit a closing paren */
     while (parser->tok != ')') {
+        bool is_varargs = false;
+
         if (!first) {
             /* there must be commas between them */
             if (parser->tok != ',') {
@@ -4936,10 +4677,13 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
         }
         first = false;
 
-        if (parser->tok == TOKEN_DOTS) {
+        param = parse_typename(parser, NULL, NULL, &is_varargs);
+        if (!param && !is_varargs)
+            goto on_error;
+        if (is_varargs) {
             /* '...' indicates a varargs function */
             variadic = true;
-            if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) {
+            if (parser->tok != ')' && parser->tok != TOKEN_IDENT) {
                 parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
                 goto on_error;
             }
@@ -4950,13 +4694,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
                     goto on_error;
                 }
             }
-        }
-        else
-        {
-            /* for anything else just parse a typename */
-            param = parse_typename(parser, NULL, NULL);
-            if (!param)
-                goto on_error;
+        } else {
             vec_push(params, param);
             if (param->expression.vtype >= TYPE_VARIANT) {
                 char tname[1024]; /* typename is reserved in C++ */
@@ -4975,12 +4713,17 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
                 }
                 if (parser->tok == TOKEN_IDENT) {
                     argcounter = util_strdup(parser_tokval(parser));
+                    ast_value_set_name(param, argcounter);
                     if (!parser_next(parser) || parser->tok != ')') {
                         parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
                         goto on_error;
                     }
                 }
             }
+            if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC && param->name[0] == '<') {
+                parseerror(parser, "parameter name omitted");
+                goto on_error;
+            }
         }
     }
 
@@ -5027,7 +4770,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);
 
@@ -5096,6 +4839,11 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
  * The base type makes up every bit of type information which comes *before* the
  * variable name.
  *
+ * NOTE: The value must either be named, have a NULL name, or a name starting
+ *       with '<'. In the first case, this will be the actual variable or type
+ *       name, in the other cases it is assumed that the name will appear
+ *       later, and an error is generated otherwise.
+ *
  * The following will be parsed in its entirety:
  *     void() foo()
  * The 'basetype' in this case is 'void()'
@@ -5103,21 +4851,25 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
  *     void() foo(), bar
  * then the type-information 'void()' can be stored in 'storebase'
  */
-static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef)
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef, bool *is_vararg)
 {
     ast_value *var, *tmp;
-    lex_ctx    ctx;
+    lex_ctx_t    ctx;
 
     const char *name = NULL;
     bool        isfield  = false;
     bool        wasarray = false;
     size_t      morefields = 0;
 
+    bool        vararg = (parser->tok == TOKEN_DOTS);
+
     ctx = parser_ctx(parser);
 
     /* types may start with a dot */
-    if (parser->tok == '.') {
+    if (parser->tok == '.' || parser->tok == TOKEN_DOTS) {
         isfield = true;
+        if (parser->tok == TOKEN_DOTS)
+            morefields += 2;
         /* if we parsed a dot we need a typename now */
         if (!parser_next(parser)) {
             parseerror(parser, "expected typename for field definition");
@@ -5127,8 +4879,14 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
         /* Further dots are handled seperately because they won't be part of the
          * basetype
          */
-        while (parser->tok == '.') {
-            ++morefields;
+        while (true) {
+            if (parser->tok == '.')
+                ++morefields;
+            else if (parser->tok == TOKEN_DOTS)
+                morefields += 3;
+            else
+                break;
+            vararg = false;
             if (!parser_next(parser)) {
                 parseerror(parser, "expected typename for field definition");
                 return NULL;
@@ -5138,6 +4896,10 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     if (parser->tok == TOKEN_IDENT)
         cached_typedef = parser_find_typedef(parser, parser_tokval(parser), 0);
     if (!cached_typedef && parser->tok != TOKEN_TYPENAME) {
+        if (vararg && is_vararg) {
+            *is_vararg = true;
+            return NULL;
+        }
         parseerror(parser, "expected typename");
         return NULL;
     }
@@ -5189,8 +4951,14 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     }
 
     /* there may be a name now */
-    if (parser->tok == TOKEN_IDENT) {
+    if (parser->tok == TOKEN_IDENT || parser->tok == TOKEN_KEYWORD) {
+        if (!strcmp(parser_tokval(parser), "break"))
+            (void)!parsewarning(parser, WARN_BREAKDEF, "break definition ignored (suggest removing it)");
+        else if (parser->tok == TOKEN_KEYWORD)
+            goto leave;
+
         name = util_strdup(parser_tokval(parser));
+
         /* parse on */
         if (!parser_next(parser)) {
             ast_delete(var);
@@ -5200,6 +4968,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
         }
     }
 
+    leave:
     /* now this may be an array */
     if (parser->tok == '[') {
         wasarray = true;
@@ -5251,11 +5020,18 @@ static bool parse_typedef(parser_t *parser)
     ast_value      *typevar, *oldtype;
     ast_expression *old;
 
-    typevar = parse_typename(parser, NULL, NULL);
+    typevar = parse_typename(parser, NULL, NULL, NULL);
 
     if (!typevar)
         return false;
 
+    /* while parsing types, the ast_value's get named '<something>' */
+    if (!typevar->name || typevar->name[0] == '<') {
+        parseerror(parser, "missing name in typedef");
+        ast_delete(typevar);
+        return false;
+    }
+
     if ( (old = parser_find_var(parser, typevar->name)) ) {
         parseerror(parser, "cannot define a type with the same name as a variable: %s\n"
                    " -> `%s` has been declared here: %s:%i",
@@ -5409,18 +5185,27 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
     bool      wasarray  = false;
 
     ast_member *me[3] = { NULL, NULL, NULL };
+    ast_member *last_me[3] = { NULL, NULL, NULL };
 
     if (!localblock && is_static)
         parseerror(parser, "`static` qualifier is not supported in global scope");
 
     /* get the first complete variable */
-    var = parse_typename(parser, &basetype, cached_typedef);
+    var = parse_typename(parser, &basetype, cached_typedef, NULL);
     if (!var) {
         if (basetype)
             ast_delete(basetype);
         return false;
     }
 
+    /* while parsing types, the ast_value's get named '<something>' */
+    if (!var->name || var->name[0] == '<') {
+        parseerror(parser, "declaration does not declare anything");
+        if (basetype)
+            ast_delete(basetype);
+        return false;
+    }
+
     while (true) {
         proto = NULL;
         wasarray = false;
@@ -5460,6 +5245,8 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         }
 
         var->cvq = qualifier;
+        if (qflags & AST_FLAG_COVERAGE) /* specified in QC, drop our default */
+            var->expression.flags &= ~(AST_FLAG_COVERAGE_MASK);
         var->expression.flags |= qflags;
 
         /*
@@ -5470,6 +5257,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             var->expression.flags & AST_FLAG_ALIAS)
             var->desc = vstring;
 
+        if (parser_find_global(parser, var->name) && var->expression.flags & AST_FLAG_ALIAS) {
+            parseerror(parser, "function aliases cannot be forward declared");
+            retval = false;
+            goto cleanup;
+        }
+
+
         /* Part 1:
          * check for validity: (end_sys_..., multiple-definitions, prototypes, ...)
          * Also: if there was a prototype, `var` will be deleted and set to `proto` which
@@ -5582,6 +5376,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             retval = false;
                             goto cleanup;
                         }
+                        if (old->flags & AST_FLAG_FINAL_DECL) {
+                            parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i",
+                                       var->name, ast_ctx(old).file, ast_ctx(old).line);
+                            retval = false;
+                            goto cleanup;
+                        }
                         proto = (ast_value*)old;
                         if (!ast_istype(old, ast_value)) {
                             parseerror(parser, "internal error: not an ast_value");
@@ -5595,6 +5395,11 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             goto cleanup;
                         }
                         proto->expression.flags |= var->expression.flags;
+                        /* copy the context for finals,
+                         * so the error can show where it was actually made 'final'
+                         */
+                        if (proto->expression.flags & AST_FLAG_FINAL_DECL)
+                            ast_ctx(old) = ast_ctx(var);
                         ast_delete(var);
                         var = proto;
                     }
@@ -5619,6 +5424,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 retval = false;
                 goto cleanup;
             }
+            /* doing this here as the above is just for a single scope */
             old = parser_find_local(parser, var->name, 0, &isparam);
             if (old && isparam) {
                 if (parsewarning(parser, WARN_LOCAL_SHADOWS,
@@ -5631,8 +5437,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 }
                 if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
                     ast_delete(var);
-                    var = NULL;
-                    goto skipvar;
+                    if (ast_istype(old, ast_value))
+                        var = proto = (ast_value*)old;
+                    else {
+                        var = NULL;
+                        goto skipvar;
+                    }
                 }
             }
         }
@@ -5687,7 +5497,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             return false;
                         }
 
-                        if (var->expression.vtype != find->vtype) {
+                        if (!ast_compare_type((ast_expression*)var, find)) {
                             char ty1[1024];
                             char ty2[1024];
 
@@ -5761,6 +5571,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                      */
                     char   *defname = NULL;
                     size_t  prefix_len, ln;
+                    size_t  sn, sn_size;
 
                     ln = strlen(parser->function->name);
                     vec_append(defname, ln, parser->function->name);
@@ -5782,6 +5593,24 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     /* now rename the global */
                     ln = strlen(var->name);
                     vec_append(defname, ln, var->name);
+                    /* if a variable of that name already existed, add the
+                     * counter value.
+                     * The counter is incremented either way.
+                     */
+                    sn_size = vec_size(parser->function->static_names);
+                    for (sn = 0; sn != sn_size; ++sn) {
+                        if (strcmp(parser->function->static_names[sn], var->name) == 0)
+                            break;
+                    }
+                    if (sn != sn_size) {
+                        char *num = NULL;
+                        int   len = util_asprintf(&num, "#%u", parser->function->static_count);
+                        vec_append(defname, len, num);
+                        mem_d(num);
+                    }
+                    else
+                        vec_push(parser->function->static_names, util_strdup(var->name));
+                    parser->function->static_count++;
                     ast_value_set_name(var, defname);
 
                     /* push it to the to-be-generated globals */
@@ -5820,6 +5649,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 }
             }
         }
+        memcpy(last_me, me, sizeof(me));
         me[0] = me[1] = me[2] = NULL;
         cleanvar = false;
         /* Part 2.2
@@ -5892,16 +5722,8 @@ skipvar:
 
         if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) {
             if (parser->tok != '=') {
-                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;
-                }
+                parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
+                break;
             }
 
             if (!parser_next(parser)) {
@@ -6035,22 +5857,52 @@ skipvar:
         } else {
             ast_expression *cexp;
             ast_value      *cval;
+            bool            folded_const = false;
 
             cexp = parse_expression_leave(parser, true, false, false);
             if (!cexp)
                 break;
+            cval = ast_istype(cexp, ast_value) ? (ast_value*)cexp : NULL;
 
-            if (!localblock) {
-                cval = (ast_value*)cexp;
+            /* deal with foldable constants: */
+            if (localblock &&
+                var->cvq == CV_CONST && cval && cval->hasvalue && cval->cvq == CV_CONST && !cval->isfield)
+            {
+                /* remove it from the current locals */
+                if (isvector) {
+                    for (i = 0; i < 3; ++i) {
+                        vec_pop(parser->_locals);
+                        vec_pop(localblock->collect);
+                    }
+                }
+                /* do sanity checking, this function really needs refactoring */
+                if (vec_last(parser->_locals) != (ast_expression*)var)
+                    parseerror(parser, "internal error: unexpected change in local variable handling");
+                else
+                    vec_pop(parser->_locals);
+                if (vec_last(localblock->locals) != var)
+                    parseerror(parser, "internal error: unexpected change in local variable handling (2)");
+                else
+                    vec_pop(localblock->locals);
+                /* push it to the to-be-generated globals */
+                vec_push(parser->globals, (ast_expression*)var);
+                if (isvector)
+                    for (i = 0; i < 3; ++i)
+                        vec_push(parser->globals, (ast_expression*)last_me[i]);
+                folded_const = true;
+            }
+
+            if (folded_const || !localblock || is_static) {
                 if (cval != parser->nil &&
-                    (!ast_istype(cval, ast_value) || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
+                    (!cval || ((!cval->hasvalue || cval->cvq != CV_CONST) && !cval->isfield))
                    )
                 {
-                    parseerror(parser, "cannot initialize a global constant variable with a non-constant expression");
+                    parseerror(parser, "initializer is non constant");
                 }
                 else
                 {
-                    if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+                    if (!is_static &&
+                        !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
                         qualifier != CV_VAR)
                     {
                         var->cvq = CV_CONST;
@@ -6090,6 +5942,13 @@ skipvar:
                 vec_free(sy.argc);
                 var->cvq = cvq;
             }
+            /* a constant initialized to an inexact value should be marked inexact:
+             * const float x = <inexact>; should propagate the inexact flag
+             */
+            if (var->cvq == CV_CONST && var->expression.vtype == TYPE_FLOAT) {
+                if (cval && cval->hasvalue && cval->cvq == CV_CONST)
+                    var->inexact = cval->inexact;
+            }
         }
 
 another:
@@ -6154,7 +6013,7 @@ static bool parser_global_statement(parser_t *parser)
     if (parser->tok == TOKEN_IDENT)
         istype = parser_find_typedef(parser, parser_tokval(parser), 0);
 
-    if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
+    if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.' || parser->tok == TOKEN_DOTS)
     {
         return parse_variable(parser, NULL, false, CV_NONE, istype, false, false, 0, NULL);
     }
@@ -6162,7 +6021,7 @@ static bool parser_global_statement(parser_t *parser)
     {
         if (cvq == CV_WRONG)
             return false;
-        return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring);
+        return parse_variable(parser, NULL, false, cvq, NULL, noref, is_static, qflags, vstring);
     }
     else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "enum"))
     {
@@ -6277,7 +6136,7 @@ static void generate_checksum(parser_t *parser, ir_builder *ir)
 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));
@@ -6293,7 +6152,7 @@ parser_t *parser_create()
         }
     }
     if (!parser->assign_op) {
-        printf("internal error: initializing parser: failed to find assign operator\n");
+        con_err("internal error: initializing parser: failed to find assign operator\n");
         mem_d(parser);
         return NULL;
     }
@@ -6305,9 +6164,6 @@ parser_t *parser_create()
 
     parser->aliases = util_htnew(PARSER_HT_SIZE);
 
-    parser->ht_imm_string = util_htnew(512);
-    parser->ht_imm_string_dotranslate = util_htnew(512);
-
     /* corrector */
     vec_push(parser->correct_variables, correct_trie_new());
     vec_push(parser->correct_variables_score, NULL);
@@ -6336,6 +6192,8 @@ parser_t *parser_create()
         parser->reserved_version = NULL;
     }
 
+    parser->fold   = fold_init  (parser);
+    parser->intrin = intrin_init(parser);
     return parser;
 }
 
@@ -6405,15 +6263,6 @@ static void parser_remove_ast(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]);
     }
@@ -6422,11 +6271,6 @@ static void parser_remove_ast(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_dotranslate);
-    util_htdel(parser->ht_imm_string);
-    vec_free(parser->imm_float);
     vec_free(parser->globals);
     vec_free(parser->fields);
 
@@ -6463,12 +6307,13 @@ static void parser_remove_ast(parser_t *parser)
     ast_value_delete(parser->const_vec[0]);
     ast_value_delete(parser->const_vec[1]);
     ast_value_delete(parser->const_vec[2]);
-    
+
     if (parser->reserved_version)
         ast_value_delete(parser->reserved_version);
 
     util_htdel(parser->aliases);
-    intrin_intrinsics_destroy(parser);
+    fold_cleanup(parser->fold);
+    intrin_cleanup(parser->intrin);
 }
 
 void parser_cleanup(parser_t *parser)
@@ -6477,11 +6322,51 @@ void parser_cleanup(parser_t *parser)
     mem_d(parser);
 }
 
+static bool parser_set_coverage_func(parser_t *parser, ir_builder *ir) {
+    size_t          i;
+    ast_expression *expr;
+    ast_value      *cov;
+    ast_function   *func;
+
+    if (!OPTS_OPTION_BOOL(OPTION_COVERAGE))
+        return true;
+
+    func = NULL;
+    for (i = 0; i != vec_size(parser->functions); ++i) {
+        if (!strcmp(parser->functions[i]->name, "coverage")) {
+            func = parser->functions[i];
+            break;
+        }
+    }
+    if (!func) {
+        if (OPTS_OPTION_BOOL(OPTION_COVERAGE)) {
+            con_out("coverage support requested but no coverage() builtin declared\n");
+            ir_builder_delete(ir);
+            return false;
+        }
+        return true;
+    }
+
+    cov  = func->vtype;
+    expr = (ast_expression*)cov;
+
+    if (expr->vtype != TYPE_FUNCTION || vec_size(expr->params) != 0) {
+        char ty[1024];
+        ast_type_to_string(expr, ty, sizeof(ty));
+        con_out("invalid type for coverage(): %s\n", ty);
+        ir_builder_delete(ir);
+        return false;
+    }
+
+    ir->coverage_func = func->ir_func->value;
+    return true;
+}
+
 bool parser_finish(parser_t *parser, const char *output)
 {
-    size_t i;
-    ir_builder *ir;
-    bool retval = true;
+    size_t          i;
+    ir_builder     *ir;
+    bool            retval = true;
 
     if (compile_errors) {
         con_out("*** there were compile errors\n");
@@ -6560,27 +6445,13 @@ 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;
+
+    /* before generating any functions we need to set the coverage_func */
+    if (!parser_set_coverage_func(parser, ir))
+        return false;
+
     for (i = 0; i < vec_size(parser->globals); ++i) {
         ast_value *asvalue;
         if (!ast_istype(parser->globals[i], ast_value))