]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: record if a variable declaration was given an explicit type
[ocean] / csrc / oceani.mdc
index 12beeb2c71952315a6373ec17407c15a1dcf6738..a421167632746e9525716f96e3729983ff8022a4 100644 (file)
@@ -446,7 +446,7 @@ Named type are stored in a simple linked list.  Objects of each type are
                int size, align;
                void (*init)(struct type *type, struct value *val);
                void (*prepare_type)(struct parse_context *c, struct type *type, int parse_time);
-               void (*print)(struct type *type, struct value *val);
+               void (*print)(struct type *type, struct value *val, FILE *f);
                void (*print_type)(struct type *type, FILE *f);
                int (*cmp_order)(struct type *t1, struct type *t2,
                                 struct value *v1, struct value *v2);
@@ -543,12 +543,12 @@ Named type are stored in a simple linked list.  Objects of each type are
                return -1;                              // NOTEST
        }
 
-       static void print_value(struct type *type, struct value *v)
+       static void print_value(struct type *type, struct value *v, FILE *f)
        {
                if (type && type->print)
-                       type->print(type, v);
+                       type->print(type, v, f);
                else
-                       printf("*Unknown*");            // NOTEST
+                       fprintf(f, "*Unknown*");                // NOTEST
        }
 
 ###### forward decls
@@ -561,7 +561,7 @@ Named type are stored in a simple linked list.  Objects of each type are
                              struct value *vold, struct value *vnew);
        static int value_cmp(struct type *tl, struct type *tr,
                             struct value *left, struct value *right);
-       static void print_value(struct type *type, struct value *v);
+       static void print_value(struct type *type, struct value *v, FILE *f);
 
 ###### free context types
 
@@ -732,23 +732,23 @@ A separate function encoding these cases will simplify some code later.
                return cmp;
        }
 
-       static void _print_value(struct type *type, struct value *v)
+       static void _print_value(struct type *type, struct value *v, FILE *f)
        {
                switch (type->vtype) {
                case Vnone:                             // NOTEST
-                       printf("*no-value*"); break;    // NOTEST
+                       fprintf(f, "*no-value*"); break;        // NOTEST
                case Vlabel:                            // NOTEST
-                       printf("*label-%p*", v->label); break; // NOTEST
+                       fprintf(f, "*label-%p*", v->label); break; // NOTEST
                case Vstr:
-                       printf("%.*s", v->str.len, v->str.txt); break;
+                       fprintf(f, "%.*s", v->str.len, v->str.txt); break;
                case Vbool:
-                       printf("%s", v->bool ? "True":"False"); break;
+                       fprintf(f, "%s", v->bool ? "True":"False"); break;
                case Vnum:
                        {
                        mpf_t fl;
                        mpf_init2(fl, 20);
                        mpf_set_q(fl, v->num);
-                       gmp_printf("%Fg", fl);
+                       gmp_fprintf(f, "%Fg", fl);
                        mpf_clear(fl);
                        break;
                        }
@@ -935,6 +935,12 @@ is used to distinguish between the first of a set of parallel scopes,
 in which declared variables must not be in scope, and subsequent
 branches, whether they may already be conditionally scoped.
 
+We need a total ordering of scopes so we can easily compare to variables
+to see if they are concurrently in scope.  To achieve this we record a
+`scope_count` which is actually a count of both beginnings and endings
+of scopes.  Then each variable has a record of the scope count where it
+enters scope, and where it leaves.
+
 To push a new frame *before* any code in the frame is parsed, we need a
 grammar reduction.  This is most easily achieved with a grammar
 element which derives the empty string, and creates the new scope when
@@ -949,8 +955,12 @@ like "if" and the code following it.
 
 ###### parse context
        int scope_depth;
+       int scope_count;
        struct scope *scope_stack;
 
+###### variable fields
+       int scope_start, scope_end;
+
 ###### ast functions
        static void scope_pop(struct parse_context *c)
        {
@@ -959,6 +969,7 @@ like "if" and the code following it.
                c->scope_stack = s->parent;
                free(s);
                c->scope_depth -= 1;
+               c->scope_count += 1;
        }
 
        static void scope_push(struct parse_context *c)
@@ -969,6 +980,7 @@ like "if" and the code following it.
                s->parent = c->scope_stack;
                c->scope_stack = s;
                c->scope_depth += 1;
+               c->scope_count += 1;
        }
 
 ###### Grammar
@@ -1005,7 +1017,10 @@ Each variable records a scope depth and is in one of four states:
 
 - "out of scope".  The variable is neither in scope nor conditionally
   in scope.  It is permanently out of scope now and can be removed from
-  the "in scope" stack.
+  the "in scope" stack.  When a variable becomes out-of-scope it is
+  moved to a separate list (`out_scope`) of variables which have fully
+  known scope.  This will be used at the end of each function to assign
+  each variable a place in the stack frame.
 
 ###### variable fields
        int depth, min_depth;
@@ -1015,6 +1030,7 @@ Each variable records a scope depth and is in one of four states:
 ###### parse context
 
        struct variable *in_scope;
+       struct variable *out_scope;
 
 All variables with the same name are linked together using the
 'previous' link.  Those variable that have been affirmatively merged all
@@ -1052,6 +1068,10 @@ need to be freed.  For this we need to be able to find it, so assume that
                            v->merged == secondary->merged) {
                                v->scope = OutScope;
                                v->merged = primary;
+                               if (v->scope_start < primary->scope_start)
+                                       primary->scope_start = v->scope_start;
+                               if (v->scope_end > primary->scope_end)
+                                       primary->scope_end = v->scope_end;      // NOTEST
                                variable_unlink_exec(v);
                        }
        }
