]> git.rm.cloudns.org Git - xonotic/gmqcc.git/commitdiff
Experimental support for implicit return assignments. This closes #107. To enable...
authorDale Weiler <killfieldengine@gmail.com>
Wed, 29 May 2013 11:13:42 +0000 (11:13 +0000)
committerDale Weiler <killfieldengine@gmail.com>
Wed, 29 May 2013 11:13:42 +0000 (11:13 +0000)
ast.c
opts.def
parser.c
tests/rassign.qc [new file with mode: 0644]
tests/rassign.tmpl [new file with mode: 0644]

diff --git a/ast.c b/ast.c
index 01a08e5966072e3222a283c337483a718f453ed0..41dfb295614b9bb086382034fca62d13af71c749 100644 (file)
--- 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;
 
index a9301a593998f5b7a99cdd857d279e39ec9ca5d4..520f9b95cb6f0d33872cd756f865d3a805e15168 100644 (file)
--- 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 */
index 482410677becfaeb2749bd79bfbdf81c389b1366..d0563c1ac43fbb21fd34705b8cd27798767b2cf7 100644 (file)
--- 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 (file)
index 0000000..f734e7e
--- /dev/null
@@ -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 (file)
index 0000000..6061b3d
--- /dev/null
@@ -0,0 +1,7 @@
+I: rassign.qc
+D: test return assignments
+T: -execute
+C: -freturn-assignments
+M: 200
+M: '2 3 4'
+M: world