]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
reclassify_token should only deal with tokens < TOKEN_START... should fix #113
[xonotic/gmqcc.git] / parser.c
index bd4f1c14b75936394f238fc9d91547c57955a30b..cbae8d3cc68aaef87fa7ea14c42d202b379a0c56 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -2,7 +2,7 @@
  * 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
@@ -21,8 +21,7 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-#include <stdio.h>
-#include <stdarg.h>
+#include <string.h>
 #include <math.h>
 
 #include "gmqcc.h"
 #define PARSER_HT_SIZE    128
 #define TYPEDEF_HT_SIZE   16
 
-typedef struct {
+typedef struct parser_s {
     lex_file *lex;
     int      tok;
 
+    bool     ast_cleaned;
+
     ast_expression **globals;
     ast_expression **fields;
     ast_function **functions;
@@ -47,6 +48,8 @@ typedef struct {
     ast_value    **imm_vector;
     size_t         translated;
 
+    ht ht_imm_string;
+
     /* must be deleted first, they reference immediates and values */
     ast_value    **accessors;
 
@@ -101,6 +104,9 @@ typedef struct {
 
     /* collected information */
     size_t     max_param_count;
+
+    /* code generator */
+    code_t     *code;
 } parser_t;
 
 static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
@@ -120,8 +126,6 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool
 static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname);
 static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
 static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
-static ast_expression *parser_builtin_pow(parser_t *);
-static ast_expression *parser_builtin_exp(parser_t *);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
@@ -182,7 +186,7 @@ vector vec3_mulvf(vector a, float b)
  * parsing
  */
 
-bool parser_next(parser_t *parser)
+static bool parser_next(parser_t *parser)
 {
     /* lex_do kills the previous token */
     parser->tok = lex_do(parser->lex);
@@ -217,6 +221,7 @@ static ast_value* parser_const_float(parser_t *parser, double d)
     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;
@@ -255,22 +260,36 @@ static char *parser_strdup(const char *str)
 
 static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate)
 {
-    size_t i;
+    size_t hash = util_hthash(parser->ht_imm_string, str);
     ast_value *out;
+    if ( (out = (ast_value*)util_htgeth(parser->ht_imm_string, str, hash)) ) {
+        if (dotranslate && out->name[0] == '#') {
+            char name[32];
+            util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
+            ast_value_set_name(out, name);
+            out->expression.flags |= AST_FLAG_INCLUDE_DEF;
+        }
+        return out;
+    }
+    /*
     for (i = 0; i < vec_size(parser->imm_string); ++i) {
         if (!strcmp(parser->imm_string[i]->constval.vstring, str))
             return parser->imm_string[i];
     }
+    */
     if (dotranslate) {
         char name[32];
-        snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
+        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(parser->ht_imm_string, str, hash, out);
     return out;
 }
 
@@ -285,6 +304,7 @@ static ast_value* parser_const_vector(parser_t *parser, vector v)
     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;
@@ -382,6 +402,9 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t
     return NULL;
 }
 
+/* include intrinsics */
+#include "intrin.h"
+
 typedef struct
 {
     size_t etype; /* 0 = expression, others are operators */
@@ -565,7 +588,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         blocks[i] = sy->out[vec_size(sy->out)+i].block;
         asvalue[i] = (ast_value*)exprs[i];
 
-        if (exprs[i]->expression.vtype == TYPE_NOEXPR &&
+        if (exprs[i]->vtype == TYPE_NOEXPR &&
             !(i != 0 && op->id == opid2('?',':')) &&
             !(i == 1 && op->id == opid1('.')))
         {
@@ -583,11 +606,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     }
 
 #define NotSameType(T) \
-             (exprs[0]->expression.vtype != exprs[1]->expression.vtype || \
-              exprs[0]->expression.vtype != T)
+             (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)->expression.vtype != TYPE_FUNCTION)
+              (A)->vtype != TYPE_FUNCTION)
 #define CanConstFold(A, B) \
              (CanConstFold1(A) && CanConstFold1(B))
 #define ConstV(i) (asvalue[(i)]->constval.vvec)
@@ -600,8 +623,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             return false;
 
         case opid1('.'):
-            if (exprs[0]->expression.vtype == TYPE_VECTOR &&
-                exprs[1]->expression.vtype == TYPE_NOEXPR)
+            if (exprs[0]->vtype == TYPE_VECTOR &&
+                exprs[1]->vtype == TYPE_NOEXPR)
             {
                 if      (exprs[1] == (ast_expression*)parser->const_vec[0])
                     out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
@@ -614,14 +637,14 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     return false;
                 }
             }
-            else if (exprs[0]->expression.vtype == TYPE_ENTITY) {
-                if (exprs[1]->expression.vtype != TYPE_FIELD) {
+            else if (exprs[0]->vtype == TYPE_ENTITY) {
+                if (exprs[1]->vtype != TYPE_FIELD) {
                     compile_error(ast_ctx(exprs[1]), "type error: right hand of member-operand should be an entity-field");
                     return false;
                 }
                 out = (ast_expression*)ast_entfield_new(ctx, exprs[0], exprs[1]);
             }
-            else if (exprs[0]->expression.vtype == TYPE_VECTOR) {
+            else if (exprs[0]->vtype == TYPE_VECTOR) {
                 compile_error(ast_ctx(exprs[1]), "vectors cannot be accessed this way");
                 return false;
             }
@@ -632,15 +655,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
 
         case opid1('['):
-            if (exprs[0]->expression.vtype != TYPE_ARRAY &&
-                !(exprs[0]->expression.vtype == TYPE_FIELD &&
-                  exprs[0]->expression.next->expression.vtype == TYPE_ARRAY))
+            if (exprs[0]->vtype != TYPE_ARRAY &&
+                !(exprs[0]->vtype == TYPE_FIELD &&
+                  exprs[0]->next->vtype == TYPE_ARRAY))
             {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "cannot index value of type %s", ty1);
                 return false;
             }
-            if (exprs[1]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[1]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[1]), "index must be of type float, not %s", ty1);
                 return false;
@@ -687,7 +710,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             out = exprs[0];
             break;
         case opid2('-','P'):
-            switch (exprs[0]->expression.vtype) {
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
                     if (CanConstFold1(exprs[0]))
                         out = (ast_expression*)parser_const_float(parser, -ConstF(0));
@@ -707,13 +730,13 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                 compile_error(ctx, "invalid types used in expression: cannot negate type %s",
-                              type_name[exprs[0]->expression.vtype]);
+                              type_name[exprs[0]->vtype]);
                 return false;
             }
             break;
 
         case opid2('!','P'):
-            switch (exprs[0]->expression.vtype) {
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
                     if (CanConstFold1(exprs[0]))
                         out = (ast_expression*)parser_const_float(parser, !ConstF(0));
@@ -749,21 +772,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                 compile_error(ctx, "invalid types used in expression: cannot logically negate type %s",
-                              type_name[exprs[0]->expression.vtype]);
+                              type_name[exprs[0]->vtype]);
                 return false;
             }
             break;
 
         case opid1('+'):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
