]> git.rm.cloudns.org Git - xonotic/gmqcc.git/commitdiff
Implemented [[accumulate]] attribute. This will hopefully be used by Xonotic to repla...
authorDale Weiler <killfieldengine@gmail.com>
Thu, 17 Oct 2013 07:21:30 +0000 (03:21 -0400)
committerDale Weiler <killfieldengine@gmail.com>
Thu, 17 Oct 2013 07:21:30 +0000 (03:21 -0400)
ast.c
ast.h
parser.c

diff --git a/ast.c b/ast.c
index cef33bd73889ca3216d73d54740249fecf174c6f..cb4ad064979ad824b70ec33dde0df709819529c7 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -1183,6 +1183,7 @@ ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype
     if (!vtype) {
         compile_error(ast_ctx(self), "internal error: ast_function_new condition 0");
         goto cleanup;
+    } else if (vtype->hasvalue || vtype->expression.vtype != TYPE_FUNCTION) {
     } else if (vtype->hasvalue || vtype->expression.vtype != TYPE_FUNCTION) {
         compile_error(ast_ctx(self), "internal error: ast_function_new condition %i %i type=%i (probably 2 bodies?)",
                  (int)!vtype,
@@ -1212,6 +1213,9 @@ ast_function* ast_function_new(lex_ctx_t ctx, const char *name, ast_value *vtype
     self->fixedparams      = NULL;
     self->return_value     = NULL;
 
+    self->accumulate   = NULL;
+    self->accumulation = 0;
+
     return self;
 
 cleanup:
@@ -1792,6 +1796,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
     ir_value    *dummy;
     ast_expression         *ec;
     ast_expression_codegen *cgen;
+
     size_t    i;
 
     (void)ir;
@@ -1868,6 +1873,14 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
         }
     }
 
+    /* generate the call for any accumulation */
+    if (self->accumulate) {
+        ast_call *call = ast_call_new(ast_ctx(self), (ast_expression*)self->accumulate->vtype);
+        for (i = 0; i < vec_size(ec->params); i++)
+            vec_push(call->params, (ast_expression*)ec->params[i]);
+        vec_push(vec_last(self->blocks)->exprs, (ast_expression*)call);
+    }
+
     for (i = 0; i < vec_size(self->blocks); ++i) {
         cgen = self->blocks[i]->expression.codegen;
         if (!(*cgen)((ast_expression*)self->blocks[i], self, false, &dummy))
diff --git a/ast.h b/ast.h
index 05a749ae8e106715dc8569d929b897c232162d41..5d198e4f92ea4659992e2e26fa219318b7b862eb 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -156,10 +156,13 @@ struct ast_expression_common
 #define AST_FLAG_IS_VARARG    (1<<6)
 #define AST_FLAG_ALIAS        (1<<7)
 #define AST_FLAG_ERASEABLE    (1<<8)
-/* An array declared as []
- * so that the size is taken from the initializer */
-#define AST_FLAG_ARRAY_INIT   (1<<9)
-#define AST_FLAG_TYPE_MASK (AST_FLAG_VARIADIC | AST_FLAG_NORETURN)
+#define AST_FLAG_ACCUMULATE   (1<<9)
+/*
+ * An array declared as []
+ * so that the size is taken from the initializer
+ */
+#define AST_FLAG_ARRAY_INIT   (1<<10)
+#define AST_FLAG_TYPE_MASK    (AST_FLAG_VARIADIC | AST_FLAG_NORETURN)
 
 /* Value
  *
@@ -614,6 +617,10 @@ struct ast_function_s
 
     int builtin;
 
+    /* function accumulation */
+    ast_function *accumulate;    /* pointer to the next function in the chain */
+    size_t        accumulation;  /* base functions # of accumulations         */
+
     ir_function *ir_func;
     ir_block    *curblock;
     ir_block    **breakblocks;
index c42e62d7957cd618af58bc1c8725dd3c494366d6..628f89d691aedefaae09b3ea10fae8cdb7edd502 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -2801,6 +2801,14 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
                     return false;
                 }
             }
+            else if (!strcmp(parser_tokval(parser), "accumulate")) {
+                flags |= AST_FLAG_ACCUMULATE;
+                if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+                    parseerror(parser, "`accumulate` attribute has no parameters, expected `]]`");
+                    *cvq = CV_WRONG;
+                    return false;
+                }
+            }
             else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
                 flags   |= AST_FLAG_ALIAS;
                 *message = NULL;
@@ -3976,19 +3984,50 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         }
     }
 
-    if (var->hasvalue) {
+    if (var->hasvalue && !(var->expression.flags & AST_FLAG_ACCUMULATE)) {
         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);
+    /* accumulation? */
+    if (var->hasvalue) {
+        ast_value    *accum    = NULL;
+        ast_function *previous = NULL;
+        char          acname[1024];
+
+        /* generate a new name increasing the accumulation count*/
+        util_snprintf(acname, sizeof(acname), "$ACCUMULATE_%s_%d", var->name, var->constval.vfunc->accumulation++);
+        accum = ast_value_new(parser_ctx(parser), acname, ((ast_expression*)var)->vtype);
+        if (!accum)
+            return false;
+
+        ast_type_adopt(accum, var);
+        func = ast_function_new(ast_ctx(var), NULL, accum);
+        if (!func)
+            return false;
+
+        parser_addglobal(parser, acname, (ast_expression*)accum);
+        vec_push(parser->functions, func);
+
+        /* update the previous calls accumulate pointer for the codegen */
+        previous = var->constval.vfunc;
+        while (previous->accumulate)
+            previous = previous->accumulate;
+
+        if (ast_istype(previous, ast_function))
+            previous->accumulate = func;
+
+    } else {
+        func = ast_function_new(ast_ctx(var), var->name, var);
+        vec_push(parser->functions, func);
+    }
+
     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);