]> git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Remove trailing whitespace
[xonotic/gmqcc.git] / parser.c
index efcacced30417a88337c6478bbcf26a24ba7ee5b..b3e98823604b553363ff9db41caa7151d1551e51 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -2,7 +2,7 @@
  * Copyright (C) 2012, 2013
  *     Wolfgang Bumiller
  *     Dale Weiler
- * 
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
  * this software and associated documentation files (the "Software"), to deal in
  * the Software without restriction, including without limitation the rights to
@@ -23,6 +23,7 @@
  */
 #include <stdio.h>
 #include <stdarg.h>
+#include <math.h>
 
 #include "gmqcc.h"
 #include "lexer.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 };
-typedef struct {
+typedef struct parser_s {
     lex_file *lex;
     int      tok;
 
@@ -47,12 +47,17 @@ typedef struct {
     ast_value    **imm_vector;
     size_t         translated;
 
+    ht ht_imm_string;
+
     /* must be deleted first, they reference immediates and values */
     ast_value    **accessors;
 
     ast_value *imm_float_zero;
     ast_value *imm_float_one;
+    ast_value *imm_float_neg_one;
+
     ast_value *imm_vector_zero;
+
     ast_value *nil;
     ast_value *reserved_version;
 
@@ -60,6 +65,7 @@ typedef struct {
     size_t crc_fields;
 
     ast_function *function;
+    ht            aliases;
 
     /* All the labels the function defined...
      * Should they be in ast_function instead?
@@ -89,12 +95,6 @@ typedef struct {
     /* we store the '=' operator info */
     const oper_info *assign_op;
 
-    /* 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];
 
@@ -142,16 +142,6 @@ static bool GMQCC_WARN parsewarning(parser_t *parser, int warntype, const char *
     return r;
 }
 
-static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
-{
-    bool    r;
-    va_list ap;
-    va_start(ap, fmt);
-    r = vcompile_warning(ctx, warntype, fmt, ap);
-    va_end(ap);
-    return r;
-}
-
 /**********************************************************************
  * some maths used for constant folding
  */
@@ -239,6 +229,12 @@ static ast_value* parser_const_float_0(parser_t *parser)
     return parser->imm_float_zero;
 }
 
+static ast_value* parser_const_float_neg1(parser_t *parser) {
+    if (!parser->imm_float_neg_one)
+        parser->imm_float_neg_one = parser_const_float(parser, -1);
+    return parser->imm_float_neg_one;
+}
+
 static ast_value* parser_const_float_1(parser_t *parser)
 {
     if (!parser->imm_float_one)
@@ -259,12 +255,22 @@ static char *parser_strdup(const char *str)
 
 static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate)
 {
-    size_t i;
+    size_t hash = util_hthash(parser->ht_imm_string, str);
     ast_value *out;
+    if ( (out = util_htgeth(parser->ht_imm_string, str, hash)) ) {
+        if (dotranslate && out->name[0] == '#') {
+            char name[32];
+            snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
+            ast_value_set_name(out, name);
+        }
+        return out;
+    }
+    /*
     for (i = 0; i < vec_size(parser->imm_string); ++i) {
         if (!strcmp(parser->imm_string[i]->constval.vstring, str))
             return parser->imm_string[i];
     }
+    */
     if (dotranslate) {
         char name[32];
         snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
@@ -275,6 +281,7 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do
     out->hasvalue = true;
     out->constval.vstring = parser_strdup(str);
     vec_push(parser->imm_string, out);
+    util_htseth(parser->ht_imm_string, str, hash, out);
     return out;
 }
 
@@ -326,6 +333,9 @@ static ast_expression* parser_find_label(parser_t *parser, const char *name)
 
 static ast_expression* parser_find_global(parser_t *parser, const char *name)
 {
+    ast_expression *var = (ast_expression*)util_htget(parser->aliases, parser_tokval(parser));
+    if (var)
+        return var;
     return (ast_expression*)util_htget(parser->htglobals, name);
 }
 
@@ -383,26 +393,34 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t
     return NULL;
 }
 
+/* include intrinsics */
+#include "intrin.h"
+
 typedef struct
 {
     size_t etype; /* 0 = expression, others are operators */
-    int             paren;
+    bool            isparen;
     size_t          off;
     ast_expression *out;
     ast_block      *block; /* for commas and function calls */
     lex_ctx ctx;
 } sy_elem;
+
+enum {
+    PAREN_EXPR,
+    PAREN_FUNC,
+    PAREN_INDEX,
+    PAREN_TERNARY1,
+    PAREN_TERNARY2
+};
 typedef struct
 {
-    sy_elem *out;
-    sy_elem *ops;
+    sy_elem        *out;
+    sy_elem        *ops;
+    size_t         *argc;
+    unsigned int   *paren;
 } shunt;
 
-#define SY_PAREN_EXPR '('
-#define SY_PAREN_FUNC 'f'
-#define SY_PAREN_INDEX '['
-#define SY_PAREN_TERNARY '?'
-
 static sy_elem syexp(lex_ctx ctx, ast_expression *v) {
     sy_elem e;
     e.etype = 0;
@@ -410,7 +428,7 @@ static sy_elem syexp(lex_ctx ctx, ast_expression *v) {
     e.out   = v;
     e.block = NULL;
     e.ctx   = ctx;
-    e.paren = 0;
+    e.isparen = false;
     return e;
 }
 
@@ -421,7 +439,7 @@ static sy_elem syblock(lex_ctx ctx, ast_block *v) {
     e.out   = (ast_expression*)v;
     e.block = v;
     e.ctx   = ctx;
-    e.paren = 0;
+    e.isparen = false;
     return e;
 }
 
@@ -432,33 +450,27 @@ static sy_elem syop(lex_ctx ctx, const oper_info *op) {
     e.out   = NULL;
     e.block = NULL;
     e.ctx   = ctx;
-    e.paren = 0;
+    e.isparen = false;
     return e;
 }
 
-static sy_elem syparen(lex_ctx ctx, int p, size_t off) {
+static sy_elem syparen(lex_ctx ctx, size_t off) {
     sy_elem e;
     e.etype = 0;
     e.off   = off;
     e.out   = NULL;
     e.block = NULL;
     e.ctx   = ctx;
-    e.paren = p;
+    e.isparen = true;
     return e;
 }
 
-#ifdef DEBUGSHUNT
-# define DEBUGSHUNTDO(x) x
-#else
-# define DEBUGSHUNTDO(x)
-#endif
-
 /* With regular precedence rules, ent.foo[n] is the same as (ent.foo)[n],
  * so we need to rotate it to become ent.(foo[n]).
  */
 static bool rotate_entfield_array_index_nodes(ast_expression **out)
 {
-    ast_array_index *index;
+    ast_array_index *index, *oldindex;
     ast_entfield    *entfield;
 
     ast_value       *field;
@@ -482,12 +494,16 @@ static bool rotate_entfield_array_index_nodes(ast_expression **out)
     sub    = index->index;
     entity = entfield->entity;
 
-    ast_delete(index);
+    oldindex = index;
 
     index = ast_array_index_new(ctx, (ast_expression*)field, sub);
     entfield = ast_entfield_new(ctx, entity, (ast_expression*)index);
     *out = (ast_expression*)entfield;
 
+    oldindex->array = NULL;
+    oldindex->index = NULL;
+    ast_delete(oldindex);
+
     return true;
 }
 
@@ -537,7 +553,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
         return false;
     }
 
-    if (vec_last(sy->ops).paren) {
+    if (vec_last(sy->ops).isparen) {
         parseerror(parser, "unmatched parenthesis");
         return false;
     }
@@ -545,8 +561,6 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
     op = &operators[vec_last(sy->ops).etype - 1];
     ctx = vec_last(sy->ops).ctx;
 
-    DEBUGSHUNTDO(con_out("apply %s\n", op->op));
-
     if (vec_size(sy->out) < op->operands) {
         compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out),
                       op->op, (int)op->id);
@@ -650,7 +664,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             {
 #if 0
                 /* This is not broken in fteqcc anymore */
-                if (opts.standard != COMPILER_GMQCC) {
+                if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
                     /* this error doesn't need to make us bail out */
                     (void)!parsewarning(parser, WARN_EXTENSIONS,
                                         "accessing array-field members of an entity without parenthesis\n"
@@ -661,6 +675,12 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
 
         case opid1(','):
+            if (vec_size(sy->paren) && vec_last(sy->paren) == PAREN_FUNC) {
+                vec_push(sy->out, syexp(ctx, exprs[0]));
+                vec_push(sy->out, syexp(ctx, exprs[1]));
+                vec_last(sy->argc)++;
+                return true;
+            }
             if (blocks[0]) {
                 if (!ast_block_add_expr(blocks[0], exprs[1]))
                     return false;
@@ -672,8 +692,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     return false;
                 }
             }
-            if (!ast_block_set_type(blocks[0], exprs[1]))
-                return false;
+            ast_block_set_type(blocks[0], exprs[1]);
 
             vec_push(sy->out, syblock(ctx, blocks[0]));
             return true;
@@ -959,10 +978,35 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 return false;
             }
             break;
+
         case opid1('%'):
+            if (NotSameType(TYPE_FLOAT)) {
+                compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s",
+                    type_name[exprs[0]->expression.vtype],
+                    type_name[exprs[1]->expression.vtype]);
+                return false;
+            }
+            if (CanConstFold(exprs[0], exprs[1])) {
+                out = (ast_expression*)parser_const_float(parser,
+                            (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1))));
+            } else {
+                /* generate a call to __builtin_mod */
+                ast_expression *mod  = intrin_func(parser, "mod");
+                ast_call       *call = NULL;
+                if (!mod) return false; /* can return null for missing floor */
+
+                call = ast_call_new(parser_ctx(parser), mod);
+                vec_push(call->params, exprs[0]);
+                vec_push(call->params, exprs[1]);
+
+                out = (ast_expression*)call;
+            }
+            break;
+
         case opid2('%','='):
-            compile_error(ctx, "qc does not have a modulo operator");
+            compile_error(ctx, "%= is unimplemented");
             return false;
+
         case opid1('|'):
         case opid1('&'):
             if (NotSameType(TYPE_FLOAT)) {
@@ -1052,11 +1096,11 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             break;
 
         case opid2('?',':'):
-            if (vec_last(parser->pot) != POT_TERNARY2) {
+            if (vec_last(sy->paren) != PAREN_TERNARY2) {
                 compile_error(ctx, "mismatched parenthesis/ternary");
                 return false;
             }
-            vec_pop(parser->pot);
+            vec_pop(sy->paren);
             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));
@@ -1069,6 +1113,69 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
             break;
 
+        case opid2('*', '*'):
+            if (NotSameType(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 exponentiation: %s and %s",
+                    ty1, ty2);
+
+                return false;
+            }
+
+            if (CanConstFold(exprs[0], exprs[1])) {
+                out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1)));
+            } else {
+                ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow"));
+                vec_push(gencall->params, exprs[0]);
+                vec_push(gencall->params, exprs[1]);
+                out = (ast_expression*)gencall;
+            }
+            break;
+
+        case opid3('<','=','>'): /* -1, 0, or 1 */
+            if (NotSameType(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 comparision: %s and %s",
+                    ty1, ty2);
+
+                return false;
+            }
+
+            if (CanConstFold(exprs[0], exprs[1])) {
+                if (ConstF(0) < ConstF(1))
+                    out = (ast_expression*)parser_const_float_neg1(parser);
+                else if (ConstF(0) == ConstF(1))
+                    out = (ast_expression*)parser_const_float_0(parser);
+                else if (ConstF(0) > ConstF(1))
+                    out = (ast_expression*)parser_const_float_1(parser);
+            } else {
+                ast_binary *eq = ast_binary_new(ctx, INSTR_EQ_F, exprs[0], exprs[1]);
+
+                eq->refs = (ast_binary_ref)false; /* references nothing */
+
+                    /* if (lt) { */
+                out = (ast_expression*)ast_ternary_new(ctx,
+                        (ast_expression*)ast_binary_new(ctx, INSTR_LT, exprs[0], exprs[1]),
+                        /* out = -1 */
+                        (ast_expression*)parser_const_float_neg1(parser),
+                    /* } else { */
+                        /* if (eq) { */
+                        (ast_expression*)ast_ternary_new(ctx, (ast_expression*)eq,
+                            /* out = 0 */
+                            (ast_expression*)parser_const_float_0(parser),
+                        /* } else { */
+                            /* out = 1 */
+                            (ast_expression*)parser_const_float_1(parser)
+                        /* } */
+                        )
+                    /* } */
+                    );
+
+            }
+            break;
+
         case opid1('>'):
             generated_op += 1; /* INSTR_GT */
         case opid1('<'):
