]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: improve reporting of variables being freed at end of block.
[ocean] / csrc / oceani.mdc
index d1967562fdf6f1c94731f151f26c73ba3a1c47a0..76c8c9b4efd96414511685e620c4d7b7e849acbd 100644 (file)
@@ -236,24 +236,30 @@ structures can be used.
                        }
                } else
                        ss = s;                         // NOTEST
+               if (!ss->code) {
+                       fprintf(stderr, "oceani: no code found in requested section\n");        // NOTEST
+                       exit(1);                        // NOTEST
+               }
+
                parse_oceani(ss->code, &context.config, dotrace ? stderr : NULL);
 
                if (!context.prog) {
                        fprintf(stderr, "oceani: no main function found.\n");
                        context.parse_error = 1;
                }
+               if (context.prog && !context.parse_error) {
+                       if (!analyse_prog(context.prog, &context)) {
+                               fprintf(stderr, "oceani: type error in program - not running.\n");
+                               context.parse_error = 1;
+                       }
+               }
                if (context.prog && doprint) {
                        ## print const decls
                        ## print type decls
                        print_exec(context.prog, 0, brackets);
                }
-               if (context.prog && doexec && !context.parse_error) {
-                       if (!analyse_prog(context.prog, &context)) {
-                               fprintf(stderr, "oceani: type error in program - not running.\n");
-                               exit(1);
-                       }
+               if (context.prog && doexec && !context.parse_error)
                        interp_prog(&context, context.prog, argc - optind, argv+optind);
-               }
                free_exec(context.prog);
 
                while (s) {
@@ -262,7 +268,7 @@ structures can be used.
                        free(s);
                        s = t;
                }
-               ## free context vars
+               ## free global vars
                ## free context types
                ## free context storage
                exit(context.parse_error ? 1 : 0);
@@ -497,8 +503,10 @@ Named type are stored in a simple linked list.  Objects of each type are
 
        static void free_value(struct type *type, struct value *v)
        {
-               if (type && v)
+               if (type && v) {
                        type->free(type, v);
+                       memset(v, 0x5a, type->size);
+               }
        }
 
        static void type_print(struct type *type, FILE *f)
@@ -845,6 +853,42 @@ cannot nest, so a declaration while a name is in-scope is an error.
                ## variable fields
        };
 
+When a scope closes, the values of the variables might need to be freed.
+This happens in the context of some `struct exec` and each `exec` will
+need to know which variables need to be freed when it completes.
+
+####### exec fields
+       struct variable *to_free;
+
+####### variable fields
+       struct exec *cleanup_exec;
+       struct variable *next_free;
+
+####### interp exec cleanup
+       {
+               struct variable *v;
+               for (v = e->to_free; v; v = v->next_free) {
+                       struct value *val = var_value(c, v);
+                       free_value(v->type, val);
+               }
+       }
+
+###### ast functions
+       static void variable_unlink_exec(struct variable *v)
+       {
+               struct variable **vp;
+               if (!v->cleanup_exec)
+                       return;
+               for (vp = &v->cleanup_exec->to_free;
+                   *vp; vp = &(*vp)->next_free) {
+                       if (*vp != v)
+                               continue;
+                       *vp = v->next_free;
+                       v->cleanup_exec = NULL;
+                       break;
+               }
+       }
+
 While the naming seems strange, we include local constants in the
 definition of variables.  A name declared `var := value` can
 subsequently be changed, but a name declared `var ::= value` cannot -
@@ -952,13 +996,13 @@ Each variable records a scope depth and is in one of four states:
   enclosed the declaration, and that has closed.
 
 - "conditionally in scope".  The "in scope" block and all parallel
-  scopes have closed, and no further mention of the name has been
-  seen.  This state includes a secondary nest depth which records the
-  outermost scope seen since the variable became conditionally in
-  scope.  If a use of the name is found, the variable becomes "in
-  scope" and that secondary depth becomes the recorded scope depth.
-  If the name is declared as a new variable, the old variable becomes
-  "out of scope" and the recorded scope depth stays unchanged.
+  scopes have closed, and no further mention of the name has been seen.
+  This state includes a secondary nest depth (`min_depth`) which records
+  the outermost scope seen since the variable became conditionally in
+  scope.  If a use of the name is found, the variable becomes "in scope"
+  and that secondary depth becomes the recorded scope depth.  If the
+  name is declared as a new variable, the old variable becomes "out of
+  scope" and the recorded scope depth stays unchanged.
 
 - "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
