Work in progress constant-folding rewrite.
authorDale Weiler <killfieldengine@gmail.com>
Wed, 31 Jul 2013 09:04:19 +0000 (09:04 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Wed, 31 Jul 2013 09:04:19 +0000 (09:04 +0000)
fold.c [new file with mode: 0644]
gmqcc.h
include.mk
intrin.h
lexer.h
parser.c
parser.h [new file with mode: 0644]

diff --git a/fold.c b/fold.c
new file mode 100644 (file)
index 0000000..26b5985
--- /dev/null
+++ b/fold.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2012, 2013
+ *     Dale Weiler
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <string.h>
+#include "ast.h"
+#include "parser.h"
+
+#define FOLD_STRING_UNTRANSLATE_HTSIZE 1024
+#define FOLD_STRING_DOTRANSLATE_HTSIZE 1024
+
+/*
+ * There is two stages to constant folding in GMQCC: there is the parse
+ * stage constant folding, where, witht he help of the AST, operator
+ * usages can be constant folded. Then there is the constant folding
+ * in the IR for things like eliding if statements, can occur.
+ * 
+ * This file is thus, split into two parts.
+ */
+ast_expression **fold_const_values = NULL;
+
+static GMQCC_INLINE bool fold_possible(const ast_value *val) {
+    return  ast_istype((ast_expression*)val, ast_value) &&
+            val->hasvalue && (val->cvq == CV_CONST)     &&
+            ((ast_expression*)val)->vtype != TYPE_FUNCTION; /* why not for functions? */
+}
+
+#define isfloat(X)     (((ast_expression*)(X))->vtype == TYPE_FLOAT  && fold_possible(X))
+#define isvector(X)    (((ast_expression*)(X))->vtype == TYPE_VECTOR && fold_possible(X))
+#define isstring(S)    (((ast_expression*)(X))->vtype == TYPE_STRING && fold_possible(X))
+#define isfloats(X,Y)  (isfloat (X) && isfloat(Y))
+#define isvectors(X,Y) (isvector(X) && isvector(Y))
+#define isstrings(X,Y) (isstring(X) && isstring(Y))
+
+/*
+ * Implementation of basic vector math for vec3_t, for trivial constant
+ * folding.
+ * 
+ * TODO: gcc/clang hinting for autovectorization
+ */
+static GMQCC_INLINE vec3_t vec3_add(vec3_t a, vec3_t b) {
+    vec3_t out;
+    out.x = a.x + b.x;
+    out.y = a.y + b.y;
+    out.z = a.z + b.z;
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_sub(vec3_t a, vec3_t b) {
+    vec3_t out;
+    out.x = a.x + b.x;
+    out.y = a.y + b.y;
+    out.z = a.z + b.z;
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_not(vec3_t a) {
+    vec3_t out;
+    out.x = !a.x;
+    out.y = !a.y;
+    out.z = !a.z;
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_neg(vec3_t a) {
+    vec3_t out;
+    out.x = -a.x;
+    out.y = -a.y;
+    out.z = -a.z;
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_xor(vec3_t a, vec3_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)((qcint_t)a.x ^ (qcint_t)b.x);
+    out.y = (qcfloat_t)((qcint_t)a.y ^ (qcint_t)b.y);
+    out.z = (qcfloat_t)((qcint_t)a.z ^ (qcint_t)b.z);
+    return out;
+}
+
+static GMQCC_INLINE vec3_t vec3_xorvf(vec3_t a, qcfloat_t b) {
+    vec3_t out;
+    out.x = (qcfloat_t)((qcint_t)a.x ^ (qcint_t)b);
+    out.y = (qcfloat_t)((qcint_t)a.y ^ (qcint_t)b);
+    out.z = (qcfloat_t)((qcint_t)a.z ^ (qcint_t)b);
+    return out;
+}
+
+#if 0
+static GMQCC_INLINE qcfloat_t vec3_mulvv(vec3_t a, vec3_t b) {
+    return (a.x * b.x + a.y * b.y + a.z * b.z);
+}
+#endif
+
+static GMQCC_INLINE vec3_t vec3_mulvf(vec3_t a, qcfloat_t b) {
+    vec3_t out;
+    out.x = a.x * b;
+    out.y = a.y * b;
+    out.z = a.z * b;
+    return out;
+}
+
+static GMQCC_INLINE bool vec3_cmp(vec3_t a, vec3_t b) {
+    return a.x == b.x &&
+           a.y == b.y &&
+           a.z == b.z;
+}
+
+static GMQCC_INLINE vec3_t vec3_create(float x, float y, float z) {
+    vec3_t out;
+    out.x = x;
+    out.y = y;
+    out.z = z;
+    return out;
+}
+
+
+static GMQCC_INLINE float fold_immvalue_float(ast_value *expr) {
+    return expr->constval.vfloat;
+}
+static GMQCC_INLINE vec3_t fold_immvalue_vector(ast_value *expr) {
+    return expr->constval.vvec;
+}
+#if 0
+static GMQCC_INLINE const char *fold_immvalue_string(ast_value *expr) {
+    return expr->constval.vstring;
+}
+#endif
+
+
+fold_t *fold_init(parser_t *parser) {
+    fold_t *fold                 = (fold_t*)mem_a(sizeof(fold_t));
+    fold->parser                 = parser;
+    fold->imm_float              = NULL;
+    fold->imm_vector             = NULL;
+    fold->imm_string             = NULL;
+    fold->imm_string_untranslate = util_htnew(FOLD_STRING_UNTRANSLATE_HTSIZE);
+    fold->imm_string_dotranslate = util_htnew(FOLD_STRING_DOTRANSLATE_HTSIZE);
+
+    /*
+     * prime the tables with common constant values at constant
+     * locations.
+     */
+    (void)fold_constgen_float (fold,  0.0f);
+    (void)fold_constgen_float (fold,  1.0f);
+    (void)fold_constgen_float (fold, -1.0f);
+
+    (void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f));
+
+    return fold;
+}
+
+bool fold_generate(fold_t *fold, ir_builder *ir) {
+    /* generate globals for immediate folded values */
+    size_t     i;
+    ast_value *cur;
+
+    for (i = 0; i < vec_size(fold->imm_float);   ++i)
+        if (!ast_global_codegen ((cur = fold->imm_float[i]), ir, false)) goto err;
+    for (i = 0; i < vec_size(fold->imm_vector);  ++i)
+        if (!ast_global_codegen((cur = fold->imm_vector[i]), ir, false)) goto err;
+    for (i = 0; i < vec_size(fold->imm_string);  ++i)
+        if (!ast_global_codegen((cur = fold->imm_string[i]), ir, false)) goto err;
+
+    return true;
+
+err:
+    con_out("failed to generate global %s\n", cur->name);
+    ir_builder_delete(ir);
+    return false;
+}
+
+void fold_cleanup(fold_t *fold) {
+    size_t i;
+
+    for (i = 0; i < vec_size(fold->imm_float);  ++i) ast_delete(fold->imm_float[i]);
+    for (i = 0; i < vec_size(fold->imm_vector); ++i) ast_delete(fold->imm_vector[i]);
+    for (i = 0; i < vec_size(fold->imm_string); ++i) ast_delete(fold->imm_string[i]);
+
+    vec_free(fold->imm_float);
+    vec_free(fold->imm_vector);
+    vec_free(fold->imm_string);
+
+    util_htdel(fold->imm_string_untranslate);
+    util_htdel(fold->imm_string_dotranslate);
+
+    mem_d(fold);
+}
+
+static lex_ctx_t fold_ctx(fold_t *fold) {
+    lex_ctx_t ctx;
+    if (fold->parser->lex)
+        return parser_ctx(fold->parser);
+
+    memset(&ctx, 0, sizeof(ctx));
+    return ctx;
+}
+
+ast_expression *fold_constgen_float(fold_t *fold, qcfloat_t value) {
+    ast_value  *out = NULL;
+    size_t      i;
+
+    for (i = 0; i < vec_size(fold->imm_float); i++) {
+        if (fold->imm_float[i]->constval.vfloat == value)
+            return (ast_expression*)fold->imm_float[i];
+    }
+
+    out                  = ast_value_new(fold_ctx(fold), "#IMMEDIATE", TYPE_FLOAT);
+    out->cvq             = CV_CONST;
+    out->hasvalue        = true;
+    out->constval.vfloat = value;
+
+    vec_push(fold->imm_float, out);
+
+    return (ast_expression*)out;
+}
+
+ast_expression *fold_constgen_vector(fold_t *fold, vec3_t value) {
+    ast_value *out;
+    size_t     i;
+
+    for (i = 0; i < vec_size(fold->imm_vector); i++) {
+        if (vec3_cmp(fold->imm_vector[i]->constval.vvec, value))
+            return (ast_expression*)fold->imm_vector[i];
+    }
+
+    out                = ast_value_new(fold_ctx(fold), "#IMMEDIATE", TYPE_VECTOR);
+    out->cvq           = CV_CONST;
+    out->hasvalue      = true;
+    out->constval.vvec = value;
+
+    vec_push(fold->imm_vector, out);
+
+    return (ast_expression*)out;
+}
+
+ast_expression *fold_constgen_string(fold_t *fold, const char *str, bool translate) {
+    hash_table_t *table = (translate) ? fold->imm_string_untranslate : fold->imm_string_dotranslate;
+    ast_value    *out   = NULL;
+    size_t        hash  = util_hthash(table, str);
+
+    if ((out = (ast_value*)util_htgeth(table, str, hash)))
+        return (ast_expression*)out;
+
+    if (translate) {
+        char name[32];
+        util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(fold->parser->translated++));
+        out                    = ast_value_new(parser_ctx(fold->parser), name, TYPE_STRING);
+        out->expression.flags |= AST_FLAG_INCLUDE_DEF; /* def needs to be included for translatables */
+    } else
+        out                    = ast_value_new(fold_ctx(fold), "#IMMEDIATE", TYPE_STRING);
+
+    out->cvq              = CV_CONST;
+    out->hasvalue         = true;
+    out->isimm            = true;
+    out->constval.vstring = parser_strdup(str);
+
+    vec_push(fold->imm_string, out);
+    util_htseth(table, str, hash, out);
+
+    return (ast_expression*)out;
+}
+
+ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **opexprs) {
+    ast_value *a = (ast_value*)opexprs[0];
+    ast_value *b = (ast_value*)opexprs[1];
+    ast_value *c = (ast_value*)opexprs[2];
+
+    /* can a fold operation be applied to this operator usage? */
+    if (!info->folds)
+        return NULL;
+
+    switch(info->operands) {
+        case 3: if(!c) return NULL;
+        case 2: if(!b) return NULL;
+    }
+
+    switch(info->id) {
+        case opid2('-', 'P'):
+            return isfloat (a)             ? fold_constgen_float (fold, fold_immvalue_float(a))
+                 : isvector(a)             ? fold_constgen_vector(fold, vec3_neg(fold_immvalue_vector(a)))
+                 : NULL;
+        case opid2('!', 'P'):
+            return isfloat (a)             ? fold_constgen_float (fold, !fold_immvalue_float(a))
+                 : isvector(a)             ? fold_constgen_vector(fold, vec3_not(fold_immvalue_vector(a)))
+                 : NULL;
+        case opid1('+'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, fold_immvalue_float(a) + fold_immvalue_float(b))
+                 : isvectors(a,b)          ? fold_constgen_vector(fold, vec3_add(fold_immvalue_vector(a), fold_immvalue_vector(b)))
+                 : NULL;
+        case opid1('-'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, fold_immvalue_float(a) - fold_immvalue_float(b))
+                 : isvectors(a,b)          ? fold_constgen_vector(fold, vec3_sub(fold_immvalue_vector(a), fold_immvalue_vector(b)))
+                 : NULL;
+        case opid1('*'):
+            if (isfloat(a))
+                return isvector(b) ? fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(b), fold_immvalue_float(a)))
+                                   : fold_constgen_float (fold, fold_immvalue_float(a) * fold_immvalue_float(b));
+            return NULL;
+        case opid1('/'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, fold_immvalue_float(a) / fold_immvalue_float(b))
+                 : isvector(a)&&isfloat(b) ? fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(a), 1.0f / fold_immvalue_float(b)))
+                 : NULL;
+        case opid1('%'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) % ((qcint_t)fold_immvalue_float(b))))
+                 : NULL;
+        case opid1('|'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))))
+                 : NULL;
+        case opid1('&'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))))
+                 : NULL;
+        case opid1('^'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) ^ ((qcint_t)fold_immvalue_float(b))))
+                 : isvectors(a,b)          ? fold_constgen_vector(fold, vec3_xor  (fold_immvalue_vector(a), fold_immvalue_vector(b)))
+                 : isvector(a)&&isfloat(b) ? fold_constgen_vector(fold, vec3_xorvf(fold_immvalue_vector(a), fold_immvalue_float (b)))
+                 : NULL;
+        case opid2('<','<'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, (qcfloat_t)(((qcuint_t)(fold_immvalue_float(a)) << ((qcuint_t)fold_immvalue_float(b)))))
+                 : NULL;
+        case opid2('>','>'):
+            return isfloats(a,b)           ? fold_constgen_float (fold, (qcfloat_t)(((qcuint_t)(fold_immvalue_float(a)) >> ((qcuint_t)fold_immvalue_float(b)))))
+                 : NULL;
+        case opid2('|','|'):     return NULL;
+        case opid2('&','&'):     return NULL;
+        case opid2('?',':'):     return NULL;
+        case opid2('*','*'):     return NULL;
+        case opid3('<','=','>'): return NULL;
+        case opid2('!','='):
+            return isfloats(a,b)           ? fold_constgen_float (fold, fold_immvalue_float(a) != fold_immvalue_float(b))
+                 : NULL;
+        case opid2('=','='):
+            return isfloats(a,b)           ? fold_constgen_float (fold, fold_immvalue_float(a) == fold_immvalue_float(b))
+                 : NULL;
+        case opid2('~','P'):
+            return isfloat(a)              ? fold_constgen_float (fold, ~(qcint_t)fold_immvalue_float(a))
+                 : NULL;
+            break;
+    }
+    return NULL;
+}
diff --git a/gmqcc.h b/gmqcc.h
index 2ab3a00..25eed13 100644 (file)
--- a/gmqcc.h
+++ b/gmqcc.h
@@ -713,9 +713,10 @@ enum {
 /* TODO: elide */
 extern const char *util_instr_str[VINSTR_END];
 
-/* TOO: _t */
-typedef float   qcfloat_t;
-typedef int32_t qcint_t;
+
+typedef float    qcfloat_t;
+typedef int32_t  qcint_t;
+typedef uint32_t qcuint_t;
 
 typedef struct {
     prog_section_statement_t *statements;
@@ -728,7 +729,7 @@ typedef struct {
     uint16_t                  crc;
     uint32_t                  entfields;
     ht                        string_cache;
-    qcint_t                     string_cached_empty;
+    qcint_t                   string_cached_empty;
 } code_t;
 
 /*
@@ -824,11 +825,6 @@ typedef struct {
     qcfloat_t x, y, z;
 } vec3_t;
 
-vec3_t  vec3_add  (vec3_t, vec3_t);
-vec3_t  vec3_sub  (vec3_t, vec3_t);
-qcfloat_t vec3_mulvv(vec3_t, vec3_t);
-vec3_t  vec3_mulvf(vec3_t, float);
-
 /*===================================================================*/
 /*============================= exec.c ==============================*/
 /*===================================================================*/
index 3fa588d..77342d9 100644 (file)
@@ -14,7 +14,7 @@ LDFLAGS +=
 LIBS    += -lm
 
 #objects
-OBJ_C = main.o lexer.o parser.o fs.o stat.o util.o code.o ast.o ir.o conout.o ftepp.o opts.o utf8.o correct.o
+OBJ_C = main.o lexer.o parser.o fs.o stat.o util.o code.o ast.o ir.o conout.o ftepp.o opts.o utf8.o correct.o fold.o
 OBJ_P = util.o fs.o conout.o opts.o pak.o stat.o
 OBJ_T = test.o util.o opts.o conout.o fs.o stat.o
 OBJ_X = exec-standalone.o util.o opts.o conout.o fs.o stat.o
index 00464aa..1503d60 100644 (file)
--- a/intrin.h
+++ b/intrin.h
@@ -115,7 +115,7 @@ static ast_expression *intrin_pow (parser_t *parser) {
                 parser_ctx(parser),
                 INSTR_STORE_F,
                 (ast_expression*)local,
-                (ast_expression*)parser_const_float_1(parser)
+                (ast_expression*)parser->fold->imm_float[1] /* 1 == 1.0f */
             )
         );
 
@@ -126,7 +126,7 @@ static ast_expression *intrin_pow (parser_t *parser) {
                 INSTR_STORE_F,
                 INSTR_MUL_F,
                 (ast_expression*)arg2,
-                (ast_expression*)parser_const_float(parser, 0.25f)
+                (ast_expression*)fold_constgen_float(parser->fold, 0.25f)
             )
         );
 
