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.

This commit is contained in:
Dale Weiler 2013-05-29 11:13:42 +00:00
parent 2923b718e1
commit 6d6a2efada
5 changed files with 127 additions and 8 deletions

2
ast.c
View file

@ -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;

View file

@ -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 */

View file

@ -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;
@ -2894,7 +2924,9 @@ onerr:
static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out)
{
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,11 +2995,17 @@ 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);
}
}
*out = (ast_expression*)ret;
return true;
}
@ -4277,6 +4353,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
vec_push(func->blocks, block);
parser->function = old;
if (!parser_leaveblock(parser))
retval = false;
@ -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))
{

23
tests/rassign.qc Normal file
View file

@ -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
}

7
tests/rassign.tmpl Normal file
View file

@ -0,0 +1,7 @@
I: rassign.qc
D: test return assignments
T: -execute
C: -freturn-assignments
M: 200
M: '2 3 4'
M: world