@@ -1115,7 +1222,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                 }
                 else
                     assignop = type_storep_instr[exprs[0]->expression.vtype];
-                if (assignop == AINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
+                if (assignop == VINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
                 {
                     ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
@@ -1142,7 +1249,7 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
                     assignop = type_store_instr[exprs[0]->expression.vtype];
                 }
 
-                if (assignop == AINSTR_END) {
+                if (assignop == VINSTR_END) {
                     ast_type_to_string(exprs[0], ty1, sizeof(ty1));
                     ast_type_to_string(exprs[1], ty2, sizeof(ty2));
                     compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
@@ -1360,15 +1467,28 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
             asbinstore->keep_dest = true;
             out = (ast_expression*)asbinstore;
             break;
+
+        case opid2('~', 'P'):
+            if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+                ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+                compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
+                return false;
+            }
+
+            if(CanConstFold1(exprs[0]))
+                out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0));
+            else
+                out = (ast_expression*)
+                    ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
+            break;
     }
 #undef NotSameType
 
     if (!out) {
-        compile_error(ctx, "failed to apply operand %s", op->op);
+        compile_error(ctx, "failed to apply operator %s", op->op);
         return false;
     }
 
-    DEBUGSHUNTDO(con_out("applied %s\n", op->op));
     vec_push(sy->out, syexp(ctx, out));
     return true;
 }
@@ -1381,20 +1501,27 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
     ast_call       *call;
 
     size_t          fid;
-    size_t          paramcount;
+    size_t          paramcount, i;
 
+    fid = vec_last(sy->ops).off;
     vec_shrinkby(sy->ops, 1);
-    fid = sy->ops[vec_size(sy->ops)].off;
 
     /* out[fid] is the function
      * everything above is parameters...
-     * 0 params = nothing
-     * 1 params = ast_expression
-     * more = ast_block
      */