@@ -149,7 +149,7 @@ static ast_expression *intrin_pow (parser_t *parser) {
                 parser_ctx(parser),
                 INSTR_AND,
                 (ast_expression*)arg2,
-                (ast_expression*)parser_const_float_1(parser)
+                (ast_expression*)parser->fold->imm_float[1] /* 1 == 1.0f */
             ),
             true, /* ! not */
             NULL,
@@ -168,7 +168,7 @@ static ast_expression *intrin_pow (parser_t *parser) {
                 INSTR_STORE_F,
                 INSTR_SUB_F,
                 (ast_expression*)arg2,
-                (ast_expression*)parser_const_float_1(parser)
+                (ast_expression*)parser->fold->imm_float[1] /* 1 == 1.0f */
             )
         );
         /* local *= x */
@@ -190,7 +190,7 @@ static ast_expression *intrin_pow (parser_t *parser) {
                 parser_ctx(parser),
                 INSTR_GT,
                 (ast_expression*)arg2,
-                (ast_expression*)parser_const_float_0(parser)
+                (ast_expression*)parser->fold->imm_float[0] /* 0 == 0.0f */
             ),
             false,
             NULL,
@@ -291,7 +291,7 @@ static ast_expression *intrin_exp(parser_t *parser) {
         INTRIN_VAL(value, "exp", func, "<float>", TYPE_FLOAT);
 
         /* push arguments for params to call */
-        vec_push(call->params, (ast_expression*)parser_const_float(parser, QC_M_E));
+        vec_push(call->params, (ast_expression*)fold_constgen_float(parser->fold, QC_M_E));
         vec_push(call->params, (ast_expression*)arg1);
 
         /* return pow(QC_M_E, x) */
diff --git a/lexer.h b/lexer.h
index efbdd64..f490d2a 100644 (file)
--- a/lexer.h
+++ b/lexer.h
@@ -161,6 +161,7 @@ typedef struct {
     unsigned int assoc;
     signed int   prec;
     unsigned int flags;
+    bool         folds;
 } oper_info;
 
 /*
@@ -173,168 +174,168 @@ typedef struct {
 #define opid3(a,b,c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c)
 
 static const oper_info c_operators[] = {
-    { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX}, /* paren expression - non function call */
+    { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX, false}, /* paren expression - non function call */
 
-    { "++",  1, opid3('S','+','+'), ASSOC_LEFT,  17, OP_SUFFIX},
-    { "--",  1, opid3('S','-','-'), ASSOC_LEFT,  17, OP_SUFFIX},
-    { ".",   2, opid1('.'),         ASSOC_LEFT,  17, 0 },
-    { "(",   0, opid1('('),         ASSOC_LEFT,  17, 0 }, /* function call */
-    { "[",   2, opid1('['),         ASSOC_LEFT,  17, 0 }, /* array subscript */
+    { "++",  1, opid3('S','+','+'), ASSOC_LEFT,  17, OP_SUFFIX, false},
+    { "--",  1, opid3('S','-','-'), ASSOC_LEFT,  17, OP_SUFFIX, false},
+    { ".",   2, opid1('.'),         ASSOC_LEFT,  17, 0,         false},
+    { "(",   0, opid1('('),         ASSOC_LEFT,  17, 0,         false}, /* function call */
+    { "[",   2, opid1('['),         ASSOC_LEFT,  17, 0,         false}, /* array subscript */
 
-    { "++",  1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX },
-    { "--",  1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX },
+    { "++",  1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
+    { "--",  1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
 
-    { "**",  2, opid2('*', '*'),    ASSOC_RIGHT, 15, 0 },
+    { "**",  2, opid2('*', '*'),    ASSOC_RIGHT, 15, 0,         true},
 
-    { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
-    { "~",   1, opid2('~', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
-    { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-    { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-/*  { "&",   1, opid2('&','P'),     ASSOC_RIGHT, 14, OP_PREFIX }, */
+    { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX, true},
+    { "~",   1, opid2('~', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX, true},
+    { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX, false},
+    { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX, true},
+/*  { "&",   1, opid2('&','P'),     ASSOC_RIGHT, 14, OP_PREFIX, false}, */
 
-    { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0 },
-    { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0 },
-    { "%",   2, opid1('%'),         ASSOC_LEFT,  13, 0 },
+    { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0,         true},
+    { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0,         true},
+    { "%",   2, opid1('%'),         ASSOC_LEFT,  13, 0,         true},
 
-    { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0 },
-    { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0 },
+    { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0,         true},
+    { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0,         true},
 
-    { "<<",  2, opid2('<','<'),     ASSOC_LEFT,  11, 0 },
-    { ">>",  2, opid2('>','>'),     ASSOC_LEFT,  11, 0 },
+    { "<<",  2, opid2('<','<'),     ASSOC_LEFT,  11, 0,         true},
+    { ">>",  2, opid2('>','>'),     ASSOC_LEFT,  11, 0,         true},
 
-    { "<",   2, opid1('<'),         ASSOC_LEFT,  10, 0 },
-    { ">",   2, opid1('>'),         ASSOC_LEFT,  10, 0 },
-    { "<=>", 2, opid3('<','=','>'), ASSOC_LEFT,  10, 0 },
-    { "<=",  2, opid2('<','='),     ASSOC_LEFT,  10, 0 },
-    { ">=",  2, opid2('>','='),     ASSOC_LEFT,  10, 0 },
+    { "<",   2, opid1('<'),         ASSOC_LEFT,  10, 0,         false},
+    { ">",   2, opid1('>'),         ASSOC_LEFT,  10, 0,         false},
+    { "<=>", 2, opid3('<','=','>'), ASSOC_LEFT,  10, 0,         true},
+    { "<=",  2, opid2('<','='),     ASSOC_LEFT,  10, 0,         false},
+    { ">=",  2, opid2('>','='),     ASSOC_LEFT,  10, 0,         false},
 
-    { "==",  2, opid2('=','='),     ASSOC_LEFT,  9,  0 },
-    { "!=",  2, opid2('!','='),     ASSOC_LEFT,  9,  0 },
+    { "==",  2, opid2('=','='),     ASSOC_LEFT,  9,  0,         false},
+    { "!=",  2, opid2('!','='),     ASSOC_LEFT,  9,  0,         false},
 
-    { "&",   2, opid1('&'),         ASSOC_LEFT,  8,  0 },
+    { "&",   2, opid1('&'),         ASSOC_LEFT,  8,  0,         true},
 
-    { "^",   2, opid1('^'),         ASSOC_LEFT,  7,  0 },
+    { "^",   2, opid1('^'),         ASSOC_LEFT,  7,  0,         true},
 
-    { "|",   2, opid1('|'),         ASSOC_LEFT,  6,  0 },
+    { "|",   2, opid1('|'),         ASSOC_LEFT,  6,  0,         true},
 
-    { "&&",  2, opid2('&','&'),     ASSOC_LEFT,  5,  0 },
+    { "&&",  2, opid2('&','&'),     ASSOC_LEFT,  5,  0,         true},
 
-    { "||",  2, opid2('|','|'),     ASSOC_LEFT,  4,  0 },
+    { "||",  2, opid2('|','|'),     ASSOC_LEFT,  4,  0,         true},
 
-    { "?",   3, opid2('?',':'),     ASSOC_RIGHT, 3,  0 },
+    { "?",   3, opid2('?',':'),     ASSOC_RIGHT, 3,  0,         true},
 
-    { "=",   2, opid1('='),         ASSOC_RIGHT, 2,  0 },
-    { "+=",  2, opid2('+','='),     ASSOC_RIGHT, 2,  0 },
-    { "-=",  2, opid2('-','='),     ASSOC_RIGHT, 2,  0 },
-    { "*=",  2, opid2('*','='),     ASSOC_RIGHT, 2,  0 },
-    { "/=",  2, opid2('/','='),     ASSOC_RIGHT, 2,  0 },
-    { "%=",  2, opid2('%','='),     ASSOC_RIGHT, 2,  0 },
-    { ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2,  0 },
-    { "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2,  0 },
-    { "&=",  2, opid2('&','='),     ASSOC_RIGHT, 2,  0 },
-    { "^=",  2, opid2('^','='),     ASSOC_RIGHT, 2,  0 },
-    { "|=",  2, opid2('|','='),     ASSOC_RIGHT, 2,  0 },
-    { "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 2,  0 },
+    { "=",   2, opid1('='),         ASSOC_RIGHT, 2,  0,         false},
+    { "+=",  2, opid2('+','='),     ASSOC_RIGHT, 2,  0,         false},
+    { "-=",  2, opid2('-','='),     ASSOC_RIGHT, 2,  0,         false},
+    { "*=",  2, opid2('*','='),     ASSOC_RIGHT, 2,  0,         false},
+    { "/=",  2, opid2('/','='),     ASSOC_RIGHT, 2,  0,         true},
+    { "%=",  2, opid2('%','='),     ASSOC_RIGHT, 2,  0,         false},
+    { ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2,  0,         false},
+    { "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2,  0,         false},
+    { "&=",  2, opid2('&','='),     ASSOC_RIGHT, 2,  0,         false},
+    { "^=",  2, opid2('^','='),     ASSOC_RIGHT, 2,  0,         false},
+    { "|=",  2, opid2('|','='),     ASSOC_RIGHT, 2,  0,         false},
+    { "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 2,  0,         false},
 
-    { ":",   0, opid2(':','?'),     ASSOC_RIGHT, 1,  0 },
+    { ":",   0, opid2(':','?'),     ASSOC_RIGHT, 1,  0,         false},
 
-    { ",",   2, opid1(','),         ASSOC_LEFT,  0,  0 }
+    { ",",   2, opid1(','),         ASSOC_LEFT,  0,  0,         false}
 };
 static const size_t c_operator_count = (sizeof(c_operators) / sizeof(c_operators[0]));
 
 static const oper_info fte_operators[] = {
-    { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX}, /* paren expression - non function call */
-
-    { "++",  1, opid3('S','+','+'), ASSOC_LEFT,  15, OP_SUFFIX},
-    { "--",  1, opid3('S','-','-'), ASSOC_LEFT,  15, OP_SUFFIX},
-    { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0 },
-    { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0 }, /* function call */
-    { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0 }, /* array subscript */
-
-    { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
-    { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-    { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-    { "++",  1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
-    { "--",  1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
-
-    { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0 },
-    { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0 },
-    { "&",   2, opid1('&'),         ASSOC_LEFT,  13, 0 },
-    { "|",   2, opid1('|'),         ASSOC_LEFT,  13, 0 },
-
-    { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0 },
-    { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0 },
-
-    { "<<",  2, opid2('<','<'),     ASSOC_LEFT,  11, 0 },
-    { ">>",  2, opid2('>','>'),     ASSOC_LEFT,  11, 0 },
-
-    { "<",   2, opid1('<'),         ASSOC_LEFT,  10, 0 },
-    { ">",   2, opid1('>'),         ASSOC_LEFT,  10, 0 },
-    { "<=",  2, opid2('<','='),     ASSOC_LEFT,  10, 0 },
-    { ">=",  2, opid2('>','='),     ASSOC_LEFT,  10, 0 },
-    { "==",  2, opid2('=','='),     ASSOC_LEFT,  10,  0 },
-    { "!=",  2, opid2('!','='),     ASSOC_LEFT,  10,  0 },
-
-    { "?",   3, opid2('?',':'),     ASSOC_RIGHT, 9,  0 },
-
-    { "=",   2, opid1('='),         ASSOC_RIGHT, 8,  0 },
-    { "+=",  2, opid2('+','='),     ASSOC_RIGHT, 8,  0 },
-    { "-=",  2, opid2('-','='),     ASSOC_RIGHT, 8,  0 },
-    { "*=",  2, opid2('*','='),     ASSOC_RIGHT, 8,  0 },
-    { "/=",  2, opid2('/','='),     ASSOC_RIGHT, 8,  0 },
-    { "%=",  2, opid2('%','='),     ASSOC_RIGHT, 8,  0 },
-    { "&=",  2, opid2('&','='),     ASSOC_RIGHT, 8,  0 },
-    { "|=",  2, opid2('|','='),     ASSOC_RIGHT, 8,  0 },
-    { "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8,  0 },
-
-    { "&&",  2, opid2('&','&'),     ASSOC_LEFT,  5,  0 },
-    { "||",  2, opid2('|','|'),     ASSOC_LEFT,  5,  0 },
+    { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX, false}, /* paren expression - non function call */
+
+    { "++",  1, opid3('S','+','+'), ASSOC_LEFT,  15, OP_SUFFIX, false},
+    { "--",  1, opid3('S','-','-'), ASSOC_LEFT,  15, OP_SUFFIX, false},
+    { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0,         false},
+    { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0,         false}, /* function call */
+    { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0,         false}, /* array subscript */
+
+    { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX, true},
+    { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX, false},
+    { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX, true},
+    { "++",  1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
+    { "--",  1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
+
+    { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0,         true},
+    { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0,         true},
+    { "&",   2, opid1('&'),         ASSOC_LEFT,  13, 0,         true},
+    { "|",   2, opid1('|'),         ASSOC_LEFT,  13, 0,         true},
+
+    { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0,         true},
+    { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0,         true},
+
+    { "<<",  2, opid2('<','<'),     ASSOC_LEFT,  11, 0,         true},
+    { ">>",  2, opid2('>','>'),     ASSOC_LEFT,  11, 0,         true},
+
+    { "<",   2, opid1('<'),         ASSOC_LEFT,  10, 0,         false},
+    { ">",   2, opid1('>'),         ASSOC_LEFT,  10, 0,         false},
+    { "<=",  2, opid2('<','='),     ASSOC_LEFT,  10, 0,         false},
+    { ">=",  2, opid2('>','='),     ASSOC_LEFT,  10, 0,         false},
+    { "==",  2, opid2('=','='),     ASSOC_LEFT,  10, 0,         false},
+    { "!=",  2, opid2('!','='),     ASSOC_LEFT,  10, 0,         false},
+
+    { "?",   3, opid2('?',':'),     ASSOC_RIGHT, 9,  0,         true},
+
+    { "=",   2, opid1('='),         ASSOC_RIGHT, 8,  0,         false},
+    { "+=",  2, opid2('+','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "-=",  2, opid2('-','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "*=",  2, opid2('*','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "/=",  2, opid2('/','='),     ASSOC_RIGHT, 8,  0,         true},
+    { "%=",  2, opid2('%','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "&=",  2, opid2('&','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "|=",  2, opid2('|','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8,  0,         false},
+
+    { "&&",  2, opid2('&','&'),     ASSOC_LEFT,  5,  0,         true},
+    { "||",  2, opid2('|','|'),     ASSOC_LEFT,  5,  0,         true},
 
     /* Leave precedence 3 for : with -fcorrect-ternary */
-    { ",",   2, opid1(','),         ASSOC_LEFT,  2,  0 },
-    { ":",   0, opid2(':','?'),     ASSOC_RIGHT, 1,  0 }
+    { ",",   2, opid1(','),         ASSOC_LEFT,  2,  0,         false},
+    { ":",   0, opid2(':','?'),     ASSOC_RIGHT, 1,  0,         false}
 };
 static const size_t fte_operator_count = (sizeof(fte_operators) / sizeof(fte_operators[0]));
 
 static const oper_info qcc_operators[] = {
-    { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX}, /* paren expression - non function call */
-
-    { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0 },
-    { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0 }, /* function call */
-    { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0 }, /* array subscript */
-
-    { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX },
-    { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-    { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX },
-
-    { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0 },
-    { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0 },
-    { "&",   2, opid1('&'),         ASSOC_LEFT,  13, 0 },
-    { "|",   2, opid1('|'),         ASSOC_LEFT,  13, 0 },
-
-    { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0 },
-    { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0 },
-
-    { "<",   2, opid1('<'),         ASSOC_LEFT,  10, 0 },
-    { ">",   2, opid1('>'),         ASSOC_LEFT,  10, 0 },
-    { "<=",  2, opid2('<','='),     ASSOC_LEFT,  10, 0 },
-    { ">=",  2, opid2('>','='),     ASSOC_LEFT,  10, 0 },
-    { "==",  2, opid2('=','='),     ASSOC_LEFT,  10,  0 },
-    { "!=",  2, opid2('!','='),     ASSOC_LEFT,  10,  0 },
-
-    { "=",   2, opid1('='),         ASSOC_RIGHT, 8,  0 },
-    { "+=",  2, opid2('+','='),     ASSOC_RIGHT, 8,  0 },
-    { "-=",  2, opid2('-','='),     ASSOC_RIGHT, 8,  0 },
-    { "*=",  2, opid2('*','='),     ASSOC_RIGHT, 8,  0 },
-    { "/=",  2, opid2('/','='),     ASSOC_RIGHT, 8,  0 },
-    { "%=",  2, opid2('%','='),     ASSOC_RIGHT, 8,  0 },
-    { "&=",  2, opid2('&','='),     ASSOC_RIGHT, 8,  0 },
-    { "|=",  2, opid2('|','='),     ASSOC_RIGHT, 8,  0 },
-
-    { "&&",  2, opid2('&','&'),     ASSOC_LEFT,  5,  0 },
-    { "||",  2, opid2('|','|'),     ASSOC_LEFT,  5,  0 },
-
-    { ",",   2, opid1(','),         ASSOC_LEFT,  2,  0 },
+    { "(",   0, opid1('('),         ASSOC_LEFT,  99, OP_PREFIX, false}, /* paren expression - non function call */
+
+    { ".",   2, opid1('.'),         ASSOC_LEFT,  15, 0,         false},
+    { "(",   0, opid1('('),         ASSOC_LEFT,  15, 0,         false}, /* function call */
+    { "[",   2, opid1('['),         ASSOC_LEFT,  15, 0,         false}, /* array subscript */
+
+    { "!",   1, opid2('!', 'P'),    ASSOC_RIGHT, 14, OP_PREFIX, true},
+    { "+",   1, opid2('+','P'),     ASSOC_RIGHT, 14, OP_PREFIX, false},
+    { "-",   1, opid2('-','P'),     ASSOC_RIGHT, 14, OP_PREFIX, true},
+
+    { "*",   2, opid1('*'),         ASSOC_LEFT,  13, 0,         true},
+    { "/",   2, opid1('/'),         ASSOC_LEFT,  13, 0,         true},
+    { "&",   2, opid1('&'),         ASSOC_LEFT,  13, 0,         true},
+    { "|",   2, opid1('|'),         ASSOC_LEFT,  13, 0,         true},
+
+    { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0,         true},
+    { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0,         true},
+
+    { "<",   2, opid1('<'),         ASSOC_LEFT,  10, 0,         false},
+    { ">",   2, opid1('>'),         ASSOC_LEFT,  10, 0,         false},
+    { "<=",  2, opid2('<','='),     ASSOC_LEFT,  10, 0,         false},
+    { ">=",  2, opid2('>','='),     ASSOC_LEFT,  10, 0,         false},
+    { "==",  2, opid2('=','='),     ASSOC_LEFT,  10, 0,         false},
+    { "!=",  2, opid2('!','='),     ASSOC_LEFT,  10, 0,         false},
+
+    { "=",   2, opid1('='),         ASSOC_RIGHT, 8,  0,         false},
+    { "+=",  2, opid2('+','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "-=",  2, opid2('-','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "*=",  2, opid2('*','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "/=",  2, opid2('/','='),     ASSOC_RIGHT, 8,  0,         true},
+    { "%=",  2, opid2('%','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "&=",  2, opid2('&','='),     ASSOC_RIGHT, 8,  0,         false},
+    { "|=",  2, opid2('|','='),     ASSOC_RIGHT, 8,  0,         false},
+
+    { "&&",  2, opid2('&','&'),     ASSOC_LEFT,  5,  0,         true},
+    { "||",  2, opid2('|','|'),     ASSOC_LEFT,  5,  0,         true},
+
+    { ",",   2, opid1(','),         ASSOC_LEFT,  2,  0,         false},
 };
 static const size_t qcc_operator_count = (sizeof(qcc_operators) / sizeof(qcc_operators[0]));
 
index 9f53b42..e7c7816 100644 (file)
--- a/parser.c
+++ b/parser.c
  */
 #include <string.h>
 #include <math.h>
+#include "parser.h"
 
-#include "gmqcc.h"
-#include "lexer.h"
-#include "ast.h"
-
-/* beginning of locals */
 #define PARSER_HT_LOCALS  2
-
 #define PARSER_HT_SIZE    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_t         *_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);
@@ -145,42 +67,6 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *
 }
 
 /**********************************************************************
- * some maths used for constant folding
- */
-
-vec3_t vec3_add(vec3_t a, vec3_t b)
-{
-    vec3_t out;
-    out.x = a.x + b.x;
-    out.y = a.y + b.y;
-    out.z = a.z + b.z;
-    return out;
-}
-
-vec3_t vec3_sub(vec3_t a, vec3_t b)
-{
-    vec3_t out;
-    out.x = a.x - b.x;
-    out.y = a.y - b.y;
-    out.z = a.z - b.z;
-    return out;
-}
-
-qcfloat_t vec3_mulvv(vec3_t a, vec3_t b)
-{
-    return (a.x * b.x + a.y * b.y + a.z * b.z);
-}
-
-vec3_t vec3_mulvf(vec3_t a, float b)
-{
-    vec3_t out;
-    out.x = a.x * b;
-    out.y = a.y * b;
-    out.z = a.z * b;
-    return out;
-}
-
-/**********************************************************************
  * parsing
  */
 
@@ -199,53 +85,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_t 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 +97,6 @@ static char *parser_strdup(const char *str)
     return util_strdup(str);
 }
 
-static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate)
-{
-    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, vec3_t 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)
-{
-    vec3_t 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);
@@ -514,32 +287,6 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     return true;
 }
 
-static bool immediate_is_true(lex_ctx_t ctx, ast_value *v)
-{
-    switch (v->expression.vtype) {
-        case TYPE_FLOAT:
-            return !!v->constval.vfloat;
-        case TYPE_INTEGER:
-            return !!v->constval.vint;
-        case TYPE_VECTOR:
-            if (OPTS_FLAG(CORRECT_LOGIC))
-                return v->constval.vvec.x &&
-                       v->constval.vvec.y &&
-                       v->constval.vvec.z;
-            else
-                return !!(v->constval.vvec.x);
-        case TYPE_STRING:
-            if (!v->constval.vstring)
-                return false;
-            if (v->constval.vstring && OPTS_FLAG(TRUE_EMPTY_STRINGS))
-                return true;
-            return !!v->constval.vstring[0];
-        default:
-            compile_error(ctx, "internal error: immediate_is_true on invalid type");
-            return !!v->constval.vfunc;
-    }
-}
-
 static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 {
     const oper_info *op;
@@ -606,14 +353,11 @@ 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)
+
+    /* preform any constant folding on operator usage first */
+    if ((out = fold_op(parser->fold, op, exprs)))
+        goto complete;
+
     switch (op->id)
     {
         default:
@@ -710,20 +454,13 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         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),
+                    out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F,
+                                                              (ast_expression*)parser->fold->imm_float[0],
                                                               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),
+                    out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V,
+                                                              (ast_expression*)parser->fold->imm_vector[0],
                                                               exprs[0]);
                     break;
                 default:
@@ -736,30 +473,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         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_unary_new(ctx, INSTR_NOT_F, exprs[0]);
+                    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
-                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[0]);
+                    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 {
-                        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]);
-                    }
+                    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:
@@ -786,18 +509,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             }
             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]);
+                    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]);
+                    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",
@@ -817,16 +532,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             }
             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]);
+                    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]);
+                    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",
@@ -851,93 +560,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             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)));
-                        else
-                            out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_FV, exprs[0], exprs[1]);
-                    }
+                        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]);
-                    }
+                        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]);
-                    }
+                        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])) {
-                            vec3_t 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])) {
-                            vec3_t 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]);
-                        }
-                        else
-                            out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_V, exprs[0], exprs[1]);
-                    }
+                        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",
@@ -953,30 +584,10 @@ 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)));
-                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);
-                }
-            }
+            if (exprs[0]->vtype == TYPE_FLOAT) 
+                out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
+            else if (exprs[0]->vtype == TYPE_VECTOR)
+                out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], out);
             else
             {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
@@ -992,10 +603,6 @@ 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_t)ConstF(0)) % ((qcint_t)ConstF(1))));
             } else {
                 /* generate a call to __builtin_mod */
                 ast_expression *mod  = intrin_func(parser, "mod");
@@ -1022,14 +629,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               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_t)ConstF(0)) | ((qcint_t)ConstF(1)) ) :
-                                            (float)( ((qcint_t)ConstF(0)) & ((qcint_t)ConstF(1)) ) ));
-            else
-                out = (ast_expression*)ast_binary_new(ctx,
-                    (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
-                    exprs[0], exprs[1]);
+            out = (ast_expression*)ast_binary_new(ctx,
+                (op->id == opid1('|') ? INSTR_BITOR : INSTR_BITAND),
+                exprs[0], exprs[1]);
             break;
         case opid1('^'):
             /*
@@ -1084,35 +686,31 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
              * 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_t)(ConstF(0)) ^ ((qcint_t)(ConstF(1)))));
-                } else {
-                    ast_binary *expr = ast_binary_new(
+                ast_binary *expr = ast_binary_new(
+                    ctx,
+                    INSTR_SUB_F,
+                    (ast_expression*)parser->fold->imm_float[2],
+                    (ast_expression*)ast_binary_new(
                         ctx,
-                        INSTR_SUB_F,
-                        (ast_expression*)parser_const_float_neg1(parser),
+                        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_BITAND,
+                            INSTR_BITOR,
                             exprs[0],
                             exprs[1]
-                        )
+                        ),
+                        (ast_expression*)expr
                     );
-                    expr->refs = AST_REF_NONE;
-
-                    out = (ast_expression*)
-                        ast_binary_new(
-                            ctx,
-                            INSTR_BITAND,
-                            (ast_expression*)ast_binary_new(
-                                ctx,
-                                INSTR_BITOR,
-                                exprs[0],
-                                exprs[1]
-                            ),
-                            (ast_expression*)expr
-                        );
-                }
             } else {
                 /*
                  * The first is a vector: vector is allowed to xor with vector and
@@ -1123,33 +721,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                      * 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_t)(ConstV(0).x)) ^ ((qcint_t)(ConstV(1).x))),
-                            (float)(((qcint_t)(ConstV(0).y)) ^ ((qcint_t)(ConstV(1).y))),
-                            (float)(((qcint_t)(ConstV(0).z)) ^ ((qcint_t)(ConstV(1).z)))
-                        );
-                    } else {
-                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
-                        return false;
-                    }
+                    compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against vector");
+                    return false;
                 } else {
-                    /*
-                     * Xor all the values of the vector components against the
-                     * scalar in question.
-                     */
-                    if (CanConstFold(exprs[0], exprs[1])) {
-                        out = (ast_expression*)parser_const_vector_f(
-                            parser,
-                            (float)(((qcint_t)(ConstV(0).x)) ^ ((qcint_t)(ConstF(1)))),
-                            (float)(((qcint_t)(ConstV(0).y)) ^ ((qcint_t)(ConstF(1)))),
-                            (float)(((qcint_t)(ConstV(0).z)) ^ ((qcint_t)(ConstF(1))))
-                        );
-                    } else {
-                        compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
-                        return false;
-                    }
+                    compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor for vector against float");
+                    return false;
                 }
             }
 
