]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: allow list of declarations as top level structure
[ocean] / csrc / oceani.mdc
index 9ab456a629badad65780a72cc1d10e366cd29ced..48ce0587b4491fa2adc4e7f054d13a9cce6bc745 100644 (file)
@@ -114,6 +114,7 @@ structures can be used.
                struct token_config config;
                char *file_name;
                int parse_error;
+               struct exec *prog;
                ## parse context
        };
 
@@ -179,7 +180,6 @@ structures can be used.
                        },
                };
                int doprint=0, dotrace=0, doexec=1, brackets=0;
-               struct exec **prog;
                int opt;
                while ((opt = getopt_long(argc, argv, options, long_options, NULL))
                       != -1) {
@@ -223,32 +223,31 @@ structures can be used.
                                        break;
                        }
                        if (ss)
-                               prog = parse_oceani(ss->code, &context.config,
-                                                   dotrace ? stderr : NULL);
+                               parse_oceani(ss->code, &context.config,
+                                            dotrace ? stderr : NULL);
                        else {
                                fprintf(stderr, "oceani: cannot find section %s\n",
                                        section);
                                exit(1);
                        }
                } else
-                       prog = parse_oceani(s->code, &context.config,
-                                   dotrace ? stderr : NULL);
-               if (!prog) {
-                       fprintf(stderr, "oceani: fatal parser error.\n");
+                       parse_oceani(s->code, &context.config,
+                                    dotrace ? stderr : NULL);
+               if (!context.prog) {
+                       fprintf(stderr, "oceani: no program found.\n");
                        context.parse_error = 1;
                }
-               if (prog && doprint)
-                       print_exec(*prog, 0, brackets);
-               if (prog && doexec && !context.parse_error) {
-                       if (!analyse_prog(*prog, &context)) {
+               if (context.prog && doprint)
+                       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);
                        }
-                       interp_prog(*prog, argv+optind+1);
+                       interp_prog(context.prog, argv+optind+1);
                }
-               if (prog) {
-                       free_exec(*prog);
-                       free(prog);
+               if (context.prog) {
+                       free_exec(context.prog);
                }
                while (s) {
                        struct section *t = s->next;
@@ -375,8 +374,8 @@ context so indicate that parsing failed.
                        }
                        fmt++;
                        switch (*fmt) {
-                       case '%': fputc(*fmt, stderr); break;
-                       default: fputc('?', stderr); break;
+                       case '%': fputc(*fmt, stderr); break;   // NOTEST
+                       default: fputc('?', stderr); break;     // NOTEST
                        case '1':
                                type_print(t1, stderr);
                                break;
@@ -523,7 +522,7 @@ which are often passed around by value.
                else if (type->print_type)
                        type->print_type(type, f);
                else
-                       fputs("*invalid*type*", f);
+                       fputs("*invalid*type*", f);     // NOTEST
        }
 
        static struct value val_prepare(struct type *type)
@@ -567,7 +566,7 @@ which are often passed around by value.
                if (v.type && v.type->print)
                        v.type->print(v);
                else
-                       printf("*Unknown*");
+                       printf("*Unknown*");            // NOTEST
        }
 
        static struct value parse_value(struct type *type, char *arg)
@@ -576,8 +575,8 @@ which are often passed around by value.
 
                if (type && type->parse)
                        return type->parse(type, arg);
-               rv.type = NULL;
-               return rv;
+               rv.type = NULL;                         // NOTEST
+               return rv;                              // NOTEST
        }
 
 ###### forward decls
@@ -685,8 +684,8 @@ to parse each type from a string.
 
                rv.type = type;
                switch(type->vtype) {
-               case Vnone:
-                       break;
+               case Vnone:             // NOTEST
+                       break;          // NOTEST
                case Vnum:
                        mpq_init(rv.num); break;
                case Vstr:
@@ -696,9 +695,9 @@ to parse each type from a string.
                case Vbool:
                        rv.bool = 0;
                        break;
-               case Vlabel:
-                       rv.label = NULL;
-                       break;
+               case Vlabel:                    // NOTEST
+                       rv.label = NULL;        // NOTEST
+                       break;                  // NOTEST
                }
                return rv;
        }