+    if (!vec_size(sy->argc)) {
+        parseerror(parser, "internal error: no argument counter available");
+        return false;
+    }
 
-    if (vec_size(sy->out) < 1 || vec_size(sy->out) <= fid) {
-        parseerror(parser, "internal error: function call needs function and parameter list...");
+    paramcount = vec_last(sy->argc);
+    vec_pop(sy->argc);
+
+    if (vec_size(sy->out) < fid) {
+        parseerror(parser, "internal error: broken function call%lu < %lu+%lu\n",
+                   (unsigned long)vec_size(sy->out),
+                   (unsigned long)fid,
+                   (unsigned long)paramcount);
         return false;
     }
 
@@ -1417,47 +1544,31 @@ 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 */
-        paramcount = 0;
-    } else if (fid+2 == vec_size(sy->out)) {
-        ast_block *params;
-        vec_shrinkby(sy->out, 1);
-        params = sy->out[vec_size(sy->out)].block;
-        if (!params) {
-            /* 1 param */
-            paramcount = 1;
-            vec_push(call->params, sy->out[vec_size(sy->out)].out);
-        } else {
-            paramcount = vec_size(params->exprs);
-            call->params = params->exprs;
-            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");
+    if (fid+1 < vec_size(sy->out))
+        ++paramcount;
+
+    if (fid+1 + paramcount != vec_size(sy->out)) {
+        parseerror(parser, "internal error: parameter count mismatch: (%lu+1+%lu), %lu",
+                   (unsigned long)fid, (unsigned long)paramcount, (unsigned long)vec_size(sy->out));
         return false;
     }
 
+    for (i = 0; i < paramcount; ++i)
+        vec_push(call->params, sy->out[fid+1 + i].out);
+    vec_shrinkby(sy->out, paramcount);
+    (void)!ast_call_check_types(call);
+    if (parser->max_param_count < paramcount)
+        parser->max_param_count = paramcount;
+
     if (ast_istype(fun, ast_value)) {
         funval = (ast_value*)fun;
         if ((fun->expression.flags & AST_FLAG_VARIADIC) &&
             !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
         {
-            size_t va_count;
-            if (paramcount < vec_size(fun->expression.params))
-                va_count = 0;
-            else
-                va_count = paramcount - vec_size(fun->expression.params);
-            call->va_count = (ast_expression*)parser_const_float(parser, (double)va_count);
+            call->va_count = (ast_expression*)parser_const_float(parser, (double)paramcount);
         }
     }
 
@@ -1518,54 +1629,48 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
     return true;
 }
 
-static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only)
+static bool parser_close_paren(parser_t *parser, shunt *sy)
 {
     if (!vec_size(sy->ops)) {
         parseerror(parser, "unmatched closing paren");
         return false;
     }
-    /* this would for bit a + (x) because there are no operators inside (x)
-    if (sy->ops[vec_size(sy->ops)-1].paren == 1) {
-        parseerror(parser, "empty parenthesis expression");
-        return false;
-    }
-    */
+
     while (vec_size(sy->ops)) {
-        if (vec_last(sy->ops).paren == SY_PAREN_FUNC) {
-            if (!parser_close_call(parser, sy))
-                return false;
-            break;
-        }
-        if (vec_last(sy->ops).paren == SY_PAREN_EXPR) {
-            if (!vec_size(sy->out)) {
-                compile_error(vec_last(sy->ops).ctx, "empty paren expression");
+        if (vec_last(sy->ops).isparen) {
+            if (vec_last(sy->paren) == PAREN_FUNC) {
+                vec_pop(sy->paren);
+                if (!parser_close_call(parser, sy))
+                    return false;
+                break;
+            }
+            if (vec_last(sy->paren) == PAREN_EXPR) {
+                vec_pop(sy->paren);
+                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 false;
+                break;
             }
-            vec_shrinkby(sy->ops, 1);
-            return !functions_only;
-        }
-        if (vec_last(sy->ops).paren == SY_PAREN_INDEX) {
-            if (functions_only)
-                return false;
-            /* pop off the parenthesis */
-            vec_shrinkby(sy->ops, 1);
-            /* then apply the index operator */
-            if (!parser_sy_apply_operator(parser, sy))
-                return false;
-            return true;
-        }
-        if (vec_last(sy->ops).paren == SY_PAREN_TERNARY) {
-            if (functions_only)
-                return false;
-            if (vec_last(parser->pot) != POT_TERNARY1) {
-                parseerror(parser, "mismatched colon in ternary expression (missing closing paren?)");
-                return false;
+            if (vec_last(sy->paren) == PAREN_INDEX) {
+                vec_pop(sy->paren);
+                /* pop off the parenthesis */
+                vec_shrinkby(sy->ops, 1);
+                /* then apply the index operator */
+                if (!parser_sy_apply_operator(parser, sy))
+                    return false;
+                break;
             }
-            vec_last(parser->pot) = POT_TERNARY2;
-            /* pop off the parenthesis */
-            vec_shrinkby(sy->ops, 1);
-            return true;
+            if (vec_last(sy->paren) == PAREN_TERNARY1) {
+                vec_last(sy->paren) = PAREN_TERNARY2;
+                /* pop off the parenthesis */
+                vec_shrinkby(sy->ops, 1);
+                break;
+            }
+            compile_error(vec_last(sy->ops).ctx, "invalid parenthesis");
+            return false;
         }
         if (!parser_sy_apply_operator(parser, sy))
             return false;
@@ -1666,324 +1771,236 @@ static ast_expression* parse_vararg_do(parser_t *parser)
 
 static ast_expression* parse_vararg(parser_t *parser)
 {
-    bool             old_noops = parser->lex->flags.noops;
-    enum parser_pot *old_pot   = parser->pot;
+    bool           old_noops = parser->lex->flags.noops;
 
     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)
+static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
 {
-    ast_expression *expr = NULL;
-    shunt sy;
-    size_t i;
-    bool wantop = 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
-     */
-    bool warn_truthvalue = true;
-
-    /* count the parens because an if starts with one, so the
-     * end of a condition is an unmatched closing paren
-     */
-    int parens = 0;
-    int ternaries = 0;
-
-    sy.out = NULL;
-    sy.ops = NULL;
-
-    parser->lex->flags.noops = false;
-
-    parser_reclassify_token(parser);
-
-    while (true)
+    if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
+        parser->tok == TOKEN_IDENT &&
+        !strcmp(parser_tokval(parser), "_"))
     {
-        if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
-            parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "_"))
-        {
-            /* a translatable string */
-            ast_value *val;
-
-            if (wantop) {
-                parseerror(parser, "expected operator or end of statement, got constant");
-                goto onerr;
-            }
+        /* a translatable string */
+        ast_value *val;
 
-            parser->lex->flags.noops = true;
-            if (!parser_next(parser) || parser->tok != '(') {
-                parseerror(parser, "use _(\"string\") to create a translatable string constant");
-                goto onerr;
-            }
-            parser->lex->flags.noops = false;
-            if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) {
-                parseerror(parser, "expected a constant string in translatable-string extension");
-                goto onerr;
-            }
-            val = parser_const_string(parser, parser_tokval(parser), true);
-            wantop = true;
-            if (!val)
-                return NULL;
-            vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
-            DEBUGSHUNTDO(con_out("push string\n"));
+        parser->lex->flags.noops = true;
+        if (!parser_next(parser) || parser->tok != '(') {
+            parseerror(parser, "use _(\"string\") to create a translatable string constant");
+            return false;
+        }
+        parser->lex->flags.noops = false;
+        if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) {
+            parseerror(parser, "expected a constant string in translatable-string extension");
+            return false;
+        }
+        val = parser_const_string(parser, parser_tokval(parser), true);
+        if (!val)
+            return false;
+        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
 
-            if (!parser_next(parser) || parser->tok != ')') {
-                parseerror(parser, "expected closing paren after translatable string");
-                goto onerr;
-            }
+        if (!parser_next(parser) || parser->tok != ')') {
+            parseerror(parser, "expected closing paren after translatable string");
+            return false;
         }
-        else if (parser->tok == TOKEN_DOTS)
+        return true;
+    }
+    else if (parser->tok == TOKEN_DOTS)
+    {
+        ast_expression *va;
+        if (!OPTS_FLAG(VARIADIC_ARGS)) {
+            parseerror(parser, "cannot access varargs (try -fvariadic-args)");
+            return false;
+        }
+        va = parse_vararg(parser);
+        if (!va)
+            return false;
+        vec_push(sy->out, syexp(parser_ctx(parser), va));
+        return true;
+    }
+    else if (parser->tok == TOKEN_FLOATCONST) {
+        ast_value *val;
+        val = parser_const_float(parser, (parser_token(parser)->constval.f));
+        if (!val)
+            return false;
+        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        return true;
+    }
+    else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
+        ast_value *val;
+        val = parser_const_float(parser, (double)(parser_token(parser)->constval.i));
+        if (!val)
+            return false;
+        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        return true;
+    }
+    else if (parser->tok == TOKEN_STRINGCONST) {
+        ast_value *val;
+        val = parser_const_string(parser, parser_tokval(parser), false);
+        if (!val)
+            return false;
+        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        return true;
+    }
+    else if (parser->tok == TOKEN_VECTORCONST) {
+        ast_value *val;
+        val = parser_const_vector(parser, parser_token(parser)->constval.v);
+        if (!val)
+            return false;
+        vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+        return true;
+    }
+    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;
+        /* 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))
         {
-            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"));
+            /* When adding more intrinsics, fix the above condition */
+            prev = NULL;
         }
-        else if (parser->tok == TOKEN_IDENT)
+        if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
         {
-            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;
+            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);
             }