-                (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+            if (exprs[0]->vtype != exprs[1]->vtype ||
+                (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
             {
                 compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
-            switch (exprs[0]->expression.vtype) {
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
                     if (CanConstFold(exprs[0], exprs[1]))
                     {
@@ -780,21 +803,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                     compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
-                                  type_name[exprs[0]->expression.vtype],
-                                  type_name[exprs[1]->expression.vtype]);
+                                  type_name[exprs[0]->vtype],
+                                  type_name[exprs[1]->vtype]);
                     return false;
             };
             break;
         case opid1('-'):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
-                (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+            if (exprs[0]->vtype != exprs[1]->vtype ||
+                (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
             {
                 compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
-                              type_name[exprs[1]->expression.vtype],
-                              type_name[exprs[0]->expression.vtype]);
+                              type_name[exprs[1]->vtype],
+                              type_name[exprs[0]->vtype]);
                 return false;
             }
-            switch (exprs[0]->expression.vtype) {
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
                     if (CanConstFold(exprs[0], exprs[1]))
                         out = (ast_expression*)parser_const_float(parser, ConstF(0) - ConstF(1));
@@ -809,27 +832,27 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                     compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
-                                  type_name[exprs[1]->expression.vtype],
-                                  type_name[exprs[0]->expression.vtype]);
+                                  type_name[exprs[1]->vtype],
+                                  type_name[exprs[0]->vtype]);
                     return false;
             };
             break;
         case opid1('*'):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype &&
-                !(exprs[0]->expression.vtype == TYPE_VECTOR &&
-                  exprs[1]->expression.vtype == TYPE_FLOAT) &&
-                !(exprs[1]->expression.vtype == TYPE_VECTOR &&
-                  exprs[0]->expression.vtype == TYPE_FLOAT)
+            if (exprs[0]->vtype != exprs[1]->vtype &&
+                !(exprs[0]->vtype == TYPE_VECTOR &&
+                  exprs[1]->vtype == TYPE_FLOAT) &&
+                !(exprs[1]->vtype == TYPE_VECTOR &&
+                  exprs[0]->vtype == TYPE_FLOAT)
                 )
             {
                 compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
-                              type_name[exprs[1]->expression.vtype],
-                              type_name[exprs[0]->expression.vtype]);
+                              type_name[exprs[1]->vtype],
+                              type_name[exprs[0]->vtype]);
                 return false;
             }
-            switch (exprs[0]->expression.vtype) {
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
-                    if (exprs[1]->expression.vtype == TYPE_VECTOR)
+                    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)));
@@ -845,7 +868,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     }
                     break;
                 case TYPE_VECTOR:
-                    if (exprs[1]->expression.vtype == TYPE_FLOAT)
+                    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)));
@@ -861,7 +884,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                             if (!vec.y && !vec.z) { /* 'n 0 0' * v */
                                 ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
                                 out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL);
-                                out->expression.node.keep = false;
+                                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);
@@ -869,7 +892,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                             else if (!vec.x && !vec.z) { /* '0 n 0' * v */
                                 ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
                                 out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL);
-                                out->expression.node.keep = false;
+                                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);
@@ -877,7 +900,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                             else if (!vec.x && !vec.y) { /* '0 n 0' * v */
                                 ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
                                 out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL);
-                                out->expression.node.keep = false;
+                                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);
@@ -890,7 +913,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                             if (!vec.y && !vec.z) { /* v * 'n 0 0' */
                                 ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
                                 out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
-                                out->expression.node.keep = false;
+                                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));
@@ -898,7 +921,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                             else if (!vec.x && !vec.z) { /* v * '0 n 0' */
                                 ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
                                 out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
-                                out->expression.node.keep = false;
+                                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));
@@ -906,7 +929,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                             else if (!vec.x && !vec.y) { /* v * '0 n 0' */
                                 ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
                                 out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
-                                out->expression.node.keep = false;
+                                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));
@@ -920,25 +943,25 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                     compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
-                                  type_name[exprs[1]->expression.vtype],
-                                  type_name[exprs[0]->expression.vtype]);
+                                  type_name[exprs[1]->vtype],
+                                  type_name[exprs[0]->vtype]);
                     return false;
             };
             break;
         case opid1('/'):
-            if (exprs[1]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[1]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                 compile_error(ctx, "invalid types used in expression: cannot divide tyeps %s and %s", ty1, ty2);
                 return false;
             }
-            if (exprs[0]->expression.vtype == TYPE_FLOAT) {
+            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]->expression.vtype == TYPE_VECTOR) {
+            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 {
@@ -964,16 +987,41 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 return false;
             }
             break;
+
         case opid1('%'):
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s",
+                    type_name[exprs[0]->vtype],
+                    type_name[exprs[1]->vtype]);
+                return false;
+            }
+            if (CanConstFold(exprs[0], exprs[1])) {
+                out = (ast_expression*)parser_const_float(parser,
+                            (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1))));
+            } else {
+                /* generate a call to __builtin_mod */
+                ast_expression *mod  = intrin_func(parser, "mod");
+                ast_call       *call = NULL;
+                if (!mod) return false; /* can return null for missing floor */
+
+                call = ast_call_new(parser_ctx(parser), mod);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+
+                out = (ast_expression*)call;
+            }
+            break;
+
         case opid2('%','='):
-            compile_error(ctx, "qc does not have a modulo operator");
+            compile_error(ctx, "%= is unimplemented");
             return false;
+
         case opid1('|'):
         case opid1('&'):
             if (NotSameType(TYPE_FLOAT)) {
                 compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
             if (CanConstFold(exprs[0], exprs[1]))
@@ -993,9 +1041,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         case opid2('>','>'):
             if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) {
                 if (op->id == opid2('<','<'))
-                    out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) << (int)(ConstF(1))));
+                    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)((int)(ConstF(0)) >> (int)(ConstF(1))));
+                    out = (ast_expression*)parser_const_float(parser, (double)((unsigned int)(ConstF(0)) >> (unsigned int)(ConstF(1))));
                 break;
             }
         case opid3('<','<','='):
@@ -1029,7 +1077,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     return false;
                 }
                 for (i = 0; i < 2; ++i) {
-                    if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->expression.vtype == TYPE_VECTOR) {
+                    if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->vtype == TYPE_VECTOR) {
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]);
                         if (!out) break;
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
@@ -1040,7 +1088,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                             break;
                         }
                     }
-                    else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->expression.vtype == TYPE_STRING) {
+                    else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->vtype == TYPE_STRING) {
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]);
                         if (!out) break;
                         out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
@@ -1087,7 +1135,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             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), parser_builtin_pow(parser));
+                ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow"));
                 vec_push(gencall->params, exprs[0]);
                 vec_push(gencall->params, exprs[1]);
                 out = (ast_expression*)gencall;
@@ -1147,49 +1195,49 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             generated_op += INSTR_LE;
             if (NotSameType(TYPE_FLOAT)) {
                 compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
             out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
             break;
         case opid2('!', '='):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) {
+            if (exprs[0]->vtype != exprs[1]->vtype) {
                 compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]);
+            out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
             break;
         case opid2('=', '='):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) {
+            if (exprs[0]->vtype != exprs[1]->vtype) {
                 compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
-                              type_name[exprs[0]->expression.vtype],
-                              type_name[exprs[1]->expression.vtype]);
+                              type_name[exprs[0]->vtype],
+                              type_name[exprs[1]->vtype]);
                 return false;
             }
-            out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]);
+            out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
             break;
 
         case opid1('='):
             if (ast_istype(exprs[0], ast_entfield)) {
                 ast_expression *field = ((ast_entfield*)exprs[0])->field;
                 if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
-                    exprs[0]->expression.vtype == TYPE_FIELD &&
-                    exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+                    exprs[0]->vtype == TYPE_FIELD &&
+                    exprs[0]->next->vtype == TYPE_VECTOR)
                 {
                     assignop = type_storep_instr[TYPE_VECTOR];
                 }
                 else
