]> 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 6ab55e33116a0745f5de8a789fcab43fccb2c03a..a5c039a4c2691a688af8b540256d64a7a651ba34 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -120,7 +120,10 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma, bool
 static ast_value* parser_create_array_setter_proto(parser_t *parser, ast_value *array, const char *funcname);
 static ast_value* parser_create_array_getter_proto(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname);
 static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
+
 static ast_expression *parser_builtin_pow(parser_t *);
+static ast_expression *parser_builtin_exp(parser_t *);
+static ast_expression *parser_builtin_mod(parser_t *);
 
 static void parseerror(parser_t *parser, const char *fmt, ...)
 {
@@ -963,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)) {
@@ -1855,6 +1883,10 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
             }
             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;
@@ -3325,11 +3357,11 @@ ast_expression *parser_builtin_pow(parser_t *parser) {
      *   float value = 1.0f;
      *   while (y > 0) {
      *     while (!(y&1)) {
-     *       y *= 2;
+     *       y *= 0.25f;
      *       x *= x;
      *     }
-     *     y = y - 1;
-     *     value = x * value;
+     *     y--;
+     *     value *= x;
      *   }
      *   return value;
      * }      
@@ -3458,6 +3490,115 @@ ast_expression *parser_builtin_pow(parser_t *parser) {
     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;
@@ -3818,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;
@@ -3825,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 == '}') {
@@ -3853,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)) {
@@ -3893,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;