]> ocean-lang.org Git - ocean/commitdiff
oceani: support functions returning multiple values.
authorNeilBrown <neil@brown.name>
Sat, 20 Nov 2021 00:54:49 +0000 (11:54 +1100)
committerNeilBrown <neil@brown.name>
Tue, 23 Nov 2021 22:30:54 +0000 (09:30 +1100)
A function can now return a list of parameters.
These can be assigned to a variable which acts as a struct
containing all those parameters.

There is some HACKy code in here that will need to be sorted later.

Signed-off-by: NeilBrown <neil@brown.name>
csrc/oceani-tests.mdc
csrc/oceani.mdc

index 1d7282da7f95babcfc8a342f48ff1748c19da9b4..3156b3d4bcd372babc885c6a658d9382e5d1d53f 100644 (file)
@@ -97,7 +97,7 @@ arguments separated from the name by commas.  For each test, there is a section
                ## valgrind test code
                @[ -n "$$SKIP_COVERAGE_CHECK" ] || awk '/NOTEST/ { next } /^ *[1-9]/ {ran+=1} /^ *###/ {skip+=1} \
                    END {printf "coverage: %6.2f%%\n", ran * 100 / (ran + skip); \
-                        if (ran < (ran + skip) *0.973) exit(1) }' \
+                        if (ran < (ran + skip) *0.972) exit(1) }' \
                        coverage/oceani.mdc.gcov
 
        coverage_oceani: oceani.c
@@ -647,9 +647,21 @@ Test functions.  They don't return anything, so we need to get them to print
                        test(n-1, "."++s)
                else
                        print "done"
+
+       func to_polar
+               x:number; y:number
+       return
+               rho:number
+               theta:number
+       do
+               rho = x + y
+               theta = x - y
+
        func main()
                for i:=0; then i = i + 1; while i < 5:
                        test(i, " ")
+               angular := to_polar(32, 23)
+               print angular.rho, angular.theta
 
 ###### output: functions
        done
@@ -657,6 +669,7 @@ Test functions.  They don't return anything, so we need to get them to print
        2  1 . done
        3  2 . 1 .. done
        4  3 . 2 .. 1 ... done
+       55 9
 
 ###### test: func_ret_type
 