@@ -1157,13 +733,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         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;
-            }
         case opid3('<','<','='):
         case opid3('>','>','='):
             compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
@@ -1173,53 +742,37 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             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);
+            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));
+                compile_error(ctx, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2);
+                return false;
             }
-            else
-            {
-                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));
-                    compile_error(ctx, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2);
-                    return false;
-                }
-                for (i = 0; i < 2; ++i) {
-                    if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->vtype == TYPE_VECTOR) {
-                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]);
-                        if (!out) break;
-                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
-                        if (!out) break;
-                        exprs[i] = out; out = NULL;
-                        if (OPTS_FLAG(PERL_LOGIC)) {
-                            /* here we want to keep the right expressions' type */
-                            break;
-                        }
+            for (i = 0; i < 2; ++i) {
+                if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->vtype == TYPE_VECTOR) {
+                    out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]);
+                    if (!out) break;
+                    out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
+                    if (!out) break;
+                    exprs[i] = out; out = NULL;
+                    if (OPTS_FLAG(PERL_LOGIC)) {
+                        /* here we want to keep the right expressions' type */
+                        break;
                     }
-                    else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->vtype == TYPE_STRING) {
-                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]);
-                        if (!out) break;
-                        out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
-                        if (!out) break;
-                        exprs[i] = out; out = NULL;
-                        if (OPTS_FLAG(PERL_LOGIC)) {
-                            /* here we want to keep the right expressions' type */
-                            break;
-                        }
+                }
+                else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->vtype == TYPE_STRING) {
+                    out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]);
+                    if (!out) break;
+                    out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
+                    if (!out) break;
+                    exprs[i] = out; out = NULL;
+                    if (OPTS_FLAG(PERL_LOGIC)) {
+                        /* here we want to keep the right expressions' type */
+                        break;
                     }
                 }