-                    assignop = type_storep_instr[exprs[0]->expression.vtype];
-                if (assignop == VINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
+                    assignop = type_storep_instr[exprs[0]->vtype];
+                if (assignop == VINSTR_END || !ast_compare_type(field->next, exprs[1]))
                 {
-                    ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
+                    ast_type_to_string(field->next, ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) &&
-                        field->expression.next->expression.vtype == TYPE_FUNCTION &&
-                        exprs[1]->expression.vtype == TYPE_FUNCTION)
+                        field->next->vtype == TYPE_FUNCTION &&
+                        exprs[1]->vtype == TYPE_FUNCTION)
                     {
                         (void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES,
                                                "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
@@ -1201,13 +1249,13 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             else
             {
                 if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
-                    exprs[0]->expression.vtype == TYPE_FIELD &&
-                    exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+                    exprs[0]->vtype == TYPE_FIELD &&
+                    exprs[0]->next->vtype == TYPE_VECTOR)
                 {
                     assignop = type_store_instr[TYPE_VECTOR];
                 }
                 else {
-                    assignop = type_store_instr[exprs[0]->expression.vtype];
+                    assignop = type_store_instr[exprs[0]->vtype];
                 }
 
                 if (assignop == VINSTR_END) {
@@ -1220,8 +1268,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) &&
-                        exprs[0]->expression.vtype == TYPE_FUNCTION &&
-                        exprs[1]->expression.vtype == TYPE_FUNCTION)
+                        exprs[0]->vtype == TYPE_FUNCTION &&
+                        exprs[1]->vtype == TYPE_FUNCTION)
                     {
                         (void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES,
                                                "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
@@ -1238,7 +1286,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         case opid3('+','+','P'):
         case opid3('-','-','P'):
             /* prefix ++ */
-            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[0]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "invalid type for prefix increment: %s", ty1);
                 return false;
@@ -1263,7 +1311,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         case opid3('S','+','+'):
         case opid3('S','-','-'):
             /* prefix ++ */
-            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[0]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "invalid type for suffix increment: %s", ty1);
                 return false;
@@ -1295,8 +1343,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
         case opid2('+','='):
         case opid2('-','='):