@@ -1009,13 +1053,14 @@ 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;
+                               variable_unlink_exec(v);
                        }
        }
 
 ###### forward decls
        static struct value *var_value(struct parse_context *c, struct variable *v);
 
-###### free context vars
+###### free global vars
 
        while (context.varlist) {
                struct binding *b = context.varlist;
@@ -1026,10 +1071,11 @@ need to be freed.  For this we need to be able to find it, so assume that
                        struct variable *t = v;
 
                        v = t->previous;
-                       free_value(t->type, var_value(&context, t));
-                       if (t->depth == 0)
-                               // This is a global constant
-                               free_exec(t->where_decl);
+                       if (t->global) {
+                               free_value(t->type, var_value(&context, t));
+                               if (t->depth == 0)
+                                       free_exec(t->where_decl);
+                       }
                        free(t);
                }
        }
@@ -1064,7 +1110,7 @@ switch.  Other scopes are "sequential".
 
 When exiting a parallel scope we check if there are any variables that
 were previously pending and are still visible. If there are, then
-there weren't redeclared in the most recent scope, so they cannot be
+they weren't redeclared in the most recent scope, so they cannot be
 merged and must become out-of-scope.  If it is not the first of
 parallel scopes (based on `child_count`), we check that there was a
 previous binding that is still pending-scope.  If there isn't, the new
@@ -1107,6 +1153,7 @@ all pending-scope variables become conditionally scoped.
                v->scope = InScope;
                v->in_scope = c->in_scope;
                c->in_scope = v;
+               ## variable init
                return v;
        }
 
@@ -1138,9 +1185,17 @@ all pending-scope variables become conditionally scoped.
                return v;
        }
 
