]> de.git.xonotic.org Git - xonotic/gmqcc.git/blobdiff - parser.c
Implemented "reverse" enum attribute, e.g enum : reverse { A, B, C, D } -> A,B,C...
[xonotic/gmqcc.git] / parser.c
index a54094a841f32beb80f736f756f7b00fee1226f8..a5c039a4c2691a688af8b540256d64a7a651ba34 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -23,6 +23,7 @@
  */
 #include <stdio.h>
 #include <stdarg.h>
+#include <math.h>
 
 #include "gmqcc.h"
 #include "lexer.h"
@@ -51,7 +52,10 @@ typedef struct {
 
     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;
 
@@ -59,6 +63,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?
@@ -116,6 +121,10 @@ static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *
 static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
 static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
 
+static ast_expression *parser_builtin_pow(parser_t *);
+static ast_expression *parser_builtin_exp(parser_t *);
+static ast_expression *parser_builtin_mod(parser_t *);
+
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
     va_list ap;
@@ -222,6 +231,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)
@@ -309,6 +324,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);
 }
 
@@ -440,7 +458,7 @@ static sy_elem syparen(lex_ctx ctx, size_t off) {
  */
 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;
@@ -464,12 +482,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;
 }
 
@@ -658,8 +680,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;
@@ -945,10 +966,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  = parser_builtin_mod(parser);
+                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)) {
@@ -1055,6 +1101,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), parser_builtin_pow(parser));
+                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('<'):
@@ -1101,7 +1210,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));
@@ -1128,7 +1237,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);
@@ -1346,6 +1455,20 @@ 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
 
@@ -1758,8 +1881,14 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
             if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
                 var = (ast_expression*)intrinsic_debug_typestring;
             }
