diff --git a/ast.c b/ast.c index cef33bd..cb4ad06 100644 --- 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 05a749a..5d198e4 100644 --- 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; diff --git a/parser.c b/parser.c index c42e62d..628f89d 100644 --- 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);