index a421167632746e9525716f96e3729983ff8022a4..87b9070937364794fa68416eb296c0c32060ab78 100644 (file)
@@ -1426,9 +1426,10 @@ stack.
 
        static void scope_finalize(struct parse_context *c, struct type *ft)
        {
-               int size = 0;
+               int size = ft->function.local_size;
                struct variable *next = ft->function.scope;
                struct variable *done = NULL;
+
                while (next) {
                        struct variable *v = next;
                        struct type *t = v->type;
@@ -1438,12 +1439,14 @@ stack.
                                continue;
                        if (!t)
                                continue;
+                       if (v->frame_pos >= 0)
+                               continue;
                        while (done && done->scope_end < v->scope_start)
                                done = done->in_scope;
                        if (done)
                                pos = done->frame_pos + done->type->size;
                        else
-                               pos = 0;
+                               pos = ft->function.local_size;
                        if (pos & (t->align - 1))
                                pos = (pos + t->align) & ~(t->align-1);
                        v->frame_pos = pos;
@@ -1742,7 +1745,7 @@ in `rval`.
        {
                struct lrval ret = _interp_exec(c, e, dest, dtype);
                if (!ret.type)
-                       return; // NOTEST
+                       return;
                if (need_free)
                        free_value(dtype, dest);
                if (ret.lval)
@@ -2405,7 +2408,7 @@ function will be needed.
                while (target != 0) {
                        int i = 0;
                        for (t = context.typelist; t ; t=t->next)
-                               if (t->print_type_decl && !t->check_args) {
+                               if (t->print_type_decl && !t->check_args && t->name.txt[0] != ' ') {
                                        i += 1;
                                        if (i == target)
                                                break;
@@ -2446,7 +2449,7 @@ be a ';' separated list)
        do
                code block
 
-In the first case a return type can follow the paentheses after a colon,
+In the first case a return type can follow the parentheses after a colon,
 in the second it is given on a line starting with the word `return`.
 
 ##### Example: functions that return
@@ -2461,8 +2464,28 @@ in the second it is given on a line starting with the word `return`.
        do
                code block
 
+Rather than returning a type, the function can specify a set of local
+variables to return as a struct.  The values of these variables when the
+function exits will be provided to the caller.  For this the return type
+is replaced with a block of result declarations, either in parentheses
+or bracketed by `return` and `do`.
+
+##### Example: functions returning multiple variables
+
+       func to_cartesian(rho:number; theta:number):(x:number; y:number)
+               x = .....
+               y = .....
+
+       func to_polar
+               x:number; y:number
+       return
+               rho:number
+               theta:number
+       do
+               rho = ....
+               theta = ....
 
-For constructing these lists we use a `List` binode, which will be
+For constructing the lists we use a `List` binode, which will be
 further detailed when Expression Lists are introduced.
 
 ###### type union fields
@@ -2471,6 +2494,7 @@ further detailed when Expression Lists are introduced.
                struct binode *params;
                struct type *return_type;
                struct variable *scope;
+               int inline_result;      // return value is at start of 'local'
                int local_size;
        } function;
 
@@ -2541,7 +2565,20 @@ further detailed when Expression Lists are introduced.
                fprintf(f, ")");
                if (type->function.return_type != Tnone) {
                        fprintf(f, ":");
-                       type_print(type->function.return_type, f);
+                       if (type->function.inline_result) {
+                               int i;
+                               struct type *t = type->function.return_type;
+                               fprintf(f, " (");
+                               for (i = 0; i < t->structure.nfields; i++) {
+                                       struct field *fl = t->structure.fields + i;
+                                       if (i)
+                                               fprintf(f, "; ");
+                                       fprintf(f, "%.*s:", fl->name.len, fl->name.txt);
+                                       type_print(fl->type, f);
+                               }
+                               fprintf(f, ")");
+                       } else
+                               type_print(type->function.return_type, f);
                }
                fprintf(f, "\n");
        }
@@ -3687,7 +3724,12 @@ arguments, form with the 'List' nodes.
                        arg = cast(binode, arg->right);
                }
                c->local = local; c->local_size = t->function.local_size;
-               rv = interp_exec(c, fbody->function, &rvtype);
+               if (t->function.inline_result && dtype) {
+                       _interp_exec(c, fbody->function, NULL, NULL);
+                       memcpy(dest, local, dtype->size);
+                       rvtype = ret.type = NULL;
+               } else
+                       rv = interp_exec(c, fbody->function, &rvtype);
                c->local = oldlocal; c->local_size = old_size;
                free(local);
                break;
@@ -4085,7 +4127,7 @@ it is declared, and error will be raised as the name is created as
                                propagate_types(b->left, c, ok, t,
                                                (b->op == Assign ? Rnoconstant : 0));
                }
-               if (t && t->dup == NULL)
+               if (t && t->dup == NULL && t->name.txt[0] != ' ') // HACK
                        type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL);
                return Tnone;
 
@@ -4834,20 +4876,62 @@ is a bit more interesting at this level.
 
 ###### ast functions
 