-                out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
             }
+            out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
             break;
 
         case opid2('?',':'):
@@ -1234,10 +787,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
-                out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
+            out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
             break;
 
         case opid2('*', '*'):
@@ -1248,10 +798,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     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"));
                 vec_push(gencall->params, exprs[0]);
@@ -1268,15 +814,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     ty1, ty2);
 
                 return false;
-            }
-
-            if (CanConstFold(exprs[0], exprs[1])) {
-                if (ConstF(0) < ConstF(1))
-                    out = (ast_expression*)parser_const_float_neg1(parser);
-                else if (ConstF(0) == ConstF(1))
-                    out = (ast_expression*)parser_const_float_0(parser);
-                else if (ConstF(0) > ConstF(1))
-                    out = (ast_expression*)parser_const_float_1(parser);
             } else {
                 ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
 
@@ -1286,15 +823,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]
                         /* } */
                         )
                     /* } */
@@ -1326,9 +863,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = CanConstFold(exprs[0], exprs[1])
-                ? (ast_expression*)parser_const_float(parser, ConstF(0) != ConstF(1))
-                : (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+            out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
             break;
         case opid2('=', '='):
             if (exprs[0]->vtype != exprs[1]->vtype) {
@@ -1337,9 +872,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                               type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = CanConstFold(exprs[0], exprs[1])
-                ? (ast_expression*)parser_const_float(parser, ConstF(0) == ConstF(1))
-                : (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
+            out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
             break;
 
         case opid1('='):
@@ -1423,11 +956,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
                                                         exprs[0],
-                                                        (ast_expression*)parser_const_float_1(parser));
+                                                        (ast_expression*)parser->fold->imm_float[1]);
             } else {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop,
                                                         exprs[0],
-                                                        (ast_expression*)parser_const_float_1(parser));
+                                                        (ast_expression*)parser->fold->imm_float[1]);
             }
             break;
         case opid3('S','+','+'):