-       static void var_block_close(struct parse_context *c, enum closetype ct)
+       static void var_block_close(struct parse_context *c, enum closetype ct,
+                                   struct exec *e)
        {
-               /* Close off all variables that are in_scope */
+               /* Close off all variables that are in_scope.
+                * Some variables in c->scope may already be not-in-scope,
+                * such as when a PendingScope variable is hidden by a new
+                * variable with the same name.
+                * So we check for v->name->var != v and drop them.
+                * If we choose to make a variable OutScope, we drop it
+                * immediately too.
+                */
                struct variable *v, **vp, *v2;
 
                scope_pop(c);
@@ -1149,11 +1204,19 @@ all pending-scope variables become conditionally scoped.
                     (v->scope == OutScope || v->name->var != v)
                     ? (*vp =  v->in_scope, 0)
                     : ( vp = &v->in_scope, 0)) {
-                       if (v->name->var != v) {
+                       v->min_depth = c->scope_depth;
+                       if (v->name->var != v)
                                /* This is still in scope, but we haven't just
                                 * closed the scope.
                                 */
                                continue;
+                       v->min_depth = c->scope_depth;
+                       if (v->scope == InScope) {
+                               /* This variable gets cleaned up when 'e' finishes */
+                               variable_unlink_exec(v);
+                               v->cleanup_exec = e;
+                               v->next_free = e->to_free;
+                               e->to_free = v;
                        }
                        switch (ct) {
                        case CloseElse:
@@ -1162,14 +1225,17 @@ all pending-scope variables become conditionally scoped.
                                case InScope:
                                case CondScope:
                                        if (c->scope_stack->child_count == 1)
+                                               /* first among parallel branches */
                                                v->scope = PendingScope;
                                        else if (v->previous &&
                                                 v->previous->scope == PendingScope)
+                                               /* all previous branches used name */
                                                v->scope = PendingScope;
-                                       else if (v->type == Tlabel)     // UNTESTED
+                                       else if (v->type == Tlabel)
+                                               /* Labels remain pending even when not used */
                                                v->scope = PendingScope;        // UNTESTED
-                                       else if (v->name->var == v)     // UNTESTED
-                                               v->scope = OutScope;    // UNTESTED
+                                       else
+                                               v->scope = OutScope;
                                        if (ct == CloseElse) {
                                                /* All Pending variables with this name
                                                 * are now Conditional */
@@ -1180,13 +1246,16 @@ all pending-scope variables become conditionally scoped.
                                        }
                                        break;
                                case PendingScope:
-                                       for (v2 = v;
-                                            v2 && v2->scope == PendingScope;
-                                            v2 = v2->previous)
-                                               if (v2->type != Tlabel)
-                                                       v2->scope = OutScope;
-                                       break;
-                               case OutScope: break;   // UNTESTED
+                                       /* Not possible as it would require
+                                        * parallel scope to be nested immediately
+                                        * in a parallel scope, and that never
+                                        * happens.
+                                        */                     // NOTEST
+                               case OutScope:
+                                       /* Not possible as we already tested for
+                                        * OutScope
+                                        */
+                                       abort();                // NOTEST
                                }
                                break;
                        case CloseSequential:
@@ -1205,10 +1274,9 @@ all pending-scope variables become conditionally scoped.
                                        for (v2 = v;
                                             v2 && v2->scope == PendingScope;
                                             v2 = v2->previous)
-                                               if (v2->type == Tlabel) {
+                                               if (v2->type == Tlabel)
                                                        v2->scope = CondScope;
-                                                       v2->min_depth = c->scope_depth;
-                                               } else
+                                               else
                                                        v2->scope = OutScope;
                                        break;
                                case CondScope:
@@ -1232,9 +1300,15 @@ the frame needs to be reallocated as it grows so it can store those
 values.  The local frame doesn't get values until the interpreted phase
 is started, so there is no need to allocate until the size is known.
 
+We initialize the `frame_pos` to an impossible value, so that we can
+tell if it was set or not later.
+
 ###### variable fields
-               short frame_pos;
-               short global;
+       short frame_pos;
+       short global;
+
+###### variable init
+       v->frame_pos = -1;
 
 ###### parse context
 
@@ -1248,7 +1322,7 @@ is started, so there is no need to allocate until the size is known.
        {
                if (!v->global) {
                        if (!c->local || !v->type)
-                               return NULL;
+                               return NULL;                    // NOTEST
                        if (v->frame_pos + v->type->size > c->local_size) {
                                printf("INVALID frame_pos\n");  // NOTEST
                                exit(2);                        // NOTEST
@@ -1364,6 +1438,7 @@ from the `exec_types` enum.
        struct exec {
                enum exec_types type;
                int line, column;
+               ## exec fields
        };
        struct binode {
                struct exec;
@@ -1461,12 +1536,23 @@ also want to know what sort of bracketing to use.
        static void print_exec(struct exec *e, int indent, int bracket)
        {
                if (!e)
-                       return;         // NOTEST
+                       return;
                switch (e->type) {
                case Xbinode:
                        print_binode(cast(binode, e), indent, bracket); break;
                ## print exec cases
                }
+               if (e->to_free) {
+                       struct variable *v;
+                       do_indent(indent, "/* FREE");
+                       for (v = e->to_free; v; v = v->next_free) {
+                               printf(" %.*s", v->name->name.len, v->name->name.txt);
+                               if (v->frame_pos >= 0)
+                                       printf("(%d+%d)", v->frame_pos,
+                                              v->type ? v->type->size:0);
+                       }
+                       printf(" */\n");
+               }
        }
 
 ###### forward decls
@@ -1588,9 +1674,9 @@ in `rval`.
 
                rvtype = ret.type = Tnone;
                if (!e) {
-                       ret.lval = lrv; // UNTESTED
-                       ret.rval = rv;  // UNTESTED
-                       return ret;     // UNTESTED
+                       ret.lval = lrv;
+                       ret.rval = rv;
+                       return ret;
                }
 
                switch(e->type) {
@@ -1612,6 +1698,7 @@ in `rval`.
                ret.lval = lrv;
                ret.rval = rv;
                ret.type = rvtype;
+               ## interp exec cleanup
                return ret;
        }
 
@@ -2270,6 +2357,9 @@ or as an indented list of one parameter per line
        do
                code block
 
+For constructing these lists we use a `List` binode, which will be
+further detailed when Expression Lists are introduced.
+
 ###### Binode types
        Func, List,
 
@@ -2283,7 +2373,7 @@ or as an indented list of one parameter per line
                        $0->op = Func;
                        $0->left = reorder_bilist($<Ar);
                        $0->right = $<Bl;
-                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential, $0);
                        if (c->scope_stack && !c->parse_error) abort();
                }$
                | func main IN OpenScope OptNL Args OUT OptNL do Block Newlines ${
@@ -2291,7 +2381,7 @@ or as an indented list of one parameter per line
                        $0->op = Func;
                        $0->left = reorder_bilist($<Ar);
                        $0->right = $<Bl;
-                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential, $0);
                        if (c->scope_stack && !c->parse_error) abort();
                }$
                | func main NEWLINE OpenScope OptNL do Block Newlines ${
@@ -2299,7 +2389,7 @@ or as an indented list of one parameter per line
                        $0->op = Func;
                        $0->left = NULL;
                        $0->right = $<Bl;
-                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential, $0);
                        if (c->scope_stack && !c->parse_error) abort();
                }$
 
@@ -2733,6 +2823,57 @@ there.
                }
                break;
 
+### Expression list
+
+We take a brief detour, now that we have expressions, to describe lists
+of expressions.  These will be needed for function parameters and
+possibly other situations.  They seem generic enough to introduce here
+to be used elsewhere.
+
+And ExpressionList will use the `List` type of `binode`, building up at
+the end.  And place where they are used will probably call
+`reorder_bilist()` to get a more normal first/next arrangement.
+
+###### declare terminals
+       $TERM ,
+
+`List` execs have no implicit semantics, so they are never propagated or
+interpreted.  The can be printed as a comma separate list, which is how
+they are parsed.  Note they are also used for function formal parameter
+lists.  In that case a separate function is used to print them.
+
+###### print binode cases
+       case List:
+               while (b) {
+                       printf(" ");
+                       print_exec(b->left, -1, bracket);
+                       if (b->right)
+                               printf(",");
+                       b = cast(binode, b->right);
+               }
+               break;
+
+###### propagate binode cases
+       case List: abort(); // NOTEST
+###### interp binode cases
+       case List: abort(); // NOTEST
+
+###### Grammar
+
+       $*binode
+       ExpressionList -> ExpressionList , Expression ${
+                       $0 = new(binode);
+                       $0->op = List;
+                       $0->left = $<1;
+                       $0->right = $<3;
+               }$
+               | Expression ${
+                       $0 = new(binode);
+                       $0->op = List;
+                       $0->left = NULL;
+                       $0->right = $<1;
+               }$
+
 ### Expressions: Boolean
 
 The next class of expressions to use the `binode` will be Boolean
@@ -3430,62 +3571,47 @@ is in-place.
 expressions and prints the values separated by spaces and terminated
 by a newline.  No control of formatting is possible.
 
-`print` faces the same list-ordering issue as blocks, and uses the
-same solution.
+`print` uses `ExpressionList` to collect the expressions and stores them
+on the left side of a `Print` binode unlessthere is a trailing comma
+when the list is stored on the `right` side and no trailing newline is
+printed.
 
 ###### Binode types
        Print,
 
 ##### expr precedence
-       $TERM print ,
+       $TERM print
 
 ###### SimpleStatement Grammar
 
        | print ExpressionList ${
-               $0 = reorder_bilist($<2);
-       }$
-       | print ExpressionList , ${
                $0 = new(binode);
                $0->op = Print;
                $0->right = NULL;
-               $0->left = $<2;
-               $0 = reorder_bilist($0);
+               $0->left = reorder_bilist($<EL);
        }$
+       | print ExpressionList , ${ {
+               $0 = new(binode);
+               $0->op = Print;
+               $0->right = reorder_bilist($<EL);
+               $0->left = NULL;
+       } }$
        | print ${
                $0 = new(binode);
                $0->op = Print;
+               $0->left = NULL;
                $0->right = NULL;
        }$
 
-###### Grammar
-
-       $*binode
-       ExpressionList -> ExpressionList , Expression ${
-               $0 = new(binode);
-               $0->op = Print;
-               $0->left = $<1;
-               $0->right = $<3;
-               }$
-               | Expression ${
-                       $0 = new(binode);
-                       $0->op = Print;
-                       $0->left = NULL;
-                       $0->right = $<1;
-               }$
-
 ###### print binode cases
 
        case Print:
                do_indent(indent, "print");
-               while (b) {
-                       if (b->left) {
-                               printf(" ");
-                               print_exec(b->left, -1, bracket);
-                               if (b->right)
-                                       printf(",");
-                       }
-                       b = cast(binode, b->right);
-               }
+               if (b->right) {
+                       print_exec(b->right, -1, bracket);
+                       printf(",");
+               } else
+                       print_exec(b->left, -1, bracket);
                if (indent >= 0)
                        printf("\n");
                break;
@@ -3494,30 +3620,33 @@ same solution.
 
        case Print:
                /* don't care but all must be consistent */
-               propagate_types(b->left, c, ok, NULL, Rnolabel);
-               propagate_types(b->right, c, ok, NULL, Rnolabel);
+               if (b->left)
+                       b = cast(binode, b->left);
+               else
+                       b = cast(binode, b->right);
+               while (b) {
+                       propagate_types(b->left, c, ok, NULL, Rnolabel);
+                       b = cast(binode, b->right);
+               }
                break;
 
 ###### interp binode cases
 
        case Print:
        {
-               char sep = 0;
-               int eol = 1;
-               for ( ; b; b = cast(binode, b->right))
-                       if (b->left) {
-                               if (sep)
-                                       putchar(sep);
-                               left = interp_exec(c, b->left, &ltype);
-                               print_value(ltype, &left);
-                               free_value(ltype, &left);
-                               if (b->right)
-                                       sep = ' ';
-                       } else if (sep)
-                               eol = 0;
-               ltype = Tnone;
-               if (eol)
+               struct binode *b2 = cast(binode, b->left);
+               if (!b2)
+                       b2 = cast(binode, b->right);
+               for (; b2; b2 = cast(binode, b2->right)) {
+                       left = interp_exec(c, b2->left, &ltype);
+                       print_value(ltype, &left);
+                       free_value(ltype, &left);
+                       if (b2->right)
+                               putchar(' ');
+               }
+               if (b->right == NULL)
                        printf("\n");
+               ltype = Tnone;
                break;
        }
 
@@ -3582,19 +3711,17 @@ it is declared, and error will be raised as the name is created as
                do_indent(indent, "");
                print_exec(b->left, indent, bracket);
                if (cast(var, b->left)->var->constant) {
+                       printf("::");
                        if (v->where_decl == v->where_set) {
-                               printf("::");
                                type_print(v->type, stdout);
                                printf(" ");
-                       } else
-                               printf(" ::");
+                       }
                } else {
+                       printf(":");
                        if (v->where_decl == v->where_set) {
-                               printf(":");
                                type_print(v->type, stdout);
                                printf(" ");
-                       } else
-                               printf(" :");
+                       }
                }
                if (b->right) {
                        printf("= ");
@@ -3654,7 +3781,6 @@ it is declared, and error will be raised as the name is created as
                struct value *val;
                v = v->merged;
                val = var_value(c, v);
-               free_value(v->type, val);
                if (v->type->prepare_type)
                        v->type->prepare_type(c, v->type, 0);
                if (b->right) {
@@ -3781,7 +3907,15 @@ the type of the `whilepart` code block is the reason for the
 `Rboolok` flag which is passed to `propagate_types()`.
 
 The `cond_statement` cannot fit into a `binode` so a new `exec` is
-defined.
+defined.  As there are two scopes which cover multiple parts - one for
+the whole statement and one for "while" and "do" - and as we will use
+the 'struct exec' to track scopes, we actually need two new types of
+exec.  One is a `binode` for the looping part, the rest is the
+`cond_statement`.  The `cond_statement` will use an auxilliary `struct
+casepart` to track a list of case parts.
+
+###### Binode types
+       Loop
 
 ###### exec type
        Xcond_statement,
@@ -3794,7 +3928,8 @@ defined.
        };
        struct cond_statement {
                struct exec;
-               struct exec *forpart, *condpart, *dopart, *thenpart, *elsepart;
+               struct exec *forpart, *condpart, *thenpart, *elsepart;
+               struct binode *looppart;
                struct casepart *casepart;
        };
 
@@ -3818,7 +3953,7 @@ defined.
                        return;
                free_exec(s->forpart);
                free_exec(s->condpart);
-               free_exec(s->dopart);
+               free_exec(s->looppart);
                free_exec(s->thenpart);
                free_exec(s->elsepart);
                free_casepart(s->casepart);
@@ -3851,42 +3986,39 @@ defined.
                        $0 = $<CS;
                        $0->forpart = $<FP;
                        $0->thenpart = $<TP;
-                       $0->condpart = $WP.condpart; $WP.condpart = NULL;
-                       $0->dopart = $WP.dopart; $WP.dopart = NULL;
-                       var_block_close(c, CloseSequential);
+                       $0->looppart = $<WP;
+                       var_block_close(c, CloseSequential, $0);
                        }$
                | ForPart OptNL WhilePart CondSuffix ${
                        $0 = $<CS;
                        $0->forpart = $<FP;
-                       $0->condpart = $WP.condpart; $WP.condpart = NULL;
-                       $0->dopart = $WP.dopart; $WP.dopart = NULL;
-                       var_block_close(c, CloseSequential);
+                       $0->looppart = $<WP;
+                       var_block_close(c, CloseSequential, $0);
                        }$
                | WhilePart CondSuffix ${
                        $0 = $<CS;
-                       $0->condpart = $WP.condpart; $WP.condpart = NULL;
-                       $0->dopart = $WP.dopart; $WP.dopart = NULL;
+                       $0->looppart = $<WP;
                        }$
                | SwitchPart OptNL CasePart CondSuffix ${
                        $0 = $<CS;
                        $0->condpart = $<SP;
                        $CP->next = $0->casepart;
                        $0->casepart = $<CP;
-                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential, $0);
                        }$
                | SwitchPart : IN OptNL CasePart CondSuffix OUT Newlines ${
                        $0 = $<CS;
                        $0->condpart = $<SP;
                        $CP->next = $0->casepart;
                        $0->casepart = $<CP;
-                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential, $0);
                        }$
                | IfPart IfSuffix ${
                        $0 = $<IS;
                        $0->condpart = $IP.condpart; $IP.condpart = NULL;
                        $0->thenpart = $IP.thenpart; $IP.thenpart = NULL;
                        // This is where we close an "if" statement
-                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential, $0);
                        }$
 
        CondSuffix -> IfSuffix ${
@@ -3910,12 +4042,12 @@ defined.
        ElsePart -> else OpenBlock Newlines ${
                        $0 = new(cond_statement);
                        $0->elsepart = $<OB;
-                       var_block_close(c, CloseElse);
+                       var_block_close(c, CloseElse, $0->elsepart);
                }$
                | else OpenScope CondStatement ${
                        $0 = new(cond_statement);
                        $0->elsepart = $<CS;
-                       var_block_close(c, CloseElse);
+                       var_block_close(c, CloseElse, $0->elsepart);
                }$
 
        $*casepart
@@ -3923,7 +4055,7 @@ defined.
                        $0 = calloc(1,sizeof(struct casepart));
                        $0->value = $<Ex;
                        $0->action = $<Bl;
-                       var_block_close(c, CloseParallel);
+                       var_block_close(c, CloseParallel, $0->action);
                }$
 
        $*exec