+       static struct type *handle_results(struct parse_context *c,
+                                          struct binode *results)
+       {
+               /* Create a 'struct' type from the results list, which
+                * is a list for 'struct var'
+                */
+               struct text result_type_name = { " function_result", 5 };
+               struct type *t = add_type(c, result_type_name, &structure_prototype);
+               int cnt = 0;
+               struct binode *b;
+
+               for (b = results; b; b = cast(binode, b->right))
+                       cnt += 1;
+               t->structure.nfields = cnt;
+               t->structure.fields = calloc(cnt, sizeof(struct field));
+               cnt = 0;
+               for (b = results; b; b = cast(binode, b->right)) {
+                       struct var *v = cast(var, b->left);
+                       struct field *f = &t->structure.fields[cnt++];
+                       int a = v->var->type->align;
+                       f->name = v->var->name->name;
+                       f->type = v->var->type;
+                       f->init = NULL;
+                       f->offset = t->size;
+                       v->var->frame_pos = f->offset;
+                       t->size += ((f->type->size - 1) | (a-1)) + 1;
+                       if (a > t->align)
+                               t->align = a;
+                       variable_unlink_exec(v->var);
+               }
+               free_binode(results);
+               return t;
+       }
+
        static struct variable *declare_function(struct parse_context *c,
                                                struct variable *name,
                                                struct binode *args,
                                                struct type *ret,
+                                               struct binode *results,
                                                struct exec *code)
        {
                struct text funcname = {" func", 5};
                if (name) {
                        struct value fn = {.function = code};
-                       name->type = add_type(c, funcname, &function_prototype);
-                       name->type->function.params = reorder_bilist(args);
-                       name->type->function.return_type = ret;
-                       global_alloc(c, name->type, name, &fn);
+                       struct type *t;
                        var_block_close(c, CloseFunction, code);
+                       t = add_type(c, funcname, &function_prototype);
+                       name->type = t;
+                       t->function.params = reorder_bilist(args);
+                       if (!ret) {
+                               ret = handle_results(c, reorder_bilist(results));
+                               t->function.inline_result = 1;
+                               t->function.local_size = ret->size;
+                       }
+                       t->function.return_type = ret;
+                       global_alloc(c, t, name, &fn);
                        name->type->function.scope = c->out_scope;
                } else {
                        free_binode(args);
@@ -4866,22 +4950,31 @@ is a bit more interesting at this level.
 
        $*variable
        DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
+                       $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
                }$
                | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
+                       $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
                }$
                | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, NULL, Tnone, $<Bl);
+                       $0 = declare_function(c, $<FN, NULL, Tnone, NULL, $<Bl);
                }$
                | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, $<Bl);
+                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
+               }$
+               | func FuncName ( OpenScope ArgsLine ) : ( ArgsLine ) Block Newlines ${
+                       $0 = declare_function(c, $<FN, $<AL, NULL, $<AL2, $<Bl);
                }$
                | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, $<Bl);
+                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
                }$
                | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
-                       $0 = declare_function(c, $<FN, NULL, $<Ty, $<Bl);
+                       $0 = declare_function(c, $<FN, NULL, $<Ty, NULL, $<Bl);
+               }$
+               | func FuncName IN OpenScope Args OUT OptNL return IN Args OUT OptNL do Block Newlines ${
+                       $0 = declare_function(c, $<FN, $<Ar, NULL, $<Ar2, $<Bl);
+               }$
+               | func FuncName NEWLINE OpenScope return IN Args OUT OptNL do Block Newlines ${
+                       $0 = declare_function(c, $<FN, NULL, NULL, $<Ar, $<Bl);
                }$
 
 ###### print func decls
@@ -4922,22 +5015,24 @@ is a bit more interesting at this level.
                int all_ok = 1;
                for (v = c->in_scope; v; v = v->in_scope) {
                        struct value *val;
+                       struct type *ret;
                        int ok = 1;
                        if (v->depth != 0 || !v->type || !v->type->check_args)
                                continue;
+                       ret = v->type->function.inline_result ?
+                               Tnone : v->type->function.return_type;
                        val = var_value(c, v);
                        do {
                                ok = 1;
-                               propagate_types(val->function, c, &ok,
-                                               v->type->function.return_type, 0);
+                               propagate_types(val->function, c, &ok, ret, 0);
                        } while (ok == 2);
                        if (ok)
                                /* Make sure everything is still consistent */
-                               propagate_types(val->function, c, &ok,
-                                               v->type->function.return_type, 0);
+                               propagate_types(val->function, c, &ok, ret, 0);
                        if (!ok)
                                all_ok = 0;
-                       if (!v->type->function.return_type->dup) {
+                       if (!v->type->function.inline_result &&
+                           !v->type->function.return_type->dup) {
                                type_err(c, "error: function cannot return value of type %1", 
                                         v->where_decl, v->type->function.return_type, 0, NULL);
                        }