X-Git-Url: https://ocean-lang.org/code/?a=blobdiff_plain;f=csrc%2Foceani.mdc;h=43616ec5be39b65fe3534a5e4356a70dcb445d9b;hb=b24e0b84da4cdd01999f87f132119083b2761b50;hp=f257736edc328baf8ba313d28f3bd7826e4e5004;hpb=17b2c8c16fd6dadf052fefc20e2ee8c7f8c3da0b;p=ocean diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index f257736..43616ec 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -691,7 +691,7 @@ A separate function encoding these cases will simplify some code later. } } - static void _dup_value(struct type *type, + static void _dup_value(struct type *type, struct value *vold, struct value *vnew) { switch (type->vtype) { @@ -1031,7 +1031,7 @@ list of in_scope names. The storage of the value of a variable will be described later. For now we just need to know that when a variable goes out of scope, it might -need to be freed. For this we need to be able to find it, so assume that +need to be freed. For this we need to be able to find it, so assume that `var_value()` will provide that. ###### variable fields @@ -1575,7 +1575,7 @@ propagation is needed. ###### ast - enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1, Rnoconstant = 2<<1}; + enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1, Rnoconstant = 1<<2}; ###### format cases case 'r': @@ -2061,7 +2061,7 @@ function will be needed. struct value *v; v = (void*) val->ptr + type->structure.fields[i].offset; if (type->structure.fields[i].init) - dup_value(type->structure.fields[i].type, + dup_value(type->structure.fields[i].type, type->structure.fields[i].init, v); else @@ -2335,27 +2335,45 @@ function will be needed. #### 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 list, such as +The parameters can be specified either in parentheses as a ';' separated +list, such as ##### Example: function 1 - func main(av:[ac::number]string) + func main(av:[ac::number]string; env:[envc::number]string) code block -or as an indented list of one parameter per line +or as an indented list of one parameter per line (though each line can +be a ';' separated list) ##### Example: function 2 func main argv:[argc::number]string + env:[envc::number]string 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. @@ -2363,6 +2381,7 @@ further detailed when Expression Lists are introduced. struct { struct binode *params; + struct type *return_type; int local_size; } function; @@ -2430,7 +2449,12 @@ further detailed when Expression Lists are introduced. 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) @@ -2477,14 +2501,22 @@ further detailed when Expression Lists are introduced. } } }$ - $*binode - Args -> ${ $0 = NULL; }$ + Args -> ArgsLine NEWLINE ${ $0 = $left; + *bp = $ ${ $0 = NULL; }$ | Varlist ${ $0 = $<1; }$ | Varlist ; ${ $0 = $<1; }$ - | Varlist NEWLINE ${ $0 = $<1; }$ - Varlist -> Varlist ; ArgDecl ${ // UNTESTED + Varlist -> Varlist ; ArgDecl ${ $0 = new(binode); $0->op = List; $0->left = $right); struct var *v = cast(var, b->left); @@ -3529,7 +3561,7 @@ arguments, form with the 'List' nodes. 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 @@ -3557,7 +3589,7 @@ arguments, form with the 'List' nodes. 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; @@ -3734,9 +3766,14 @@ is in-place. for (e = b; e; e = cast(binode, e->right)) { t = propagate_types(e->left, c, ok, NULL, rules); - if ((rules & Rboolok) && t == Tbool) + if ((rules & Rboolok) && (t == Tbool || t == Tnone)) t = NULL; - if (t && t != Tnone && t != Tbool) { + if (t == Tnone && e->right) + /* Only the final statement *must* return a value + * when not Rboolok + */ + t = NULL; + if (t) { if (!type) type = t; else if (t != type) @@ -3987,16 +4024,16 @@ it is declared, and error will be raised as the name is created as ### 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, ###### expr precedence - $TERM use + $TERM use ###### SimpleStatement Grammar | use Expression ${ @@ -4696,53 +4733,56 @@ 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. +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, + struct type *ret, + 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); + var_block_close(c, CloseSequential, code); + } else + var_block_close(c, CloseSequential, NULL); + return name; + } -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. +###### declare terminals + $TERM return ###### top level grammar $*variable - DeclareFunction -> func FuncName ( OpenScope Args ) Block Newlines ${ { - struct text funcname = { " func", 5}; - $0 = $type = add_type(c, funcname, &function_prototype); - $0->type->function.params = reorder_bilist($type, $0, &fn); - var_block_close(c, CloseSequential, fn.function); - } else - var_block_close(c, CloseSequential, NULL); - } }$ - | func FuncName then IN OpenScope OptNL Args OUT OptNL do Block Newlines ${ { - // FIXME that 'then' should not be there. - struct text funcname = { " func", 5}; - $0 = $type = add_type(c, funcname, &function_prototype); - $0->type->function.params = reorder_bilist($type, $0, &fn); - var_block_close(c, CloseSequential, fn.function); - } else - var_block_close(c, CloseSequential, NULL); - } }$ - | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${ { - struct text funcname = { " func", 5}; - $0 = $type = add_type(c, funcname, &function_prototype); - $0->type->function.params = NULL; - global_alloc(c, $0->type, $0, &fn); - var_block_close(c, CloseSequential, fn.function); - } else - var_block_close(c, CloseSequential, NULL); - } }$ + DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${ + $0 = declare_function(c, $in_scope; ok && v; v = v->in_scope) { + int all_ok = 1; + for (v = c->in_scope; v; v = v->in_scope) { struct value *val; + int ok = 1; if (v->depth != 0 || !v->type || !v->type->check_args) continue; 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 */ - 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); } - return ok; + return all_ok; } static int analyse_main(struct type *type, struct parse_context *c) @@ -4871,7 +4916,6 @@ analysis is a bit more interesting at this level. array_init(v->var->type, vl); for (i = 0; i < argc; i++) { struct value *vl2 = vl->array + i * v->var->type->array.member->size; - arg.str.txt = argv[i]; arg.str.len = strlen(argv[i]);