Implemented __builtin_mod, and % operator. Added floor builtin to the standalone...
authorDale Weiler <killfieldengine@gmail.com>
Fri, 8 Mar 2013 09:17:54 +0000 (09:17 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Fri, 8 Mar 2013 09:17:54 +0000 (09:17 +0000)
exec.c
lexer.c
parser.c

diff --git a/exec.c b/exec.c
index f44bd62..d9ced65 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -825,6 +825,16 @@ static int qc_strcmp(qc_program *prog)
     return 0;
 }
 
+static int qc_floor(qc_program *prog)
+{
+    qcany *num, out;
+    CheckArgs(1);
+    num = GetArg(0);
+    out._float = floor(num->_float);
+    Return(out);
+    return 0;
+}
+
 static prog_builtin qc_builtins[] = {
     NULL,
     &qc_print,       /*   1   */
@@ -839,7 +849,8 @@ static prog_builtin qc_builtins[] = {
     &qc_strcat,      /*   10  */
     &qc_strcmp,      /*   11  */
     &qc_normalize,   /*   12  */
-    &qc_sqrt         /*   13  */
+    &qc_sqrt,        /*   13  */
+    &qc_floor        /*   14  */
 };
 static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
 
diff --git a/lexer.c b/lexer.c
index 4235613..97fcbf3 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -1361,6 +1361,12 @@ int lex_do(lex_file *lex)
         return (lex->tok.ttype = TOKEN_OPERATOR);
     }
 
+    if (ch == '%') {
+        lex_tokench(lex, ch);
+        lex_endtoken(lex);
+        return (lex->tok.ttype = TOKEN_OPERATOR);
+    }
+
     if (isident_start(ch))
     {
         const char *v;
index bd4f1c1..5340f1a 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -120,8 +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, ...)
 {
@@ -964,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)) {
@@ -1858,6 +1885,8 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
                 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;
@@ -3501,6 +3530,72 @@ static ast_expression *parser_builtin_exp(parser_t *parser) {
     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 % and __builtin_mod)");
+            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;