mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2024-11-27 14:12:36 +00:00
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:
parent
2923b718e1
commit
6d6a2efada
5 changed files with 127 additions and 8 deletions
8
ast.c
8
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;
|
||||
|
||||
|
|
1
opts.def
1
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 */
|
||||
|
|
96
parser.c
96
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))
|
||||
{
|
||||
|
|
23
tests/rassign.qc
Normal file
23
tests/rassign.qc
Normal 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
7
tests/rassign.tmpl
Normal 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
|
Loading…
Reference in a new issue