@@ -1082,13 +1102,15 @@ need to be freed.  For this we need to be able to find it, so assume that
 
 #### Manipulating Bindings
 
-When a name is conditionally visible, a new declaration discards the
-old binding - the condition lapses.  Conversely a usage of the name
-affirms the visibility and extends it to the end of the containing
-block - i.e. the block that contains both the original declaration and
-the latest usage.  This is determined from `min_depth`.  When a
-conditionally visible variable gets affirmed like this, it is also
-merged with other conditionally visible variables with the same name.
+When a name is conditionally visible, a new declaration discards the old
+binding - the condition lapses.  Similarly when we reach the end of a
+function (outermost non-global scope) any conditional scope must lapse.
+Conversely a usage of the name affirms the visibility and extends it to
+the end of the containing block - i.e.  the block that contains both the
+original declaration and the latest usage.  This is determined from
+`min_depth`.  When a conditionally visible variable gets affirmed like
+this, it is also merged with other conditionally visible variables with
+the same name.
 
 When we parse a variable declaration we either report an error if the
 name is currently bound, or create a new variable at the current nest
@@ -1123,7 +1145,7 @@ we need to mark all pending-scope variable as out-of-scope.  Otherwise
 all pending-scope variables become conditionally scoped.
 
 ###### ast
-       enum closetype { CloseSequential, CloseParallel, CloseElse };
+       enum closetype { CloseSequential, CloseFunction, CloseParallel, CloseElse };
 
 ###### ast functions
 
@@ -1152,6 +1174,7 @@ all pending-scope variables become conditionally scoped.
                v->min_depth = v->depth = c->scope_depth;
                v->scope = InScope;
                v->in_scope = c->in_scope;
+               v->scope_start = c->scope_count;
                c->in_scope = v;
                ## variable init
                return v;
@@ -1185,6 +1208,19 @@ all pending-scope variables become conditionally scoped.
                return v;
        }
 