-            else
-            {
+            if (!strcmp(parser_tokval(parser), "__builtin_pow"))
+                var = parser_builtin_pow(parser);
+            if (!strcmp(parser_tokval(parser), "__builtin_exp"))
+                var = parser_builtin_exp(parser);
+            if (!strcmp(parser_tokval(parser), "__builtin_mod"))
+                var = parser_builtin_mod(parser);
+                
+            if (!var) {
                 char *correct = NULL;
                 size_t i;
 
@@ -1871,8 +2000,8 @@ 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];
@@ -2864,8 +2993,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;
@@ -3186,6 +3351,254 @@ static bool parse_switch_go(parser_t *parser, ast_block *block, ast_expression *
     return true;
 }
 
+ast_expression *parser_builtin_pow(parser_t *parser) {
+    /*
+     * float __builtin_pow(float x, float y) { 
+     *   float value = 1.0f;
+     *   while (y > 0) {
+     *     while (!(y&1)) {
+     *       y *= 0.25f;
+     *       x *= x;
+     *     }
+     *     y--;
+     *     value *= x;
+     *   }
+     *   return value;
+     * }      
+     */
+    static ast_function  *pow_func       = NULL;
+    static ast_value     *pow_func_val   = NULL; 
+    if (!pow_func) {
+        ast_value    *pow_arguments[2];
+        ast_value    *pow_value          = ast_value_new   (parser_ctx(parser), "value",         TYPE_FLOAT);
+        ast_block    *pow_body           = ast_block_new   (parser_ctx(parser));
+        ast_block    *pow_loop_body      = ast_block_new   (parser_ctx(parser));
+        ast_block    *pow_loop_nest_body = ast_block_new   (parser_ctx(parser));
+        ast_loop     *pow_loop           = NULL;
+        ast_loop     *pow_loop_nest      = NULL;
+
+        pow_arguments[0]                 = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
+        pow_arguments[1]                 = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
+        pow_func_val                     = ast_value_new   (parser_ctx(parser), "__builtin_pow", TYPE_FUNCTION);
+        pow_func_val->expression.next    = (ast_expression*)ast_value_new(parser_ctx(parser), "<float>", TYPE_FLOAT);
+
+        vec_push(pow_func_val->expression.params, pow_arguments[0]);
+        vec_push(pow_func_val->expression.params, pow_arguments[1]);
+
+        pow_func = ast_function_new(parser_ctx(parser), "__builtin_pow", pow_func_val);
+
+        /* float value; */
+        vec_push(pow_body->locals, pow_value);
+        /* value = 1.0f; */
+        vec_push(pow_body->exprs,
+            (ast_expression*)ast_store_new(
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                (ast_expression*)pow_value,
+                (ast_expression*)parser_const_float_1(parser)
+            )
+        );
+
+        /* y >>= 2 */
+        vec_push(pow_loop_nest_body->exprs,
+            (ast_expression*)ast_binstore_new(
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                INSTR_MUL_F,
+                (ast_expression*)pow_arguments[1],
+                (ast_expression*)parser_const_float(parser, 0.25f)
+            )
+        );
+        vec_push(pow_loop_nest_body->exprs,
+            (ast_expression*)ast_binstore_new(
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                INSTR_MUL_F,
+                (ast_expression*)pow_arguments[0],
+                (ast_expression*)pow_arguments[0]
+            )
+        );
+
+        /* while (!(y&1)) */
+        pow_loop_nest = ast_loop_new (
+            parser_ctx(parser),
+            NULL,
+            (ast_expression*)ast_binary_new(
+                parser_ctx(parser),
+                INSTR_AND,
+                (ast_expression*)pow_arguments[1],
+                (ast_expression*)parser_const_float_1(parser)
+            ),
+            true,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)pow_loop_nest_body
+        );
+        
+        vec_push(pow_loop_body->exprs, (ast_expression*)pow_loop_nest);
+        vec_push(pow_loop_body->exprs,
+            (ast_expression*)ast_binstore_new(
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                INSTR_SUB_F,
+                (ast_expression*)pow_arguments[1],
+                (ast_expression*)parser_const_float_1(parser)
+            )
+        );
+        vec_push(pow_loop_body->exprs,
+            (ast_expression*)ast_binstore_new(
+                parser_ctx(parser),
+                INSTR_STORE_F,
+                INSTR_MUL_F,
+                (ast_expression*)pow_value,
+                (ast_expression*)pow_arguments[0]
+            )
+        );
+
+        /* while (y > 0) { */
+        pow_loop = ast_loop_new(
+            parser_ctx(parser),
+            NULL,
+            (ast_expression*)ast_binary_new(
+                parser_ctx(parser),
+                INSTR_GT,
+                (ast_expression*)pow_arguments[1],
+                (ast_expression*)parser_const_float_0(parser)
+            ),
+            false,
+            NULL,
+            false,
+            NULL,
+            (ast_expression*)pow_loop_body
+        );
+        /* } */
+        vec_push(pow_body->exprs, (ast_expression*)pow_loop);
+        /* return value; */
+        vec_push(pow_body->exprs,
+            (ast_expression*)ast_return_new(
+                parser_ctx(parser),
+                (ast_expression*)pow_value
+            )
+        );
+
+        vec_push(pow_func->blocks, pow_body);
+        vec_push(parser->globals, (ast_expression*)pow_func_val);
+        vec_push(parser->functions, pow_func);
+    }
+
+    return (ast_expression*)pow_func_val;
+}
+
+#ifndef M_E
+#define M_E 2.71828182845905
+#endif
+static ast_expression *parser_builtin_exp(parser_t *parser) {
+    /*
+     * float __builtin_exp(float x) {
+     *     return __builtin_exp(E, x);
+     * }
+     */ 
+    static ast_value *exp_func_val = NULL;
+
+    if (!exp_func_val) {
+        ast_function *exp_func         = NULL;
+        ast_value    *arg              = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
+        ast_block    *exp_body         = ast_block_new   (parser_ctx(parser));
+        ast_call     *exp_call         = ast_call_new    (parser_ctx(parser), parser_builtin_pow(parser));
+        exp_func_val                   = ast_value_new   (parser_ctx(parser), "__builtin_exp", TYPE_FUNCTION);
+        exp_func_val->expression.next  = (ast_expression*)ast_value_new(parser_ctx(parser), "<float>", TYPE_FLOAT);
+        exp_func                       = ast_function_new(parser_ctx(parser), "__builtin_exp", exp_func_val);
+
+        vec_push(exp_call->params, (ast_expression*)parser_const_float(parser, M_E));
+        vec_push(exp_call->params, (ast_expression*)arg);
+
+        vec_push(exp_body->exprs,
+            (ast_expression*)ast_return_new(
+                parser_ctx(parser),
+                (ast_expression*)exp_call
+            )
+        );
+
+        vec_push(exp_func_val->expression.params, arg);
+        vec_push(exp_func->blocks, exp_body);
+
+        vec_push(parser->functions, exp_func);
+        vec_push(parser->globals, (ast_expression*)exp_func_val);
+    }
+
+    return (ast_expression*)exp_func_val;
+}
+
+static ast_expression *parser_builtin_mod(parser_t *parser) {
+    /*
+     * float __builtin_mod(float x, float y) {
+     *     return x - y * floor(x / y);
+     * }
+     */ 
+    static ast_value      *mod_func_val = NULL;
+    static ast_expression *mod_floor    = NULL;
+
+    if (!mod_floor) {
+        if (!(mod_floor = parser_find_global(parser, "floor"))) {
+            parseerror(parser,
+                "internal error: no suitable definition found for `floor`"
+                "(required for `%%` operator and `__builtin_mod` intrinsic)"
+            );
+            return NULL;
+        }
+    }
+
+    if (!mod_func_val) {
+        ast_value    *mod_args[2];
+        ast_function *mod_func         = NULL;
+        ast_block    *mod_body         = ast_block_new   (parser_ctx(parser));
+        ast_call     *mod_call         = ast_call_new    (parser_ctx(parser), mod_floor);
+        mod_func_val                   = ast_value_new   (parser_ctx(parser), "__builtin_mod", TYPE_FUNCTION);
+        mod_func_val->expression.next  = (ast_expression*)ast_value_new(parser_ctx(parser), "<float>", TYPE_FLOAT);
+        mod_func                       = ast_function_new(parser_ctx(parser), "__builtin_mod", mod_func_val);
+        mod_args[0]                    = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
+        mod_args[1]                    = ast_value_new   (parser_ctx(parser), "x", TYPE_FLOAT);
+
+        /* floor(x/y) */
+        vec_push(mod_call->params,
+            (ast_expression*)ast_binary_new(
+                parser_ctx(parser),
+                INSTR_DIV_F,
+                (ast_expression*)mod_args[0],
+                (ast_expression*)mod_args[1]
+            )
+        );
+
+        vec_push(mod_body->exprs,
+            (ast_expression*)ast_return_new(
+                parser_ctx(parser),
+                (ast_expression*)ast_binary_new(
+                    parser_ctx(parser),
+                    INSTR_SUB_F,
+                    (ast_expression*)mod_args[0],
+                    (ast_expression*)ast_binary_new(
+                        parser_ctx(parser),
+                        INSTR_MUL_F,
+                        (ast_expression*)mod_args[1],
+                        (ast_expression*)mod_call
+                    )
+                )
+            )
+        );
+
+        vec_push(mod_func_val->expression.params, mod_args[0]);
+        vec_push(mod_func_val->expression.params, mod_args[1]);
+
+        vec_push(mod_func->blocks, mod_body);
+
+        vec_push(parser->functions, mod_func);
+        vec_push(parser->globals, (ast_expression*)mod_func_val);
+    }
+
+    return (ast_expression*)mod_func_val;
+}
+
 /* parse computed goto sides */
 static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) {
     ast_expression *on_true;
@@ -3292,26 +3705,15 @@ static bool parse_eol(parser_t *parser)
     return parser->tok == TOKEN_EOL;
 }
 
