From 50d5f6884a6c99e771b4907a03062955e7edbab5 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 20 Nov 2021 11:54:49 +1100 Subject: [PATCH] oceani: support functions returning multiple values. 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 --- csrc/oceani-tests.mdc | 15 ++++- csrc/oceani.mdc | 143 +++++++++++++++++++++++++++++++++++------- 2 files changed, 133 insertions(+), 25 deletions(-) diff --git a/csrc/oceani-tests.mdc b/csrc/oceani-tests.mdc index 1d7282d..3156b3d 100644 --- a/csrc/oceani-tests.mdc +++ b/csrc/oceani-tests.mdc @@ -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 diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index a421167..87b9070 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -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, $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); } -- 2.43.0