]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Make the varargs counter more stable, it'll now work with a function pointer with...
[xonotic/gmqcc.git] / parser.c
index 97a4dffc5c40fbd071d7f0bc9c4903ffde0527b0..f5891e7f9864e74c264804154859c56dafbbd63d 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012
+ * Copyright (C) 2012, 2013
  *     Wolfgang Bumiller
  *     Dale Weiler
  * 
 
 #include "gmqcc.h"
 #include "lexer.h"
+#include "ast.h"
 
 /* beginning of locals */
 #define PARSER_HT_LOCALS  2
 
-#define PARSER_HT_SIZE    1024
+#define PARSER_HT_SIZE    128
 #define TYPEDEF_HT_SIZE   16
 
 enum parser_pot { POT_PAREN, POT_TERNARY1, POT_TERNARY2 };
@@ -53,6 +54,7 @@ typedef struct {
     ast_value *imm_float_one;
     ast_value *imm_vector_zero;
     ast_value *nil;
+    ast_value *reserved_version;
 
     size_t crc_globals;
     size_t crc_fields;
@@ -73,6 +75,10 @@ typedef struct {
     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;
@@ -83,35 +89,39 @@ typedef struct {
     /* we store the '=' operator info */
     const oper_info *assign_op;
 
-    /* TYPE_FIELD -> parser_find_fields is used instead of find_var
-     * TODO: TYPE_VECTOR -> x, y and z are accepted in the gmqcc standard
-     * anything else: type error
-     */
-    qcint  memberof;
-
     /* Keep track of our ternary vs parenthesis nesting state.
      * If we reach a 'comma' operator in a ternary without a paren,
      * we shall trigger -Wternary-precedence.
      */
     enum parser_pot *pot;
 
+    /* magic values */
+    ast_value *const_vec[3];
+
     /* pragma flags */
     bool noref;
+
+    /* collected information */
+    size_t     max_param_count;
 } parser_t;
 
-static const ast_expression *intrinsic_debug_typestring = (ast_expression*)0x10;
+static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1;
 
 static void parser_enterblock(parser_t *parser);
 static bool parser_leaveblock(parser_t *parser);
 static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
+static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e);
 static bool parse_typedef(parser_t *parser);
 static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, int qualifier, ast_value *cached_typedef, bool noref, bool is_static, uint32_t qflags, char *vstring);
 static ast_block* parse_block(parser_t *parser);
 static bool parse_block_into(parser_t *parser, ast_block *block);
 static bool parse_statement_or_block(parser_t *parser, ast_expression **out);
 static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases);
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue);
-static ast_expression* parse_expression(parser_t *parser, bool stopatcomma);
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
+static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels);
+static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname);
+static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
+static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
@@ -203,12 +213,18 @@ static ast_value* parser_const_float(parser_t *parser, double d)
 {
     size_t i;
     ast_value *out;
+    lex_ctx ctx;
     for (i = 0; i < vec_size(parser->imm_float); ++i) {
         const double compare = parser->imm_float[i]->constval.vfloat;
         if (memcmp((const void*)&compare, (const void *)&d, sizeof(double)) == 0)
             return parser->imm_float[i];
     }
-    out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_FLOAT);
+    if (parser->lex)
+        ctx = parser_ctx(parser);
+    else {
+        memset(&ctx, 0, sizeof(ctx));
+    }
+    out = ast_value_new(ctx, "#IMMEDIATE", TYPE_FLOAT);
     out->cvq      = CV_CONST;
     out->hasvalue = true;
     out->constval.vfloat = d;
@@ -299,6 +315,15 @@ static ast_expression* parser_find_field(parser_t *parser, const char *name)
     return ( ast_expression*)util_htget(parser->htfields, name);
 }
 
+static ast_expression* parser_find_label(parser_t *parser, const char *name)
+{
+    size_t i;
+    for(i = 0; i < vec_size(parser->labels); i++)
+        if (!strcmp(parser->labels[i]->name, name))
+            return (ast_expression*)parser->labels[i];
+    return NULL;
+}
+
 static ast_expression* parser_find_global(parser_t *parser, const char *name)
 {
     return (ast_expression*)util_htget(parser->htglobals, name);
@@ -523,8 +548,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     DEBUGSHUNTDO(con_out("apply %s\n", op->op));
 
     if (vec_size(sy->out) < op->operands) {
-        parseerror(parser, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out),
-                   op->op, (int)op->id);
+        compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out),
+                      op->op, (int)op->id);
         return false;
     }
 
@@ -539,10 +564,21 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         exprs[i]  = sy->out[vec_size(sy->out)+i].out;
         blocks[i] = sy->out[vec_size(sy->out)+i].block;
         asvalue[i] = (ast_value*)exprs[i];
+
+        if (exprs[i]->expression.vtype == TYPE_NOEXPR &&
+            !(i != 0 && op->id == opid2('?',':')) &&
+            !(i == 1 && op->id == opid1('.')))
+        {
+            if (ast_istype(exprs[i], ast_label))
+                compile_error(ast_ctx(exprs[i]), "expected expression, got an unknown identifier");
+            else
+                compile_error(ast_ctx(exprs[i]), "not an expression");
+            (void)!compile_warning(ast_ctx(exprs[i]), WARN_DEBUG, "expression %u\n", (unsigned int)i);
+        }
     }
 
     if (blocks[0] && !vec_size(blocks[0]->exprs) && op->id != opid1(',')) {
-        parseerror(parser, "internal error: operator cannot be applied on empty blocks");
+        compile_error(ctx, "internal error: operator cannot be applied on empty blocks");
         return false;
     }
 
@@ -560,23 +596,37 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     switch (op->id)
     {
         default:
-            parseerror(parser, "internal error: unhandled operator: %s (%i)", op->op, (int)op->id);
+            compile_error(ctx, "internal error: unhandled operator: %s (%i)", op->op, (int)op->id);
             return false;
 
         case opid1('.'):
-            if (exprs[0]->expression.vtype == TYPE_ENTITY) {
+            if (exprs[0]->expression.vtype == TYPE_VECTOR &&
+                exprs[1]->expression.vtype == TYPE_NOEXPR)
+            {
+                if      (exprs[1] == (ast_expression*)parser->const_vec[0])
+                    out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
+                else if (exprs[1] == (ast_expression*)parser->const_vec[1])
+                    out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
+                else if (exprs[1] == (ast_expression*)parser->const_vec[2])
+                    out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
+                else {
+                    compile_error(ctx, "access to invalid vector component");
+                    return false;
+                }
+            }
+            else if (exprs[0]->expression.vtype == TYPE_ENTITY) {
                 if (exprs[1]->expression.vtype != TYPE_FIELD) {
-                    parseerror(parser, "type error: right hand of member-operand should be an entity-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) {
-                parseerror(parser, "internal error: vector access is not supposed to be handled at this point");
+                compile_error(ast_ctx(exprs[1]), "vectors cannot be accessed this way");
                 return false;
             }
             else {
-                parseerror(parser, "type error: member-of operator on something that is not an entity or vector");
+                compile_error(ast_ctx(exprs[1]), "type error: member-of operator on something that is not an entity or vector");
                 return false;
             }
             break;
@@ -587,12 +637,12 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                   exprs[0]->expression.next->expression.vtype == TYPE_ARRAY))
             {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
-                parseerror(parser, "cannot index value of type %s", ty1);
+                compile_error(ast_ctx(exprs[0]), "cannot index value of type %s", ty1);
                 return false;
             }
             if (exprs[1]->expression.vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
-                parseerror(parser, "index must be of type float, not %s", ty1);
+                compile_error(ast_ctx(exprs[1]), "index must be of type float, not %s", ty1);
                 return false;
             }
             out = (ast_expression*)ast_array_index_new(ctx, exprs[0], exprs[1]);
@@ -651,8 +701,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                                                               exprs[0]);
                     break;
                 default:
-                parseerror(parser, "invalid types used in expression: cannot negate type %s",
-                           type_name[exprs[0]->expression.vtype]);
+                compile_error(ctx, "invalid types used in expression: cannot negate type %s",
+                              type_name[exprs[0]->expression.vtype]);
                 return false;
             }
             break;