-            wantop = true;
-            /* 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))
-            {
-                /* When adding more intrinsics, fix the above condition */
-                prev = NULL;
+        }
+        if (!var) {
+            /* intrinsics */
+            if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
+                var = (ast_expression*)intrinsic_debug_typestring;
             }
-            if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
-            {
-                var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
+            /* now we try for the real intrinsic hashtable. If the string
+             * begins with __builtin, we simply skip past it, otherwise we
+             * use the identifier as is.
+             */
+            else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
+                var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */);
             } 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);
-                }
+                var = intrin_func(parser, parser_tokval(parser));
             }
-            if (!var) {
-                /* intrinsics */
-                if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
-                    var = (ast_expression*)intrinsic_debug_typestring;
-                }
-                else
-                {
-                    size_t i;
-                    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;
-                            }
+            if (!var) {
+                char *correct = NULL;
+                size_t i;
+
+                /*
+                 * 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));
+                            return false;
                         }
                     }
+                }
 
-                    /*
-                     * 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);
+                /*
+                 * 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_OPTION_BOOL(OPTION_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);
-                            goto onerr;
+                            correct = NULL;
                         }
                     }
-                    parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
-                    goto onerr;
-                }
-            }
-            else
-            {
-                if (ast_istype(var, ast_value)) {
-                    ((ast_value*)var)->uses++;
-                }
-                else if (ast_istype(var, ast_member)) {
-                    ast_member *mem = (ast_member*)var;
-                    if (ast_istype(mem->owner, ast_value))
-                        ((ast_value*)(mem->owner))->uses++;
+                    correct_free(&corr);
+
+                    if (correct) {
+                        parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+                        mem_d(correct);
+                        return false;
+                    }
                 }
+                parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
+                return false;
             }
-            vec_push(sy.out, syexp(parser_ctx(parser), var));
-            DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser)));
-        }
-        else if (parser->tok == TOKEN_FLOATCONST) {
-            ast_value *val;
-            if (wantop) {
-                parseerror(parser, "expected operator or end of statement, got constant");
-                goto onerr;
-            }
-            wantop = true;
-            val = parser_const_float(parser, (parser_token(parser)->constval.f));
-            if (!val)
-                return NULL;
-            vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
-            DEBUGSHUNTDO(con_out("push %g\n", parser_token(parser)->constval.f));
-        }
-        else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
-            ast_value *val;
-            if (wantop) {
-                parseerror(parser, "expected operator or end of statement, got constant");
-                goto onerr;
-            }
-            wantop = true;
-            val = parser_const_float(parser, (double)(parser_token(parser)->constval.i));
-            if (!val)
-                return NULL;
-            vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
-            DEBUGSHUNTDO(con_out("push %i\n", parser_token(parser)->constval.i));
-        }
-        else if (parser->tok == TOKEN_STRINGCONST) {
-            ast_value *val;
-            if (wantop) {
-                parseerror(parser, "expected operator or end of statement, got constant");
-                goto onerr;
-            }
-            wantop = true;
-            val = parser_const_string(parser, parser_tokval(parser), false);
-            if (!val)
-                return NULL;
-            vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
-            DEBUGSHUNTDO(con_out("push string\n"));
         }
-        else if (parser->tok == TOKEN_VECTORCONST) {
-            ast_value *val;
-            if (wantop) {
-                parseerror(parser, "expected operator or end of statement, got constant");
-                goto onerr;
+        else
+        {
+            if (ast_istype(var, ast_value)) {
+                ((ast_value*)var)->uses++;
             }
-            wantop = true;
-            val = parser_const_vector(parser, parser_token(parser)->constval.v);
-            if (!val)
-                return NULL;
-            vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
-            DEBUGSHUNTDO(con_out("push '%g %g %g'\n",
-                                parser_token(parser)->constval.v.x,
-                                parser_token(parser)->constval.v.y,
-                                parser_token(parser)->constval.v.z));
-        }
-        else if (parser->tok == '(') {
-            parseerror(parser, "internal error: '(' should be classified as operator");
-            goto onerr;
-        }
-        else if (parser->tok == '[') {
-            parseerror(parser, "internal error: '[' should be classified as operator");
-            goto onerr;
-        }
-        else if (parser->tok == ')') {
-            if (wantop) {
-                DEBUGSHUNTDO(con_out("do[op] )\n"));
-                --parens;
-                if (parens < 0)
-                    break;
-                /* we do expect an operator next */
-                /* closing an opening paren */
-                if (!parser_close_paren(parser, &sy, false))
-                    goto onerr;
-                if (vec_last(parser->pot) != POT_PAREN) {
-                    parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
-                    goto onerr;
-                }
-                vec_pop(parser->pot);
-            } else {
-                DEBUGSHUNTDO(con_out("do[nop] )\n"));
-                --parens;
-                if (parens < 0)
-                    break;
-                /* allowed for function calls */
-                if (!parser_close_paren(parser, &sy, true))
-                    goto onerr;
-                if (vec_last(parser->pot) != POT_PAREN) {
-                    parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
-                    goto onerr;
-                }
-                vec_pop(parser->pot);
+            else if (ast_istype(var, ast_member)) {
+                ast_member *mem = (ast_member*)var;
+                if (ast_istype(mem->owner, ast_value))
+                    ((ast_value*)(mem->owner))->uses++;
             }
-            wantop = true;
         }