-/*
- * Traditionally you'd implement warning, error, and message
- * directives in the preprocessor. However, like #pragma, these
- * shouldn't depend on -fftepp to utilize.  So they're instead
- * implemented here.
- */   
-enum {
-    PARSE_DIRECTIVE_ERROR,
-    PARSE_DIRECTIVE_MESSAGE,
-    PARSE_DIRECTIVE_WARNING,
-    PARSE_DIRECTIVE_COUNT
-};
-
-static const char *parser_directives[PARSE_DIRECTIVE_COUNT] = {
-    "error",
-    "message",
-    "warning"
-};
-
-static bool parse_pragma_do(parser_t *parser) {
+static bool parse_pragma_do(parser_t *parser)
+{
+    if (!parser_next(parser) ||
+        parser->tok != TOKEN_IDENT ||
+        strcmp(parser_tokval(parser), "pragma"))
+    {
+        parseerror(parser, "expected `pragma` keyword after `#`, got `%s`", parser_tokval(parser));
+        return false;
+    }
     if (!parse_skipwhite(parser) || parser->tok != TOKEN_IDENT) {
         parseerror(parser, "expected pragma, got `%s`", parser_tokval(parser));
         return false;
@@ -3322,99 +3724,35 @@ static bool parse_pragma_do(parser_t *parser) {
             parseerror(parser, "`noref` pragma requires an argument: 0 or 1");
             return false;
         }
-
         parser->noref = !!parser_token(parser)->constval.i;
-
         if (!parse_eol(parser)) {
             parseerror(parser, "parse error after `noref` pragma");
             return false;
         }
-    } else {
-        (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser));
-        return false;
     }
-    return true;
-}
-
-static bool parse_directive_or_pragma_do(parser_t *parser, bool *pragma) {
-
-    size_t type = PARSE_DIRECTIVE_COUNT;
-
-    if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
-        parseerror(parser, "expected `pragma, error, message, warning` after `#`, got `%s`",
-            parser_tokval(parser));
-
+    else
+    {
+        (void)!parsewarning(parser, WARN_UNKNOWN_PRAGMAS, "ignoring #pragma %s", parser_tokval(parser));
         return false;
     }
 
-    if (!strcmp(parser_tokval(parser), "pragma" )) {
-        *pragma = true;
-
-        return parse_pragma_do(parser);
-    }
-
-    if (!strcmp(parser_tokval(parser), "error"  )) type = PARSE_DIRECTIVE_ERROR;
-    if (!strcmp(parser_tokval(parser), "message")) type = PARSE_DIRECTIVE_MESSAGE;
-    if (!strcmp(parser_tokval(parser), "warning")) type = PARSE_DIRECTIVE_WARNING;
-
-    switch (type) {
-        case PARSE_DIRECTIVE_ERROR:
-        case PARSE_DIRECTIVE_MESSAGE:
-        case PARSE_DIRECTIVE_WARNING:
-            *pragma = false;
-
-            if (!parse_skipwhite(parser) || parser->tok != TOKEN_STRINGCONST) {
-                parseerror(parser, "expected %s, got `%`", parser_directives[type], parser_tokval(parser));
-                return false;
-            }
-
-            switch (type) {
-                case PARSE_DIRECTIVE_ERROR:
-                    con_cprintmsg(&parser->lex->tok.ctx, LVL_ERROR, "error", parser_tokval(parser));
-                    compile_errors ++; /* hack */
-                    break;
-                    /*break;*/
-
-                case PARSE_DIRECTIVE_MESSAGE:
-                    con_cprintmsg(&parser->lex->tok.ctx, LVL_MSG, "message", parser_tokval(parser));
-                    break;
-
-                case PARSE_DIRECTIVE_WARNING:
-                    con_cprintmsg(&parser->lex->tok.ctx, LVL_WARNING, "warning", parser_tokval(parser));
-                    break;
-            }
-
-            if (!parse_eol(parser)) {
-                parseerror(parser, "parse error after `%` directive", parser_directives[type]);
-                return false;
-            }
-
-            return (type != PARSE_DIRECTIVE_ERROR);
-    }
-
-    parseerror(parser, "invalid directive `%s`", parser_tokval(parser));
-    return false;
+    return true;
 }
 