@@ -693,8 +743,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_FNC, exprs[0]);
                     break;
                 default:
-                parseerror(parser, "invalid types used in expression: cannot logically negate type %s",
-                           type_name[exprs[0]->expression.vtype]);
+                compile_error(ctx, "invalid types used in expression: cannot logically negate type %s",
+                              type_name[exprs[0]->expression.vtype]);
                 return false;
             }
             break;
@@ -703,9 +753,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
                 (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
             {
-                parseerror(parser, "invalid types used in expression: cannot add type %s and %s",
-                           type_name[exprs[0]->expression.vtype],
-                           type_name[exprs[1]->expression.vtype]);
+                compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
+                              type_name[exprs[0]->expression.vtype],
+                              type_name[exprs[1]->expression.vtype]);
                 return false;
             }
             switch (exprs[0]->expression.vtype) {
@@ -724,9 +774,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         out = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_V, exprs[0], exprs[1]);
                     break;
                 default:
-                    parseerror(parser, "invalid types used in expression: cannot add type %s and %s",
-                               type_name[exprs[0]->expression.vtype],
-                               type_name[exprs[1]->expression.vtype]);
+                    compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
+                                  type_name[exprs[0]->expression.vtype],
+                                  type_name[exprs[1]->expression.vtype]);
                     return false;
             };
             break;
@@ -734,9 +784,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
                 (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
             {
-                parseerror(parser, "invalid types used in expression: cannot subtract type %s from %s",
-                           type_name[exprs[1]->expression.vtype],
-                           type_name[exprs[0]->expression.vtype]);
+                compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
+                              type_name[exprs[1]->expression.vtype],
+                              type_name[exprs[0]->expression.vtype]);
                 return false;
             }
             switch (exprs[0]->expression.vtype) {
@@ -753,9 +803,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_V, exprs[0], exprs[1]);
                     break;
                 default:
-                    parseerror(parser, "invalid types used in expression: cannot subtract type %s from %s",
-                               type_name[exprs[1]->expression.vtype],
-                               type_name[exprs[0]->expression.vtype]);
+                    compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
+                                  type_name[exprs[1]->expression.vtype],
+                                  type_name[exprs[0]->expression.vtype]);
                     return false;
             };
             break;
@@ -767,9 +817,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                   exprs[0]->expression.vtype == TYPE_FLOAT)
                 )
             {
-                parseerror(parser, "invalid types used in expression: cannot multiply types %s and %s",
-                           type_name[exprs[1]->expression.vtype],
-                           type_name[exprs[0]->expression.vtype]);
+                compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
+                              type_name[exprs[1]->expression.vtype],
+                              type_name[exprs[0]->expression.vtype]);
                 return false;
             }
             switch (exprs[0]->expression.vtype) {
@@ -864,34 +914,61 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     }
                     break;
                 default:
-                    parseerror(parser, "invalid types used in expression: cannot multiply types %s and %s",
-                               type_name[exprs[1]->expression.vtype],
-                               type_name[exprs[0]->expression.vtype]);
+                    compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
+                                  type_name[exprs[1]->expression.vtype],
+                                  type_name[exprs[0]->expression.vtype]);
                     return false;
             };
             break;
         case opid1('/'):
-            if (NotSameType(TYPE_FLOAT)) {
-                parseerror(parser, "invalid types used in expression: cannot divide types %s and %s",
-                           type_name[exprs[0]->expression.vtype],
-                           type_name[exprs[1]->expression.vtype]);
+            if (exprs[1]->expression.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 (CanConstFold(exprs[0], exprs[1]))
-                out = (ast_expression*)parser_const_float(parser, ConstF(0) / ConstF(1));
+            if (exprs[0]->expression.vtype == TYPE_FLOAT) {
+                if (CanConstFold(exprs[0], exprs[1]))
+                    out = (ast_expression*)parser_const_float(parser, ConstF(0) / ConstF(1));
+                else
+                    out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
+            }
+            else if (exprs[0]->expression.vtype == TYPE_VECTOR) {
+                if (CanConstFold(exprs[0], exprs[1]))
+                    out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), 1.0/ConstF(1)));
+                else {
+                    if (CanConstFold1(exprs[1])) {
+                        out = (ast_expression*)parser_const_float(parser, 1.0 / ConstF(1));
+                    } else {
+                        out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
+                                                              (ast_expression*)parser_const_float_1(parser),
+                                                              exprs[1]);
+                    }
+                    if (!out) {
+                        compile_error(ctx, "internal error: failed to generate division");
+                        return false;
+                    }
+                    out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_VF, exprs[0], out);
+                }
+            }
             else
-                out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, 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 used in expression: cannot divide tyeps %s and %s", ty1, ty2);
+                return false;
+            }
             break;
         case opid1('%'):
         case opid2('%','='):
-            parseerror(parser, "qc does not have a modulo operator");
+            compile_error(ctx, "qc does not have a modulo operator");
             return false;
         case opid1('|'):
         case opid1('&'):
             if (NotSameType(TYPE_FLOAT)) {
-                parseerror(parser, "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]);
+                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]);
                 return false;
             }
             if (CanConstFold(exprs[0], exprs[1]))
@@ -904,30 +981,27 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     exprs[0], exprs[1]);
             break;
         case opid1('^'):
-            parseerror(parser, "TODO: bitxor");
+            compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-xor via ^");
             return false;
 
         case opid2('<','<'):
         case opid2('>','>'):
+            if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) {
+                if (op->id == opid2('<','<'))
+                    out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) << (int)(ConstF(1))));
+                else
+                    out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) >> (int)(ConstF(1))));
+                break;
+            }
         case opid3('<','<','='):
         case opid3('>','>','='):
-            parseerror(parser, "TODO: shifts");
+            compile_error(ast_ctx(exprs[0]), "Not Yet Implemented: bit-shifts");
             return false;
 
         case opid2('|','|'):
             generated_op += 1; /* INSTR_OR */
         case opid2('&','&'):
             generated_op += INSTR_AND;
-#if 0
-            if (NotSameType(TYPE_FLOAT)) {
-                parseerror(parser, "invalid types used in expression: cannot perform logical operations between types %s and %s",
-                           type_name[exprs[0]->expression.vtype],
-                           type_name[exprs[1]->expression.vtype]);
-                parseerror(parser, "TODO: logical ops for arbitrary types using INSTR_NOT");
-                parseerror(parser, "TODO: optional early out");
-                return false;
-            }
-#endif
             if (CanConstFold(exprs[0], exprs[1]))
             {
                 if (OPTS_FLAG(PERL_LOGIC)) {
@@ -946,7 +1020,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 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));
-                    parseerror(parser, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, 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) {
@@ -979,14 +1053,14 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 
         case opid2('?',':'):
             if (vec_last(parser->pot) != POT_TERNARY2) {
-                parseerror(parser, "mismatched parenthesis/ternary");
+                compile_error(ctx, "mismatched parenthesis/ternary");
                 return false;
             }
             vec_pop(parser->pot);
             if (!ast_compare_type(exprs[1], exprs[2])) {
                 ast_type_to_string(exprs[1], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[2], ty2, sizeof(ty2));
-                parseerror(parser, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2);
+                compile_error(ctx, "operands of ternary expression must have the same type, got %s and %s", ty1, ty2);
                 return false;
             }
             if (CanConstFold1(exprs[0]))
@@ -1004,27 +1078,27 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         case opid2('<', '='):
             generated_op += INSTR_LE;
             if (NotSameType(TYPE_FLOAT)) {
-                parseerror(parser, "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]);
+                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]);
                 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) {
-                parseerror(parser, "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]);
+                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]);
                 return false;
             }
             out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]);
             break;
         case opid2('=', '='):
             if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) {
-                parseerror(parser, "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]);
+                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]);
                 return false;
             }
             out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]);