@@ -1451,17 +984,17 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
                                                         exprs[0],
-                                                        (ast_expression*)parser_const_float_1(parser));
+                                                        (ast_expression*)parser->fold->imm_float[1]);
             } else {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STORE_F, addop,
                                                         exprs[0],
-                                                        (ast_expression*)parser_const_float_1(parser));
+                                                        (ast_expression*)parser->fold->imm_float[1]);
             }
             if (!out)
                 return false;
             out = (ast_expression*)ast_binary_new(ctx, subop,
                                                   out,
-                                                  (ast_expression*)parser_const_float_1(parser));
+                                                  (ast_expression*)parser->fold->imm_float[1]);
             break;
         case opid2('+','='):
         case opid2('-','='):
@@ -1529,14 +1062,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         out = (ast_expression*)ast_binstore_new(ctx, assignop, INSTR_MUL_VF,
                                                                 exprs[0], exprs[1]);
                     } else {
-                        /* there's no DIV_VF */
-                        if (CanConstFold1(exprs[1])) {
-                            out = (ast_expression*)parser_const_float(parser, 1.0 / ConstF(1));
-                        } else {
-                            out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
-                                                                  (ast_expression*)parser_const_float_1(parser),
+                        out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
+                                                                  (ast_expression*)parser->fold->imm_float[1],
                                                                   exprs[1]);
-                        }
                         if (!out) {
                             compile_error(ctx, "internal error: failed to generate division");
                             return false;
@@ -1605,16 +1133,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 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_t)ConstF(0));
-            else
-                out = (ast_expression*)
-                    ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
+            out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser->fold->imm_float[2], exprs[0]);
             break;
     }
 #undef NotSameType