@@ -3934,36 +4066,43 @@ defined.
 
        ThenPart -> then OpenBlock ${
                        $0 = $<OB;
-                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential, $0);
                }$
 
-       $cond_statement
+       $*binode
        // This scope is closed in CondStatement
-       WhilePart -> while UseBlock OptNL do Block ${
-                       $0.condpart = $<UB;
-                       $0.dopart = $<Bl;
-                       var_block_close(c, CloseSequential);
+       WhilePart -> while UseBlock OptNL do OpenBlock ${
+                       $0 = new(binode);
+                       $0->op = Loop;
+                       $0->left = $<UB;
+                       $0->right = $<OB;
+                       var_block_close(c, CloseSequential, $0->right);
+                       var_block_close(c, CloseSequential, $0);
                }$
-               | while OpenScope Expression ColonBlock ${
-                       $0.condpart = $<Exp;
-                       $0.dopart = $<Bl;
-                       var_block_close(c, CloseSequential);
+               | while OpenScope Expression OpenScope ColonBlock ${
+                       $0 = new(binode);
+                       $0->op = Loop;
+                       $0->left = $<Exp;
+                       $0->right = $<CB;
+                       var_block_close(c, CloseSequential, $0->right);
+                       var_block_close(c, CloseSequential, $0);
                }$
 
+       $cond_statement
        IfPart -> if UseBlock OptNL then OpenBlock ${
                        $0.condpart = $<UB;
                        $0.thenpart = $<OB;
-                       var_block_close(c, CloseParallel);
+                       var_block_close(c, CloseParallel, $0.thenpart);
                }$
                | if OpenScope Expression OpenScope ColonBlock ${
                        $0.condpart = $<Ex;
                        $0.thenpart = $<CB;
-                       var_block_close(c, CloseParallel);
+                       var_block_close(c, CloseParallel, $0.thenpart);
                }$
                | if OpenScope Expression OpenScope OptNL then Block ${
                        $0.condpart = $<Ex;
                        $0.thenpart = $<Bl;
-                       var_block_close(c, CloseParallel);
+                       var_block_close(c, CloseParallel, $0.thenpart);
                }$
 
        $*exec
@@ -3975,6 +4114,35 @@ defined.
                        $0 = $<Bl;
                }$
 
+###### print binode cases
+       case Loop:
+               if (b->left && b->left->type == Xbinode &&
+                   cast(binode, b->left)->op == Block) {
+                       if (bracket)
+                               do_indent(indent, "while {\n");
+                       else
+                               do_indent(indent, "while\n");
+                       print_exec(b->left, indent+1, bracket);
+                       if (bracket)
+                               do_indent(indent, "} do {\n");
+                       else
+                               do_indent(indent, "do\n");
+                       print_exec(b->right, indent+1, bracket);
+                       if (bracket)
+                               do_indent(indent, "}\n");
+               } else {
+                       do_indent(indent, "while ");
+                       print_exec(b->left, 0, bracket);
+                       if (bracket)
+                               printf(" {\n");
+                       else
+                               printf(":\n");
+                       print_exec(b->right, indent+1, bracket);
+                       if (bracket)
+                               do_indent(indent, "}\n");
+               }
+               break;
+
 ###### print exec cases
 
        case Xcond_statement:
@@ -3994,33 +4162,8 @@ defined.
                        }
                        if (bracket) do_indent(indent, "}\n");
                }