@@ -1049,11 +1123,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         field->expression.next->expression.vtype == TYPE_FUNCTION &&
                         exprs[1]->expression.vtype == TYPE_FUNCTION)
                     {
-                        (void)!parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES,
-                                            "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
+                        (void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES,
+                                               "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                     }
                     else
-                        parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
+                        compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
             }
             else
@@ -1071,7 +1145,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 if (assignop == AINSTR_END) {
                     ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
-                    parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
+                    compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
                 else if (!ast_compare_type(exprs[0], exprs[1]))
                 {
@@ -1081,15 +1155,15 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                         exprs[0]->expression.vtype == TYPE_FUNCTION &&
                         exprs[1]->expression.vtype == TYPE_FUNCTION)
                     {
-                        (void)!parsewarning(parser, WARN_ASSIGN_FUNCTION_TYPES,
-                                            "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
+                        (void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES,
+                                               "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                     }
                     else
-                        parseerror(parser, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
+                        compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
                 }
             }
             if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
+                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
             out = (ast_expression*)ast_store_new(ctx, assignop, exprs[0], exprs[1]);
             break;
@@ -1098,7 +1172,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             /* prefix ++ */
             if (exprs[0]->expression.vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
-                parseerror(parser, "invalid type for prefix increment: %s", ty1);
+                compile_error(ast_ctx(exprs[0]), "invalid type for prefix increment: %s", ty1);
                 return false;
             }
             if (op->id == opid3('+','+','P'))
@@ -1106,7 +1180,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             else
                 addop = INSTR_SUB_F;
             if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
+                compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
@@ -1123,7 +1197,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             /* prefix ++ */
             if (exprs[0]->expression.vtype != TYPE_FLOAT) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
-                parseerror(parser, "invalid type for suffix increment: %s", ty1);
+                compile_error(ast_ctx(exprs[0]), "invalid type for suffix increment: %s", ty1);
                 return false;
             }
             if (op->id == opid3('S','+','+')) {
@@ -1134,7 +1208,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 subop = INSTR_ADD_F;
             }
             if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
+                compile_error(ast_ctx(exprs[0]), "assignment to constant `%s`", asvalue[0]->name);
             }
             if (ast_istype(exprs[0], ast_entfield)) {
                 out = (ast_expression*)ast_binstore_new(ctx, INSTR_STOREP_F, addop,
@@ -1158,12 +1232,12 @@ 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));
-                parseerror(parser, "invalid types used in expression: cannot add or subtract type %s and %s",
-                           ty1, ty2);
+                compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s",
+                              ty1, ty2);
                 return false;
             }
             if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
+                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];
@@ -1181,9 +1255,9 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                                                             exprs[0], exprs[1]);
                     break;
                 default:
-                    parseerror(parser, "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]);
+                    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]);
                     return false;
             };
             break;
@@ -1195,12 +1269,12 @@ 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));
-                parseerror(parser, "invalid types used in expression: %s and %s",
-                           ty1, ty2);
+                compile_error(ctx, "invalid types used in expression: %s and %s",
+                              ty1, ty2);
                 return false;
             }
             if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
+                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];
@@ -1218,19 +1292,25 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                                                                 exprs[0], exprs[1]);
                     } else {
                         /* there's no DIV_VF */
-                        out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F,
-                                                              (ast_expression*)parser_const_float_1(parser),
-                                                              exprs[1]);
-                        if (!out)
+                        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_binstore_new(ctx, assignop, INSTR_MUL_VF,
                                                                 exprs[0], out);
                     }
                     break;
                 default:
-                    parseerror(parser, "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]);
+                    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]);
                     return false;
             };
             break;
@@ -1239,12 +1319,12 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (NotSameType(TYPE_FLOAT)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
-                parseerror(parser, "invalid types used in expression: %s and %s",
-                           ty1, ty2);
+                compile_error(ctx, "invalid types used in expression: %s and %s",
+                              ty1, ty2);
                 return false;
             }
             if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
+                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];
@@ -1262,8 +1342,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (NotSameType(TYPE_FLOAT)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                 ast_type_to_string(exprs[1], ty2, sizeof(ty2));
-                parseerror(parser, "invalid types used in expression: %s and %s",
-                           ty1, ty2);
+                compile_error(ctx, "invalid types used in expression: %s and %s",
+                              ty1, ty2);
                 return false;
             }
             if (ast_istype(exprs[0], ast_entfield))
@@ -1274,7 +1354,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             if (!out)
                 return false;
             if (ast_istype(exprs[0], ast_value) && asvalue[0]->cvq == CV_CONST) {
-                parseerror(parser, "assignment to constant `%s`", asvalue[0]->name);
+                compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
             }
             asbinstore = ast_binstore_new(ctx, assignop, INSTR_SUB_F, exprs[0], out);
             asbinstore->keep_dest = true;
@@ -1284,7 +1364,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
 #undef NotSameType
 
     if (!out) {
-        parseerror(parser, "failed to apply operand %s", op->op);
+        compile_error(ctx, "failed to apply operand %s", op->op);
         return false;
     }
 
@@ -1297,6 +1377,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
 {
     /* was a function call */
     ast_expression *fun;
+    ast_value      *funval = NULL;
     ast_call       *call;
 
     size_t          fid;
@@ -1336,10 +1417,8 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
     }
 
     call = ast_call_new(sy->ops[vec_size(sy->ops)].ctx, fun);
-    if (!call) {
-        parseerror(parser, "internal error: failed to create ast_call node");
+    if (!call)
         return false;
-    }
 
     if (fid+1 == vec_size(sy->out)) {
         /* no arguments */
@@ -1358,12 +1437,23 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
             params->exprs = NULL;
             ast_delete(params);
         }
+        if (parser->max_param_count < paramcount)
+            parser->max_param_count = paramcount;
         (void)!ast_call_check_types(call);
     } else {
         parseerror(parser, "invalid function call");
         return false;
     }
 
+    if (ast_istype(fun, ast_value)) {
+        funval = (ast_value*)fun;
+        if ((fun->expression.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);
+        }
+    }
+
     /* overwrite fid, the function, with a call */
     sy->out[fid] = syexp(call->expression.node.context, (ast_expression*)call);
 
