]> ocean-lang.org Git - ocean/commitdiff
oceani: add simple return type
authorNeilBrown <neil@brown.name>
Fri, 12 Nov 2021 23:10:44 +0000 (10:10 +1100)
committerNeilBrown <neil@brown.name>
Sat, 13 Nov 2021 22:50:58 +0000 (09:50 +1100)
This allow a simple typed value to be returned from a function using the
'use' statement.
It probably doesn't work for structs - don't try it.

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

index 8f10b89324aa7d7dbee42bdfeff3dac4664ba3bf..396b13e8aa69b24f95a3797c7018f2d2c6c2dfa1 100644 (file)
@@ -616,7 +616,7 @@ Time to test if structure declarations and accesses work correctly.
 Test functions.  They don't return anything, so we need to get them to print
 
 ###### test list
 Test functions.  They don't return anything, so we need to get them to print
 
 ###### test list
-       oceani_tests += functions
+       oceani_tests += functions func_ret_type
 
 ###### test: functions
 
 
 ###### test: functions
 
@@ -655,6 +655,34 @@ Test functions.  They don't return anything, so we need to get them to print
        3  2 . 1 .. done
        4  3 . 2 .. 1 ... done
 
        3  2 . 1 .. done
        4  3 . 2 .. 1 ... done
 
+###### test: func_ret_type
+
+       func double(n:number):number
+               use n+n
+
+       func answer
+               prefix:string
+               suffix:string
+       return string
+       do
+               use prefix ++ suffix
+
+       func noarg_returns
+       return Boolean
+       do
+               use 22/7 == 3.14159
+
+       func main()
+               for j:=10; then j = j - 3; while j > -5:
+                       print answer("dou","ble"), j, "is", double(j)
+
+###### output: func_ret_type
+       double 10 is 20
+       double 7 is 14
+       double 4 is 8
+       double 1 is 2
+       double -2 is -4
+
 ## Test code with syntax errors
 
 Syntax errors aren't handled well yet - the result is almost always a
 ## Test code with syntax errors
 
 Syntax errors aren't handled well yet - the result is almost always a
@@ -959,7 +987,15 @@ Test for type errors with functions
                if test1 == test2:
                        pass
 
                if test1 == test2:
                        pass
 
+       func test4(a:number):string
+               use a * a
+
+       func test5(a:number):string
+               print a
+
 ###### output: func_err_args
 ###### output: func_err_args
+       .tmp.code:28:14: error: expected string, found none
+       .tmp.code:25:8: error: expected string, found number
        .tmp.code:15:14: error: insufficient arguments to function.
        .tmp.code:16:14: error: expected number found string
        .tmp.code:16:22: error: expected string found number
        .tmp.code:15:14: error: insufficient arguments to function.
        .tmp.code:16:14: error: expected number found string
        .tmp.code:16:22: error: expected string found number
index beb4fc9830f41e25f4fbe65e54bb4195a3abde18..43616ec5be39b65fe3534a5e4356a70dcb445d9b 100644 (file)
@@ -2335,10 +2335,9 @@ function will be needed.
 #### Functions
 
 A function is a chunk of code which can be passed parameters and can
 #### Functions
 
 A function is a chunk of code which can be passed parameters and can
-return results (though results are not yet implemented).  Each function
-has a type which includes the set of parameters and the return value.
-As yet these types cannot be declared separately from the function
-itself.
+return results.  Each function has a type which includes the set of
+parameters and the return value.  As yet these types cannot be declared
+separately from the function itself.
 
 The parameters can be specified either in parentheses as a ';' separated
 list, such as
 
 The parameters can be specified either in parentheses as a ';' separated
 list, such as
@@ -2359,6 +2358,22 @@ be a ';' separated list)
        do
                code block
 
        do
                code block
 
+In the first case a return type can follow the paentheses after a colon,
+in the second it is given on a line starting with the word `return`.
+
+##### Example: functions that return
+
+       func add(a:number; b:number): number
+               code block
+
+       func catenate
+               a: string
+               b: string
+       return string
+       do
+               code block
+
+
 For constructing these lists we use a `List` binode, which will be
 further detailed when Expression Lists are introduced.
 
 For constructing these lists we use a `List` binode, which will be
 further detailed when Expression Lists are introduced.
 
@@ -2366,6 +2381,7 @@ further detailed when Expression Lists are introduced.
 
        struct {
                struct binode *params;
 
        struct {
                struct binode *params;
+               struct type *return_type;
                int local_size;
        } function;
 
                int local_size;
        } function;
 
@@ -2433,7 +2449,12 @@ further detailed when Expression Lists are introduced.
                        if (b->right)
                                fprintf(f, "; ");
                }
                        if (b->right)
                                fprintf(f, "; ");
                }
-               fprintf(f, ")\n");
+               fprintf(f, ")");
+               if (type->function.return_type != Tnone) {
+                       fprintf(f, ":");
+                       type_print(type->function.return_type, f);
+               }
+               fprintf(f, "\n");
        }
 
        static void function_free_type(struct type *t)
        }
 
        static void function_free_type(struct type *t)