-               if (cs->dopart) {
-                       // a loop
-                       if (cs->condpart && cs->condpart->type == Xbinode &&
-                           cast(binode, cs->condpart)->op == Block) {
-                               if (bracket)
-                                       do_indent(indent, "while {\n");
-                               else
-                                       do_indent(indent, "while\n");
-                               print_exec(cs->condpart, indent+1, bracket);
-                               if (bracket)
-                                       do_indent(indent, "} do {\n");
-                               else
-                                       do_indent(indent, "do\n");
-                               print_exec(cs->dopart, indent+1, bracket);
-                               if (bracket)
-                                       do_indent(indent, "}\n");
-                       } else {
-                               do_indent(indent, "while ");
-                               print_exec(cs->condpart, 0, bracket);
-                               if (bracket)
-                                       printf(" {\n");
-                               else
-                                       printf(":\n");
-                               print_exec(cs->dopart, indent+1, bracket);
-                               if (bracket)
-                                       do_indent(indent, "}\n");
-                       }
+               if (cs->looppart) {
+                       print_exec(cs->looppart, indent, bracket);
                } else {
                        // a condition
                        if (cs->casepart)
@@ -4079,11 +4222,18 @@ defined.
                break;
        }
 
+###### propagate binode cases
+       case Loop:
+               t = propagate_types(b->right, c, ok, Tnone, 0);
+               if (!type_compat(Tnone, t, 0))
+                       *ok = 0;        // UNTESTED
+               return propagate_types(b->left, c, ok, type, rules);
+
 ###### propagate exec cases
        case Xcond_statement:
        {
-               // forpart and dopart must return Tnone
-               // thenpart must return Tnone if there is a dopart,
+               // forpart and looppart->right must return Tnone
+               // thenpart must return Tnone if there is a loopart,
                // otherwise it is like elsepart.
                // condpart must:
                //    be bool if there is no casepart
@@ -4098,17 +4248,16 @@ defined.
                t = propagate_types(cs->forpart, c, ok, Tnone, 0);
                if (!type_compat(Tnone, t, 0))
                        *ok = 0;        // UNTESTED
-               t = propagate_types(cs->dopart, c, ok, Tnone, 0);
-               if (!type_compat(Tnone, t, 0))
-                       *ok = 0;        // UNTESTED
-               if (cs->dopart) {
+
+               if (cs->looppart) {
                        t = propagate_types(cs->thenpart, c, ok, Tnone, 0);
                        if (!type_compat(Tnone, t, 0))
                                *ok = 0;        // UNTESTED
                }
-               if (cs->casepart == NULL)
+               if (cs->casepart == NULL) {
                        propagate_types(cs->condpart, c, ok, Tbool, 0);
-               else {
+                       propagate_types(cs->looppart, c, ok, Tbool, 0);
+               } else {
                        /* Condpart must match case values, with bool permitted */
                        t = NULL;
                        for (cp = cs->casepart;
@@ -4116,15 +4265,18 @@ defined.
                                t = propagate_types(cp->value, c, ok, NULL, 0);
                        if (!t && cs->condpart)
                                t = propagate_types(cs->condpart, c, ok, NULL, Rboolok);        // UNTESTED
+                       if (!t && cs->looppart)
+                               t = propagate_types(cs->looppart, c, ok, NULL, Rboolok);        // UNTESTED
                        // Now we have a type (I hope) push it down
                        if (t) {
                                for (cp = cs->casepart; cp; cp = cp->next)
                                        propagate_types(cp->value, c, ok, t, 0);
                                propagate_types(cs->condpart, c, ok, t, Rboolok);
+                               propagate_types(cs->looppart, c, ok, t, Rboolok);
                        }
                }
                // (if)then, else, and case parts must return expected type.
-               if (!cs->dopart && !type)
+               if (!cs->looppart && !type)
                        type = propagate_types(cs->thenpart, c, ok, NULL, rules);
                if (!type)
                        type = propagate_types(cs->elsepart, c, ok, NULL, rules);
@@ -4133,7 +4285,7 @@ defined.
                     cp = cp->next)     // UNTESTED
                        type = propagate_types(cp->action, c, ok, NULL, rules); // UNTESTED
                if (type) {
-                       if (!cs->dopart)
+                       if (!cs->looppart)
                                propagate_types(cs->thenpart, c, ok, type, rules);
                        propagate_types(cs->elsepart, c, ok, type, rules);
                        for (cp = cs->casepart; cp ; cp = cp->next)
@@ -4143,6 +4295,16 @@ defined.
                        return NULL;
        }
 
+###### interp binode cases
+       case Loop:
+               // This just performs one iterration of the loop
+               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
+                       interp_exec(c, b->right, NULL);
+               break;
+
 ###### interp exec cases
        case Xcond_statement:
        {
@@ -4153,27 +4315,20 @@ defined.
 
                if (cs->forpart)
                        interp_exec(c, cs->forpart, NULL);
-               do {
-                       if (cs->condpart)
-                               cnd = interp_exec(c, cs->condpart, &cndtype);
-                       else
-                               cndtype = Tnone;        // UNTESTED
-                       if (!(cndtype == Tnone ||
-                             (cndtype == Tbool && cnd.bool != 0)))
-                               break;
-                       // cnd is Tnone or Tbool, doesn't need to be freed
-                       if (cs->dopart)
-                               interp_exec(c, cs->dopart, NULL);
-
-                       if (cs->thenpart) {
+               if (cs->looppart) {
+                       while ((cnd = interp_exec(c, cs->looppart, &cndtype)),
+                              cndtype == Tnone || (cndtype == Tbool && cnd.bool != 0))
+                               interp_exec(c, cs->thenpart, NULL);
+               } else {
+                       cnd = interp_exec(c, cs->condpart, &cndtype);
+                       if ((cndtype == Tnone ||
+                           (cndtype == Tbool && cnd.bool != 0))) {
+                               // cnd is Tnone or Tbool, doesn't need to be freed
                                rv = interp_exec(c, cs->thenpart, &rvtype);
-                               if (rvtype != Tnone || !cs->dopart)
-                                       goto Xcond_done;
-                               free_value(rvtype, &rv);
-                               rvtype = Tnone;
+                               // skip else (and cases)
+                               goto Xcond_done;
                        }
-               } while (cs->dopart);
-
+               }
                for (cp = cs->casepart; cp; cp = cp->next) {
                        v = interp_exec(c, cp->value, &vtype);
                        if (value_cmp(cndtype, vtype, &v, &cnd) == 0) {
@@ -4367,7 +4522,6 @@ analysis is a bit more interesting at this level.
 
 ###### print binode cases
        case Func:
-       case List:
                do_indent(indent, "func main(");
                for (b2 = cast(binode, b->left); b2; b2 = cast(binode, b2->right)) {
                        struct variable *v = cast(var, b2->left)->var;
@@ -4386,7 +4540,6 @@ analysis is a bit more interesting at this level.
                break;
 
 ###### propagate binode cases
-       case List:
        case Func: abort();             // NOTEST
 
 ###### core functions
@@ -4474,13 +4627,14 @@ analysis is a bit more interesting at this level.
                        }
                        al = cast(binode, al->right);
                }
-               v = interp_exec(c, p->right, &vtype);
+               v = interp_exec(c, p, &vtype);
                free_value(vtype, &v);
        }
 
 ###### interp binode cases
-       case List:
-       case Func: abort();     // NOTEST
+       case Func:
+               rv = interp_exec(c, b->right, &rvtype);
+               break;
 
 ## And now to test it out.