-        else if (parser->tok == ']') {
-            if (!wantop)
-                parseerror(parser, "operand expected");
-            --parens;
-            if (parens < 0)
-                break;
-            if (!parser_close_paren(parser, &sy, false))
-                goto onerr;
-            if (vec_last(parser->pot) != POT_PAREN) {
-                parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
-                goto onerr;
-            }
-            vec_pop(parser->pot);
-            wantop = true;
-        }
-        else if (parser->tok == TOKEN_TYPENAME) {
+        vec_push(sy->out, syexp(parser_ctx(parser), var));
+        return true;
+    }
+    parseerror(parser, "unexpected token `%s`", parser_tokval(parser));
+    return false;
+}
+
+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;
+    /* only warn once about an assignment in a truth value because the current code
+     * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
+     */
+    bool warn_truthvalue = true;
+
+    /* count the parens because an if starts with one, so the
+     * end of a condition is an unmatched closing paren
+     */
+    int ternaries = 0;
+
+    memset(&sy, 0, sizeof(sy));
+
+    parser->lex->flags.noops = false;
+
+    parser_reclassify_token(parser);
+
+    while (true)
+    {
+        if (parser->tok == TOKEN_TYPENAME) {
             parseerror(parser, "unexpected typename");
             goto onerr;
         }
-        else if (parser->tok != TOKEN_OPERATOR) {
-            if (wantop) {
-                parseerror(parser, "expected operator or end of statement");
-                goto onerr;
-            }
-            break;
-        }
-        else
+
+        if (parser->tok == TOKEN_OPERATOR)
         {
             /* classify the operator */
             const oper_info *op;
@@ -1998,14 +2015,14 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                 }
             }
             if (o == operator_count) {
-                /* no operator found... must be the end of the statement */
-                break;
+                compile_error(parser_ctx(parser), "unknown operator: %s", parser_tokval(parser));
+                goto onerr;
             }
             /* found an operator */
             op = &operators[o];
 
             /* when declaring variables, a comma starts a new variable */
-            if (op->id == opid1(',') && !parens && stopatcomma) {
+            if (op->id == opid1(',') && !vec_size(sy.paren) && stopatcomma) {
                 /* fixup the token */
                 parser->tok = ',';
                 break;
@@ -2018,12 +2035,12 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             }
 
             if (op->id == opid1(',')) {
-                if (vec_size(parser->pot) && vec_last(parser->pot) == POT_TERNARY2) {
+                if (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) {
                     (void)!parsewarning(parser, WARN_TERNARY_PRECEDENCE, "suggesting parenthesis around ternary expression");
                 }
             }
 
-            if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
+            if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
                 olast = &operators[vec_last(sy.ops).etype-1];
 
 #define IsAssignOp(x) (\
@@ -2040,7 +2057,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             if (warn_truthvalue) {
                 if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
                      (olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
-                     (truthvalue && !vec_size(parser->pot) && IsAssignOp(op->id))
+                     (truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id))
                    )
                 {
                     (void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
@@ -2054,7 +2071,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             {
                 if (!parser_sy_apply_operator(parser, &sy))
                     goto onerr;
-                if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
+                if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
                     olast = &operators[vec_last(sy.ops).etype-1];
                 else
                     olast = NULL;
@@ -2063,14 +2080,13 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
             if (op->id == opid1('(')) {
                 if (wantop) {
                     size_t sycount = vec_size(sy.out);
-                    DEBUGSHUNTDO(con_out("push [op] (\n"));
-                    ++parens; vec_push(parser->pot, POT_PAREN);
                     /* we expected an operator, this is the function-call operator */
-                    vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_FUNC, sycount-1));
+                    vec_push(sy.paren, PAREN_FUNC);
+                    vec_push(sy.ops, syparen(parser_ctx(parser), sycount-1));
+                    vec_push(sy.argc, 0);
                 } else {
-                    ++parens; vec_push(parser->pot, POT_PAREN);
-                    vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_EXPR, 0));
-                    DEBUGSHUNTDO(con_out("push [nop] (\n"));
+                    vec_push(sy.paren, PAREN_EXPR);
+                    vec_push(sy.ops, syparen(parser_ctx(parser), 0));
                 }
                 wantop = false;
             } else if (op->id == opid1('[')) {
@@ -2078,42 +2094,104 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
                     parseerror(parser, "unexpected array subscript");
                     goto onerr;
                 }
-                ++parens; vec_push(parser->pot, POT_PAREN);
+                vec_push(sy.paren, PAREN_INDEX);
                 /* push both the operator and the paren, this makes life easier */
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
-                vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0));
+                vec_push(sy.ops, syparen(parser_ctx(parser), 0));
                 wantop = false;
             } else if (op->id == opid2('?',':')) {
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
-                vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_TERNARY, 0));
+                vec_push(sy.ops, syparen(parser_ctx(parser), 0));
                 wantop = false;
                 ++ternaries;
-                vec_push(parser->pot, POT_TERNARY1);
+                vec_push(sy.paren, PAREN_TERNARY1);
             } else if (op->id == opid2(':','?')) {
-                if (!vec_size(parser->pot)) {
+                if (!vec_size(sy.paren)) {
                     parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)");
                     goto onerr;
                 }
-                if (vec_last(parser->pot) != POT_TERNARY1) {
+                if (vec_last(sy.paren) != PAREN_TERNARY1) {
                     parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)");
                     goto onerr;
                 }
-                if (!parser_close_paren(parser, &sy, false))
+                if (!parser_close_paren(parser, &sy))
                     goto onerr;
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
                 wantop = false;
                 --ternaries;
             } else {
-                DEBUGSHUNTDO(con_out("push operator %s\n", op->op));
                 vec_push(sy.ops, syop(parser_ctx(parser), op));
                 wantop = !!(op->flags & OP_SUFFIX);
             }
         }