@@ -1434,16 +1524,21 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only)
     }
     */
     while (vec_size(sy->ops)) {
-        if (sy->ops[vec_size(sy->ops)-1].paren == SY_PAREN_FUNC) {
+        if (vec_last(sy->ops).paren == SY_PAREN_FUNC) {
             if (!parser_close_call(parser, sy))
                 return false;
             break;
         }
-        if (sy->ops[vec_size(sy->ops)-1].paren == SY_PAREN_EXPR) {
+        if (vec_last(sy->ops).paren == SY_PAREN_EXPR) {
+            if (!vec_size(sy->out)) {
+                compile_error(vec_last(sy->ops).ctx, "empty paren expression");
+                vec_shrinkby(sy->ops, 1);
+                return false;
+            }
             vec_shrinkby(sy->ops, 1);
             return !functions_only;
         }
-        if (sy->ops[vec_size(sy->ops)-1].paren == SY_PAREN_INDEX) {
+        if (vec_last(sy->ops).paren == SY_PAREN_INDEX) {
             if (functions_only)
                 return false;
             /* pop off the parenthesis */
@@ -1453,7 +1548,7 @@ static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only)
                 return false;
             return true;
         }
-        if (sy->ops[vec_size(sy->ops)-1].paren == SY_PAREN_TERNARY) {
+        if (vec_last(sy->ops).paren == SY_PAREN_TERNARY) {
             if (functions_only)
                 return false;
             if (vec_last(parser->pot) != POT_TERNARY1) {
@@ -1482,12 +1577,108 @@ static void parser_reclassify_token(parser_t *parser)
     }
 }
 
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue)
+static ast_expression* parse_vararg_do(parser_t *parser)
+{
+    ast_expression *idx, *out;
+    ast_value      *typevar;
+    ast_value      *funtype = parser->function->vtype;
+
+    lex_ctx ctx = parser_ctx(parser);
+
+    if (!parser_next(parser) || parser->tok != '(') {
+        parseerror(parser, "expected parameter index and type in parenthesis");
+        return NULL;
+    }
+    if (!parser_next(parser)) {
+        parseerror(parser, "error parsing parameter index");
+        return NULL;
+    }
+
+    idx = parse_expression_leave(parser, true, false, false);
+    if (!idx)
+        return NULL;
+
+    if (parser->tok != ',') {
+        ast_unref(idx);
+        parseerror(parser, "expected comma after parameter index");
+        return NULL;
+    }
+
+    if (!parser_next(parser) || (parser->tok != TOKEN_IDENT && parser->tok != TOKEN_TYPENAME)) {
+        ast_unref(idx);
+        parseerror(parser, "expected typename for vararg");
+        return NULL;
+    }
+
+    typevar = parse_typename(parser, NULL, NULL);
+    if (!typevar) {
+        ast_unref(idx);
+        return NULL;
+    }
+
+    if (parser->tok != ')') {
+        ast_unref(idx);
+        ast_delete(typevar);
+        parseerror(parser, "expected closing paren");
+        return NULL;
+    }
+
+#if 0
+    if (!parser_next(parser)) {
+        ast_unref(idx);
+        ast_delete(typevar);
+        parseerror(parser, "parse error after vararg");
+        return NULL;
+    }
+#endif
+
+    if (!parser->function->varargs) {
+        ast_unref(idx);
+        ast_delete(typevar);
+        parseerror(parser, "function has no variable argument list");
+        return NULL;
+    }
+
+    if (funtype->expression.varparam &&
+        !ast_compare_type((ast_expression*)typevar, (ast_expression*)funtype->expression.varparam))
+    {
+        char ty1[1024];
+        char ty2[1024];
+        ast_type_to_string((ast_expression*)typevar, ty1, sizeof(ty1));
+        ast_type_to_string((ast_expression*)funtype->expression.varparam, ty2, sizeof(ty2));
+        compile_error(ast_ctx(typevar),
+                      "function was declared to take varargs of type `%s`, requested type is: %s",
+                      ty2, ty1);
+    }
+
+    out = (ast_expression*)ast_array_index_new(ctx, (ast_expression*)(parser->function->varargs), idx);
+    ast_type_adopt(out, typevar);
+    ast_delete(typevar);
+    return out;
+}
+
+static ast_expression* parse_vararg(parser_t *parser)
+{
+    bool             old_noops = parser->lex->flags.noops;
+    enum parser_pot *old_pot   = parser->pot;
+
+    ast_expression *out;
+
+    parser->pot = NULL;
+    parser->lex->flags.noops = true;
+    out = parse_vararg_do(parser);
+
+    parser->pot              = old_pot;
+    parser->lex->flags.noops = old_noops;
+    return out;
+}
+
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels)
 {
     ast_expression *expr = NULL;
     shunt sy;
+    size_t i;
     bool wantop = false;
-    bool gotmemberof = false;
     /* only warn once about an assignment in a truth value because the current code
      * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
      */
@@ -1508,11 +1699,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 
     while (true)
     {
-        if (gotmemberof)
-            gotmemberof = false;
-        else
-            parser->memberof = 0;
-
         if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
             parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "_"))
         {
@@ -1546,47 +1732,111 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 goto onerr;
             }
         }
+        else if (parser->tok == TOKEN_DOTS)
+        {
+            ast_expression *va;
+            if (!OPTS_FLAG(VARIADIC_ARGS)) {
+                parseerror(parser, "cannot access varargs (try -fvariadic-args)");
+                goto onerr;
+            }
+            if (wantop) {
+                parseerror(parser, "expected operator or end of statement");
+                goto onerr;
+            }
+            wantop = true;
+            va = parse_vararg(parser);
+            if (!va)
+                goto onerr;
+            vec_push(sy.out, syexp(parser_ctx(parser), va));
+            DEBUGSHUNTDO(con_out("push `...`\n"));
+        }
         else if (parser->tok == TOKEN_IDENT)
         {
+            const char     *ctoken = parser_tokval(parser);
+            ast_expression *prev = vec_size(sy.out) ? vec_last(sy.out).out : NULL;
             ast_expression *var;
             if (wantop) {
                 parseerror(parser, "expected operator or end of statement");
                 goto onerr;
             }
             wantop = true;
-            /* variable */
-            if (opts.standard == COMPILER_GMQCC)
+            /* a_vector.{x,y,z} */
+            if (!vec_size(sy.ops) ||
+                !vec_last(sy.ops).etype ||
+                operators[vec_last(sy.ops).etype-1].id != opid1('.') ||
+                (prev >= intrinsic_debug_typestring &&
+                 prev <= intrinsic_debug_typestring))
             {
-                if (parser->memberof == TYPE_ENTITY) {
-                    /* still get vars first since there could be a fieldpointer */
-                    var = parser_find_var(parser, parser_tokval(parser));
-                    if (!var)
-                        var = parser_find_field(parser, parser_tokval(parser));
-                }
-                else if (parser->memberof == TYPE_VECTOR)
-                {
-                    parseerror(parser, "TODO: implement effective vector member access");
-                    goto onerr;
-                }
-                else if (parser->memberof) {
-                    parseerror(parser, "namespace for member not found");
-                    goto onerr;
-                }
-                else
-                    var = parser_find_var(parser, parser_tokval(parser));
+                /* 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])
+            {
+                var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
             } else {
                 var = parser_find_var(parser, parser_tokval(parser));
                 if (!var)
                     var = parser_find_field(parser, parser_tokval(parser));
             }
+            if (!var && with_labels) {
+                var = (ast_expression*)parser_find_label(parser, parser_tokval(parser));
+                if (!with_labels) {
+                    ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true);
+                    var = (ast_expression*)lbl;
+                    vec_push(parser->labels, lbl);
+                }
+            }
             if (!var) {
                 /* intrinsics */
                 if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
                     var = (ast_expression*)intrinsic_debug_typestring;
-
                 }
                 else
                 {
+                    char *correct = NULL;
+
+                    /*
+                     * sometimes people use preprocessing predefs without enabling them
+                     * 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));
+                                goto onerr;
+                            }
+                        }
+                    }
+
+                    /*
+                     * TODO: determine the best score for the identifier: be it
+                     * a variable, a field.
+                     *
+                     * We should also consider adding correction tables for
+                     * other things as well.
+                     */
+                    if (opts.correction) {
+                        correction_t corr;
+                        correct_init(&corr);
+
+                        for (i = 0; i < vec_size(parser->correct_variables); i++) {
+                            correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser));
+                            if (strcmp(correct, parser_tokval(parser))) {
+                                break;
+                            } else if (correct) {
+                                mem_d(correct);
+                                correct = NULL;
+                            }
+                        }
+                        correct_free(&corr);
+
+                        if (correct) {
+                            parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+                            mem_d(correct);
+                            goto onerr;
+                        }
+                    }
                     parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
                     goto onerr;
                 }
@@ -1802,24 +2052,6 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     olast = NULL;
             }
 
-            if (op->id == opid1('.') && opts.standard == COMPILER_GMQCC) {
-                /* for gmqcc standard: open up the namespace of the previous type */
-                ast_expression *prevex = vec_last(sy.out).out;
-                if (!prevex) {
-                    parseerror(parser, "unexpected member operator");
-                    goto onerr;
-                }
-                if (prevex->expression.vtype == TYPE_ENTITY)
-                    parser->memberof = TYPE_ENTITY;
-                else if (prevex->expression.vtype == TYPE_VECTOR)
-                    parser->memberof = TYPE_VECTOR;
-                else {
-                    parseerror(parser, "type error: type has no members");
-                    goto onerr;
-                }
-                gotmemberof = true;
-            }
-
             if (op->id == opid1('(')) {
                 if (wantop) {
                     size_t sycount = vec_size(sy.out);
@@ -1873,7 +2105,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             goto onerr;
         }
         if (parser->tok == ';' ||
-            (!parens && parser->tok == ']'))
+            (!parens && (parser->tok == ']' || parser->tok == ')' || parser->tok == '}')))
         {
             break;
         }
@@ -1902,18 +2134,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
 
 onerr:
     parser->lex->flags.noops = true;
+    for (i = 0; i < vec_size(sy.out); ++i) {
+        if (sy.out[i].out)
+            ast_unref(sy.out[i].out);
+    }
     vec_free(sy.out);
     vec_free(sy.ops);
     return NULL;
 }
 
