From 6d6a2efadac46d5f8d2b44e5f73bd94549529681 Mon Sep 17 00:00:00 2001 From: Dale Weiler Date: Wed, 29 May 2013 11:13:42 +0000 Subject: [PATCH] Experimental support for implicit return assignments. This closes #107. To enable return assignment support use -freturn-assignments. This allows you to do the following in QC code. T name() { return = expr_eval_T; return; /* returns expr_eval_T */ }. It allows for concise code and to rid locals in functions. It also saves a tremendous amount of space since only types of certian globals need to be allocated for returns. --- ast.c | 8 ++-- opts.def | 1 + parser.c | 96 ++++++++++++++++++++++++++++++++++++++++++++-- tests/rassign.qc | 23 +++++++++++ tests/rassign.tmpl | 7 ++++ 5 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 tests/rassign.qc create mode 100644 tests/rassign.tmpl diff --git a/ast.c b/ast.c index 01a08e5..41dfb29 100644 --- a/ast.c +++ b/ast.c @@ -1101,9 +1101,9 @@ ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype) vtype->hasvalue = true; vtype->constval.vfunc = self; - self->varargs = NULL; - self->argc = NULL; - self->fixedparams = NULL; + self->varargs = NULL; + self->argc = NULL; + self->fixedparams = NULL; return self; } @@ -1419,7 +1419,7 @@ error: /* clean up */ return false; } -static bool ast_local_codegen(ast_value *self, ir_function *func, bool param) +bool ast_local_codegen(ast_value *self, ir_function *func, bool param) { ir_value *v = NULL; diff --git a/opts.def b/opts.def index a9301a5..520f9b9 100644 --- a/opts.def +++ b/opts.def @@ -51,6 +51,7 @@ GMQCC_DEFINE_FLAG(VARIADIC_ARGS) GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS) GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS) + GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS) #endif /* warning flags */ diff --git a/parser.c b/parser.c index 4824106..d0563c1 100644 --- a/parser.c +++ b/parser.c @@ -106,6 +106,13 @@ typedef struct parser_s { /* code generator */ code_t *code; + + /* vector of global return vars. + * for example, you can return string, float, vector, or other + * things, hese will be created as globals here instead of + * locals in a function (saves space). + */ + ast_value **returns; } parser_t; static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1; @@ -345,6 +352,29 @@ static ast_expression* parser_find_global(parser_t *parser, const char *name) return (ast_expression*)util_htget(parser->htglobals, name); } +static ast_value* parser_find_returnvalue(parser_t *parser, int vtype) +{ + ast_value *out; + size_t i; + char *name = NULL; + + /* find existing global for the job */ + for (i = 0; i < vec_size(parser->returns); i++) + if (parser->returns[i]->expression.vtype == vtype) + return parser->returns[i]; + + util_asprintf(&name, "#ret_%s", type_name[vtype]); + + out = ast_value_new(parser_ctx(parser), name, vtype); + out->hasvalue = false; + out->isimm = false; + + vec_push(parser->returns, out); + + mem_d(name); + return out; +} + static ast_expression* parser_find_param(parser_t *parser, const char *name) { size_t i; @@ -2893,8 +2923,10 @@ onerr: static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out) { - ast_expression *exp = NULL; - ast_return *ret = NULL; + ast_expression *exp = NULL; + ast_expression *var = NULL; + ast_return *ret = NULL; + ast_expression *find = NULL; ast_value *expected = parser->function->vtype; lex_ctx ctx = parser_ctx(parser); @@ -2906,6 +2938,44 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou return false; } + /* return assignments */ + if (parser->tok == '=') { + if (!OPTS_FLAG(RETURN_ASSIGNMENTS)) { + parseerror(parser, "return assignments not activated, try using -freturn-assigments"); + return false; + } + + if (!parser_next(parser)) { + parseerror(parser, "expected return assignment expression"); + return false; + } + + if (!(exp = parse_expression_leave(parser, false, false, false))) + return false; + + if (exp->vtype != TYPE_NIL && + exp->vtype != ((ast_expression*)expected)->next->vtype) + { + parseerror(parser, "return assignment with invalid expression"); + } + + /* store to 'return' local variable */ + var = (ast_expression*)ast_store_new( + ctx, + type_store_instr[exp->vtype], + (ast_expression*)parser_find_returnvalue(parser, exp->vtype), + (ast_expression*)exp + ); + + if (!var) { + ast_unref(exp); + return false; + } + + *out = var; + return true; + } + if (parser->tok != ';') { exp = parse_expression(parser, false, false); if (!exp) @@ -2925,10 +2995,16 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou } else { if (!parser_next(parser)) parseerror(parser, "parse error"); - if (expected->expression.next->vtype != TYPE_VOID) { + + /* build expression to return */ + if ((find = (ast_expression*)parser_find_returnvalue(parser, expected->expression.next->vtype)) && OPTS_FLAG(RETURN_ASSIGNMENTS)) + ret = ast_return_new(ctx, find); + + else if (expected->expression.next->vtype != TYPE_VOID) + { (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value"); + ret = ast_return_new(ctx, NULL); } - ret = ast_return_new(ctx, NULL); } *out = (ast_expression*)ret; return true; @@ -4276,6 +4352,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } vec_push(func->blocks, block); + parser->function = old; if (!parser_leaveblock(parser)) @@ -6067,6 +6144,7 @@ parser_t *parser_create() parser->reserved_version = NULL; } + parser->returns = NULL; return parser; } @@ -6148,6 +6226,9 @@ void parser_cleanup(parser_t *parser) for (i = 0; i < vec_size(parser->globals); ++i) { ast_delete(parser->globals[i]); } + for (i = 0; i < vec_size(parser->returns); ++i) { + ast_delete(parser->returns[i]); + } vec_free(parser->accessors); vec_free(parser->functions); vec_free(parser->imm_vector); @@ -6339,6 +6420,13 @@ bool parser_finish(parser_t *parser, const char *output) return false; } } + for (i = 0; i < vec_size(parser->returns); ++i) { + if (!ast_global_codegen(parser->returns[i], ir, false)) { + con_out("internal error: failed to generate return assignment %s\n", parser->returns[i]->name); + ir_builder_delete(ir); + return false; + } + } if (parser->reserved_version && !ast_global_codegen(parser->reserved_version, ir, false)) { diff --git a/tests/rassign.qc b/tests/rassign.qc new file mode 100644 index 0000000..f734e7e --- /dev/null +++ b/tests/rassign.qc @@ -0,0 +1,23 @@ +float f_float() { + return = 100.0f; + return = 200.0f; + return; +} + +vector f_vector() { + return = '1 2 3'; + return = '2 3 4'; + return; +} + +string f_string() { + return = "hello"; + return = "world"; + return; +} + +void main() { + print(ftos(f_float()), "\n"); // 200.0f + print(vtos(f_vector()), "\n"); // '1 2 3' + print(f_string(), "\n"); // world +} diff --git a/tests/rassign.tmpl b/tests/rassign.tmpl new file mode 100644 index 0000000..6061b3d --- /dev/null +++ b/tests/rassign.tmpl @@ -0,0 +1,7 @@ +I: rassign.qc +D: test return assignments +T: -execute +C: -freturn-assignments +M: 200 +M: '2 3 4' +M: world -- 2.39.2