-
+complete:
     if (!out) {
         compile_error(ctx, "failed to apply operator %s", op->op);
         return false;
@@ -1669,7 +1192,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
         ast_type_to_string(vec_last(sy->out).out, ty, sizeof(ty));
         ast_unref(vec_last(sy->out).out);
         sy->out[fid] = syexp(ast_ctx(vec_last(sy->out).out),
-                             (ast_expression*)parser_const_string(parser, ty, false));
+                             (ast_expression*)fold_constgen_string(parser->fold, ty, false));
         vec_shrinkby(sy->out, 1);
         return true;
     }
@@ -1699,7 +1222,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);
         }
     }
 
@@ -1930,7 +1453,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));
@@ -1955,35 +1478,31 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
         return true;
     }
     else if (parser->tok == TOKEN_FLOATCONST) {
-        ast_value *val;
-        val = parser_const_float(parser, (parser_token(parser)->constval.f));
+        ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f));
         if (!val)
             return false;
-        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
-        ast_value *val;
-        val = parser_const_float(parser, (double)(parser_token(parser)->constval.i));
+        ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i));
         if (!val)
             return false;
-        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_STRINGCONST) {
-        ast_value *val;
-        val = parser_const_string(parser, parser_tokval(parser), false);
+        ast_expression *val = fold_constgen_string(parser->fold, parser_tokval(parser), false);
         if (!val)
             return false;
-        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_VECTORCONST) {
-        ast_value *val;
-        val = parser_const_vector(parser, parser_token(parser)->constval.v);
+        ast_expression *val = fold_constgen_vector(parser->fold, parser_token(parser)->constval.v);
         if (!val)
             return false;
-        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_IDENT)
@@ -2018,7 +1537,7 @@ 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")) {
@@ -2318,7 +1837,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;
                     }