-static ast_expression* parse_expression(parser_t *parser, bool stopatcomma)
+static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool with_labels)
 {
-    ast_expression *e = parse_expression_leave(parser, stopatcomma, false);
+    ast_expression *e = parse_expression_leave(parser, stopatcomma, false, with_labels);
     if (!e)
         return NULL;
+    if (parser->tok != ';') {
+        parseerror(parser, "semicolon expected after expression");
+        ast_unref(e);
+        return NULL;
+    }
     if (!parser_next(parser)) {
-        ast_delete(e);
+        ast_unref(e);
         return NULL;
     }
     return e;
@@ -1926,6 +2167,10 @@ static void parser_enterblock(parser_t *parser)
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs));
     vec_push(parser->_block_ctx, parser_ctx(parser));
+
+    /* corrector */
+    vec_push(parser->correct_variables, correct_trie_new());
+    vec_push(parser->correct_variables_score, NULL);
 }
 
 static bool parser_leaveblock(parser_t *parser)
@@ -1939,7 +2184,11 @@ static bool parser_leaveblock(parser_t *parser)
     }
 
     util_htdel(vec_last(parser->variables));
+    correct_del(vec_last(parser->correct_variables), vec_last(parser->correct_variables_score));
+
     vec_pop(parser->variables);
+    vec_pop(parser->correct_variables);
+    vec_pop(parser->correct_variables_score);
     if (!vec_size(parser->_blocklocals)) {
         parseerror(parser, "internal error: parser_leaveblock with no block (2)");
         return false;
@@ -1966,6 +2215,7 @@ static bool parser_leaveblock(parser_t *parser)
     vec_pop(parser->typedefs);
 
     vec_pop(parser->_block_ctx);
+
     return rv;
 }
 
@@ -1973,6 +2223,26 @@ static void parser_addlocal(parser_t *parser, const char *name, ast_expression *
 {
     vec_push(parser->_locals, e);
     util_htset(vec_last(parser->variables), name, (void*)e);
+
+    /* corrector */
+    correct_add (
+         vec_last(parser->correct_variables),
+        &vec_last(parser->correct_variables_score),
+        name
+    );
+}
+
+static void parser_addglobal(parser_t *parser, const char *name, ast_expression *e)
+{
+    vec_push(parser->globals, e);
+    util_htset(parser->htglobals, name, e);
+
+    /* corrector */
+    correct_add (
+         parser->correct_variables[0],
+        &parser->correct_variables_score[0],
+        name
+    );
 }
 
 static ast_expression* process_condition(parser_t *parser, ast_expression *cond, bool *_ifnot)
@@ -2059,7 +2329,7 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2078,6 +2348,8 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out)
         ast_delete(cond);
         return false;
     }
+    if (!ontrue)
+        ontrue = (ast_expression*)ast_block_new(parser_ctx(parser));
     /* check for an else */
     if (!strcmp(parser_tokval(parser), "else")) {
         /* parse into the 'else' branch */
@@ -2180,7 +2452,7 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression **
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2295,7 +2567,7 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression
         return false;
     }
     /* parse the condition */
-    cond = parse_expression_leave(parser, false, true);
+    cond = parse_expression_leave(parser, false, true, false);
     if (!cond)
         return false;
     /* closing paren */
@@ -2424,7 +2696,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
     }
     else if (parser->tok != ';')
     {
-        initexpr = parse_expression_leave(parser, false, false);
+        initexpr = parse_expression_leave(parser, false, false, false);
         if (!initexpr)
             goto onerr;
     }
@@ -2441,7 +2713,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the condition */
     if (parser->tok != ';') {
-        cond = parse_expression_leave(parser, false, true);
+        cond = parse_expression_leave(parser, false, true, false);
         if (!cond)
             goto onerr;
     }
@@ -2458,7 +2730,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the incrementor */
     if (parser->tok != ')') {
-        increment = parse_expression_leave(parser, false, false);
+        increment = parse_expression_leave(parser, false, false, false);
         if (!increment)
             goto onerr;
         if (!ast_side_effects(increment)) {
@@ -2515,7 +2787,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
     }
 
     if (parser->tok != ';') {
-        exp = parse_expression(parser, false);
+        exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
 
@@ -2534,10 +2806,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou
         if (!parser_next(parser))
             parseerror(parser, "parse error");
         if (expected->expression.next->expression.vtype != TYPE_VOID) {
-            if (opts.standard != COMPILER_GMQCC)
-                (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
-            else
-                parseerror(parser, "return without value");
+            (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
         }
         ret = ast_return_new(ctx, NULL);
     }
@@ -2558,6 +2827,13 @@ static bool parse_break_continue(parser_t *parser, ast_block *block, ast_express
         return false;
     }
 
+    if (!vec_size(loops)) {
+        if (is_continue)
+            parseerror(parser, "`continue` can only be used inside loops");
+        else
+            parseerror(parser, "`break` can only be used inside loops or switches");
+    }
+
     if (parser->tok == TOKEN_IDENT) {
         if (!OPTS_FLAG(LOOP_LABELS))
             parseerror(parser, "labeled loops not activated, try using -floop-labels");
@@ -2642,7 +2918,7 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
             else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) {
                 flags   |= AST_FLAG_DEPRECATED;
                 *message = NULL;
-                
+
                 if (!parser_next(parser)) {
                     parseerror(parser, "parse error in attribute");
                     goto argerr;
@@ -2695,7 +2971,7 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                 }
             }
         }
-        else if (!strcmp(parser_tokval(parser), "static"))
+        else if (with_local && !strcmp(parser_tokval(parser), "static"))
             had_static = true;
         else if (!strcmp(parser_tokval(parser), "const"))
             had_const = true;
@@ -2804,7 +3080,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
         return false;
     }
     /* parse the operand */
-    operand = parse_expression_leave(parser, false, false);
+    operand = parse_expression_leave(parser, false, false, false);
     if (!operand)
         return false;
 
@@ -2868,7 +3144,7 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
                 parseerror(parser, "expected expression for case");
                 return false;
             }
-            swcase.value = parse_expression_leave(parser, false, false);
+            swcase.value = parse_expression_leave(parser, false, false, false);
             if (!swcase.value) {
                 ast_delete(switchnode);
                 parseerror(parser, "expected expression for case");
@@ -2959,25 +3235,81 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
     return true;
 }
 