-            if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
-                (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+            if (exprs[0]->vtype != exprs[1]->vtype ||
+                (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
             {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
@@ -1308,10 +1356,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                assignop = type_storep_instr[exprs[0]->vtype];
             else
-                assignop = type_store_instr[exprs[0]->expression.vtype];
-            switch (exprs[0]->expression.vtype) {
+                assignop = type_store_instr[exprs[0]->vtype];
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
                     out = (ast_expression*)ast_binstore_new(ctx, assignop,
                                                             (op->id == opid2('+','=') ? INSTR_ADD_F : INSTR_SUB_F),
@@ -1324,16 +1372,16 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                     compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s",
-                                  type_name[exprs[0]->expression.vtype],
-                                  type_name[exprs[1]->expression.vtype]);
+                                  type_name[exprs[0]->vtype],
+                                  type_name[exprs[1]->vtype]);
                     return false;
             };
             break;
         case opid2('*','='):
         case opid2('/','='):
-            if (exprs[1]->expression.vtype != TYPE_FLOAT ||
-                !(exprs[0]->expression.vtype == TYPE_FLOAT ||
-                  exprs[0]->expression.vtype == TYPE_VECTOR))
+            if (exprs[1]->vtype != TYPE_FLOAT ||
+                !(exprs[0]->vtype == TYPE_FLOAT ||
+                  exprs[0]->vtype == TYPE_VECTOR))
             {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
@@ -1345,10 +1393,10 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                assignop = type_storep_instr[exprs[0]->vtype];
             else
-                assignop = type_store_instr[exprs[0]->expression.vtype];
-            switch (exprs[0]->expression.vtype) {
+                assignop = type_store_instr[exprs[0]->vtype];
+            switch (exprs[0]->vtype) {
                 case TYPE_FLOAT:
                     out = (ast_expression*)ast_binstore_new(ctx, assignop,
                                                             (op->id == opid2('*','=') ? INSTR_MUL_F : INSTR_DIV_F),
@@ -1377,8 +1425,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     break;
                 default:
                     compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s",
-                                  type_name[exprs[0]->expression.vtype],
-                                  type_name[exprs[1]->expression.vtype]);
+                                  type_name[exprs[0]->vtype],
+                                  type_name[exprs[1]->vtype]);
                     return false;
             };
             break;
@@ -1395,9 +1443,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield))
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                assignop = type_storep_instr[exprs[0]->vtype];
             else
-                assignop = type_store_instr[exprs[0]->expression.vtype];
+                assignop = type_store_instr[exprs[0]->vtype];
             out = (ast_expression*)ast_binstore_new(ctx, assignop,
                                                     (op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
                                                     exprs[0], exprs[1]);
@@ -1415,9 +1463,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 return false;
             }
             if (ast_istype(exprs[0], ast_entfield))
-                assignop = type_storep_instr[exprs[0]->expression.vtype];
+                assignop = type_storep_instr[exprs[0]->vtype];
             else
-                assignop = type_store_instr[exprs[0]->expression.vtype];
+                assignop = type_store_instr[exprs[0]->vtype];
             out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
             if (!out)
                 return false;
@@ -1430,7 +1478,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
 
         case opid2('~', 'P'):
-            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+            if (exprs[0]->vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
                 return false;
@@ -1526,7 +1574,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
 
     if (ast_istype(fun, ast_value)) {
         funval = (ast_value*)fun;
-        if ((fun->expression.flags & AST_FLAG_VARIADIC) &&
+        if ((fun->flags & AST_FLAG_VARIADIC) &&
             !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
         {
             call->va_count = (ast_expression*)parser_const_float(parser, (double)paramcount);
@@ -1536,18 +1584,18 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
     /* overwrite fid, the function, with a call */
     sy->out[fid] = syexp(call->expression.node.context, (ast_expression*)call);
 
-    if (fun->expression.vtype != TYPE_FUNCTION) {
-        parseerror(parser, "not a function (%s)", type_name[fun->expression.vtype]);
+    if (fun->vtype != TYPE_FUNCTION) {
+        parseerror(parser, "not a function (%s)", type_name[fun->vtype]);
         return false;
     }
 
-    if (!fun->expression.next) {
+    if (!fun->next) {
         parseerror(parser, "could not determine function return type");
         return false;
     } else {
         ast_value *fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL);
 
-        if (fun->expression.flags & AST_FLAG_DEPRECATED) {
+        if (fun->flags & AST_FLAG_DEPRECATED) {
             if (!fval) {
                 return !parsewarning(parser, WARN_DEPRECATED,
                         "call to function (which is marked deprecated)\n",
@@ -1567,22 +1615,22 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
                     ast_ctx(fun).line);
         }
 
-        if (vec_size(fun->expression.params) != paramcount &&
-            !((fun->expression.flags & AST_FLAG_VARIADIC) &&
-              vec_size(fun->expression.params) < paramcount))
+        if (vec_size(fun->params) != paramcount &&
+            !((fun->flags & AST_FLAG_VARIADIC) &&
+              vec_size(fun->params) < paramcount))
         {
-            const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many";
+            const char *fewmany = (vec_size(fun->params) > paramcount) ? "few" : "many";
             if (fval)
                 return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
                                      "too %s parameters for call to %s: expected %i, got %i\n"
                                      " -> `%s` has been declared here: %s:%i",
-                                     fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
+                                     fewmany, fval->name, (int)vec_size(fun->params), (int)paramcount,
                                      fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
             else
                 return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
                                      "too %s parameters for function call: expected %i, got %i\n"
                                      " -> it has been declared here: %s:%i",
-                                     fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+                                     fewmany, (int)vec_size(fun->params), (int)paramcount,
                                      ast_ctx(fun).file, (int)ast_ctx(fun).line);
         }
     }
@@ -1642,6 +1690,8 @@ static bool parser_close_paren(parser_t *parser, shunt *sy)
 static void parser_reclassify_token(parser_t *parser)
 {
     size_t i;
+    if (parser->tok >= TOKEN_START)
+        return;
     for (i = 0; i < operator_count; ++i) {
         if (!strcmp(parser_tokval(parser), operators[i].op)) {
             parser->tok = TOKEN_OPERATOR;
@@ -1743,6 +1793,9 @@ static ast_expression* parse_vararg(parser_t *parser)
     return out;
 }
 
+/* not to be exposed */
+extern bool ftepp_predef_exists(const char *name);
+
 static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
 {
     if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
@@ -1833,7 +1886,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
             /* When adding more intrinsics, fix the above condition */
             prev = NULL;
         }
-        if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
+        if (prev && prev->vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
         {
             var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
         } else {
@@ -1849,16 +1902,21 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                 vec_push(parser->labels, lbl);
             }
         }
+        if (!var && !strcmp(parser_tokval(parser), "__FUNC__"))
+            var = (ast_expression*)parser_const_string(parser, parser->function->name, false);
         if (!var) {
             /* intrinsics */
             if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
                 var = (ast_expression*)intrinsic_debug_typestring;
             }
-            if (!strcmp(parser_tokval(parser), "__builtin_pow"))
-                var = parser_builtin_pow(parser);
-            if (!strcmp(parser_tokval(parser), "__builtin_exp"))
-                var = parser_builtin_exp(parser);
-                
+            /* now we try for the real intrinsic hashtable. If the string
+             * begins with __builtin, we simply skip past it, otherwise we
+             * use the identifier as is.
+             */
+            else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
+                var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */);
+            }
+
             if (!var) {
                 char *correct = NULL;
                 size_t i;
@@ -1868,13 +1926,9 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                  * i've done this thousands of times already myself.  Lets check for
                  * it in the predef table.  And diagnose it better :)
                  */
-                if (!OPTS_FLAG(FTEPP_PREDEFS)) {
-                    for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) {
-                        if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) {
-                            parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
-                            return false;
-                        }
-                    }
+                if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) {
+                    parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
+                    return false;
                 }
 
                 /*
@@ -1884,7 +1938,7 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                  * We should also consider adding correction tables for
                  * other things as well.
                  */
-                if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) {
+                if (OPTS_OPTION_BOOL(OPTION_CORRECTION) && strlen(parser_tokval(parser)) <= 16) {
                     correction_t corr;
                     correct_init(&corr);
 
@@ -2138,8 +2192,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             wantop = true;
         }
         else {
-            parseerror(parser, "expected operator or end of statement");
-            goto onerr;
+            /* in this case we might want to allow constant string concatenation */
+            bool concatenated = false;
+            if (parser->tok == TOKEN_STRINGCONST && vec_size(sy.out)) {
+                ast_expression *lexpr = vec_last(sy.out).out;
+                if (ast_istype(lexpr, ast_value)) {
+                    ast_value *last = (ast_value*)lexpr;
+                    if (last->isimm == true && last->cvq == CV_CONST &&
+                        last->hasvalue && last->expression.vtype == TYPE_STRING)
+                    {
+                        char *newstr = NULL;
+                        util_asprintf(&newstr, "%s%s", last->constval.vstring, parser_tokval(parser));
+                        vec_last(sy.out).out = (ast_expression*)parser_const_string(parser, newstr, false);
+                        mem_d(newstr);
+                        concatenated = true;
+                    }
+                }
+            }
+            if (!concatenated) {
+                parseerror(parser, "expected operator or end of statement");
+                goto onerr;
+            }
         }
 
         if (!parser_next(parser)) {
@@ -2159,8 +2232,8 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
     }
 
     parser->lex->flags.noops = true;
-    if (!vec_size(sy.out)) {
-        parseerror(parser, "empty expression");
+    if (vec_size(sy.out) != 1) {
+        parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out));
         expr = NULL;
     } else
         expr = sy.out[0].out;
@@ -2295,13 +2368,13 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond,
     ast_unary *unary;
     ast_expression *prev;
 
-    if (cond->expression.vtype == TYPE_VOID || cond->expression.vtype >= TYPE_VARIANT) {
+    if (cond->vtype == TYPE_VOID || cond->vtype >= TYPE_VARIANT) {
         char ty[1024];
         ast_type_to_string(cond, ty, sizeof(ty));
         compile_error(ast_ctx(cond), "invalid type for if() condition: %s", ty);
     }
 
-    if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING)
+    if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->vtype == TYPE_STRING)
     {
         prev = cond;
         cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond);
@@ -2312,7 +2385,7 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond,
         }
         ifnot = !ifnot;
     }
-    else if (OPTS_FLAG(CORRECT_LOGIC) && cond->expression.vtype == TYPE_VECTOR)
+    else if (OPTS_FLAG(CORRECT_LOGIC) && cond->vtype == TYPE_VECTOR)
     {
         /* vector types need to be cast to true booleans */
         ast_binary *bin = (ast_binary*)cond;
@@ -2385,17 +2458,17 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
     /* closing paren */
     if (parser->tok != ')') {
         parseerror(parser, "expected closing paren after 'if' condition");
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     /* parse into the 'then' branch */
     if (!parser_next(parser)) {
         parseerror(parser, "expected statement for on-true branch of 'if'");
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     if (!parse_statement_or_block(parser, &ontrue)) {
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     if (!ontrue)
@@ -2406,12 +2479,12 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         if (!parser_next(parser)) {
             parseerror(parser, "expected on-false branch after 'else'");
             ast_delete(ontrue);
-            ast_delete(cond);
+            ast_unref(cond);
             return false;
         }
         if (!parse_statement_or_block(parser, &onfalse)) {
             ast_delete(ontrue);
-            ast_delete(cond);
+            ast_unref(cond);
             return false;
         }
     }
@@ -2508,23 +2581,23 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **
     /* closing paren */
     if (parser->tok != ')') {
         parseerror(parser, "expected closing paren after 'while' condition");
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     /* parse into the 'then' branch */
     if (!parser_next(parser)) {
         parseerror(parser, "expected while-loop body");
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     if (!parse_statement_or_block(parser, &ontrue)) {
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
 
     cond = process_condition(parser, cond, &ifnot);
     if (!cond) {
-        ast_delete(ontrue);
+        ast_unref(ontrue);
         return false;
     }
     aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue);
@@ -2624,21 +2697,21 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression
     if (parser->tok != ')') {
         parseerror(parser, "expected closing paren after 'while' condition");
         ast_delete(ontrue);
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
     /* parse on */
     if (!parser_next(parser) || parser->tok != ';') {
         parseerror(parser, "expected semicolon after condition");
         ast_delete(ontrue);
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
 
     if (!parser_next(parser)) {
         parseerror(parser, "parse error");
         ast_delete(ontrue);
-        ast_delete(cond);
+        ast_unref(cond);
         return false;
     }
 
@@ -2711,7 +2784,6 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
     ast_expression *initexpr, *cond, *increment, *ontrue;
     ast_value      *typevar;
 
-    bool retval = true;
     bool ifnot  = false;
 
     lex_ctx ctx = parser_ctx(parser);
@@ -2811,21 +2883,25 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
     aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue);
     *out = (ast_expression*)aloop;
 
-    if (!parser_leaveblock(parser))
-        retval = false;
-    return retval;
+    if (!parser_leaveblock(parser)) {
+        ast_delete(aloop);
+        return false;
+    }
+    return true;
 onerr:
-    if (initexpr)  ast_delete(initexpr);
-    if (cond)      ast_delete(cond);
-    if (increment) ast_delete(increment);
+    if (initexpr)  ast_unref(initexpr);
+    if (cond)      ast_unref(cond);
+    if (increment) ast_unref(increment);
     (void)!parser_leaveblock(parser);
     return false;
 }
 
 static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out)
 {
-    ast_expression *exp = NULL;
-    ast_return     *ret = NULL;
+    ast_expression *exp      = NULL;
+    ast_expression *var      = NULL;
+    ast_return     *ret      = NULL;
+    ast_value      *retval   = parser->function->return_value;
     ast_value      *expected = parser->function->vtype;
 
     lex_ctx ctx = parser_ctx(parser);
@@ -2837,29 +2913,87 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
         return false;
     }
 
+    /* return assignments */
+    if (parser->tok == '=') {
+        if (!OPTS_FLAG(RETURN_ASSIGNMENTS)) {
+            parseerror(parser, "return assignments not activated, try using -freturn-assigments");
+            return false;
+        }
+
+        if (type_store_instr[expected->expression.next->vtype] == VINSTR_END) {
+            char ty1[1024];
+            ast_type_to_string(expected->expression.next, ty1, sizeof(ty1));
+            parseerror(parser, "invalid return type: `%s'", ty1);
+            return false;
+        }
+
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected return assignment expression");
+            return false;
+        }
+
+        if (!(exp = parse_expression_leave(parser, false, false, false)))
+            return false;
+
+        /* prepare the return value */
+        if (!retval) {
+            retval = ast_value_new(ctx, "#LOCAL_RETURN", TYPE_VOID);
+            ast_type_adopt(retval, expected->expression.next);
+            parser->function->return_value = retval;
+        }
+
+        if (!ast_compare_type(exp, (ast_expression*)retval)) {
+            char ty1[1024], ty2[1024];
+            ast_type_to_string(exp, ty1, sizeof(ty1));
+            ast_type_to_string(&retval->expression, ty2, sizeof(ty2));
+            parseerror(parser, "invalid type for return value: `%s', expected `%s'", ty1, ty2);
+        }
+
+        /* store to 'return' local variable */
+        var = (ast_expression*)ast_store_new(
+            ctx,
+            type_store_instr[expected->expression.next->vtype],
+            (ast_expression*)retval, exp);
+
+        if (!var) {
+            ast_unref(exp);
+            return false;
+        }
+
+        if (parser->tok != ';')
+            parseerror(parser, "missing semicolon after return assignment");
+        else if (!parser_next(parser))
+            parseerror(parser, "parse error after return assignment");
+
+        *out = var;
+        return true;
+    }
+
     if (parser->tok != ';') {
         exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
 
-        if (exp->expression.vtype != TYPE_NIL &&
-            exp->expression.vtype != expected->expression.next->expression.vtype)
+        if (exp->vtype != TYPE_NIL &&
+            exp->vtype != ((ast_expression*)expected)->next->vtype)
         {
             parseerror(parser, "return with invalid expression");
         }
 
         ret = ast_return_new(ctx, exp);
         if (!ret) {
-            ast_delete(exp);
+            ast_unref(exp);
             return false;
         }
     } else {
         if (!parser_next(parser))
             parseerror(parser, "parse error");
-        if (expected->expression.next->expression.vtype != TYPE_VOID) {
+
+        if (!retval && expected->expression.next->vtype != TYPE_VOID)
+        {
             (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
         }
-        ret = ast_return_new(ctx, NULL);
+        ret = ast_return_new(ctx, (ast_expression*)retval);
     }
     *out = (ast_expression*)ret;
     return true;
@@ -3322,185 +3456,6 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
     return true;
 }
 
-ast_expression *parser_builtin_pow(parser_t *parser) {
-    /*
-     * float __builtin_pow(float x, float y) { 
-     *   float value = 1.0f;
-     *   while (y > 0) {
-     *     while (!(y&1)) {
-     *       y *= 0.25f;
-     *       x *= x;
-     *     }
-     *     y--;
-     *     value *= x;
-     *   }
-     *   return value;
-     * }      
-     */
-    static ast_function  *pow_func       = NULL;
-    static ast_value     *pow_func_val   = NULL; 
-    if (!pow_func) {
-        ast_value    *pow_arguments[2];
-        ast_value    *pow_value          = ast_value_new   (parser_ctx(parser), "value",         TYPE_FLOAT);
-        ast_block    *pow_body           = ast_block_new   (parser_ctx(parser));
-        ast_block    *pow_loop_body      = ast_block_new   (parser_ctx(parser));
-        ast_block    *pow_loop_nest_body = ast_block_new   (parser_ctx(parser));
-        ast_loop     *pow_loop           = NULL;
-        ast_loop     *pow_loop_nest      = NULL;
-
-        pow_arguments[0]                 = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
-        pow_arguments[1]                 = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
-        pow_func_val                     = ast_value_new   (parser_ctx(parser), "__builtin_pow", TYPE_FUNCTION);
-        pow_func_val->expression.next    = (ast_expression*)ast_value_new(parser_ctx(parser), "<float>", TYPE_FLOAT);
-
-        vec_push(pow_func_val->expression.params, pow_arguments[0]);
-        vec_push(pow_func_val->expression.params, pow_arguments[1]);
-
-        pow_func = ast_function_new(parser_ctx(parser), "__builtin_pow", pow_func_val);
-
-        /* float value; */
-        vec_push(pow_body->locals, pow_value);
-        /* value = 1.0f; */
-        vec_push(pow_body->exprs,
-            (ast_expression*)ast_store_new(
-                parser_ctx(parser),
-                INSTR_STORE_F,
-                (ast_expression*)pow_value,
-                (ast_expression*)parser_const_float_1(parser)
-            )
-        );
-
-        /* y >>= 2 */
-        vec_push(pow_loop_nest_body->exprs,
-            (ast_expression*)ast_binstore_new(
-                parser_ctx(parser),
-                INSTR_STORE_F,
-                INSTR_MUL_F,
-                (ast_expression*)pow_arguments[1],
-                (ast_expression*)parser_const_float(parser, 0.25f)
-            )
-        );
-        vec_push(pow_loop_nest_body->exprs,
-            (ast_expression*)ast_binstore_new(
-                parser_ctx(parser),
-                INSTR_STORE_F,
-                INSTR_MUL_F,
-                (ast_expression*)pow_arguments[0],
-                (ast_expression*)pow_arguments[0]
-            )
-        );
-
-        /* while (!(y&1)) */
-        pow_loop_nest = ast_loop_new (
-            parser_ctx(parser),
-            NULL,
-            (ast_expression*)ast_binary_new(
-                parser_ctx(parser),
-                INSTR_AND,
-                (ast_expression*)pow_arguments[1],
-                (ast_expression*)parser_const_float_1(parser)
-            ),
-            true,
-            NULL,
-            false,
-            NULL,
-            (ast_expression*)pow_loop_nest_body
-        );
-        
-        vec_push(pow_loop_body->exprs, (ast_expression*)pow_loop_nest);
-        vec_push(pow_loop_body->exprs,
-            (ast_expression*)ast_binstore_new(
-                parser_ctx(parser),
-                INSTR_STORE_F,
-                INSTR_SUB_F,
-                (ast_expression*)pow_arguments[1],
-                (ast_expression*)parser_const_float_1(parser)
-            )
-        );
-        vec_push(pow_loop_body->exprs,
-            (ast_expression*)ast_binstore_new(
-                parser_ctx(parser),
-                INSTR_STORE_F,
-                INSTR_MUL_F,
-                (ast_expression*)pow_value,
-                (ast_expression*)pow_arguments[0]
-            )
-        );
-
-        /* while (y > 0) { */
-        pow_loop = ast_loop_new(
-            parser_ctx(parser),
-            NULL,
-            (ast_expression*)ast_binary_new(
-                parser_ctx(parser),
-                INSTR_GT,
-                (ast_expression*)pow_arguments[1],
-                (ast_expression*)parser_const_float_0(parser)
-            ),
-            false,
-            NULL,
-            false,
-            NULL,
-            (ast_expression*)pow_loop_body
-        );
-        /* } */
-        vec_push(pow_body->exprs, (ast_expression*)pow_loop);
-        /* return value; */
-        vec_push(pow_body->exprs,
-            (ast_expression*)ast_return_new(
-                parser_ctx(parser),
-                (ast_expression*)pow_value
-            )
-        );
-
-        vec_push(pow_func->blocks, pow_body);
-        vec_push(parser->globals, (ast_expression*)pow_func_val);
-        vec_push(parser->functions, pow_func);
-    }
-
-    return (ast_expression*)pow_func_val;
-}
-
-#ifndef M_E
-#define M_E 2.71828182845905
-#endif
-static ast_expression *parser_builtin_exp(parser_t *parser) {
-    /*
-     * float __builtin_exp(float x) {
-     *     return __builtin_exp(E, x);
-     * }
-     */ 
-    static ast_value *exp_func_val = NULL;
-
-    if (!exp_func_val) {
-        ast_function *exp_func         = NULL;
-        ast_value    *arg              = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
-        ast_block    *exp_body         = ast_block_new   (parser_ctx(parser));
-        ast_call     *exp_call         = ast_call_new    (parser_ctx(parser), parser_builtin_pow(parser));
-        exp_func_val                   = ast_value_new   (parser_ctx(parser), "__builtin_exp", TYPE_FUNCTION);
-        exp_func_val->expression.next  = (ast_expression*)ast_value_new(parser_ctx(parser), "<float>", TYPE_FLOAT);
-        exp_func                       = ast_function_new(parser_ctx(parser), "__builtin_exp", exp_func_val);
-
-        vec_push(exp_call->params, (ast_expression*)parser_const_float(parser, M_E));
-        vec_push(exp_call->params, (ast_expression*)arg);
-
-        vec_push(exp_body->exprs,
-            (ast_expression*)ast_return_new(
-                parser_ctx(parser),
-                (ast_expression*)exp_call
-            )
-        );
-
-        vec_push(exp_func_val->expression.params, arg);
-        vec_push(exp_func->blocks, exp_body);
-
-        vec_push(parser->functions, exp_func);
-        vec_push(parser->globals, (ast_expression*)exp_func_val);
-    }
-
-    return (ast_expression*)exp_func_val;
-}
-
 /* parse computed goto sides */
 static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) {
     ast_expression *on_true;
@@ -3786,7 +3741,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             }
             return parse_typedef(parser);
         }
-        parseerror(parser, "Unexpected keyword");
+        parseerror(parser, "Unexpected keyword: `%s'", parser_tokval(parser));
         return false;
     }
     else if (parser->tok == '{')
@@ -3862,6 +3817,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
 static bool parse_enum(parser_t *parser)
 {
     bool        flag = false;
+    bool        reverse = false;
     qcfloat     num = 0;
     ast_value **values = NULL;
     ast_value  *var = NULL;
@@ -3876,19 +3832,28 @@ static bool parse_enum(parser_t *parser)
 
     /* enumeration attributes (can add more later) */
     if (parser->tok == ':') {
-        if (!parser_next(parser) || parser->tok != TOKEN_IDENT || strcmp(parser_tokval(parser), "flag")) {
-            parseerror(parser, "expected `flag` after enumeration attribute ':'");
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT){
+            parseerror(parser, "expected `flag` or `reverse` for enumeration attribute");
             return false;
         }
 
-        if (!parser_next(parser) || parser->tok != '{') {
-            parseerror(parser, "expected `{` after enum attribute `flag`");
+        /* attributes? */
+        if (!strcmp(parser_tokval(parser), "flag")) {
+            num  = 1;
+            flag = true;
+        }
+        else if (!strcmp(parser_tokval(parser), "reverse")) {
+            reverse = true;
+        }
+        else {
+            parseerror(parser, "invalid attribute `%s` for enumeration", parser_tokval(parser));
             return false;
         }
 
-        /* flagged enumeration start from 1 */
-        num  = 1;
-        flag = true;
+        if (!parser_next(parser) || parser->tok != '{') {
+            parseerror(parser, "expected `{` after enum attribute ");
+            return false;
+        }
     }
 
     while (true) {
@@ -3917,7 +3882,6 @@ static bool parse_enum(parser_t *parser)
 
         /* for flagged enumerations increment in POTs of TWO */
         var->constval.vfloat = (flag) ? (num *= 2) : (num ++);
-
         parser_addglobal(parser, var->name, (ast_expression*)var);
 
         if (!parser_next(parser)) {
@@ -3956,6 +3920,13 @@ static bool parse_enum(parser_t *parser)
         }
     }
 
+    /* patch them all (for reversed attribute) */
+    if (reverse) {
+        size_t i;
+        for (i = 0; i < vec_size(values); i++)
+            values[i]->constval.vfloat = vec_size(values) - i - 1;
+    }
+
     if (parser->tok != '}') {
         parseerror(parser, "internal error: breaking without `}`");
         goto onerror;
@@ -4161,9 +4132,9 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             /* qc allows the use of not-yet-declared functions here
              * - this automatically creates a prototype */
             ast_value      *thinkfunc;
-            ast_expression *functype = fld_think->expression.next;
+            ast_expression *functype = fld_think->next;
 
-            thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->expression.vtype);
+            thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->vtype);
             if (!thinkfunc) { /* || !ast_type_adopt(thinkfunc, functype)*/
                 ast_unref(framenum);
                 parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser));
@@ -4318,7 +4289,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
 
         if (param->expression.vtype != TYPE_VECTOR &&
             (param->expression.vtype != TYPE_FIELD ||
-             param->expression.next->expression.vtype != TYPE_VECTOR))
+             param->expression.next->vtype != TYPE_VECTOR))
         {
             continue;
         }
@@ -4346,13 +4317,13 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         varargs->expression.flags |= AST_FLAG_IS_VARARG;
         varargs->expression.next = (ast_expression*)ast_value_new(ast_ctx(var), NULL, TYPE_VECTOR);
         varargs->expression.count = 0;
-        snprintf(name, sizeof(name), "%s##va##SET", var->name);
+        util_snprintf(name, sizeof(name), "%s##va##SET", var->name);
         if (!parser_create_array_setter_proto(parser, varargs, name)) {
             ast_delete(varargs);
             ast_block_delete(block);
             goto enderrfn;
         }
-        snprintf(name, sizeof(name), "%s##va##GET", var->name);
+        util_snprintf(name, sizeof(name), "%s##va##GET", var->name);
         if (!parser_create_array_getter_proto(parser, varargs, varargs->expression.next, name)) {
             ast_delete(varargs);
             ast_block_delete(block);
@@ -4370,6 +4341,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     }
 
     vec_push(func->blocks, block);
+    
 
     parser->function = old;
     if (!parser_leaveblock(parser))
@@ -4448,7 +4420,7 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
         ast_store       *st;
         int assignop = type_store_instr[value->expression.vtype];
 
-        if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+        if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
             assignop = INSTR_STORE_V;
 
         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
@@ -4514,7 +4486,7 @@ static ast_expression *array_field_setter_node(
         ast_store       *st;
         int assignop = type_storep_instr[value->expression.vtype];
 
-        if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+        if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
             assignop = INSTR_STOREP_V;
 
         subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
@@ -4824,6 +4796,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
 
     /* for the sake of less code we parse-in in this function */
     if (!parser_next(parser)) {
+        ast_delete(var);
         parseerror(parser, "expected parameter list");
         return NULL;
     }
@@ -4923,6 +4896,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
 on_error:
     if (argcounter)
         mem_d(argcounter);
+    if (varparam)
+        ast_delete(varparam);
     ast_delete(var);
     for (i = 0; i < vec_size(params); ++i)
         ast_delete(params[i]);
@@ -5089,6 +5064,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
         /* parse on */
         if (!parser_next(parser)) {
             ast_delete(var);
+            mem_d(name);
             parseerror(parser, "error after variable or field declaration");
             return NULL;
         }
@@ -5098,8 +5074,10 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     if (parser->tok == '[') {
         wasarray = true;
         var = parse_arraysize(parser, var);
-        if (!var)
+        if (!var) {
+            if (name) mem_d(name);
             return NULL;
+        }
     }
 
     /* This is the point where we can turn it into a field */
@@ -5118,8 +5096,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     while (parser->tok == '(') {
         var = parse_parameter_list(parser, var);
         if (!var) {
-            if (name)
-                mem_d((void*)name);
+            if (name) mem_d(name);
             return NULL;
         }
     }
@@ -5128,11 +5105,12 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     if (name) {
         if (!ast_value_set_name(var, name)) {
             ast_delete(var);
+            mem_d(name);
             parseerror(parser, "internal error: failed to set name");
             return NULL;
         }
         /* free the name, ast_value_set_name duplicates */
-        mem_d((void*)name);
+        mem_d(name);
     }
 
     return var;
@@ -5231,7 +5209,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
     bool      cleanvar  = true;
     bool      wasarray  = false;
 
-    ast_member *me[3];
+    ast_member *me[3] = { NULL, NULL, NULL };
 
     if (!localblock && is_static)
         parseerror(parser, "`static` qualifier is not supported in global scope");
@@ -5288,7 +5266,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         /*
          * store the vstring back to var for alias and
          * deprecation messages.
-         */   
+         */
         if (var->expression.flags & AST_FLAG_DEPRECATED ||
             var->expression.flags & AST_FLAG_ALIAS)
             var->desc = vstring;
@@ -5363,7 +5341,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             {
                 /* deal with other globals */
                 old = parser_find_global(parser, var->name);
-                if (old && var->expression.vtype == TYPE_FUNCTION && old->expression.vtype == TYPE_FUNCTION)
+                if (old && var->expression.vtype == TYPE_FUNCTION && old->vtype == TYPE_FUNCTION)
                 {
                     /* This is a function which had a prototype */
                     if (!ast_istype(old, ast_value)) {
@@ -5385,7 +5363,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                         ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name);
                     if (!parser_check_qualifiers(parser, var, proto)) {
                         retval = false;
-                        if (proto->desc) 
+                        if (proto->desc)
                             mem_d(proto->desc);
                         proto = NULL;
                         goto cleanup;
@@ -5471,7 +5449,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
             if (var->expression.vtype == TYPE_VECTOR)
                 isvector = true;
             else if (var->expression.vtype == TYPE_FIELD &&
-                     var->expression.next->expression.vtype == TYPE_VECTOR)
+                     var->expression.next->vtype == TYPE_VECTOR)
                 isvector = true;
 
             if (isvector) {
@@ -5510,7 +5488,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             return false;
                         }
 
-                        if (var->expression.vtype != find->expression.vtype) {
+                        if (var->expression.vtype != find->vtype) {
                             char ty1[1024];
                             char ty2[1024];
 
@@ -5526,13 +5504,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                         /*
                          * add alias to aliases table and to corrector
                          * so corrections can apply for aliases as well.
-                         */  
+                         */
                         util_htset(parser->aliases, var->name, find);
 
                         /*
                          * add to corrector so corrections can work
                          * even for aliases too.
-                         */ 
+                         */
                         correct_add (
                              vec_last(parser->correct_variables),
                             &vec_last(parser->correct_variables_score),
@@ -5558,7 +5536,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                             /*
                              * add to corrector so corrections can work
                              * even for aliases too.
-                             */  
+                             */
                             correct_add (
                                  vec_last(parser->correct_variables),
                                 &vec_last(parser->correct_variables_score),
@@ -5650,16 +5628,16 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
          */
         if (var->expression.vtype == TYPE_ARRAY) {
             char name[1024];
-            snprintf(name, sizeof(name), "%s##SET", var->name);
+            util_snprintf(name, sizeof(name), "%s##SET", var->name);
             if (!parser_create_array_setter(parser, var, name))
                 goto cleanup;
-            snprintf(name, sizeof(name), "%s##GET", var->name);
+            util_snprintf(name, sizeof(name), "%s##GET", var->name);
             if (!parser_create_array_getter(parser, var, var->expression.next, name))
                 goto cleanup;
         }
         else if (!localblock && !nofields &&
                  var->expression.vtype == TYPE_FIELD &&
-                 var->expression.next->expression.vtype == TYPE_ARRAY)
+                 var->expression.next->vtype == TYPE_ARRAY)
         {
             char name[1024];
             ast_expression *telem;
@@ -5671,14 +5649,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 goto cleanup;
             }
 
-            snprintf(name, sizeof(name), "%s##SETF", var->name);
+            util_snprintf(name, sizeof(name), "%s##SETF", var->name);
             if (!parser_create_array_field_setter(parser, array, name))
                 goto cleanup;
 
             telem = ast_type_copy(ast_ctx(var), array->expression.next);
             tfield = ast_value_new(ast_ctx(var), "<.type>", TYPE_FIELD);
             tfield->expression.next = telem;
-            snprintf(name, sizeof(name), "%s##GETFP", var->name);
+            util_snprintf(name, sizeof(name), "%s##GETFP", var->name);
             if (!parser_create_array_getter(parser, array, (ast_expression*)tfield, name)) {
                 ast_delete(tfield);
                 goto cleanup;
@@ -5716,7 +5694,7 @@ skipvar:
             }
         }
 
-        if (parser->tok != '{') {
+        if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) {
             if (parser->tok != '=') {
                 parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
                 break;
@@ -5732,7 +5710,11 @@ skipvar:
         }
 
         if (parser->tok == '#') {
-            ast_function *func = NULL;
+            ast_function *func   = NULL;
+            ast_value    *number = NULL;
+            float         fractional;
+            float         integral;
+            int           builtin_num;
 
             if (localblock) {
                 parseerror(parser, "cannot declare builtins within functions");
@@ -5746,12 +5728,42 @@ skipvar:
                 parseerror(parser, "expected builtin number");
                 break;
             }
-            if (parser->tok != TOKEN_INTCONST) {
-                parseerror(parser, "builtin number must be an integer constant");
-                break;
-            }
-            if (parser_token(parser)->constval.i < 0) {
-                parseerror(parser, "builtin number must be an integer greater than zero");
+
+            if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS)) {
+                number = (ast_value*)parse_expression_leave(parser, true, false, false);
+                if (!number) {
+                    parseerror(parser, "builtin number expected");
+                    break;
+                }
+                if (!ast_istype(number, ast_value) || !number->hasvalue || number->cvq != CV_CONST)
+                {
+                    ast_unref(number);
+                    parseerror(parser, "builtin number must be a compile time constant");
+                    break;
+                }
+                if (number->expression.vtype == TYPE_INTEGER)
+                    builtin_num = number->constval.vint;
+                else if (number->expression.vtype == TYPE_FLOAT)
+                    builtin_num = number->constval.vfloat;
+                else {
+                    ast_unref(number);
+                    parseerror(parser, "builtin number must be an integer constant");
+                    break;
+                }
+                ast_unref(number);
+
+                fractional = modff(builtin_num, &integral);
+                if (builtin_num < 0 || fractional != 0) {
+                    parseerror(parser, "builtin number must be an integer greater than zero");
+                    break;
+                }
+
+                /* we only want the integral part anyways */
+                builtin_num = integral;
+            } else if (parser->tok == TOKEN_INTCONST) {
+                builtin_num = parser_token(parser)->constval.i;
+            } else {
+                parseerror(parser, "builtin number must be a compile time constant");
                 break;
             }
 
@@ -5770,10 +5782,13 @@ skipvar:
                 }
                 vec_push(parser->functions, func);
 
-                func->builtin = -parser_token(parser)->constval.i-1;
+                func->builtin = -builtin_num-1;
             }
 
-            if (!parser_next(parser)) {
+            if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS)
+                    ? (parser->tok != ',' && parser->tok != ';')
+                    : (!parser_next(parser)))
+            {
                 parseerror(parser, "expected comma or semicolon");
                 if (func)
                     ast_function_delete(func);
@@ -5781,7 +5796,22 @@ skipvar:
                 break;
             }
         }
-        else if (parser->tok == '{' || parser->tok == '[')
+        else if (var->expression.vtype == TYPE_ARRAY && parser->tok == '{')
+        {
+            if (localblock) {
+                /* Note that fteqcc and most others don't even *have*
+                 * local arrays, so this is not a high priority.
+                 */
+                parseerror(parser, "TODO: initializers for local arrays");
+                break;
+            }
+            /*
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
+*/
+            parseerror(parser, "TODO: initializing global arrays is not supported yet!");
+            break;
+        }
+        else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '['))
         {
             if (localblock) {
                 parseerror(parser, "cannot declare functions within functions");
@@ -5854,6 +5884,7 @@ skipvar:
                 }
                 vec_free(sy.out);
                 vec_free(sy.ops);
+                vec_free(sy.argc);
                 var->cvq = cvq;
             }
         }
@@ -6024,7 +6055,7 @@ static void generate_checksum(parser_t *parser)
         if (!ast_istype(parser->fields[i], ast_value))
             continue;
         value = (ast_value*)(parser->fields[i]);
-        switch (value->expression.next->expression.vtype) {
+        switch (value->expression.next->vtype) {
             case TYPE_FLOAT:    crc = progdefs_crc_both(crc, "\tfloat\t"); break;
             case TYPE_VECTOR:   crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
             case TYPE_STRING:   crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
@@ -6038,22 +6069,26 @@ static void generate_checksum(parser_t *parser)
     }
     crc = progdefs_crc_both(crc, "} entvars_t;\n\n");
 
-    code_crc = crc;
+    parser->code->crc = crc;
 }
 
-static parser_t *parser;
-
-bool parser_init()
+parser_t *parser_create()
 {
+    parser_t *parser;
     lex_ctx empty_ctx;
     size_t i;
 
     parser = (parser_t*)mem_a(sizeof(parser_t));
     if (!parser)
-        return false;
+        return NULL;
 
     memset(parser, 0, sizeof(*parser));
 
+    if (!(parser->code = code_init())) {
+        mem_d(parser);
+        return NULL;
+    }
+
     for (i = 0; i < operator_count; ++i) {
         if (operators[i].id == opid1('=')) {
             parser->assign_op = operators+i;
@@ -6063,7 +6098,7 @@ bool parser_init()
     if (!parser->assign_op) {
         printf("internal error: initializing parser: failed to find assign operator\n");
         mem_d(parser);
-        return false;
+        return NULL;
     }
 
     vec_push(parser->variables, parser->htfields  = util_htnew(PARSER_HT_SIZE));
@@ -6073,6 +6108,8 @@ bool parser_init()
 
     parser->aliases = util_htnew(PARSER_HT_SIZE);
 
+    parser->ht_imm_string = util_htnew(512);
+
     /* corrector */
     vec_push(parser->correct_variables, correct_trie_new());
     vec_push(parser->correct_variables_score, NULL);
@@ -6099,10 +6136,11 @@ bool parser_init()
     } else {
         parser->reserved_version = NULL;
     }
-    return true;
+
+    return parser;
 }
 
-bool parser_compile()
+static bool parser_compile(parser_t *parser)
 {
     /* initial lexer/parser state */
     parser->lex->flags.noops = true;
@@ -6134,29 +6172,32 @@ bool parser_compile()
     return !compile_errors;
 }
 
-bool parser_compile_file(const char *filename)
+bool parser_compile_file(parser_t *parser, const char *filename)
 {
     parser->lex = lex_open(filename);
     if (!parser->lex) {
         con_err("failed to open file \"%s\"\n", filename);
         return false;
     }
-    return parser_compile();
+    return parser_compile(parser);
 }
 
-bool parser_compile_string(const char *name, const char *str, size_t len)
+bool parser_compile_string(parser_t *parser, const char *name, const char *str, size_t len)
 {
     parser->lex = lex_open_string(str, len, name);
     if (!parser->lex) {
         con_err("failed to create lexer for string \"%s\"\n", name);
         return false;
     }
-    return parser_compile();
+    return parser_compile(parser);
 }
 
-void parser_cleanup()
+static void parser_remove_ast(parser_t *parser)
 {
     size_t i;
+    if (parser->ast_cleaned)
+        return;
+    parser->ast_cleaned = true;
     for (i = 0; i < vec_size(parser->accessors); ++i) {
         ast_delete(parser->accessors[i]->constval.vfunc);
         parser->accessors[i]->constval.vfunc = NULL;
@@ -6184,6 +6225,7 @@ void parser_cleanup()
     vec_free(parser->functions);
     vec_free(parser->imm_vector);
     vec_free(parser->imm_string);
+    util_htdel(parser->ht_imm_string);
     vec_free(parser->imm_float);
     vec_free(parser->globals);
     vec_free(parser->fields);
@@ -6224,11 +6266,18 @@ void parser_cleanup()
     ast_value_delete(parser->const_vec[2]);
 
     util_htdel(parser->aliases);
+    intrin_intrinsics_destroy(parser);
+}
+
+void parser_cleanup(parser_t *parser)
+{
+    parser_remove_ast(parser);
+    code_cleanup(parser->code);
 
     mem_d(parser);
 }
 
-bool parser_finish(const char *output)
+bool parser_finish(parser_t *parser, const char *output)
 {
     size_t i;
     ir_builder *ir;
@@ -6263,11 +6312,11 @@ bool parser_finish(const char *output)
             ast_expression *subtype;
             field->hasvalue = true;
             subtype = field->expression.next;
-            ifld = ir_builder_create_field(ir, field->name, subtype->expression.vtype);
-            if (subtype->expression.vtype == TYPE_FIELD)
-                ifld->fieldtype = subtype->expression.next->expression.vtype;
-            else if (subtype->expression.vtype == TYPE_FUNCTION)
-                ifld->outtype = subtype->expression.next->expression.vtype;
+            ifld = ir_builder_create_field(ir, field->name, subtype->vtype);
+            if (subtype->vtype == TYPE_FIELD)
+                ifld->fieldtype = subtype->next->vtype;
+            else if (subtype->vtype == TYPE_FUNCTION)
+                ifld->outtype = subtype->next->vtype;
             (void)!ir_value_set_field(field->ir_v, ifld);
         }
     }
@@ -6355,7 +6404,7 @@ bool parser_finish(const char *output)
     }
     for (i = 0; i < vec_size(parser->fields); ++i) {
         ast_value *asvalue;
-        asvalue = (ast_value*)(parser->fields[i]->expression.next);
+        asvalue = (ast_value*)(parser->fields[i]->next);
 
         if (!ast_istype((ast_expression*)asvalue, ast_value))
             continue;
@@ -6381,6 +6430,8 @@ bool parser_finish(const char *output)
             return false;
         }
     }
+
+    generate_checksum(parser);
     if (OPTS_OPTION_BOOL(OPTION_DUMP))
         ir_builder_dump(ir, con_out);
     for (i = 0; i < vec_size(parser->functions); ++i) {
@@ -6390,6 +6441,7 @@ bool parser_finish(const char *output)
             return false;
         }
     }
+    parser_remove_ast(parser);
 
     if (compile_Werrors) {
         con_out("*** there were warnings treated as errors\n");
@@ -6401,15 +6453,12 @@ bool parser_finish(const char *output)
         if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
             ir_builder_dump(ir, con_out);
 
-        generate_checksum(parser);
-
-        if (!ir_builder_generate(ir, output)) {
+        if (!ir_builder_generate(parser->code, ir, output)) {
             con_out("*** failed to generate output file\n");
             ir_builder_delete(ir);
             return false;
         }
     }
-
     ir_builder_delete(ir);
     return retval;
 }