Implement exponentiation operator `**` as well as __builtin_pow (used for exponentiat...
authorDale Weiler <killfieldengine@gmail.com>
Thu, 7 Mar 2013 21:31:19 +0000 (21:31 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Thu, 7 Mar 2013 21:31:19 +0000 (21:31 +0000)
Makefile
lexer.c
lexer.h
parser.c

index 410a97d19c4c6d98c5c35662aa506581b0f06856..fb17d6b9111be423ce201dc6e1ee8cf964b8e194 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -147,7 +147,7 @@ $(QCVM): $(OBJ_X)
        $(CC) -o $@ $^ $(CFLAGS) -lm
 
 $(GMQCC): $(OBJ_C) $(OBJ_D)
-       $(CC) -o $@ $^ $(CFLAGS)
+       $(CC) -o $@ $^ $(CFLAGS) -lm
 
 $(TESTSUITE): $(OBJ_T)
        $(CC) -o $@ $^ $(CFLAGS) -lm
diff --git a/lexer.c b/lexer.c
index 5f23952615ce6c1aa8d3f89607aa1c75821d7c11..4235613680f4e78c9b6b5d097f1b3adaae687f12 100644 (file)
--- a/lexer.c
+++ b/lexer.c
@@ -1232,7 +1232,7 @@ int lex_do(lex_file *lex)
             /*
             case '+':
             case '-':
-            */
+            */ 
             case '*':
             case '/':
             case '<':
@@ -1352,7 +1352,7 @@ int lex_do(lex_file *lex)
         lex_tokench(lex, ch);
 
         nextch = lex_getch(lex);
-        if (nextch == '=') {
+        if (nextch == '=' || nextch == '*') {
             lex_tokench(lex, nextch);
         } else
             lex_ungetch(lex, nextch);
diff --git a/lexer.h b/lexer.h
index 9724a7b90d97c386e1de013a97e08d654e05d280..e560f6d781aa54fd4b46cb637cf5cd7d585cb32c 100644 (file)
--- a/lexer.h
+++ b/lexer.h
@@ -185,6 +185,7 @@ static const oper_info c_operators[] = {
     { "%",   2, opid1('%'),         ASSOC_LEFT,  13, 0 },
 
     { "+",   2, opid1('+'),         ASSOC_LEFT,  12, 0 },
+    { "**",  2, opid2('*', '*'),    ASSOC_LEFT,  12, 0 },
     { "-",   2, opid1('-'),         ASSOC_LEFT,  12, 0 },
 
     { "<<",  2, opid2('<','<'),     ASSOC_LEFT,  11, 0 },
index 681207c192c01d55b74f565856ae537d78b6fdaa..b97d92c3896ee0d26fb9c3a240afc7da2e02bf9b 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"
@@ -119,6 +120,7 @@ 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 void parseerror(parser_t *parser, const char *fmt, ...)
 {
@@ -1071,6 +1073,23 @@ 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 {
+                out = parser_builtin_pow(parser);
+            }
+            break;
+
         case opid3('<','=','>'): /* -1, 0, or 1 */
             if (NotSameType(TYPE_FLOAT)) {
                 ast_type_to_string(exprs[0], ty1, sizeof(ty1));
@@ -1830,6 +1849,8 @@ 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;
             }
+            if (!strcmp(parser_tokval(parser), "__builtin_pow"))
+                var = parser_builtin_pow(parser);
                 
             if (!var) {
                 char *correct = NULL;
@@ -3294,6 +3315,145 @@ 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 *= 2;
+     *       x *= x;
+     *     }
+     *     y = y - 1;
+     *     value = x * value;
+     *   }
+     *   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;
+}
+
 /* parse computed goto sides */
 static ast_expression *parse_goto_computed(parser_t *parser, ast_expression **side) {
     ast_expression *on_true;