+/* parse computed goto sides */
+static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) {
+    ast_expression *on_true;
+    ast_expression *on_false;
+    ast_expression *cond;
+
+    if (!*side)
+        return NULL;
+
+    if (ast_istype(*side, ast_ternary)) {
+        ast_ternary *tern = (ast_ternary*)*side;
+        on_true  = parse_goto_computed(parser, &tern->on_true);
+        on_false = parse_goto_computed(parser, &tern->on_false);
+
+        if (!on_true || !on_false) {
+            parseerror(parser, "expected label or expression in ternary");
+            if (on_true) ast_unref(on_true);
+            if (on_false) ast_unref(on_false);
+            return NULL;
+        }
+
+        cond = tern->cond;
+        tern->cond = NULL;
+        ast_delete(tern);
+        *side = NULL;
+        return (ast_expression*)ast_ifthen_new(parser_ctx(parser), cond, on_true, on_false);
+    } else if (ast_istype(*side, ast_label)) {
+        ast_goto *gt = ast_goto_new(parser_ctx(parser), ((ast_label*)*side)->name);
+        ast_goto_set_label(gt, ((ast_label*)*side));
+        *side = NULL;
+        return (ast_expression*)gt;
+    }
+    return NULL;
+}
+
 static bool parse_goto(parser_t *parser, ast_expression **out)
 {
-    size_t    i;
-    ast_goto *gt;
+    ast_goto       *gt = NULL;
+    ast_expression *lbl;
 
-    if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
-        parseerror(parser, "expected label name after `goto`");
+    if (!parser_next(parser))
         return false;
+
+    if (parser->tok != TOKEN_IDENT) {
+        ast_expression *expression;
+
+        /* could be an expression i.e computed goto :-) */
+        if (parser->tok != '(') {
+            parseerror(parser, "expected label name after `goto`");
+            return false;
+        }
+
+        /* failed to parse expression for goto */
+        if (!(expression = parse_expression(parser, false, true)) ||
+            !(*out = parse_goto_computed(parser, &expression))) {
+            parseerror(parser, "invalid goto expression");
+            ast_unref(expression);
+            return false;
+        }
+
+        return true;
     }
 
+    /* not computed goto */
     gt = ast_goto_new(parser_ctx(parser), parser_tokval(parser));
-
-    for (i = 0; i < vec_size(parser->labels); ++i) {
-        if (!strcmp(parser->labels[i]->name, parser_tokval(parser))) {
-            ast_goto_set_label(gt, parser->labels[i]);
-            break;
+    lbl = parser_find_label(parser, gt->name);
+    if (lbl) {
+        if (!ast_istype(lbl, ast_label)) {
+            parseerror(parser, "internal error: label is not an ast_label");
+            ast_delete(gt);
+            return false;
         }
+        ast_goto_set_label(gt, (ast_label*)lbl);
     }
-    if (i == vec_size(parser->labels))
+    else
         vec_push(parser->gotos, gt);
 
     if (!parser_next(parser) || parser->tok != ';') {
@@ -3212,10 +3544,18 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             parseerror(parser, "label must be an identifier");
             return false;
         }
-        label = ast_label_new(parser_ctx(parser), parser_tokval(parser));
-        if (!label)
-            return false;
-        vec_push(parser->labels, label);
+        label = (ast_label*)parser_find_label(parser, parser_tokval(parser));
+        if (label) {
+            if (!label->undefined) {
+                parseerror(parser, "label `%s` already defined", label->name);
+                return false;
+            }
+            label->undefined = false;
+        }
+        else {
+            label = ast_label_new(parser_ctx(parser), parser_tokval(parser), false);
+            vec_push(parser->labels, label);
+        }
         *out = (ast_expression*)label;
         if (!parser_next(parser)) {
             parseerror(parser, "parse error after label");
@@ -3240,7 +3580,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
     else
     {
-        ast_expression *exp = parse_expression(parser, false);
+        ast_expression *exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
         *out = exp;
@@ -3252,6 +3592,106 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
 }
 
+static bool parse_enum(parser_t *parser)
+{
+    qcfloat     num = 0;
+    ast_value **values = NULL;
+    ast_value  *var = NULL;
+    ast_value  *asvalue;
+
+    ast_expression *old;
+
+    if (!parser_next(parser) || parser->tok != '{') {
+        parseerror(parser, "expected `{` after `enum` keyword");
+        return false;
+    }
+
+    while (true) {
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
+            if (parser->tok == '}') {
+                /* allow an empty enum */
+                break;
+            }
+            parseerror(parser, "expected identifier or `}`");
+            goto onerror;
+        }
+
+        old = parser_find_field(parser, parser_tokval(parser));
+        if (!old)
+            old = parser_find_global(parser, parser_tokval(parser));
+        if (old) {
+            parseerror(parser, "value `%s` has already been declared here: %s:%i",
+                       parser_tokval(parser), ast_ctx(old).file, ast_ctx(old).line);
+            goto onerror;
+        }
+
+        var = ast_value_new(parser_ctx(parser), parser_tokval(parser), TYPE_FLOAT);
+        vec_push(values, var);
+        var->cvq             = CV_CONST;
+        var->hasvalue        = true;
+        var->constval.vfloat = num++;
+
+        parser_addglobal(parser, var->name, (ast_expression*)var);
+
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected `=`, `}` or comma after identifier");
+            goto onerror;
+        }
+
+        if (parser->tok == ',')
+            continue;
+        if (parser->tok == '}')
+            break;
+        if (parser->tok != '=') {
+            parseerror(parser, "expected `=`, `}` or comma after identifier");
+            goto onerror;
+        }
+
+        if (!parser_next(parser)) {
+            parseerror(parser, "expected expression after `=`");
+            goto onerror;
+        }
+
+        /* We got a value! */
+        old = parse_expression_leave(parser, true, false, false);
+        asvalue = (ast_value*)old;
+        if (!ast_istype(old, ast_value) || asvalue->cvq != CV_CONST || !asvalue->hasvalue) {
+            compile_error(ast_ctx(var), "constant value or expression expected");
+            goto onerror;
+        }
+        num = (var->constval.vfloat = asvalue->constval.vfloat) + 1;
+
+        if (parser->tok == '}')
+            break;
+        if (parser->tok != ',') {
+            parseerror(parser, "expected `}` or comma after expression");
+            goto onerror;
+        }
+    }
+
+    if (parser->tok != '}') {
+        parseerror(parser, "internal error: breaking without `}`");
+        goto onerror;
+    }
+
+    if (!parser_next(parser) || parser->tok != ';') {
+        parseerror(parser, "expected semicolon after enumeration");
+        goto onerror;
+    }
+
+    if (!parser_next(parser)) {
+        parseerror(parser, "parse error after enumeration");
+        goto onerror;
+    }
+
+    vec_free(values);
+    return true;
+
+onerror:
+    vec_free(values);
+    return false;
+}
+
 static bool parse_block_into(parser_t *parser, ast_block *block)
 {
     bool   retval = true;
@@ -3365,9 +3805,9 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         return false;
     }
 
-    if (var->expression.flags & AST_FLAG_VARIADIC) {
+    if (!OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) {
         if (parsewarning(parser, WARN_VARIADIC_FUNCTION,
-                         "variadic function with implementation will not be able to access additional parameters"))
+                         "variadic function with implementation will not be able to access additional parameters (try -fvariadic-args)"))
         {
             return false;
         }
@@ -3401,7 +3841,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         if (!parser_next(parser))
             return false;
 
-        framenum = parse_expression_leave(parser, true, false);
+        framenum = parse_expression_leave(parser, true, false, false);
         if (!framenum) {
             parseerror(parser, "expected a framenumber constant in[frame,think] notation");
             return false;
@@ -3444,12 +3884,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
                 return false;
             }
 
-            vec_push(parser->globals, (ast_expression*)thinkfunc);
-            util_htset(parser->htglobals, thinkfunc->name, thinkfunc);
+            parser_addglobal(parser, thinkfunc->name, (ast_expression*)thinkfunc);
+
             nextthink = (ast_expression*)thinkfunc;
 
         } else {
-            nextthink = parse_expression_leave(parser, true, false);
+            nextthink = parse_expression_leave(parser, true, false, false);
             if (!nextthink) {
                 ast_unref(framenum);
                 parseerror(parser, "expected a think-function in [frame,think] notation");
@@ -3562,6 +4002,14 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         }
     }
 
+    func = ast_function_new(ast_ctx(var), var->name, var);
+    if (!func) {
+        parseerror(parser, "failed to allocate function for `%s`", var->name);
+        ast_block_delete(block);
+        goto enderr;
+    }
+    vec_push(parser->functions, func);
+
     parser_enterblock(parser);
 
     for (parami = 0; parami < vec_size(var->expression.params); ++parami) {
@@ -3578,7 +4026,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
 
         if (!create_vector_members(param, me)) {
             ast_block_delete(block);
-            return false;
+            goto enderrfn;
         }
 
         for (e = 0; e < 3; ++e) {
@@ -3587,13 +4035,34 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         }
     }
 
-    func = ast_function_new(ast_ctx(var), var->name, var);
-    if (!func) {
-        parseerror(parser, "failed to allocate function for `%s`", var->name);
-        ast_block_delete(block);
-        goto enderr;
+    if (var->argcounter) {
+        ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT);
+        parser_addlocal(parser, argc->name, (ast_expression*)argc);
+        func->argc = argc;
+    }
+
+    if (OPTS_FLAG(VARIADIC_ARGS) && var->expression.flags & AST_FLAG_VARIADIC) {
+        char name[1024];
+        ast_value *varargs = ast_value_new(ast_ctx(var), "reserved:va_args", TYPE_ARRAY);
+        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);
+        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);
+        if (!parser_create_array_getter_proto(parser, varargs, varargs->expression.next, name)) {
+            ast_delete(varargs);
+            ast_block_delete(block);
+            goto enderrfn;
+        }
+        func->varargs = varargs;
+
+        func->fixedparams = parser_const_float(parser, vec_size(var->expression.params));
     }
-    vec_push(parser->functions, func);
 
     parser->function = func;
     if (!parse_block_into(parser, block)) {
@@ -3618,12 +4087,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     return retval;
 
 enderrfn:
+    (void)!parser_leaveblock(parser);
     vec_pop(parser->functions);
     ast_function_delete(func);
     var->constval.vfunc = NULL;
 
 enderr:
-    (void)!parser_leaveblock(parser);
     parser->function = old;
     return false;
 }