@@ -708,8 +707,8 @@ to parse each type from a string.
                struct value rv;
                rv.type = v.type;
                switch (rv.type->vtype) {
-               case Vnone:
-                       break;
+               case Vnone:             // NOTEST
+                       break;          // NOTEST
                case Vlabel:
                        rv.label = v.label;
                        break;
@@ -733,13 +732,13 @@ to parse each type from a string.
        {
                int cmp;
                if (left.type != right.type)
-                       return left.type - right.type;
+                       return left.type - right.type;  // NOTEST
                switch (left.type->vtype) {
                case Vlabel: cmp = left.label == right.label ? 0 : 1; break;
                case Vnum: cmp = mpq_cmp(left.num, right.num); break;
                case Vstr: cmp = text_cmp(left.str, right.str); break;
                case Vbool: cmp = left.bool - right.bool; break;
-               case Vnone: cmp = 0;
+               case Vnone: cmp = 0;                    // NOTEST
                }
                return cmp;
        }
@@ -747,10 +746,10 @@ to parse each type from a string.
        static void _print_value(struct value v)
        {
                switch (v.type->vtype) {
-               case Vnone:
-                       printf("*no-value*"); break;
-               case Vlabel:
-                       printf("*label-%p*", v.label); break;
+               case Vnone:                             // NOTEST
+                       printf("*no-value*"); break;    // NOTEST
+               case Vlabel:                            // NOTEST
+                       printf("*label-%p*", v.label); break; // NOTEST
                case Vstr:
                        printf("%.*s", v.str.len, v.str.txt); break;
                case Vbool:
@@ -776,10 +775,10 @@ to parse each type from a string.
 
                val.type = type;
                switch(type->vtype) {
-               case Vlabel:
-               case Vnone:
-                       val.type = NULL;
-                       break;
+               case Vlabel:                            // NOTEST
+               case Vnone:                             // NOTEST
+                       val.type = NULL;                // NOTEST
+                       break;                          // NOTEST
                case Vstr:
                        val.str.len = strlen(arg);
                        val.str.txt = malloc(val.str.len);
@@ -1316,6 +1315,8 @@ subclasses, and to access these we need to be able to `cast` the
 
        static int __fput_loc(struct exec *loc, FILE *f)
        {
+               if (!loc)
+                       return 0;               // NOTEST
                if (loc->line >= 0) {
                        fprintf(f, "%d:%d: ", loc->line, loc->column);
                        return 1;
@@ -1328,7 +1329,7 @@ subclasses, and to access these we need to be able to `cast` the
        static void fput_loc(struct exec *loc, FILE *f)
        {
                if (!__fput_loc(loc, f))
-                       fprintf(f, "??:??: ");
+                       fprintf(f, "??:??: ");  // NOTEST
        }
 
 Each different type of `exec` node needs a number of functions
@@ -1399,7 +1400,7 @@ also want to know what sort of bracketing to use.
        static void print_exec(struct exec *e, int indent, int bracket)
        {
                if (!e)
-                       return;
+                       return;         // NOTEST
                switch (e->type) {
                case Xbinode:
                        print_binode(cast(binode, e), indent, bracket); break;
@@ -1523,6 +1524,237 @@ Each `exec` can return a value, which may be `Tnone` but must be non-NULL;
                return ret;
        }
 
+### Complex types
+
+Now that we have the shape of the interpreter in place we can add some
+complex types and connected them in to the data structures and the
+different phases of parse, analyse, print, interpret.
+
+For now, just arrays.
+
+#### Arrays
+
+Arrays can be declared by giving a size and a type, as `[size]type' so
+`freq:[26]number` declares `freq` to be an array of 26 numbers.  The
+size can be an arbitrary expression which is evaluated when the name
+comes into scope.
+
+Arrays cannot be assigned.  When pointers are introduced we will also
+introduce array slices which can refer to part or all of an array -
+the assignment syntax will create a slice.  For now, an array can only
+ever be referenced by the name it is declared with.  It is likely that
+a "`copy`" primitive will eventually be define which can be used to
+make a copy of an array with controllable depth.
+
+###### type union fields
+
+       struct {
+               int size;
+               struct variable *vsize;
+               struct type *member;
+       } array;
+
+###### value union fields
+       struct {
+               struct value *elmnts;
+       } array;
+
+###### value functions
+
+       static struct value array_prepare(struct type *type)
+       {
+               struct value ret;
+
+               ret.type = type;
+               ret.array.elmnts = NULL;
+               return ret;
+       }
+
+       static struct value array_init(struct type *type)
+       {
+               struct value ret;
+               int i;
+
+               ret.type = type;
+               if (type->array.vsize) {
+                       mpz_t q;
+                       mpz_init(q);
+                       mpz_tdiv_q(q, mpq_numref(type->array.vsize->val.num),
+                                  mpq_denref(type->array.vsize->val.num));
+                       type->array.size = mpz_get_si(q);
+                       mpz_clear(q);
+               }
+               ret.array.elmnts = calloc(type->array.size,
+                                         sizeof(ret.array.elmnts[0]));
+               for (i = 0; ret.array.elmnts && i < type->array.size; i++)
+                       ret.array.elmnts[i] = val_init(type->array.member);
+               return ret;
+       }
+
+       static void array_free(struct value val)
+       {
+               int i;
+
+               if (val.array.elmnts)
+                       for (i = 0; i < val.type->array.size; i++)
+                               free_value(val.array.elmnts[i]);
+               free(val.array.elmnts);
+       }
+
+       static int array_compat(struct type *require, struct type *have)
+       {
+               if (have->compat != require->compat)
+                       return 0;
+               /* Both are arrays, so we can look at details */
+               if (!type_compat(require->array.member, have->array.member, 0))
+                       return 0;
+               if (require->array.vsize == NULL && have->array.vsize == NULL)
+                       return require->array.size == have->array.size;
+
+               return require->array.vsize == have->array.vsize;
+       }
+
+       static void array_print_type(struct type *type, FILE *f)
+       {
+               fputs("[", f);
+               if (type->array.vsize) {
+                       struct binding *b = type->array.vsize->name;
+                       fprintf(f, "%.*s]", b->name.len, b->name.txt);
+               } else
+                       fprintf(f, "%d]", type->array.size);
+               type_print(type->array.member, f);
+       }
+
+       static struct type array_prototype = {
+               .prepare = array_prepare,
+               .init = array_init,
+               .print_type = array_print_type,
+               .compat = array_compat,
+               .free = array_free,
+       };
+
+###### type grammar
+
+       | [ NUMBER ] Type ${
+               $0 = calloc(1, sizeof(struct type));
+               *($0) = array_prototype;
+               $0->array.member = $<4;
+               $0->array.vsize = NULL;
+               {
+               struct parse_context *c = config2context(config);
+               char tail[3];
+               mpq_t num;
+               if (number_parse(num, tail, $2.txt) == 0)
+                       tok_err(c, "error: unrecognised number", &$2);
+               else if (tail[0])
+                       tok_err(c, "error: unsupported number suffix", &$2);
+               else {
+                       $0->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",
+                                       &$2);
+                       } else if (mpz_cmp_ui(mpq_numref(num), 1UL << 30) >= 0)
+                               tok_err(c, "error: array size is too large",
+                                       &$2);
+                       mpq_clear(num);
+               }
+               $0->next= c->anon_typelist;
+               c->anon_typelist = $0;
+               }
+       }$
+
+       | [ IDENTIFIER ] Type ${ {
+               struct parse_context *c = config2context(config);
+               struct variable *v = var_ref(c, $2.txt);
+
+               if (!v)
+                       tok_err(config2context(config), "error: name undeclared", &$2);
+               else if (!v->constant)
+                       tok_err(config2context(config), "error: array size must be a constant", &$2);
+
+               $0 = calloc(1, sizeof(struct type));
+               *($0) = array_prototype;
+               $0->array.member = $<4;
+               $0->array.size = 0;
+               $0->array.vsize = v;
+               $0->next= c->anon_typelist;
+               c->anon_typelist = $0;
+       } }$
+
+###### parse context
+
+       struct type *anon_typelist;
+
+###### free context types
+
+       while (context.anon_typelist) {
+               struct type *t = context.anon_typelist;
+
+               context.anon_typelist = t->next;
+               free(t);
+       }
+
+###### Binode types
+       Index,
+
+###### variable grammar
+
+       | Variable [ Expression ] ${ {
+               struct binode *b = new(binode);
+               b->op = Index;
+               b->left = $<1;
+               b->right = $<3;
+               $0 = b;
+       } }$
+
+###### print binode cases
+       case Index:
+               print_exec(b->left, -1, 0);
+               printf("[");
+               print_exec(b->right, -1, 0);
+               printf("]");
+               break;
+
+###### propagate binode cases
+       case Index:
+               /* left must be an array, right must be a number,
+                * result is the member type of the array
+                */
+               propagate_types(b->right, c, ok, Tnum, 0);
+               t = propagate_types(b->left, c, ok, NULL, rules & Rnoconstant);
+               if (!t || t->compat != array_compat) {
+                       type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL);
+                       *ok = 0;
+                       return NULL;
+               } else {
+                       if (!type_compat(type, t->array.member, rules)) {
+                               type_err(c, "error: have %1 but need %2", prog,
+                                        t->array.member, rules, type);
+                               *ok = 0;
+                       }
+                       return t->array.member;
+               }
+               break;
+
+###### interp binode cases
+       case Index: {
+               mpz_t q;
+               long i;
+
+               lleft = linterp_exec(b->left);
+               right = interp_exec(b->right);
+               mpz_init(q);
+               mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
+               i = mpz_get_si(q);
+               mpz_clear(q);
+
+               if (i >= 0 && i < lleft->type->array.size)
+                       lrv = &lleft->array.elmnts[i];
+               else
+                       rv = val_init(lleft->type->array.member);
+               break;
+       }
+
 ## Language elements
 
 Each language element needs to be parsed, printed, analysed,
@@ -1758,6 +1990,7 @@ link to find the primary instance.
                }
                cast(var, $0)->var = v;
        } }$
+       ## variable grammar
 
        $*type
        Type -> IDENTIFIER ${
@@ -1769,6 +2002,7 @@ link to find the primary instance.
                        $0 = Tnone;
                }
        }$
+       ## type grammar
 
 ###### print exec cases
        case Xvar:
@@ -1789,9 +2023,9 @@ link to find the primary instance.
                                struct binding *b = v->var->name;
                                fprintf(stderr, "%.*s", b->name.len, b->name.txt);
                        } else
-                               fputs("???", stderr);
+                               fputs("???", stderr);   // NOTEST
                } else
-                       fputs("NOTVAR", stderr);
+                       fputs("NOTVAR", stderr);        // NOTEST
                break;
 
 ###### propagate exec cases
@@ -1801,9 +2035,9 @@ link to find the primary instance.
                struct var *var = cast(var, prog);
                struct variable *v = var->var;
                if (!v) {
-                       type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone);
-                       *ok = 0;
-                       return Tnone;
+                       type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone); // NOTEST
+                       *ok = 0;                                        // NOTEST
+                       return Tnone;                                   // NOTEST
                }
                if (v->merged)
                        v = v->merged;
@@ -1857,30 +2091,111 @@ link to find the primary instance.
 ###### free exec cases
        case Xvar: free_var(cast(var, e)); break;
 
+### Expressions: Conditional
+
+Our first user of the `binode` will be conditional expressions, which
+is a bit odd as they actually have three components.  That will be
+handled by having 2 binodes for each expression.  The conditional
+expression is the lowest precedence operatior, so it gets to define
+what an "Expression" is.  The next level up is "BoolExpr", which
+comes next.
+
+Conditional expressions are of the form "value `if` condition `else`
+other_value".  There is no associativite with this operator: the
+values and conditions can only be other conditional expressions if
+they are enclosed in parentheses.  Allowing nesting without
+parentheses would be too confusing.
+
+###### Binode types
+       CondExpr,
+
+###### Grammar
+
+       $*exec
+       Expression -> BoolExpr if BoolExpr else BoolExpr ${ {
+                       struct binode *b1 = new(binode);
+                       struct binode *b2 = new(binode);
+                       b1->op = CondExpr;
+                       b1->left = $<3;
+                       b1->right = b2;
+                       b2->op = CondExpr;
+                       b2->left = $<1;
+                       b2->right = $<5;
+                       $0 = b1;
+               } }$
+               | BoolExpr ${ $0 = $<1; }$
+
+###### print binode cases
+
+       case CondExpr:
+               b2 = cast(binode, b->right);
+               print_exec(b2->left, -1, 0);
+               printf(" if ");
+               print_exec(b->left, -1, 0);
+               printf(" else ");
+               print_exec(b2->right, -1, 0);
+               break;
+
+###### propagate binode cases
+
+       case CondExpr: {
+               /* cond must be Tbool, others must match */
+               struct binode *b2 = cast(binode, b->right);
+               struct type *t2;
+
+               propagate_types(b->left, c, ok, Tbool, 0);
+               t = propagate_types(b2->left, c, ok, type, Rnolabel);
+               t2 = propagate_types(b2->right, c, ok, type ?: t, Rnolabel);
+               return t ?: t2;
+       }
+
+###### interp binode cases
+
+       case CondExpr: {
+               struct binode *b2 = cast(binode, b->right);
+               left = interp_exec(b->left);
+               if (left.bool)
+                       rv = interp_exec(b2->left);
+               else
+                       rv = interp_exec(b2->right);
+               }
+               break;
+
 ### Expressions: Boolean
 
-Our first user of the `binode` will be expressions, and particularly
-Boolean expressions.  As I haven't implemented precedence in the
-parser generator yet, we need different names for each precedence
-level used by expressions.  The outer most or lowest level precedence
-are Boolean `or` `and`, and `not` which form an `Expression` out of `BTerm`s
-and `BFact`s.
+The next class of expressions to use the `binode` will be Boolean
+expressions.  As I haven't implemented precedence in the parser
+generator yet, we need different names for each precedence level used
+by expressions.  The outer most or lowest level precedence are
+conditional expressions are Boolean operators which form an `BoolExpr`
+out of `BTerm`s and `BFact`s.  As well as `or` `and`, and `not` we
+have `and then` and `or else` which only evaluate the second operand
+if the result would make a difference.
 
 ###### Binode types
        And,
+       AndThen,
        Or,
+       OrElse,
        Not,
 
 ###### Grammar
 
        $*exec
-       Expression -> Expression or BTerm ${ {
+       BoolExpr -> BoolExpr or BTerm ${ {
                        struct binode *b = new(binode);
                        b->op = Or;
                        b->left = $<1;
                        b->right = $<3;
                        $0 = b;
                } }$
+               | BoolExpr or else BTerm ${ {
+                       struct binode *b = new(binode);
+                       b->op = OrElse;
+                       b->left = $<1;
+                       b->right = $<4;
+                       $0 = b;
+               } }$
                | BTerm ${ $0 = $<1; }$
 
        BTerm -> BTerm and BFact ${ {
@@ -1890,6 +2205,13 @@ and `BFact`s.
                        b->right = $<3;
                        $0 = b;
                } }$
+               | BTerm and then BFact ${ {
+                       struct binode *b = new(binode);
+                       b->op = AndThen;
+                       b->left = $<1;
+                       b->right = $<4;
+                       $0 = b;
+               } }$
                | BFact ${ $0 = $<1; }$
 
        BFact -> not BFact ${ {
@@ -1906,11 +2228,21 @@ and `BFact`s.
                printf(" and ");
                print_exec(b->right, -1, 0);
                break;
+       case AndThen:
+               print_exec(b->left, -1, 0);
+               printf(" and then ");
+               print_exec(b->right, -1, 0);
+               break;
        case Or:
                print_exec(b->left, -1, 0);
                printf(" or ");
                print_exec(b->right, -1, 0);
                break;
+       case OrElse:
+               print_exec(b->left, -1, 0);
+               printf(" or else ");
+               print_exec(b->right, -1, 0);
+               break;
        case Not:
                printf("not ");
                print_exec(b->right, -1, 0);
@@ -1918,7 +2250,9 @@ and `BFact`s.
 
 ###### propagate binode cases
        case And:
+       case AndThen:
        case Or:
+       case OrElse:
        case Not:
                /* both must be Tbool, result is Tbool */
                propagate_types(b->left, c, ok, Tbool, 0);
@@ -1936,11 +2270,21 @@ and `BFact`s.
                right = interp_exec(b->right);
                rv.bool = rv.bool && right.bool;
                break;
+       case AndThen:
+               rv = interp_exec(b->left);
+               if (rv.bool)
+                       rv = interp_exec(b->right);
+               break;
        case Or:
                rv = interp_exec(b->left);
                right = interp_exec(b->right);
                rv.bool = rv.bool || right.bool;
                break;
+       case OrElse:
+               rv = interp_exec(b->left);
+               if (!rv.bool)
+                       rv = interp_exec(b->right);
+               break;
        case Not:
                rv = interp_exec(b->right);
                rv.bool = !rv.bool;
@@ -2012,7 +2356,7 @@ expression operator.
                case GtrEq:  printf(" >= "); break;
                case Eql:    printf(" == "); break;
                case NEql:   printf(" != "); break;
-               default: abort();
+               default: abort();               // NOTEST
                }
                print_exec(b->right, -1, 0);
                break;
@@ -2060,7 +2404,7 @@ expression operator.
                case GtrEq:     rv.bool = cmp >= 0; break;
                case Eql:       rv.bool = cmp == 0; break;
                case NEql:      rv.bool = cmp != 0; break;
-               default: rv.bool = 0; break;
+               default: rv.bool = 0; break;    // NOTEST
                }
                break;
        }
@@ -2149,8 +2493,8 @@ precedence is handled better I might be able to discard this.
                case Divide: fputs(" / ", stdout); break;
                case Rem:    fputs(" % ", stdout); break;
                case Concat: fputs(" ++ ", stdout); break;
-               default: abort();
-               }
+               default: abort();       // NOTEST
+               }                       // NOTEST
                print_exec(b->right, indent, 0);
                break;
        case Absolute:
@@ -2664,7 +3008,7 @@ it is declared, and error will be raised as the name is created as
                        free_value(*lleft);
                        *lleft = right;
                } else
-                       free_value(right);
+                       free_value(right);      // NOTEST
                right.type = NULL;
                break;
 
@@ -3199,12 +3543,44 @@ defined.
                break;
        }
 
+### Top level structure
+
+All the language elements so far can be used in various places.  Now
+it is time to clarify what those places are.
+
+At the top level of a file there will be a number of declarations.
+Many of the things that can be declared haven't been described yet,
+such as functions, procedures, imports, named types, and probably
+more.
+For now there are two sorts of things that can appear at the top
+level.  They are predefined constants and the main program.  While the
+syntax will allow the main program to appear multiple times, that will
+trigger an error if it is actually attempted.
+
+The various declarations do not return anything.  They store the
+various declarations in the parse context.
+
+###### Parser: grammar
+
+       $void
+       Ocean -> DeclarationList
+
+       DeclarationList -> Declaration
+               | DeclarationList Declaration
+
+       Declaration -> DeclareConstant
+               | DeclareProgram
+               | NEWLINE
+
+       ## top level grammar
+
 ### Finally the whole program.
 
 Somewhat reminiscent of Pascal a (current) Ocean program starts with
 the keyword "program" and a list of variable names which are assigned
 values from command line arguments.  Following this is a `block` which
-is the code to execute.
+is the code to execute.  Unlike Pascal, constants and other
+declarations come *before* the program.
 
 As this is the top level, several things are handled a bit
 differently.
@@ -3215,7 +3591,17 @@ analysis is a bit more interesting at this level.
 ###### Binode types
        Program,
 
-###### Parser: grammar
+###### top level grammar
+
+       DeclareProgram -> Program ${ {
+               struct parse_context *c = config2context(config);
+               if (c->prog)
+                       type_err(c, "Program defined a second time",
+                                $1, NULL, 0, NULL);
+               else
+                       c->prog = $<1;
+       } }$
+
 
        $*binode
        Program -> program OpenScope Varlist Block OptNL ${
@@ -3265,7 +3651,7 @@ analysis is a bit more interesting at this level.
                break;
 
 ###### propagate binode cases
-       case Program: abort();
+       case Program: abort();          // NOTEST
 
 ###### core functions
 
@@ -3275,7 +3661,7 @@ analysis is a bit more interesting at this level.
                int ok = 1;
 
                if (!b)
-                       return 0;
+                       return 0;       // NOTEST
                do {
                        ok = 1;
                        propagate_types(b->right, c, &ok, Tnone, 0);
@@ -3310,7 +3696,7 @@ analysis is a bit more interesting at this level.
                struct value v;
 
                if (!prog)
-                       return;
+                       return;         // NOTEST
                al = cast(binode, p->left);
                while (al) {
                        struct var *v = cast(var, al->left);
@@ -3332,7 +3718,7 @@ analysis is a bit more interesting at this level.
        }
 
 ###### interp binode cases
-       case Program: abort();
+       case Program: abort();  // NOTEST
 
 ## And now to test it out.
 
@@ -3372,7 +3758,7 @@ Fibonacci, and performs a binary search for a number.
                a : number
                a = A;
                b:number = B
-               if a > 0 and b > 0:
+               if a > 0 and then b > 0:
                        while a != b:
                                if a < b:
                                        b = b - a
@@ -3416,3 +3802,24 @@ Fibonacci, and performs a binary search for a number.
                        print "Yay, I found", target
                case GiveUp:
                        print "Closest I found was", mid
+
+               size::=55
+               list:[size]number
+               list[0] = 1234
+               for i:=1; then i = i + 1; while i < size:
+                       n := list[i-1] * list[i-1]
+                       list[i] = (n / 100) % 10000
+
+               print "Before sort:"
+               for i:=0; then i = i + 1; while i < size:
+                       print "list[",i,"]=",list[i]
+
+               for i := 1; then i=i+1; while i < size:
+                       for j:=i-1; then j=j-1; while j >= 0:
+                               if list[j] > list[j+1]:
+                                       t:= list[j]
+                                       list[j] = list[j+1]
+                                       list[j+1] = t
+               print "After sort:"
+               for i:=0; then i = i + 1; while i < size:
+                       print "list[",i,"]=",list[i]