+        else if (parser->tok == ')') {
+            while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) {
+                if (!parser_sy_apply_operator(parser, &sy))
+                    goto onerr;
+            }
+            if (!vec_size(sy.paren))
+                break;
+            if (wantop) {
+                if (vec_last(sy.paren) == PAREN_TERNARY1) {
+                    parseerror(parser, "mismatched parentheses (closing paren in ternary expression?)");
+                    goto onerr;
+                }
+                if (!parser_close_paren(parser, &sy))
+                    goto onerr;
+            } else {
+                /* must be a function call without parameters */
+                if (vec_last(sy.paren) != PAREN_FUNC) {
+                    parseerror(parser, "closing paren in invalid position");
+                    goto onerr;
+                }
+                if (!parser_close_paren(parser, &sy))
+                    goto onerr;
+            }
+            wantop = true;
+        }
+        else if (parser->tok == '(') {
+            parseerror(parser, "internal error: '(' should be classified as operator");
+            goto onerr;
+        }
+        else if (parser->tok == '[') {
+            parseerror(parser, "internal error: '[' should be classified as operator");
+            goto onerr;
+        }
+        else if (parser->tok == ']') {
+            while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) {
+                if (!parser_sy_apply_operator(parser, &sy))
+                    goto onerr;
+            }
+            if (!vec_size(sy.paren))
+                break;
+            if (vec_last(sy.paren) != PAREN_INDEX) {
+                parseerror(parser, "mismatched parentheses, unexpected ']'");
+                goto onerr;
+            }
+            if (!parser_close_paren(parser, &sy))
+                goto onerr;
+            wantop = true;
+        }
+        else if (!wantop) {
+            if (!parse_sya_operand(parser, &sy, with_labels))
+                goto onerr;
+#if 0
+            if (vec_size(sy.paren) && vec_last(sy.ops).isparen && vec_last(sy.paren) == PAREN_FUNC)
+                vec_last(sy.argc)++;
+#endif
+            wantop = true;
+        }
+        else {
+            parseerror(parser, "expected operator or end of statement");
+            goto onerr;
+        }
+
         if (!parser_next(parser)) {
             goto onerr;
         }
         if (parser->tok == ';' ||
-            (!parens && (parser->tok == ']' || parser->tok == ')' || parser->tok == '}')))
+            ((!vec_size(sy.paren) || (vec_size(sy.paren) == 1 && vec_last(sy.paren) == PAREN_TERNARY2)) &&
+            (parser->tok == ']' || parser->tok == ')' || parser->tok == '}')))
         {
             break;
         }
@@ -2132,12 +2210,12 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma
         expr = sy.out[0].out;
     vec_free(sy.out);
     vec_free(sy.ops);
-    DEBUGSHUNTDO(con_out("shunt done\n"));
-    if (vec_size(parser->pot)) {
-        parseerror(parser, "internal error: vec_size(parser->pot) = %lu", (unsigned long)vec_size(parser->pot));
+    if (vec_size(sy.paren)) {
+        parseerror(parser, "internal error: vec_size(sy.paren) = %lu", (unsigned long)vec_size(sy.paren));
         return NULL;
     }
-    vec_free(parser->pot);
+    vec_free(sy.paren);
+    vec_free(sy.argc);
     return expr;
 
 onerr:
@@ -2148,6 +2226,8 @@ onerr:
     }
     vec_free(sy.out);
     vec_free(sy.ops);
+    vec_free(sy.paren);
+    vec_free(sy.argc);
     return NULL;
 }
 
@@ -2259,6 +2339,12 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond,
     ast_unary *unary;
     ast_expression *prev;
 
