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,
self->fixedparams = NULL;
self->return_value = NULL;
+ self->accumulate = NULL;
+ self->accumulation = 0;
+
return self;
cleanup:
ir_value *dummy;
ast_expression *ec;
ast_expression_codegen *cgen;
+
size_t i;
(void)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))
#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
*
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;
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;
}
}
- 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);