@@ -3753,6 +4222,9 @@ static ast_expression *array_field_setter_node(
         if (!subscript)
             return NULL;
 
+        subscript->expression.next = ast_type_copy(ast_ctx(subscript), (ast_expression*)subscript);
+        subscript->expression.vtype = TYPE_FIELD;
+
         entfield = ast_entfield_new_force(ctx,
                                           (ast_expression*)entity,
                                           (ast_expression*)subscript,
@@ -3865,9 +4337,8 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con
     return true;
 }
 
-static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
+static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname)
 {
-    ast_expression *root = NULL;
     ast_value      *index = NULL;
     ast_value      *value = NULL;
     ast_function   *func;
@@ -3875,11 +4346,11 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
 
     if (!ast_istype(array->expression.next, ast_value)) {
         parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type");
-        return false;
+        return NULL;
     }
 
     if (!parser_create_array_accessor(parser, array, funcname, &fval))
-        return false;
+        return NULL;
     func = fval->constval.vfunc;
     fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
 
@@ -3894,21 +4365,39 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
     vec_push(fval->expression.params, index);
     vec_push(fval->expression.params, value);
 
-    root = array_setter_node(parser, array, index, value, 0, array->expression.count);
-    if (!root) {
-        parseerror(parser, "failed to build accessor search tree");
-        goto cleanup;
-    }
-
     array->setter = fval;
-    return ast_block_add_expr(func->blocks[0], root);
+    return fval;
 cleanup:
     if (index) ast_delete(index);
     if (value) ast_delete(value);
-    if (root)  ast_delete(root);
     ast_delete(func);
     ast_delete(fval);
-    return false;
+    return NULL;
+}
+
+static bool parser_create_array_setter_impl(parser_t *parser, ast_value *array)
+{
+    ast_expression *root = NULL;
+    root = array_setter_node(parser, array,
+                             array->setter->expression.params[0],
+                             array->setter->expression.params[1],
+                             0, array->expression.count);
+    if (!root) {
+        parseerror(parser, "failed to build accessor search tree");
+        return false;
+    }
+    if (!ast_block_add_expr(array->setter->constval.vfunc->blocks[0], root)) {
+        ast_delete(root);
+        return false;
+    }
+    return true;
+}
+
+static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
+{
+    if (!parser_create_array_setter_proto(parser, array, funcname))
+        return false;
+    return parser_create_array_setter_impl(parser, array);
 }
 
 static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname)
@@ -3960,9 +4449,8 @@ cleanup:
     return false;
 }
 
-static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
+static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
 {
-    ast_expression *root = NULL;
     ast_value      *index = NULL;
     ast_value      *fval;
     ast_function   *func;
@@ -3972,11 +4460,11 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
      */
     if (!ast_istype(array->expression.next, ast_value)) {
         parseerror(parser, "internal error: array accessor needs to build an ast_value with a copy of the element type");
-        return false;
+        return NULL;
     }
 
     if (!parser_create_array_accessor(parser, array, funcname, &fval))
-        return false;
+        return NULL;
     func = fval->constval.vfunc;
     fval->expression.next = ast_type_copy(ast_ctx(array), elemtype);
 
@@ -3988,20 +4476,36 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
     }
     vec_push(fval->expression.params, index);
 
-    root = array_getter_node(parser, array, index, 0, array->expression.count);
-    if (!root) {
-        parseerror(parser, "failed to build accessor search tree");
-        goto cleanup;
-    }
-
     array->getter = fval;
-    return ast_block_add_expr(func->blocks[0], root);
+    return fval;
 cleanup:
     if (index) ast_delete(index);
-    if (root)  ast_delete(root);
     ast_delete(func);
     ast_delete(fval);
-    return false;
+    return NULL;
+}
+
+static bool parser_create_array_getter_impl(parser_t *parser, ast_value *array)
+{
+    ast_expression *root = NULL;
+
+    root = array_getter_node(parser, array, array->getter->expression.params[0], 0, array->expression.count);
+    if (!root) {
+        parseerror(parser, "failed to build accessor search tree");
+        return false;
+    }
+    if (!ast_block_add_expr(array->getter->constval.vfunc->blocks[0], root)) {
+        ast_delete(root);
+        return false;
+    }
+    return true;
+}
+
+static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
+{
+    if (!parser_create_array_getter_proto(parser, array, elemtype, funcname))
+        return false;
+    return parser_create_array_getter_impl(parser, array);
 }
 
 static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
@@ -4014,6 +4518,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
     ast_value  *fval;
     bool        first = true;
     bool        variadic = false;
+    ast_value  *varparam = NULL;
+    char       *argcounter = NULL;
 
     ctx = parser_ctx(parser);
 
@@ -4043,14 +4549,17 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
         if (parser->tok == TOKEN_DOTS) {
             /* '...' indicates a varargs function */
             variadic = true;
-            if (!parser_next(parser)) {
-                parseerror(parser, "expected parameter");
-                return NULL;
-            }
-            if (parser->tok != ')') {
+            if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) {
                 parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
                 goto on_error;
             }
+            if (parser->tok == TOKEN_IDENT) {
+                argcounter = util_strdup(parser_tokval(parser));
+                if (!parser_next(parser) || parser->tok != ')') {
+                    parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
+                    goto on_error;
+                }
+            }
         }
         else
         {
@@ -4065,6 +4574,23 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
                 parseerror(parser, "type not supported as part of a parameter list: %s", tname);
                 goto on_error;
             }
+            /* type-restricted varargs */
+            if (parser->tok == TOKEN_DOTS) {
+                variadic = true;
+                varparam = vec_last(params);
+                vec_pop(params);
+                if (!parser_next(parser) || (parser->tok != ')' && parser->tok != TOKEN_IDENT)) {
+                    parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
+                    goto on_error;
+                }
+                if (parser->tok == TOKEN_IDENT) {
+                    argcounter = util_strdup(parser_tokval(parser));
+                    if (!parser_next(parser) || parser->tok != ')') {
+                        parseerror(parser, "`...` must be the last parameter of a variadic function declaration");
+                        goto on_error;
+                    }
+                }
+            }
         }
     }
 
@@ -4088,12 +4614,16 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
         fval->expression.flags |= AST_FLAG_VARIADIC;
     var = fval;
 
-    var->expression.params = params;
+    var->expression.params   = params;
+    var->expression.varparam = (ast_expression*)varparam;
+    var->argcounter          = argcounter;
     params = NULL;
 
     return var;
 
 on_error:
+    if (argcounter)
+        mem_d(argcounter);
     ast_delete(var);
     for (i = 0; i < vec_size(params); ++i)
         ast_delete(params[i]);
@@ -4115,7 +4645,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
         return NULL;
     }
 
-    cexp = parse_expression_leave(parser, true, false);
+    cexp = parse_expression_leave(parser, true, false, false);
 
     if (!cexp || !ast_istype(cexp, ast_value)) {
         if (cexp)
@@ -4291,7 +4821,6 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
         if (!var) {
             if (name)
                 mem_d((void*)name);
-            ast_delete(var);
             return NULL;
         }
     }
@@ -4515,7 +5044,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     goto cleanup;
                     */
                 }