-static bool parse_directive_or_pragma(parser_t *parser)
+static bool parse_pragma(parser_t *parser)
 {
     bool rv;
-    bool pragma; /* true when parsing pragma */
     parser->lex->flags.preprocessing = true;
-    parser->lex->flags.mergelines    = true;
-
-    rv = parse_directive_or_pragma_do(parser, &pragma);
-
+    parser->lex->flags.mergelines = true;
+    rv = parse_pragma_do(parser);
     if (parser->tok != TOKEN_EOL) {
-        parseerror(parser, "junk after %s", (pragma) ? "pragma" : "directive");
+        parseerror(parser, "junk after pragma");
         rv = false;
     }
     parser->lex->flags.preprocessing = false;
-    parser->lex->flags.mergelines    = false;
-
+    parser->lex->flags.mergelines = false;
     if (!parser_next(parser)) {
-        parseerror(parser, "parse error after %s", (pragma) ? "pragma" : "directive");
+        parseerror(parser, "parse error after pragma");
         rv = false;
     }
     return rv;
@@ -3621,6 +3959,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;
@@ -3628,11 +3968,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 == '}') {
@@ -3656,8 +4022,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)) {
@@ -3696,6 +4063,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;
@@ -3827,6 +4201,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;
@@ -3899,11 +4278,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);
@@ -4029,6 +4409,12 @@ 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);
@@ -5012,7 +5398,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:
@@ -5217,10 +5609,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
+                            );
                         }
                     }
                 }
@@ -5595,7 +6062,7 @@ static bool parser_global_statement(parser_t *parser)
     }
     else if (parser->tok == '#')
     {
-        return parse_directive_or_pragma(parser);
+        return parse_pragma(parser);
     }
     else if (parser->tok == '$')
     {
@@ -5606,7 +6073,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;
@@ -5718,6 +6185,8 @@ bool parser_init()
     vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
     vec_push(parser->_blocktypedefs, 0);
 
+    parser->aliases = util_htnew(PARSER_HT_SIZE);
+
     /* corrector */
     vec_push(parser->correct_variables, correct_trie_new());
     vec_push(parser->correct_variables_score, NULL);
@@ -5868,6 +6337,8 @@ void parser_cleanup()
     ast_value_delete(parser->const_vec[1]);
     ast_value_delete(parser->const_vec[2]);
 
+    util_htdel(parser->aliases);
+
     mem_d(parser);
 }