X-Git-Url: https://ocean-lang.org/code/?a=blobdiff_plain;f=csrc%2Foceani.mdc;h=0334af198c9656e7f261beb6e773c838aeda2a8a;hb=b880d8942e1539875d8209e18bf3d21011220ff8;hp=f257736edc328baf8ba313d28f3bd7826e4e5004;hpb=17b2c8c16fd6dadf052fefc20e2ee8c7f8c3da0b;p=ocean diff --git a/csrc/oceani.mdc b/csrc/oceani.mdc index f257736..0334af1 100644 --- a/csrc/oceani.mdc +++ b/csrc/oceani.mdc @@ -261,9 +261,10 @@ structures can be used. free(s); s = t; } - if (!context.parse_error) { - ## free global vars - } + // FIXME parser should pop scope even on error + while (context.scope_depth > 0) + scope_pop(&context); + ## free global vars ## free context types ## free context storage exit(context.parse_error ? 1 : 0); @@ -691,7 +692,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 +1032,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 +1576,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': @@ -1875,9 +1876,10 @@ with a const size by whether they are prepared at parse time or not. t->array.vsize = NULL; if (number_parse(num, tail, $2.txt) == 0) tok_err(c, "error: unrecognised number", &$2); - else if (tail[0]) + else if (tail[0]) { tok_err(c, "error: unsupported number suffix", &$2); - else { + mpq_clear(num); + } else { t->array.size = mpz_get_ui(mpq_numref(num)); if (mpz_cmp_ui(mpq_denref(num), 1) != 0) { tok_err(c, "error: array size must be an integer", @@ -2061,7 +2063,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 +2337,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 +2383,7 @@ further detailed when Expression Lists are introduced. struct { struct binode *params; + struct type *return_type; int local_size; } function; @@ -2430,7 +2451,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 +2503,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 +3563,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 +3591,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 +3768,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) @@ -3878,6 +3917,7 @@ it is declared, and error will be raised as the name is created as type_err(c, "Variable declared with no type or value: %v", $1, NULL, 0, NULL); + free_var($1); } else { $0 = new(binode); $0->op = Declare; @@ -3987,16 +4027,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 ${ @@ -4638,11 +4678,12 @@ searching through for the Nth constant for decreasing N. v->where_set = var; var->var = v; v->constant = 1; + v->global = 1; } else { - v = var_ref(c, $1.txt); + struct variable *vorig = var_ref(c, $1.txt); tok_err(c, "error: name already declared", &$1); type_err(c, "info: this is where '%v' was first declared", - v->where_decl, NULL, 0, NULL); + vorig->where_decl, NULL, 0, NULL); } do { ok = 1; @@ -4696,53 +4737,60 @@ 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. -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 { + free_binode(args); + free_type(ret); + free_exec(code); + var_block_close(c, CloseSequential, NULL); + } + return name; + } + +###### 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 +4924,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]);