@@ -4337,7 +3856,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         self_think     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think);
 
         time_plus_1    = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F,
-                         gbl_time, (ast_expression*)parser_const_float(parser, 0.1));
+                         gbl_time, (ast_expression*)fold_constgen_float(parser->fold, 0.1));
 
         if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
             if (self_frame)     ast_delete(self_frame);
@@ -4451,9 +3970,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));
     }
 
     parser->function = func;
@@ -4512,7 +4030,7 @@ static ast_expression *array_accessor_split(
 
     cmp = ast_binary_new(ctx, INSTR_LT,
                          (ast_expression*)index,
-                         (ast_expression*)parser_const_float(parser, middle));
+                         (ast_expression*)fold_constgen_float(parser->fold, middle));
     if (!cmp) {
         ast_delete(left);
         ast_delete(right);
@@ -4545,7 +4063,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));
         if (!subscript)
             return NULL;
 
@@ -4611,7 +4129,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));
         if (!subscript)
             return NULL;
 
@@ -4674,7 +4192,7 @@ static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast
         ast_return      *ret;
         ast_array_index *subscript;
 
-        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
         if (!subscript)
             return NULL;
 
@@ -6309,9 +5827,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);
@@ -6340,6 +5855,7 @@ parser_t *parser_create()
         parser->reserved_version = NULL;
     }
 