+       static int var_refile(struct parse_context *c, struct variable *v)
+       {
+               /* Variable just went out of scope.  Add it to the out_scope
+                * list, sorted by ->scope_start
+                */
+               struct variable **vp = &c->out_scope;
+               while ((*vp) && (*vp)->scope_start < v->scope_start)
+                       vp = &(*vp)->in_scope;
+               v->in_scope = *vp;
+               *vp = v;
+               return 0;               
+       }
+
        static void var_block_close(struct parse_context *c, enum closetype ct,
                                    struct exec *e)
        {
@@ -1202,7 +1238,7 @@ all pending-scope variables become conditionally scoped.
                for (vp = &c->in_scope;
                     (v = *vp) && v->min_depth > c->scope_depth;
                     (v->scope == OutScope || v->name->var != v)
-                    ? (*vp =  v->in_scope, 0)
+                    ? (*vp =  v->in_scope, var_refile(c, v))
                     : ( vp = &v->in_scope, 0)) {
                        v->min_depth = c->scope_depth;
                        if (v->name->var != v)
@@ -1211,7 +1247,9 @@ all pending-scope variables become conditionally scoped.
                                 */
                                continue;
                        v->min_depth = c->scope_depth;
-                       if (v->scope == InScope && e) {
+                       if (v->scope == InScope)
+                               v->scope_end = c->scope_count;
+                       if (v->scope == InScope && e && !v->global) {
                                /* This variable gets cleaned up when 'e' finishes */
                                variable_unlink_exec(v);
                                v->cleanup_exec = e;
@@ -1258,6 +1296,11 @@ all pending-scope variables become conditionally scoped.
                                        abort();                // NOTEST
                                }
                                break;
+                       case CloseFunction:
+                               if (v->scope == CondScope)
+                                       /* Condition cannot continue past end of function */
+                                       v->scope = InScope;
+                               /* fallthrough */
                        case CloseSequential:
                                if (v->type == Tlabel)
                                        v->scope = PendingScope;
@@ -1367,35 +1410,50 @@ tell if it was set or not later.
 As global values are found -- struct field initializers, labels etc --
 `global_alloc()` is called to record the value in the global frame.
 
-When the program is fully parsed, we need to walk the list of variables
-to find any that weren't merged away and that aren't global, and to
-calculate the frame size and assign a frame position for each
-variable.  For this we have `scope_finalize()`.
+When the program is fully parsed, each function is analysed, we need to
+walk the list of variables local to that function and assign them an
+offset in the stack frame.  For this we have `scope_finalize()`.
+
+We keep the stack from dense by re-using space for between variables
+that are not in scope at the same time.  The `out_scope` list is sorted
+by `scope_start` and as we process a varible, we move it to an FIFO
+stack.  For each variable we consider, we first discard any from the
+stack anything that went out of scope before the new variable came in.
+Then we place the new variable just after the one at the top of the
+stack.
 
 ###### ast functions
 
-       static int scope_finalize(struct parse_context *c)
+       static void scope_finalize(struct parse_context *c, struct type *ft)
        {
-               struct binding *b;
                int size = 0;
-
-               for (b = c->varlist; b; b = b->next) {
-                       struct variable *v;
-                       for (v = b->var; v; v = v->previous) {
-                               struct type *t = v->type;
-                               if (v->merged != v)
-                                       continue;
-                               if (v->global)
-                                       continue;
-                               if (!t)
-                                       continue;
-                               if (size & (t->align - 1))
-                                       size = (size + t->align) & ~(t->align-1);
-                               v->frame_pos = size;
-                               size += v->type->size;
-                       }
+               struct variable *next = ft->function.scope;
+               struct variable *done = NULL;
+               while (next) {
+                       struct variable *v = next;
+                       struct type *t = v->type;
+                       int pos;
+                       next = v->in_scope;
+                       if (v->merged != v)
+                               continue;
+                       if (!t)
+                               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;
+                       if (pos & (t->align - 1))
+                               pos = (pos + t->align) & ~(t->align-1);
+                       v->frame_pos = pos;
+                       if (size < pos + v->type->size)
+                               size = pos + v->type->size;
+                       v->in_scope = done;
+                       done = v;
                }
-               return size;
+               c->out_scope = NULL;
+               ft->function.local_size = size;
        }
 
 ###### free context storage
@@ -1549,6 +1607,7 @@ also want to know what sort of bracketing to use.
                        do_indent(indent, "/* FREE");
                        for (v = e->to_free; v; v = v->next_free) {
                                printf(" %.*s", v->name->name.len, v->name->name.txt);
+                               printf("[%d,%d]", v->scope_start, v->scope_end);
                                if (v->frame_pos >= 0)
                                        printf("(%d+%d)", v->frame_pos,
                                               v->type ? v->type->size:0);
@@ -1642,12 +1701,16 @@ in `rval`.
                struct value rval, *lval;
        };
 
-       static struct lrval _interp_exec(struct parse_context *c, struct exec *e);
+       /* If dest is passed, dtype must give the expected type, and
+        * result can go there, in which case type is returned as NULL.
+        */
+       static struct lrval _interp_exec(struct parse_context *c, struct exec *e,
+                                        struct value *dest, struct type *dtype);
 
        static struct value interp_exec(struct parse_context *c, struct exec *e,
                                        struct type **typeret)
        {
-               struct lrval ret = _interp_exec(c, e);
+               struct lrval ret = _interp_exec(c, e, NULL, NULL);
 
                if (!ret.type) abort();
                if (typeret)
@@ -1660,8 +1723,9 @@ in `rval`.
        static struct value *linterp_exec(struct parse_context *c, struct exec *e,
                                          struct type **typeret)
        {
-               struct lrval ret = _interp_exec(c, e);
+               struct lrval ret = _interp_exec(c, e, NULL, NULL);
 
+               if (!ret.type) abort();
                if (ret.lval)
                        *typeret = ret.type;
                else
@@ -1669,8 +1733,28 @@ in `rval`.
                return ret.lval;
        }
 
-       static struct lrval _interp_exec(struct parse_context *c, struct exec *e)
+       /* dinterp_exec is used when the destination type is certain and
+        * the value has a place to go.
+        */
+       static void dinterp_exec(struct parse_context *c, struct exec *e,
+                                struct value *dest, struct type *dtype,
+                                int need_free)
+       {
+               struct lrval ret = _interp_exec(c, e, dest, dtype);
+               if (!ret.type)
+                       return; // NOTEST
+               if (need_free)
+                       free_value(dtype, dest);
+               if (ret.lval)
+                       dup_value(dtype, ret.lval, dest);
+               else
+                       memcpy(dest, &ret.rval, dtype->size);
+       }
+
+       static struct lrval _interp_exec(struct parse_context *c, struct exec *e,
+                                        struct value *dest, struct type *dtype)
        {
+               /* If the result is copied to dest, ret.type is set to NULL */
                struct lrval ret;
                struct value rv = {}, *lrv = NULL;
                struct type *rvtype;
@@ -1698,9 +1782,11 @@ in `rval`.
                }
                ## interp exec cases
                }
-               ret.lval = lrv;
-               ret.rval = rv;
-               ret.type = rvtype;
+               if (rvtype) {
+                       ret.lval = lrv;
+                       ret.rval = rv;
+                       ret.type = rvtype;
+               }
                ## interp exec cleanup
                return ret;
        }
@@ -1992,7 +2078,7 @@ with a const size by whether they are prepared at parse time or not.
                if (i >= 0 && i < ltype->array.size)
                        lrv = ptr + i * rvtype->size;
                else
-                       val_init(ltype->array.member, &rv);
+                       val_init(ltype->array.member, &rv); // UNSAFE
                ltype = NULL;
                break;
        }
@@ -2260,7 +2346,7 @@ function will be needed.
                | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
 
        Field -> IDENTIFIER : Type = Expression ${ {
-                       int ok; // UNTESTED
+                       int ok;
 
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
@@ -2289,9 +2375,9 @@ function will be needed.
        static void structure_print_type(struct type *t, FILE *f);
 
 ###### value functions
-       static void structure_print_type(struct type *t, FILE *f)       // UNTESTED
-       {       // UNTESTED
-               int i;  // UNTESTED
+       static void structure_print_type(struct type *t, FILE *f)
+       {
+               int i;
 
                fprintf(f, "struct %.*s\n", t->name.len, t->name.txt);
 
@@ -2303,17 +2389,17 @@ function will be needed.
                                fprintf(f, " = ");
                                if (fl->type == Tstr)
                                        fprintf(f, "\"");       // UNTESTED
-                               print_value(fl->type, fl->init);
+                               print_value(fl->type, fl->init, f);
                                if (fl->type == Tstr)
                                        fprintf(f, "\"");       // UNTESTED
                        }
-                       printf("\n");
+                       fprintf(f, "\n");
                }
        }
 
 ###### print type decls
-       {       // UNTESTED
-               struct type *t; // UNTESTED
+       {
+               struct type *t;
                int target = -1;
 
                while (target != 0) {
@@ -2384,6 +2470,7 @@ further detailed when Expression Lists are introduced.
        struct {
                struct binode *params;
                struct type *return_type;
+               struct variable *scope;
                int local_size;
        } function;
 
@@ -2434,7 +2521,7 @@ further detailed when Expression Lists are introduced.
                                 args, NULL, 0, NULL);
        }
 
-       static void function_print(struct type *type, struct value *val)
+       static void function_print(struct type *type, struct value *val, FILE *f)
        {
                print_exec(val->function, 1, 0);
        }
@@ -2621,7 +2708,7 @@ an executable.
                struct val *v = cast(val, e);
                if (v->vtype == Tstr)
                        printf("\"");
-               print_value(v->vtype, &v->val);
+               print_value(v->vtype, &v->val, stdout);
                if (v->vtype == Tstr)
                        printf("\"");
                break;
@@ -2684,6 +2771,10 @@ because it really is the same variable no matter where it appears.
 When a variable is used, we need to remember to follow the `->merged`
 link to find the primary instance.
 
+When a variable is declared, it may or may not be given an explicit
+type.  We need to record which so that we can report the parsed code
+correctly.
+
 ###### exec type
        Xvar,
 
@@ -2693,6 +2784,9 @@ link to find the primary instance.
                struct variable *var;
        };
 
+###### variable fields
+       int explicit_type;
+
 ###### Grammar
 
        $TERM : ::
@@ -2737,6 +2831,7 @@ link to find the primary instance.
                        v->where_decl = $0;
                        v->where_set = $0;
                        v->type = $<Type;
+                       v->explicit_type = 1;
                } else {
                        v = var_ref(c, $1.txt);
                        $0->var = v;
@@ -2755,6 +2850,7 @@ link to find the primary instance.
                        v->where_set = $0;
                        v->type = $<Type;
                        v->constant = 1;
+                       v->explicit_type = 1;
                } else {
                        v = var_ref(c, $1.txt);
                        $0->var = v;
@@ -3870,7 +3966,7 @@ printed.
                        b2 = cast(binode, b->right);
                for (; b2; b2 = cast(binode, b2->right)) {
                        left = interp_exec(c, b2->left, &ltype);
-                       print_value(ltype, &left);
+                       print_value(ltype, &left, stdout);
                        free_value(ltype, &left);
                        if (b2->right)
                                putchar(' ');
@@ -3944,13 +4040,13 @@ it is declared, and error will be raised as the name is created as
                print_exec(b->left, indent, bracket);
                if (cast(var, b->left)->var->constant) {
                        printf("::");
-                       if (v->where_decl == v->where_set) {
+                       if (v->explicit_type) {
                                type_print(v->type, stdout);
                                printf(" ");
                        }
                } else {
                        printf(":");
-                       if (v->where_decl == v->where_set) {
+                       if (v->explicit_type) {
                                type_print(v->type, stdout);
                                printf(" ");
                        }
@@ -3999,12 +4095,9 @@ it is declared, and error will be raised as the name is created as
 
        case Assign:
                lleft = linterp_exec(c, b->left, &ltype);
-               right = interp_exec(c, b->right, &rtype);
-               if (lleft) {
-                       free_value(ltype, lleft);
-                       dup_value(ltype, &right, lleft);
-                       ltype = NULL;
-               }
+               if (lleft)
+                       dinterp_exec(c, b->right, lleft, ltype, 1);
+               ltype = Tnone;
                break;
 
        case Declare:
@@ -4015,13 +4108,10 @@ it is declared, and error will be raised as the name is created as
                val = var_value(c, v);
                if (v->type->prepare_type)
                        v->type->prepare_type(c, v->type, 0);
-               if (b->right) {
-                       right = interp_exec(c, b->right, &rtype);
-                       memcpy(val, &right, rtype->size);
-                       rtype = Tnone;
-               } else {
+               if (b->right)
+                       dinterp_exec(c, b->right, val, v->type, 0);
+               else
                        val_init(v->type, val);
-               }
                break;
        }
 
@@ -4533,7 +4623,7 @@ casepart` to track a list of case parts.
                rv = interp_exec(c, b->left, &rvtype);
                if (rvtype == Tnone ||
                    (rvtype == Tbool && rv.bool != 0))
-                       // cnd is Tnone or Tbool, doesn't need to be freed
+                       // rvtype is Tnone or Tbool, doesn't need to be freed
                        interp_exec(c, b->right, NULL);
                break;
 
@@ -4722,7 +4812,7 @@ searching through for the Nth constant for decreasing N.
                                printf(" = ");
                                if (v->type == Tstr)
                                        printf("\"");
-                               print_value(v->type, val);
+                               print_value(v->type, val, stdout);
                                if (v->type == Tstr)
                                        printf("\"");
                                printf("\n");
@@ -4757,13 +4847,15 @@ is a bit more interesting at this level.
                        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);
+                       var_block_close(c, CloseFunction, code);
+                       name->type->function.scope = c->out_scope;
                } else {
                        free_binode(args);
                        free_type(ret);
                        free_exec(code);
-                       var_block_close(c, CloseSequential, NULL);
+                       var_block_close(c, CloseFunction, NULL);
                }
+               c->out_scope = NULL;
                return name;
        }
 
@@ -4815,7 +4907,7 @@ is a bit more interesting at this level.
                                if (brackets)
                                        print_exec(val->function, 0, brackets);
                                else
-                                       print_value(v->type, val);
+                                       print_value(v->type, val, stdout);
                                printf("/* frame size %d */\n", v->type->function.local_size);
                                target -= 1;
                        }
@@ -4850,7 +4942,7 @@ is a bit more interesting at this level.
                                         v->where_decl, v->type->function.return_type, 0, NULL);
                        }
 
-                       v->type->function.local_size = scope_finalize(c);
+                       scope_finalize(c, v->type);
                }
                return all_ok;
        }