-                if (opts.standard == COMPILER_QCC &&
+                if ((opts.standard == COMPILER_QCC || opts.standard == COMPILER_FTEQCC) &&
                     (old = parser_find_global(parser, var->name)))
                 {
                     parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
@@ -4564,35 +5093,28 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                 {
                     /* other globals */
                     if (old) {
-                        if (opts.standard == COMPILER_GMQCC) {
-                            parseerror(parser, "global `%s` already declared here: %s:%i",
-                                       var->name, ast_ctx(old).file, ast_ctx(old).line);
+                        if (parsewarning(parser, WARN_DOUBLE_DECLARATION,
+                                         "global `%s` already declared here: %s:%i",
+                                         var->name, ast_ctx(old).file, ast_ctx(old).line))
+                        {
                             retval = false;
                             goto cleanup;
-                        } else {
-                            if (parsewarning(parser, WARN_DOUBLE_DECLARATION,
-                                             "global `%s` already declared here: %s:%i",
-                                             var->name, ast_ctx(old).file, ast_ctx(old).line))
-                            {
-                                retval = false;
-                                goto cleanup;
-                            }
-                            proto = (ast_value*)old;
-                            if (!ast_istype(old, ast_value)) {
-                                parseerror(parser, "internal error: not an ast_value");
-                                retval = false;
-                                proto = NULL;
-                                goto cleanup;
-                            }
-                            if (!parser_check_qualifiers(parser, var, proto)) {
-                                retval = false;
-                                proto = NULL;
-                                goto cleanup;
-                            }
-                            proto->expression.flags |= var->expression.flags;
-                            ast_delete(var);
-                            var = proto;
                         }
+                        proto = (ast_value*)old;
+                        if (!ast_istype(old, ast_value)) {
+                            parseerror(parser, "internal error: not an ast_value");
+                            retval = false;
+                            proto = NULL;
+                            goto cleanup;
+                        }
+                        if (!parser_check_qualifiers(parser, var, proto)) {
+                            retval = false;
+                            proto = NULL;
+                            goto cleanup;
+                        }
+                        proto->expression.flags |= var->expression.flags;
+                        ast_delete(var);
+                        var = proto;
                     }
                     if (opts.standard == COMPILER_QCC &&
                         (old = parser_find_field(parser, var->name)))
@@ -4668,12 +5190,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     }
                 }
                 else {
-                    vec_push(parser->globals, (ast_expression*)var);
-                    util_htset(parser->htglobals, var->name, var);
+                    parser_addglobal(parser, var->name, (ast_expression*)var);
                     if (isvector) {
                         for (i = 0; i < 3; ++i) {
-                            vec_push(parser->globals, (ast_expression*)me[i]);
-                            util_htset(parser->htglobals, me[i]->name, me[i]);
+                            parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
                         }
                     }
                 }
@@ -4694,6 +5214,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
                     /* Add it to the local scope */
                     util_htset(vec_last(parser->variables), var->name, (void*)var);
+
+                    /* corrector */
+                    correct_add (
+                         vec_last(parser->correct_variables),
+                        &vec_last(parser->correct_variables_score),
+                        var->name
+                    );
+
                     /* now rename the global */
                     ln = strlen(var->name);
                     vec_append(defname, ln, var->name);
@@ -4707,6 +5235,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                         for (i = 0; i < 3; ++i) {
                             util_htset(vec_last(parser->variables), me[i]->name, (void*)(me[i]));
 
+                            /* corrector */
+                            correct_add(
+                                 vec_last(parser->correct_variables),
+                                &vec_last(parser->correct_variables_score),
+                                me[i]->name
+                            );
+
                             vec_shrinkto(defname, prefix_len);
                             ln = strlen(me[i]->name);
                             vec_append(defname, ln, me[i]->name);
@@ -4888,7 +5423,7 @@ skipvar:
             ast_expression *cexp;
             ast_value      *cval;
 
-            cexp = parse_expression_leave(parser, true, false);
+            cexp = parse_expression_leave(parser, true, false, false);
             if (!cexp)
                 break;
 
@@ -4902,8 +5437,7 @@ skipvar:
                 }
                 else
                 {
-                    if (opts.standard != COMPILER_GMQCC &&
-                        !OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
+                    if (!OPTS_FLAG(INITIALIZED_NONCONSTANTS) &&
                         qualifier != CV_VAR)
                     {
                         var->cvq = CV_CONST;
@@ -5016,6 +5550,10 @@ static bool parser_global_statement(parser_t *parser)
             return false;
         return parse_variable(parser, NULL, true, cvq, NULL, noref, is_static, qflags, vstring);
     }
+    else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "enum"))
+    {
+        return parse_enum(parser);
+    }
     else if (parser->tok == TOKEN_KEYWORD)
     {
         if (!strcmp(parser_tokval(parser), "typedef")) {
@@ -5153,12 +5691,32 @@ bool parser_init()
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, 0);
 
+    /* corrector */
+    vec_push(parser->correct_variables, correct_trie_new());
+    vec_push(parser->correct_variables_score, NULL);
+
     empty_ctx.file = "<internal>";
     empty_ctx.line = 0;
     parser->nil = ast_value_new(empty_ctx, "nil", TYPE_NIL);
     parser->nil->cvq = CV_CONST;
     if (OPTS_FLAG(UNTYPED_NIL))
         util_htset(parser->htglobals, "nil", (void*)parser->nil);
+
+    parser->max_param_count = 1;
+
+    parser->const_vec[0] = ast_value_new(empty_ctx, "<vector.x>", TYPE_NOEXPR);
+    parser->const_vec[1] = ast_value_new(empty_ctx, "<vector.y>", TYPE_NOEXPR);
+    parser->const_vec[2] = ast_value_new(empty_ctx, "<vector.z>", TYPE_NOEXPR);
+
+    if (opts.add_info) {
+        parser->reserved_version = ast_value_new(empty_ctx, "reserved:version", TYPE_STRING);
+        parser->reserved_version->cvq = CV_CONST;
+        parser->reserved_version->hasvalue = true;
+        parser->reserved_version->expression.flags |= AST_FLAG_INCLUDE_DEF;
+        parser->reserved_version->constval.vstring = util_strdup(GMQCC_FULL_VERSION_STRING);
+    } else {
+        parser->reserved_version = NULL;
+    }
     return true;
 }
 
@@ -5254,6 +5812,14 @@ void parser_cleanup()
     vec_free(parser->_blocklocals);
     vec_free(parser->_locals);
 
+    /* corrector */
+    for (i = 0; i < vec_size(parser->correct_variables); ++i) {
+        correct_del(parser->correct_variables[i], parser->correct_variables_score[i]);
+    }
+    vec_free(parser->correct_variables);
+    vec_free(parser->correct_variables_score);
+
+
     for (i = 0; i < vec_size(parser->_typedefs); ++i)
         ast_delete(parser->_typedefs[i]);
     vec_free(parser->_typedefs);
@@ -5271,6 +5837,10 @@ void parser_cleanup()
 
     ast_value_delete(parser->nil);
 
+    ast_value_delete(parser->const_vec[0]);
+    ast_value_delete(parser->const_vec[1]);
+    ast_value_delete(parser->const_vec[2]);
+
     mem_d(parser);
 }
 
@@ -5332,6 +5902,31 @@ bool parser_finish(const char *output)
             return false;
         }
     }
+    /* Build function vararg accessor ast tree now before generating
+     * immediates, because the accessors may add new immediates
+     */
+    for (i = 0; i < vec_size(parser->functions); ++i) {
+        ast_function *f = parser->functions[i];
+        if (f->varargs) {
+            if (parser->max_param_count > vec_size(f->vtype->expression.params)) {
+                f->varargs->expression.count = parser->max_param_count - vec_size(f->vtype->expression.params);
+                if (!parser_create_array_setter_impl(parser, f->varargs)) {
+                    con_out("failed to generate vararg setter for %s\n", f->name);
+                    ir_builder_delete(ir);
+                    return false;
+                }
+                if (!parser_create_array_getter_impl(parser, f->varargs)) {
+                    con_out("failed to generate vararg getter for %s\n", f->name);
+                    ir_builder_delete(ir);
+                    return false;
+                }
+            } else {
+                ast_delete(f->varargs);
+                f->varargs = NULL;
+            }
+        }
+    }
+    /* 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);
@@ -5387,9 +5982,17 @@ bool parser_finish(const char *output)
             return false;
         }
     }
+    if (parser->reserved_version &&
+        !ast_global_codegen(parser->reserved_version, ir, false))
+    {
+        con_out("failed to generate reserved::version");
+        ir_builder_delete(ir);
+        return false;
+    }
     for (i = 0; i < vec_size(parser->functions); ++i) {
-        if (!ast_function_codegen(parser->functions[i], ir)) {
-            con_out("failed to generate function %s\n", parser->functions[i]->name);
+        ast_function *f = parser->functions[i];
+        if (!ast_function_codegen(f, ir)) {
+            con_out("failed to generate function %s\n", f->name);
             ir_builder_delete(ir);
             return false;
         }