+    parser->fold = fold_init(parser);
     return parser;
 }
 
@@ -6409,15 +5925,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]);
     }
@@ -6426,11 +5933,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);
 
@@ -6473,6 +5975,7 @@ static void parser_remove_ast(parser_t *parser)
 
     util_htdel(parser->aliases);
     intrin_intrinsics_destroy(parser);
+    fold_cleanup(parser->fold);
 }
 
 void parser_cleanup(parser_t *parser)
@@ -6564,27 +6067,9 @@ bool parser_finish(parser_t *parser, const char *output)
         }
     }
     /* Now we can generate immediates */
-    for (i = 0; i < vec_size(parser->imm_float); ++i) {
-        if (!ast_global_codegen(parser->imm_float[i], ir, false)) {
-            con_out("failed to generate global %s\n", parser->imm_float[i]->name);
-            ir_builder_delete(ir);
-            return false;
-        }
-    }
-    for (i = 0; i < vec_size(parser->imm_string); ++i) {
-        if (!ast_global_codegen(parser->imm_string[i], ir, false)) {
-            con_out("failed to generate global %s\n", parser->imm_string[i]->name);
-            ir_builder_delete(ir);
-            return false;
-        }
-    }
-    for (i = 0; i < vec_size(parser->imm_vector); ++i) {
-        if (!ast_global_codegen(parser->imm_vector[i], ir, false)) {
-            con_out("failed to generate global %s\n", parser->imm_vector[i]->name);
-            ir_builder_delete(ir);
-            return false;
-        }
-    }
+    if (!fold_generate(parser->fold, ir))
+        return false;
+
     for (i = 0; i < vec_size(parser->globals); ++i) {
         ast_value *asvalue;
         if (!ast_istype(parser->globals[i], ast_value))
diff --git a/parser.h b/parser.h
new file mode 100644 (file)
index 0000000..0869210
--- /dev/null
+++ b/parser.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2012, 2013
+ *     Wolfgang Bumiller
+ *     Dale Weiler
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef GMQCC_PARSER_HDR
+#define GMQCC_PARSER_HDR
+#include "gmqcc.h"
+#include "lexer.h"
+#include "ast.h"
+
+typedef struct {
+    struct parser_s *parser;
+    ast_value      **imm_float;              /* vector<ast_value*> */
+    ast_value      **imm_vector;             /* vector<ast_value*> */
+    ast_value      **imm_string;             /* vector<ast_value*> */
+    hash_table_t    *imm_string_untranslate; /* map<string, ast_value*> */
+    hash_table_t    *imm_string_dotranslate; /* map<string, ast_value*> */
+} fold_t;
+
+#define parser_ctx(p)    ((p)->lex->tok.ctx)
+typedef struct parser_s {
+    lex_file *lex;
+    int      tok;
+
+    bool     ast_cleaned;
+
+    ast_expression **globals;
+    ast_expression **fields;
+    ast_function **functions;
+    size_t         translated;
+
+    /* must be deleted first, they reference immediates and values */
+    ast_value    **accessors;
+
+    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_t         *_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;
+
+    fold_t *fold;
+} parser_t;
+
+
+char *parser_strdup(const char *str);
+
+/* fold.c */
+fold_t         *fold_init           (parser_t *);
+void            fold_cleanup        (fold_t *);
+ast_expression *fold_constgen_float (fold_t *, qcfloat_t);
+ast_expression *fold_constgen_vector(fold_t *, vec3_t);
+ast_expression *fold_constgen_string(fold_t *, const char *, bool);
+bool            fold_generate       (fold_t *, ir_builder *);
+ast_expression *fold_op             (fold_t *, const oper_info *, ast_expression**);
+#endif