+    if (cond->expression.vtype == TYPE_VOID || cond->expression.vtype >= TYPE_VARIANT) {
+        char ty[1024];
+        ast_type_to_string(cond, ty, sizeof(ty));
+        compile_error(ast_ctx(cond), "invalid type for if() condition: %s", ty);
+    }
+
     if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING)
     {
         prev = cond;
@@ -2693,7 +2779,7 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     if (typevar || parser->tok == TOKEN_TYPENAME) {
 #if 0
-        if (opts.standard != COMPILER_GMQCC) {
+        if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
             if (parsewarning(parser, WARN_EXTENSIONS,
                              "current standard does not allow variable declarations in for-loop initializers"))
                 goto onerr;
@@ -2738,11 +2824,12 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou
 
     /* parse the incrementor */
     if (parser->tok != ')') {
+        lex_ctx condctx = parser_ctx(parser);
         increment = parse_expression_leave(parser, false, false, false);
         if (!increment)
             goto onerr;
         if (!ast_side_effects(increment)) {
-            if (genwarning(ast_ctx(increment), WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
+            if (compile_warning(condctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
                 goto onerr;
         }
     }
@@ -2921,8 +3008,44 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
+                flags   |= AST_FLAG_ALIAS;
+                *message = NULL;
+
+                if (!parser_next(parser)) {
+                    parseerror(parser, "parse error in attribute");
+                    goto argerr;
+                }
+
+                if (parser->tok == '(') {
+                    if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) {
+                        parseerror(parser, "`alias` attribute missing parameter");
+                        goto argerr;
+                    }
+
+                    *message = util_strdup(parser_tokval(parser));
 
+                    if (!parser_next(parser)) {
+                        parseerror(parser, "parse error in attribute");
+                        goto argerr;
+                    }
 
+                    if (parser->tok != ')') {
+                        parseerror(parser, "`alias` attribute expected `)` after parameter");
+                        goto argerr;
+                    }
+
+                    if (!parser_next(parser)) {
+                        parseerror(parser, "parse error in attribute");
+                        goto argerr;
+                    }
+                }
+
+                if (parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`alias` attribute expected `]]`");
+                    goto argerr;
+                }
+            }
             else if (!strcmp(parser_tokval(parser), "deprecated") && !(flags & AST_FLAG_DEPRECATED)) {
                 flags   |= AST_FLAG_DEPRECATED;
                 *message = NULL;
@@ -3422,7 +3545,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
             parseerror(parser, "cannot declare a variable from here");
             return false;
         }
-        if (opts.standard == COMPILER_QCC) {
+        if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
             if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
                 return false;
         }
@@ -3489,7 +3612,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
         }
         else if (!strcmp(parser_tokval(parser), "for"))
         {
-            if (opts.standard == COMPILER_QCC) {
+            if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
                 if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?"))
                     return false;
             }
@@ -3588,12 +3711,13 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
     }
     else
     {
+        lex_ctx ctx = parser_ctx(parser);
         ast_expression *exp = parse_expression(parser, false, false);
         if (!exp)
             return false;
         *out = exp;
         if (!ast_side_effects(exp)) {
-            if (genwarning(ast_ctx(exp), WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
+            if (compile_warning(ctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
                 return false;
         }
         return true;
@@ -3602,6 +3726,8 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
 
 static bool parse_enum(parser_t *parser)
 {
+    bool        flag = false;
+    bool        reverse = false;
     qcfloat     num = 0;
     ast_value **values = NULL;
     ast_value  *var = NULL;
@@ -3609,11 +3735,37 @@ static bool parse_enum(parser_t *parser)
 
     ast_expression *old;
 
-    if (!parser_next(parser) || parser->tok != '{') {
-        parseerror(parser, "expected `{` after `enum` keyword");
+    if (!parser_next(parser) || (parser->tok != '{' && parser->tok != ':')) {
+        parseerror(parser, "expected `{` or `:` after `enum` keyword");
         return false;
     }
 
+    /* enumeration attributes (can add more later) */
+    if (parser->tok == ':') {
+        if (!parser_next(parser) || parser->tok != TOKEN_IDENT){
+            parseerror(parser, "expected `flag` or `reverse` for enumeration attribute");
+            return false;
+        }
+
+        /* attributes? */
+        if (!strcmp(parser_tokval(parser), "flag")) {
+            num  = 1;
+            flag = true;
+        }
+        else if (!strcmp(parser_tokval(parser), "reverse")) {
+            reverse = true;
+        }
+        else {
+            parseerror(parser, "invalid attribute `%s` for enumeration", parser_tokval(parser));
+            return false;
+        }
+
+        if (!parser_next(parser) || parser->tok != '{') {
+            parseerror(parser, "expected `{` after enum attribute ");
+            return false;
+        }
+    }
+
     while (true) {
         if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
             if (parser->tok == '}') {
@@ -3637,8 +3789,9 @@ static bool parse_enum(parser_t *parser)
         vec_push(values, var);
         var->cvq             = CV_CONST;
         var->hasvalue        = true;
-        var->constval.vfloat = num++;
 
+        /* for flagged enumerations increment in POTs of TWO */
+        var->constval.vfloat = (flag) ? (num *= 2) : (num ++);
         parser_addglobal(parser, var->name, (ast_expression*)var);
 
         if (!parser_next(parser)) {
@@ -3677,6 +3830,13 @@ static bool parse_enum(parser_t *parser)
         }
     }
 
+    /* patch them all (for reversed attribute) */
+    if (reverse) {
+        size_t i;
+        for (i = 0; i < vec_size(values); i++)
+            values[i]->constval.vfloat = vec_size(values) - i - 1;
+    }
+
     if (parser->tok != '}') {
         parseerror(parser, "internal error: breaking without `}`");
         goto onerror;
@@ -3808,6 +3968,11 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
     has_frame_think = false;
     old = parser->function;
 
+    if (var->expression.flags & AST_FLAG_ALIAS) {
+        parseerror(parser, "function aliases cannot have bodies");
+        return false;
+    }
+
     if (vec_size(parser->gotos) || vec_size(parser->labels)) {
         parseerror(parser, "gotos/labels leaking");
         return false;
@@ -3880,11 +4045,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             ast_expression *functype = fld_think->expression.next;
 
             thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->expression.vtype);
-            if (!thinkfunc || !ast_type_adopt(thinkfunc, functype)) {
+            if (!thinkfunc) { /* || !ast_type_adopt(thinkfunc, functype)*/
                 ast_unref(framenum);
                 parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser));
                 return false;
             }
+            ast_type_adopt(thinkfunc, functype);
 
             if (!parser_next(parser)) {
                 ast_unref(framenum);
@@ -4010,6 +4176,20 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         }
     }
 
+    if (var->hasvalue) {
+        parseerror(parser, "function `%s` declared with multiple bodies", var->name);
+        ast_block_delete(block);
+        goto enderr;
+    }
+
+    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) {
@@ -4026,7 +4206,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) {
@@ -4035,14 +4215,6 @@ 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);
-
     if (var->argcounter) {
         ast_value *argc = ast_value_new(ast_ctx(var), var->argcounter, TYPE_FLOAT);
         parser_addlocal(parser, argc->name, (ast_expression*)argc);
@@ -4068,6 +4240,8 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             goto enderrfn;
         }
         func->varargs = varargs;
+
+        func->fixedparams = parser_const_float(parser, vec_size(var->expression.params));
     }
 
     parser->function = func;
@@ -4088,17 +4262,17 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
 
     if (parser->tok == ';')
         return parser_next(parser);
-    else if (opts.standard == COMPILER_QCC)
+    else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
         parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)");
     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;
 }
@@ -4604,7 +4778,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
         vec_free(params);
 
     /* sanity check */
-    if (vec_size(params) > 8 && opts.standard == COMPILER_QCC)
+    if (vec_size(params) > 8 && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
         (void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard");
 
     /* parse-out */
@@ -4630,6 +4804,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
 on_error:
     if (argcounter)
         mem_d(argcounter);
+    if (varparam)
+        ast_delete(varparam);
     ast_delete(var);
     for (i = 0; i < vec_size(params); ++i)
         ast_delete(params[i]);
@@ -4818,7 +4994,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_va
     }
 
     /* now there may be function parens again */
-    if (parser->tok == '(' && opts.standard == COMPILER_QCC)
+    if (parser->tok == '(' && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
         parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
     if (parser->tok == '(' && wasarray)
         parseerror(parser, "arrays as part of a return type is not supported");
@@ -4957,7 +5133,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
         /* Part 0: finish the type */
         if (parser->tok == '(') {
-            if (opts.standard == COMPILER_QCC)
+            if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
                 parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
             var = parse_parameter_list(parser, var);
             if (!var) {
@@ -4980,7 +5156,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
         }
         /* for functions returning functions */
         while (parser->tok == '(') {
-            if (opts.standard == COMPILER_QCC)
+            if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
                 parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
             var = parse_parameter_list(parser, var);
             if (!var) {
@@ -4991,7 +5167,13 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
 
         var->cvq = qualifier;
         var->expression.flags |= qflags;
-        if (var->expression.flags & AST_FLAG_DEPRECATED)
+
+        /*
+         * store the vstring back to var for alias and
+         * deprecation messages.
+         */
+        if (var->expression.flags & AST_FLAG_DEPRECATED ||
+            var->expression.flags & AST_FLAG_ALIAS)
             var->desc = vstring;
 
         /* Part 1:
@@ -5050,7 +5232,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     goto cleanup;
                     */
                 }
-                if ((opts.standard == COMPILER_QCC || opts.standard == COMPILER_FTEQCC) &&
+                if ((OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC || OPTS_OPTION_U32(OPTION_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");
@@ -5086,7 +5268,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                         ast_value_set_name(proto->expression.params[i], var->expression.params[i]->name);
                     if (!parser_check_qualifiers(parser, var, proto)) {
                         retval = false;
-                        if (proto->desc) 
+                        if (proto->desc)
                             mem_d(proto->desc);
                         proto = NULL;
                         goto cleanup;
@@ -5122,7 +5304,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                         ast_delete(var);
                         var = proto;
                     }
-                    if (opts.standard == COMPILER_QCC &&
+                    if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC &&
                         (old = parser_find_field(parser, var->name)))
                     {
                         parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
@@ -5153,7 +5335,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     retval = false;
                     goto cleanup;
                 }
-                if (opts.standard != COMPILER_GMQCC) {
+                if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
                     ast_delete(var);
                     var = NULL;
                     goto skipvar;
@@ -5196,10 +5378,85 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
                     }
                 }
                 else {
-                    parser_addglobal(parser, var->name, (ast_expression*)var);
-                    if (isvector) {
-                        for (i = 0; i < 3; ++i) {
-                            parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
+                    if (!(var->expression.flags & AST_FLAG_ALIAS)) {
+                        parser_addglobal(parser, var->name, (ast_expression*)var);
+                        if (isvector) {
+                            for (i = 0; i < 3; ++i) {
+                                parser_addglobal(parser, me[i]->name, (ast_expression*)me[i]);
+                            }
+                        }
+                    } else {
+                        ast_expression *find  = parser_find_global(parser, var->desc);
+
+                        if (!find) {
+                            compile_error(parser_ctx(parser), "undeclared variable `%s` for alias `%s`", var->desc, var->name);
+                            return false;
+                        }
+
+                        if (var->expression.vtype != find->expression.vtype) {
+                            char ty1[1024];
+                            char ty2[1024];
+
+                            ast_type_to_string(find,                  ty1, sizeof(ty1));
+                            ast_type_to_string((ast_expression*)var,  ty2, sizeof(ty2));
+
+                            compile_error(parser_ctx(parser), "incompatible types `%s` and `%s` for alias `%s`",
+                                ty1, ty2, var->name
+                            );
+                            return false;
+                        }
+
+                        /*
+                         * add alias to aliases table and to corrector
+                         * so corrections can apply for aliases as well.
+                         */
+                        util_htset(parser->aliases, var->name, find);
+
+                        /*
+                         * add to corrector so corrections can work
+                         * even for aliases too.
+                         */
+                        correct_add (
+                             vec_last(parser->correct_variables),
+                            &vec_last(parser->correct_variables_score),
+                            var->name
+                        );
+
+                        /* generate aliases for vector components */
+                        if (isvector) {
+                            char *buffer[3];
+
+                            util_asprintf(&buffer[0], "%s_x", var->desc);
+                            util_asprintf(&buffer[1], "%s_y", var->desc);
+                            util_asprintf(&buffer[2], "%s_z", var->desc);
+
+                            util_htset(parser->aliases, me[0]->name, parser_find_global(parser, buffer[0]));
+                            util_htset(parser->aliases, me[1]->name, parser_find_global(parser, buffer[1]));
+                            util_htset(parser->aliases, me[2]->name, parser_find_global(parser, buffer[2]));
+
+                            mem_d(buffer[0]);
+                            mem_d(buffer[1]);
+                            mem_d(buffer[2]);
+
+                            /*
+                             * add to corrector so corrections can work
+                             * even for aliases too.
+                             */
+                            correct_add (
+                                 vec_last(parser->correct_variables),
+                                &vec_last(parser->correct_variables_score),
+                                me[0]->name
+                            );
+                            correct_add (
+                                 vec_last(parser->correct_variables),
+                                &vec_last(parser->correct_variables_score),
+                                me[1]->name
+                            );
+                            correct_add (
+                                 vec_last(parser->correct_variables),
+                                &vec_last(parser->correct_variables_score),
+                                me[2]->name
+                            );
                         }
                     }
                 }
@@ -5333,7 +5590,7 @@ skipvar:
             break;
         }
 
-        if (localblock && opts.standard == COMPILER_QCC) {
+        if (localblock && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
             if (parsewarning(parser, WARN_LOCAL_CONSTANTS,
                              "initializing expression turns variable `%s` into a constant in this standard",
                              var->name) )
@@ -5353,7 +5610,7 @@ skipvar:
                 break;
             }
         }
-        else if (opts.standard == COMPILER_QCC) {
+        else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
             parseerror(parser, "expected '=' before function body in this standard");
         }
 
@@ -5464,7 +5721,7 @@ skipvar:
                 }
             } else {
                 int cvq;
-                shunt sy = { NULL, NULL };
+                shunt sy = { NULL, NULL, NULL, NULL };
                 cvq = var->cvq;
                 var->cvq = CV_NONE;
                 vec_push(sy.out, syexp(ast_ctx(var), (ast_expression*)var));
@@ -5585,7 +5842,7 @@ static bool parser_global_statement(parser_t *parser)
     }
     else
     {
-        parseerror(parser, "unexpected token: %s", parser->lex->tok.value);
+        parseerror(parser, "unexpected token: `%s`", parser->lex->tok.value);
         return false;
     }
     return true;
@@ -5667,16 +5924,15 @@ static void generate_checksum(parser_t *parser)
     code_crc = crc;
 }
 
-static parser_t *parser;
-
-bool parser_init()
+parser_t *parser_create()
 {
+    parser_t *parser;
     lex_ctx empty_ctx;
     size_t i;
 
     parser = (parser_t*)mem_a(sizeof(parser_t));
     if (!parser)
-        return false;
+        return NULL;
 
     memset(parser, 0, sizeof(*parser));
 
@@ -5689,7 +5945,7 @@ bool parser_init()
     if (!parser->assign_op) {
         printf("internal error: initializing parser: failed to find assign operator\n");
         mem_d(parser);
-        return false;
+        return NULL;
     }
 
     vec_push(parser->variables, parser->htfields  = util_htnew(PARSER_HT_SIZE));
@@ -5697,6 +5953,10 @@ bool parser_init()
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, 0);
 
+    parser->aliases = util_htnew(PARSER_HT_SIZE);
+
+    parser->ht_imm_string = util_htnew(512);
+
     /* corrector */
     vec_push(parser->correct_variables, correct_trie_new());
     vec_push(parser->correct_variables_score, NULL);
@@ -5708,11 +5968,13 @@ bool parser_init()
     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) {
+    if (OPTS_OPTION_BOOL(OPTION_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;
@@ -5721,10 +5983,11 @@ bool parser_init()
     } else {
         parser->reserved_version = NULL;
     }
-    return true;
+
+    return parser;
 }
 
-bool parser_compile()
+bool parser_compile(parser_t *parser)
 {
     /* initial lexer/parser state */
     parser->lex->flags.noops = true;
@@ -5756,27 +6019,27 @@ bool parser_compile()
     return !compile_errors;
 }
 
-bool parser_compile_file(const char *filename)
+bool parser_compile_file(parser_t *parser, const char *filename)
 {
     parser->lex = lex_open(filename);
     if (!parser->lex) {
         con_err("failed to open file \"%s\"\n", filename);
         return false;
     }
-    return parser_compile();
+    return parser_compile(parser);
 }
 
-bool parser_compile_string(const char *name, const char *str, size_t len)
+bool parser_compile_string(parser_t *parser, const char *name, const char *str, size_t len)
 {
     parser->lex = lex_open_string(str, len, name);
     if (!parser->lex) {
         con_err("failed to create lexer for string \"%s\"\n", name);
         return false;
     }
-    return parser_compile();
+    return parser_compile(parser);
 }
 
-void parser_cleanup()
+void parser_cleanup(parser_t *parser)
 {
     size_t i;
     for (i = 0; i < vec_size(parser->accessors); ++i) {
@@ -5806,6 +6069,7 @@ void parser_cleanup()
     vec_free(parser->functions);
     vec_free(parser->imm_vector);
     vec_free(parser->imm_string);
+    util_htdel(parser->ht_imm_string);
     vec_free(parser->imm_float);
     vec_free(parser->globals);
     vec_free(parser->fields);
@@ -5845,10 +6109,14 @@ void parser_cleanup()
     ast_value_delete(parser->const_vec[1]);
     ast_value_delete(parser->const_vec[2]);
 
+    util_htdel(parser->aliases);
+
+    intrin_intrinsics_destroy(parser);
+
     mem_d(parser);
 }
 
-bool parser_finish(const char *output)
+bool parser_finish(parser_t *parser, const char *output)
 {
     size_t i;
     ir_builder *ir;
@@ -5897,8 +6165,8 @@ bool parser_finish(const char *output)
             continue;
         asvalue = (ast_value*)(parser->globals[i]);
         if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) {
-            retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
-                                           "unused global: `%s`", asvalue->name);
+            retval = retval && !compile_warning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
+                                                "unused global: `%s`", asvalue->name);
         }
         if (!ast_global_codegen(asvalue, ir, false)) {
             con_out("failed to generate global %s\n", asvalue->name);
@@ -6001,7 +6269,7 @@ bool parser_finish(const char *output)
             return false;
         }
     }
-    if (opts.dump)
+    if (OPTS_OPTION_BOOL(OPTION_DUMP))
         ir_builder_dump(ir, con_out);
     for (i = 0; i < vec_size(parser->functions); ++i) {
         if (!ir_function_finalize(parser->functions[i]->ir_func)) {
@@ -6018,7 +6286,7 @@ bool parser_finish(const char *output)
     }
 
     if (retval) {
-        if (opts.dumpfin)
+        if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
             ir_builder_dump(ir, con_out);
 
         generate_checksum(parser);