@@ -3529,7 +3550,7 @@ arguments, form with the 'List' nodes.
 
        case Funcall: {
                /* Every arg must match formal parameter, and result
 
        case Funcall: {
                /* Every arg must match formal parameter, and result
-                * is return type of function (currently Tnone).
+                * is return type of function
                 */
                struct binode *args = cast(binode, b->right);
                struct var *v = cast(var, b->left);
                 */
                struct binode *args = cast(binode, b->right);
                struct var *v = cast(var, b->left);
@@ -3540,7 +3561,7 @@ arguments, form with the 'List' nodes.
                        return NULL;
                }
                v->var->type->check_args(c, ok, v->var->type, args);
                        return NULL;
                }
                v->var->type->check_args(c, ok, v->var->type, args);
-               return Tnone;
+               return v->var->type->function.return_type;
        }
 
 ###### interp binode cases
        }
 
 ###### interp binode cases
@@ -3568,7 +3589,7 @@ arguments, form with the 'List' nodes.
                        arg = cast(binode, arg->right);
                }
                c->local = local; c->local_size = t->function.local_size;
                        arg = cast(binode, arg->right);
                }
                c->local = local; c->local_size = t->function.local_size;
-               right = interp_exec(c, fbody->function, &rtype);
+               rv = interp_exec(c, fbody->function, &rvtype);
                c->local = oldlocal; c->local_size = old_size;
                free(local);
                break;
                c->local = oldlocal; c->local_size = old_size;
                free(local);
                break;
@@ -4003,10 +4024,10 @@ it is declared, and error will be raised as the name is created as
 
 ### The `use` statement
 
 
 ### The `use` statement
 
-The `use` statement is the last "simple" statement.  It is needed when
-the condition in a conditional statement is a block.  `use` works much
-like `return` in C, but only completes the `condition`, not the whole
-function.
+The `use` statement is the last "simple" statement.  It is needed when a
+statement block can return a value.  This includes the body of a
+function which has a return type, and the "condition" code blocks in
+`if`, `while`, and `switch` statements.
 
 ###### Binode types
        Use,
 
 ###### Binode types
        Use,
@@ -4712,17 +4733,17 @@ The code in an Ocean program is all stored in function declarations.
 One of the functions must be named `main` and it must accept an array of
 strings as a parameter - the command line arguments.
 
 One of the functions must be named `main` and it must accept an array of
 strings as a parameter - the command line arguments.
 
-As this is the top level, several things are handled a bit
-differently.
-The function is not interpreted by `interp_exec` as that isn't
-passed the argument list which the program requires.  Similarly type
-analysis is a bit more interesting at this level.
+As this is the top level, several things are handled a bit differently.
+The function is not interpreted by `interp_exec` as that isn't passed
+the argument list which the program requires.  Similarly type analysis
+is a bit more interesting at this level.
 
 ###### ast functions
 
        static struct variable *declare_function(struct parse_context *c,
                                                struct variable *name,
                                                struct binode *args,
 
 ###### ast functions
 
        static struct variable *declare_function(struct parse_context *c,
                                                struct variable *name,
                                                struct binode *args,
+                                               struct type *ret,
                                                struct exec *code)
        {
                struct text funcname = {" func", 5};
                                                struct exec *code)
        {
                struct text funcname = {" func", 5};
@@ -4730,6 +4751,7 @@ analysis is a bit more interesting at this level.
                        struct value fn = {.function = code};
                        name->type = add_type(c, funcname, &function_prototype);
                        name->type->function.params = reorder_bilist(args);
                        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);
                        var_block_close(c, CloseSequential, code);
                } else
                        global_alloc(c, name->type, name, &fn);
                        var_block_close(c, CloseSequential, code);
                } else
@@ -4737,17 +4759,29 @@ analysis is a bit more interesting at this level.
                return name;
        }
 
                return name;
        }
 
+###### declare terminals
+       $TERM return
+
 ###### top level grammar
 
        $*variable
        DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${
 ###### top level grammar
 
        $*variable
        DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, $<Bl);
+                       $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
                }$
                | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
                }$
                | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, $<Bl);
+                       $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
                }$
                | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
                }$
                | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, NULL, $<Bl);
+                       $0 = declare_function(c, $<FN, NULL, Tnone, $<Bl);
+               }$
+               | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
+                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, $<Bl);
+               }$
+               | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
+                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, $<Bl);
+               }$
+               | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
+                       $0 = declare_function(c, $<FN, NULL, $<Ty, $<Bl);
                }$
 
 ###### print func decls
                }$
 
 ###### print func decls
@@ -4794,11 +4828,13 @@ analysis is a bit more interesting at this level.
                        val = var_value(c, v);
                        do {
                                ok = 1;
                        val = var_value(c, v);
                        do {
                                ok = 1;
-                               propagate_types(val->function, c, &ok, Tnone, 0);
+                               propagate_types(val->function, c, &ok,
+                                               v->type->function.return_type, 0);
                        } while (ok == 2);
                        if (ok)
                                /* Make sure everything is still consistent */
                        } while (ok == 2);
                        if (ok)
                                /* Make sure everything is still consistent */
-                               propagate_types(val->function, c, &ok, Tnone, 0);
+                               propagate_types(val->function, c, &ok,
+                                               v->type->function.return_type, 0);
                        if (!ok)
                                all_ok = 0;
                        v->type->function.local_size = scope_finalize(c);
                        if (!ok)
                                all_ok = 0;
                        v->type->function.local_size = scope_finalize(c);