]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: add comments to var_block_close() and remove dead code.
[ocean] / csrc / oceani.mdc
index 18e593065705ea6324a6ddf68250142df4ec3d72..7e1f2ac788117dddae2f9e2529e3d34ec8389aac 100644 (file)
@@ -41,10 +41,12 @@ short-circuit Boolean operators, and the "if ... else" trinary
 operator which can select between two expressions based on a third
 (which appears syntactically in the middle).
 
-Elements that are present purely to make a usable language, and
-without any expectation that they will remain, are the "program'
-clause, which provides a list of variables to received command-line
-arguments, and the "print" statement which performs simple output.
+The "func" clause currently only allows a "main" function to be
+declared.  That will be extended when proper function support is added.
+
+An element that is present purely to make a usable language, and
+without any expectation that they will remain, is the "print" statement
+which performs simple output.
 
 The current scalar types are "number", "Boolean", and "string".
 Boolean will likely stay in its current form, the other two might, but
@@ -72,7 +74,8 @@ So the main requirements of the interpreter are:
 - Parse the program, possibly with tracing,
 - Analyse the parsed program to ensure consistency,
 - Print the program,
-- Execute the program, if no parsing or consistency errors were found.
+- Execute the "main" function in the program, if no parsing or
+  consistency errors were found.
 
 This is all performed by a single C program extracted with
 `parsergen`.
@@ -105,6 +108,7 @@ structures can be used.
 
 ###### Parser: header
        ## macros
+       struct parse_context;
        ## ast
        struct parse_context {
                struct token_config config;
@@ -162,6 +166,12 @@ structures can be used.
                {NULL,        0, NULL, 0},
        };
        const char *options = "tpnbs";
+
+       static void pr_err(char *msg)                   // NOTEST
+       {
+               fprintf(stderr, "%s\n", msg);           // NOTEST
+       }                                               // NOTEST
+
        int main(int argc, char *argv[])
        {
                int fd;
@@ -203,7 +213,7 @@ structures can be used.
                context.file_name = argv[optind];
                len = lseek(fd, 0, 2);
                file = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0);
-               s = code_extract(file, file+len, NULL);
+               s = code_extract(file, file+len, pr_err);
                if (!s) {
                        fprintf(stderr, "oceani: could not find any code in %s\n",
                                argv[optind]);
@@ -225,25 +235,26 @@ structures can be used.
                                exit(1);
                        }
                } else
-                       ss = s;
+                       ss = s;                         // NOTEST
                parse_oceani(ss->code, &context.config, dotrace ? stderr : NULL);
 
                if (!context.prog) {
-                       fprintf(stderr, "oceani: no program found.\n");
+                       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);
-                       }
-                       interp_prog(context.prog, argv+optind+1);
-               }
+               if (context.prog && doexec && !context.parse_error)
+                       interp_prog(&context, context.prog, argc - optind, argv+optind);
                free_exec(context.prog);
 
                while (s) {
@@ -254,6 +265,7 @@ structures can be used.
                }
                ## free context vars
                ## free context types
+               ## free context storage
                exit(context.parse_error ? 1 : 0);
        }
 
@@ -429,7 +441,7 @@ Named type are stored in a simple linked list.  Objects of each type are
                struct type *next;
                int size, align;
                void (*init)(struct type *type, struct value *val);
-               void (*prepare_type)(struct type *type);
+               void (*prepare_type)(struct parse_context *c, struct type *type, int parse_time);
                void (*print)(struct type *type, struct value *val);
                void (*print_type)(struct type *type, FILE *f);
                int (*cmp_order)(struct type *t1, struct type *t2,
@@ -493,7 +505,7 @@ Named type are stored in a simple linked list.  Objects of each type are
        static void type_print(struct type *type, FILE *f)
        {
                if (!type)
-                       fputs("*unknown*type*", f);
+                       fputs("*unknown*type*", f);     // NOTEST
                else if (type->name.len)
                        fprintf(f, "%.*s", type->name.len, type->name.txt);
                else if (type->print_type)
@@ -508,7 +520,7 @@ Named type are stored in a simple linked list.  Objects of each type are
                        type->init(type, val);
        }
 
-       static void dup_value(struct type *type, 
+       static void dup_value(struct type *type,
                              struct value *vold, struct value *vnew)
        {
                if (type && type->dup)
@@ -520,9 +532,9 @@ Named type are stored in a simple linked list.  Objects of each type are
        {
                if (tl && tl->cmp_order)
                        return tl->cmp_order(tl, tr, left, right);
-               if (tl && tl->cmp_eq)
-                       return tl->cmp_eq(tl, tr, left, right);
-               return -1;
+               if (tl && tl->cmp_eq)                   // NOTEST
+                       return tl->cmp_eq(tl, tr, left, right); // NOTEST
+               return -1;                              // NOTEST
        }
 
        static void print_value(struct type *type, struct value *v)
@@ -533,21 +545,6 @@ Named type are stored in a simple linked list.  Objects of each type are
                        printf("*Unknown*");            // NOTEST
        }
 
-       static struct value *val_alloc(struct type *t, struct value *init)
-       {
-               struct value *ret;
-
-               if (t->prepare_type)
-                       t->prepare_type(t);
-
-               ret = calloc(1, t->size);
-               if (init)
-                       memcpy(ret, init, t->size);
-               else
-                       val_init(t, ret);
-               return ret;
-       }
-
 ###### forward decls
 
        static void free_value(struct type *type, struct value *v);
@@ -571,6 +568,30 @@ Named type are stored in a simple linked list.  Objects of each type are
                free(t);
        }
 
+Type can be specified for local variables, for fields in a structure,
+for formal parameters to functions, and possibly elsewhere.  Different
+rules may apply in different contexts.  As a minimum, a named type may
+always be used.  Currently the type of a formal parameter can be
+different from types in other contexts, so we have a separate grammar
+symbol for those.
+
+###### Grammar
+
+       $*type
+       Type -> IDENTIFIER ${
+               $0 = find_type(c, $1.txt);
+               if (!$0) {
+                       tok_err(c,
+                               "error: undefined type", &$1);
+
+                       $0 = Tnone;
+               }
+       }$
+       ## type grammar
+
+       FormalType -> Type ${ $0 = $<1; }$
+       ## formal type grammar
+
 #### Base Types
 
 Values of the base types can be numbers, which we represent as
@@ -590,18 +611,18 @@ later.  In some cases a Boolean can be accepted as well as some other
 primary type, and in others any type is acceptable except a label (`Vlabel`).
 A separate function encoding these cases will simplify some code later.
 
-## type functions
+###### type functions
 
        int (*compat)(struct type *this, struct type *other);
 
-## ast functions
+###### ast functions
 
        static int type_compat(struct type *require, struct type *have, int rules)
        {
                if ((rules & Rboolok) && have == Tbool)
-                       return 1;
+                       return 1;       // NOTEST
                if ((rules & Rnolabel) && have == Tlabel)
-                       return 0;
+                       return 0;       // NOTEST
                if (!require || !have)
                        return 1;
 
@@ -633,7 +654,7 @@ A separate function encoding these cases will simplify some code later.
        static void _free_value(struct type *type, struct value *v)
        {
                if (!v)
-                       return;
+                       return;         // NOTEST
                switch (type->vtype) {
                case Vnone: break;
                case Vstr: free(v->str.txt); break;
@@ -659,9 +680,9 @@ A separate function encoding these cases will simplify some code later.
                case Vbool:
                        val->bool = 0;
                        break;
-               case Vlabel:                    // NOTEST
-                       val->label = NULL;      // NOTEST
-                       break;                  // NOTEST
+               case Vlabel:
+                       val->label = NULL;
+                       break;
                }
        }
 
@@ -753,7 +774,7 @@ A separate function encoding these cases will simplify some code later.
                t->size = size;
                t->align = size > sizeof(void*) ? sizeof(void*) : size;
                if (t->size & (t->align - 1))
-                       t->size = (t->size | (t->align - 1)) + 1;
+                       t->size = (t->size | (t->align - 1)) + 1;       // NOTEST
                return t;
        }
 
@@ -819,7 +840,6 @@ cannot nest, so a declaration while a name is in-scope is an error.
        struct variable {
                struct variable *previous;
                struct type *type;
-               struct value *val;
                struct binding *name;
                struct exec *where_decl;// where name was declared
                struct exec *where_set; // where type was set
@@ -913,7 +933,6 @@ like "if" and the code following it.
 
        $void
        OpenScope -> ${ scope_push(c); }$
-       ClosePara -> ${ var_block_close(c, CloseParallel); }$
 
 Each variable records a scope depth and is in one of four states:
 
@@ -934,13 +953,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
@@ -969,6 +988,11 @@ recent instance.  These variables don't really belong in the
 is found.  Instead, they are detected and ignored when considering the
 list of in_scope names.
 
+The storage of the value of a variable will be described later.  For now
+we just need to know that when a variable goes out of scope, it might
+need to be freed.  For this we need to be able to find it, so assume that 
+`var_value()` will provide that.
+
 ###### variable fields
        struct variable *merged;
 
@@ -978,19 +1002,20 @@ list of in_scope names.
        {
                struct variable *v;
 
-               if (primary->merged)
-                       // shouldn't happen
-                       primary = primary->merged;
+               primary = primary->merged;
 
                for (v = primary->previous; v; v=v->previous)
                        if (v == secondary || v == secondary->merged ||
                            v->merged == secondary ||
-                           (v->merged && v->merged == secondary->merged)) {
+                           v->merged == secondary->merged) {
                                v->scope = OutScope;
                                v->merged = primary;
                        }
        }
 
+###### forward decls
+       static struct value *var_value(struct parse_context *c, struct variable *v);
+
 ###### free context vars
 
        while (context.varlist) {
@@ -1002,8 +1027,7 @@ list of in_scope names.
                        struct variable *t = v;
 
                        v = t->previous;
-                       free_value(t->type, t->val);
-                       free(t->val);
+                       free_value(t->type, var_value(&context, t));
                        if (t->depth == 0)
                                // This is a global constant
                                free_exec(t->where_decl);
@@ -1079,11 +1103,11 @@ all pending-scope variables become conditionally scoped.
                v->previous = b->var;
                b->var = v;
                v->name = b;
+               v->merged = v;
                v->min_depth = v->depth = c->scope_depth;
                v->scope = InScope;
                v->in_scope = c->in_scope;
                c->in_scope = v;
-               v->val = NULL;
                return v;
        }
 
@@ -1117,28 +1141,44 @@ all pending-scope variables become conditionally scoped.
 
        static void var_block_close(struct parse_context *c, enum closetype ct)
        {
-               /* 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);
                for (vp = &c->in_scope;
-                    v = *vp, v && v->depth > c->scope_depth && v->min_depth > c->scope_depth;
-                    ) {
-                       if (v->name->var == v) switch (ct) {
+                    (v = *vp) && v->min_depth > c->scope_depth;
+                    (v->scope == OutScope || v->name->var != v)
+                    ? (*vp =  v->in_scope, 0)
+                    : ( vp = &v->in_scope, 0)) {
+                       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;
+                       switch (ct) {
                        case CloseElse:
                        case CloseParallel: /* handle PendingScope */
                                switch(v->scope) {
                                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)
-                                               v->scope = PendingScope;
-                                       else if (v->name->var == v)
-                                               v->scope = OutScope;
+                                       else if (v->type == Tlabel)     // UNTESTED
+                                               /* Labels remain pending even when not used */
+                                               v->scope = PendingScope;        // UNTESTED
                                        if (ct == CloseElse) {
                                                /* All Pending variables with this name
                                                 * are now Conditional */
@@ -1149,13 +1189,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;
+                                       /* Not possible as it would require
+                                        * parallel scope to be nested immediately
+                                        * in a parallel scope, and that never
+                                        * happens.
+                                        */
+                               case OutScope:
+                                       /* Not possible as we already tested for
+                                        * OutScope
+                                        */
+                                       abort();                // NOTEST
                                }
                                break;
                        case CloseSequential:
@@ -1176,7 +1219,6 @@ all pending-scope variables become conditionally scoped.
                                             v2 = v2->previous)
                                                if (v2->type == Tlabel) {
                                                        v2->scope = CondScope;
-                                                       v2->min_depth = c->scope_depth;
                                                } else
                                                        v2->scope = OutScope;
                                        break;
@@ -1185,13 +1227,115 @@ all pending-scope variables become conditionally scoped.
                                }
                                break;
                        }
-                       if (v->scope == OutScope || v->name->var != v)
-                               *vp = v->in_scope;
-                       else
-                               vp = &v->in_scope;
                }
        }
 
+#### Storing Values
+
+The value of a variable is store separately from the variable, on an
+analogue of a stack frame.  There are (currently) two frames that can be
+active.  A global frame which currently only stores constants, and a
+stacked frame which stores local variables.  Each variable knows if it
+is global or not, and what its index into the frame is.
+
+Values in the global frame are known immediately they are relevant, so
+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.
+
+###### variable fields
+               short frame_pos;
+               short global;
+
+###### parse context
+
+       short global_size, global_alloc;
+       short local_size;
+       void *global, *local;
+
+###### ast functions
+
+       static struct value *var_value(struct parse_context *c, struct variable *v)
+       {
+               if (!v->global) {
+                       if (!c->local || !v->type)
+                               return NULL;
+                       if (v->frame_pos + v->type->size > c->local_size) {
+                               printf("INVALID frame_pos\n");  // NOTEST
+                               exit(2);                        // NOTEST
+                       }
+                       return c->local + v->frame_pos;
+               }
+               if (c->global_size > c->global_alloc) {
+                       int old = c->global_alloc;
+                       c->global_alloc = (c->global_size | 1023) + 1024;
+                       c->global = realloc(c->global, c->global_alloc);
+                       memset(c->global + old, 0, c->global_alloc - old);
+               }
+               return c->global + v->frame_pos;
+       }
+
+       static struct value *global_alloc(struct parse_context *c, struct type *t,
+                                         struct variable *v, struct value *init)
+       {
+               struct value *ret;
+               struct variable scratch;
+
+               if (t->prepare_type)
+                       t->prepare_type(c, t, 1);       // NOTEST
+
+               if (c->global_size & (t->align - 1))
+                       c->global_size = (c->global_size + t->align) & ~(t->align-1);   // UNTESTED
+               if (!v) {
+                       v = &scratch;
+                       v->type = t;
+               }
+               v->frame_pos = c->global_size;
+               v->global = 1;
+               c->global_size += v->type->size;
+               ret = var_value(c, v);
+               if (init)
+                       memcpy(ret, init, t->size);
+               else
+                       val_init(t, ret);
+               return ret;
+       }
+
+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()`.
+
+###### ast functions
+
+       static void scope_finalize(struct parse_context *c)
+       {
+               struct binding *b;
+
+               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 (c->local_size & (t->align - 1))
+                                       c->local_size = (c->local_size + t->align) & ~(t->align-1);
+                               v->frame_pos = c->local_size;
+                               c->local_size += v->type->size;
+                       }
+               }
+               c->local = calloc(1, c->local_size);
+       }
+
+###### free context storage
+       free(context.global);
+       free(context.local);
+
 ### Executables
 
 Executables can be lots of different things.  In many cases an
@@ -1245,15 +1389,15 @@ from the `exec_types` enum.
        static int __fput_loc(struct exec *loc, FILE *f)
        {
                if (!loc)
-                       return 0;               // NOTEST
+                       return 0;
                if (loc->line >= 0) {
                        fprintf(f, "%d:%d: ", loc->line, loc->column);
                        return 1;
                }
                if (loc->type == Xbinode)
                        return __fput_loc(cast(binode,loc)->left, f) ||
-                              __fput_loc(cast(binode,loc)->right, f);
-               return 0;
+                              __fput_loc(cast(binode,loc)->right, f);  // NOTEST
+               return 0;                       // NOTEST
        }
        static void fput_loc(struct exec *loc, FILE *f)
        {
@@ -1403,8 +1547,8 @@ propagation is needed.
 
 Interpreting an `exec` doesn't require anything but the `exec`.  State
 is stored in variables and each variable will be directly linked from
-within the `exec` tree.  The exception to this is the whole `program`
-which needs to look at command line arguments.  The `program` will be
+within the `exec` tree.  The exception to this is the `main` function
+which needs to look at command line arguments.  This function will be
 interpreted separately.
 
 Each `exec` can return a value combined with a type in `struct lrval`.
@@ -1413,7 +1557,6 @@ the location of a value, which can be updated, in `lval`.  Others will
 set `lval` to NULL indicating that there is a value of appropriate type
 in `rval`.
 
-
 ###### core functions
 
        struct lrval {
@@ -1421,11 +1564,12 @@ in `rval`.
                struct value rval, *lval;
        };
 
-       static struct lrval _interp_exec(struct exec *e);
+       static struct lrval _interp_exec(struct parse_context *c, struct exec *e);
 
-       static struct value interp_exec(struct exec *e, struct type **typeret)
+       static struct value interp_exec(struct parse_context *c, struct exec *e,
+                                       struct type **typeret)
        {
-               struct lrval ret = _interp_exec(e);
+               struct lrval ret = _interp_exec(c, e);
 
                if (!ret.type) abort();
                if (typeret)
@@ -1435,9 +1579,10 @@ in `rval`.
                return ret.rval;
        }
 
-       static struct value *linterp_exec(struct exec *e, struct type **typeret)
+       static struct value *linterp_exec(struct parse_context *c, struct exec *e,
+                                         struct type **typeret)
        {
-               struct lrval ret = _interp_exec(e);
+               struct lrval ret = _interp_exec(c, e);
 
                if (ret.lval)
                        *typeret = ret.type;
@@ -1446,7 +1591,7 @@ in `rval`.
                return ret.lval;
        }
 
-       static struct lrval _interp_exec(struct exec *e)
+       static struct lrval _interp_exec(struct parse_context *c, struct exec *e)
        {
                struct lrval ret;
                struct value rv = {}, *lrv = NULL;
@@ -1496,6 +1641,12 @@ Arrays can be declared by giving a size and a type, as `[size]type' so
 size can be either a literal number, or a named constant.  Some day an
 arbitrary expression will be supported.
 
+As a formal parameter to a function, the array can be declared with a
+new variable as the size: `name:[size::number]string`.  The `size`
+variable is set to the size of the array and must be a constant.  As
+`number` is the only supported type, it can be left out:
+`name:[size::]string`.
+
 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
@@ -1503,41 +1654,69 @@ 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 recursive depth.
 
+For now we have two sorts of array, those with fixed size either because
+it is given as a literal number or because it is a struct member (which
+cannot have a runtime-changing size), and those with a size that is
+determined at runtime - local variables with a const size.  The former
+have their size calculated at parse time, the latter at run time.
+
+For the latter type, the `size` field of the type is the size of a
+pointer, and the array is reallocated every time it comes into scope.
+
+We differentiate struct fields with a const size from local variables
+with a const size by whether they are prepared at parse time or not.
+
 ###### type union fields
 
        struct {
-               int size;
+               int unspec;     // size is unspecified - vsize must be set.
+               short size;
+               short static_size;
                struct variable *vsize;
                struct type *member;
        } array;
 
+###### value union fields
+       void *array;  // used if not static_size
+
 ###### value functions
 
-       static void array_prepare_type(struct type *type)
+       static void array_prepare_type(struct parse_context *c, struct type *type,
+                                      int parse_time)
        {
+               struct value *vsize;
                mpz_t q;
-               if (!type->array.vsize)
+               if (!type->array.vsize || type->array.static_size)
                        return;
 
+               vsize = var_value(c, type->array.vsize);
                mpz_init(q);
-               mpz_tdiv_q(q, mpq_numref(type->array.vsize->val->num),
-                          mpq_denref(type->array.vsize->val->num));
+               mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
                type->array.size = mpz_get_si(q);
                mpz_clear(q);
 
-               type->size = type->array.size * type->array.member->size;
-               type->align = type->array.member->align;
+               if (parse_time) {
+                       type->array.static_size = 1;
+                       type->size = type->array.size * type->array.member->size;
+                       type->align = type->array.member->align;
+               }
        }
 
        static void array_init(struct type *type, struct value *val)
        {
                int i;
+               void *ptr = val->ptr;
 
                if (!val)
-                       return;         
+                       return;                         // NOTEST
+               if (!type->array.static_size) {
+                       val->array = calloc(type->array.size,
+                                           type->array.member->size);
+                       ptr = val->array;
+               }
                for (i = 0; i < type->array.size; i++) {
                        struct value *v;
-                       v = (void*)val->ptr + i * type->array.member->size;
+                       v = (void*)ptr + i * type->array.member->size;
                        val_init(type->array.member, v);
                }
        }
@@ -1545,25 +1724,39 @@ make a copy of an array with controllable recursive depth.
        static void array_free(struct type *type, struct value *val)
        {
                int i;
+               void *ptr = val->ptr;
 
+               if (!type->array.static_size)
+                       ptr = val->array;
                for (i = 0; i < type->array.size; i++) {
                        struct value *v;
-                       v = (void*)val->ptr + i * type->array.member->size;
+                       v = (void*)ptr + i * type->array.member->size;
                        free_value(type->array.member, v);
                }
+               if (!type->array.static_size)
+                       free(ptr);
        }
 
        static int array_compat(struct type *require, struct type *have)
        {
                if (have->compat != require->compat)
-                       return 0;
+                       return 0;       // UNTESTED
                /* Both are arrays, so we can look at details */
                if (!type_compat(require->array.member, have->array.member, 0))
                        return 0;
+               if (have->array.unspec && require->array.unspec) {
+                       if (have->array.vsize && require->array.vsize &&
+                           have->array.vsize != require->array.vsize)  // UNTESTED
+                               /* sizes might not be the same */
+                               return 0;       // UNTESTED
+                       return 1;
+               }
+               if (have->array.unspec || require->array.unspec)
+                       return 1;       // UNTESTED
                if (require->array.vsize == NULL && have->array.vsize == NULL)
                        return require->array.size == have->array.size;
 
-               return require->array.vsize == have->array.vsize;
+               return require->array.vsize == have->array.vsize;       // UNTESTED
        }
 
        static void array_print_type(struct type *type, FILE *f)
@@ -1571,7 +1764,8 @@ make a copy of an array with controllable recursive depth.
                fputs("[", f);
                if (type->array.vsize) {
                        struct binding *b = type->array.vsize->name;
-                       fprintf(f, "%.*s]", b->name.len, b->name.txt);
+                       fprintf(f, "%.*s%s]", b->name.len, b->name.txt,
+                               type->array.unspec ? "::" : "");
                } else
                        fprintf(f, "%d]", type->array.size);
                type_print(type->array.member, f);
@@ -1583,6 +1777,8 @@ make a copy of an array with controllable recursive depth.
                .print_type = array_print_type,
                .compat = array_compat,
                .free = array_free,
+               .size = sizeof(void*),
+               .align = sizeof(void*),
        };
 
 ###### declare terminals
@@ -1613,6 +1809,7 @@ make a copy of an array with controllable recursive depth.
                                        &$2);
                        mpq_clear(num);
                }
+               t->array.static_size = 1;
                t->size = t->array.size * t->array.member->size;
                t->align = t->array.member->align;
        } }$
@@ -1632,6 +1829,28 @@ make a copy of an array with controllable recursive depth.
                $0->array.vsize = v;
        } }$
 
+###### Grammar
+       $*type
+       OptType -> Type ${ $0 = $<1; }$
+               | ${ $0 = NULL; }$
+
+###### formal type grammar
+
+       | [ IDENTIFIER :: OptType ] Type ${ {
+               struct variable *v = var_decl(c, $ID.txt);
+               struct text noname = { "", 0 };
+
+               v->type = $<OT;
+               v->constant = 1;
+               if (!v->type)
+                       v->type = Tnum;
+               $0 = add_type(c, noname, &array_prototype);
+               $0->array.member = $<6;
+               $0->array.size = 0;
+               $0->array.unspec = 1;
+               $0->array.vsize = v;
+       } }$
+
 ###### Binode types
        Index,
 
@@ -1676,17 +1895,22 @@ make a copy of an array with controllable recursive depth.
        case Index: {
                mpz_t q;
                long i;
+               void *ptr;
 
-               lleft = linterp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               lleft = linterp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                mpz_init(q);
                mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
                i = mpz_get_si(q);
                mpz_clear(q);
 
+               if (ltype->array.static_size)
+                       ptr = lleft;
+               else
+                       ptr = *(void**)lleft;
                rvtype = ltype->array.member;
                if (i >= 0 && i < ltype->array.size)
-                       lrv = (void*)lleft + i * rvtype->size;
+                       lrv = ptr + i * rvtype->size;
                else
                        val_init(ltype->array.member, &rv);
                ltype = NULL;
@@ -1758,7 +1982,12 @@ function will be needed.
                for (i = 0; i < type->structure.nfields; i++) {
                        struct value *v;
                        v = (void*) val->ptr + type->structure.fields[i].offset;
-                       val_init(type->structure.fields[i].type, v);
+                       if (type->structure.fields[i].init)
+                               dup_value(type->structure.fields[i].type, 
+                                         type->structure.fields[i].init,
+                                         v);
+                       else
+                               val_init(type->structure.fields[i].type, v);
                }
        }
 
@@ -1780,7 +2009,6 @@ function will be needed.
                        if (t->structure.fields[i].init) {
                                free_value(t->structure.fields[i].type,
                                           t->structure.fields[i].init);
-                               free(t->structure.fields[i].init);
                        }
                free(t->structure.fields);
        }
@@ -1850,7 +2078,7 @@ function will be needed.
                struct type *st = propagate_types(f->left, c, ok, NULL, 0);
 
                if (!st)
-                       type_err(c, "error: unknown type for field access", f->left,
+                       type_err(c, "error: unknown type for field access", f->left,    // UNTESTED
                                 NULL, 0, NULL);
                else if (st->init != structure_init)
                        type_err(c, "error: field reference attempted on %1, not a struct",
@@ -1876,7 +2104,7 @@ function will be needed.
        {
                struct fieldref *f = cast(fieldref, e);
                struct type *ltype;
-               struct value *lleft = linterp_exec(f->left, &ltype);
+               struct value *lleft = linterp_exec(c, f->left, &ltype);
                lrv = (void*)lleft->ptr + ltype->structure.fields[f->index].offset;
                rvtype = ltype->structure.fields[f->index].type;
                break;
@@ -1895,8 +2123,8 @@ function will be needed.
                        return;
                free_fieldlist(f->prev);
                if (f->f.init) {
-                       free_value(f->f.type, f->f.init);
-                       free(f->f.init);
+                       free_value(f->f.type, f->f.init);       // UNTESTED
+                       free(f->f.init);        // UNTESTED
                }
                free(f);
        }
@@ -1952,7 +2180,7 @@ function will be needed.
                | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
 
        Field -> IDENTIFIER : Type = Expression ${ {
-                       int ok;
+                       int ok; // UNTESTED
 
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
@@ -1963,26 +2191,27 @@ function will be needed.
                                propagate_types($<5, c, &ok, $3, 0);
                        } while (ok == 2);
                        if (!ok)
-                               c->parse_error = 1;
+                               c->parse_error = 1;     // UNTESTED
                        else {
-                               struct value vl = interp_exec($5, NULL);
-                               $0->f.init = val_alloc($0->f.type, &vl);
+                               struct value vl = interp_exec(c, $5, NULL);
+                               $0->f.init = global_alloc(c, $0->f.type, NULL, &vl);
                        }
                } }$
                | IDENTIFIER : Type ${
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
                        $0->f.type = $<3;
-                       $0->f.init = val_alloc($0->f.type, NULL);
+                       if ($0->f.type->prepare_type)
+                               $0->f.type->prepare_type(c, $0->f.type, 1);
                }$
 
 ###### forward decls
        static void structure_print_type(struct type *t, FILE *f);
 
 ###### value functions
-       static void structure_print_type(struct type *t, FILE *f)
-       {
-               int i;
+       static void structure_print_type(struct type *t, FILE *f)       // UNTESTED
+       {       // UNTESTED
+               int i;  // UNTESTED
 
                fprintf(f, "struct %.*s\n", t->name.len, t->name.txt);
 
@@ -1993,18 +2222,18 @@ function will be needed.
                        if (fl->type->print && fl->init) {
                                fprintf(f, " = ");
                                if (fl->type == Tstr)
-                                       fprintf(f, "\"");
+                                       fprintf(f, "\"");       // UNTESTED
                                print_value(fl->type, fl->init);
                                if (fl->type == Tstr)
-                                       fprintf(f, "\"");
+                                       fprintf(f, "\"");       // UNTESTED
                        }
                        printf("\n");
                }
        }
 
 ###### print type decls
-       {
-               struct type *t;
+       {       // UNTESTED
+               struct type *t; // UNTESTED
                int target = -1;
 
                while (target != 0) {
@@ -2025,6 +2254,92 @@ function will be needed.
                }
        }
 
+### Functions
+
+A function is a named chunk of code which can be passed parameters and
+can return results.  Each function has an implicit type which includes
+the set of parameters and the return value.  As yet these types cannot
+be declared separate from the function itself.
+
+In fact, only one function is currently possible - `main`.  `main` is
+passed an array of strings together with the size of the array, and
+doesn't return anything.  The strings are command line arguments.
+
+The parameters can be specified either in parentheses as a list, such as
+
+##### Example: function 1
+
+       func main(av:[ac::number]string)
+               code block
+
+or as an indented list of one parameter per line
+
+##### Example: function 2
+
+       func main
+               argv:[argc::number]string
+       do
+               code block
+
+###### Binode types
+       Func, List,
+
+###### Grammar
+
+       $TERM func main
+
+       $*binode
+       MainFunction -> func main ( OpenScope Args ) Block Newlines ${
+                       $0 = new(binode);
+                       $0->op = Func;
+                       $0->left = reorder_bilist($<Ar);
+                       $0->right = $<Bl;
+                       var_block_close(c, CloseSequential);
+                       if (c->scope_stack && !c->parse_error) abort();
+               }$
+               | func main IN OpenScope OptNL Args OUT OptNL do Block Newlines ${
+                       $0 = new(binode);
+                       $0->op = Func;
+                       $0->left = reorder_bilist($<Ar);
+                       $0->right = $<Bl;
+                       var_block_close(c, CloseSequential);
+                       if (c->scope_stack && !c->parse_error) abort();
+               }$
+               | func main NEWLINE OpenScope OptNL do Block Newlines ${
+                       $0 = new(binode);
+                       $0->op = Func;
+                       $0->left = NULL;
+                       $0->right = $<Bl;
+                       var_block_close(c, CloseSequential);
+                       if (c->scope_stack && !c->parse_error) abort();
+               }$
+
+       Args -> ${ $0 = NULL; }$
+               | Varlist ${ $0 = $<1; }$
+               | Varlist ; ${ $0 = $<1; }$
+               | Varlist NEWLINE ${ $0 = $<1; }$
+
+       Varlist -> Varlist ; ArgDecl ${ // UNTESTED
+                       $0 = new(binode);
+                       $0->op = List;
+                       $0->left = $<Vl;
+                       $0->right = $<AD;
+               }$
+               | ArgDecl ${
+                       $0 = new(binode);
+                       $0->op = List;
+                       $0->left = NULL;
+                       $0->right = $<AD;
+               }$
+
+       $*var
+       ArgDecl -> IDENTIFIER : FormalType ${ {
+               struct variable *v = var_decl(c, $1.txt);
+               $0 = new(var);
+               $0->var = v;
+               v->type = $<FT;
+       } }$
+
 ## Executables: the elements of code
 
 Each code element needs to be parsed, printed, analysed,
@@ -2074,7 +2389,7 @@ an executable.
                        {
                        char tail[3];
                        if (number_parse($0->val.num, tail, $1.txt) == 0)
-                               mpq_init($0->val.num);
+                               mpq_init($0->val.num);  // UNTESTED
                                if (tail[0])
                                        tok_err(c, "error: unsupported number suffix",
                                                &$1);
@@ -2223,7 +2538,6 @@ link to find the primary instance.
                        v->where_decl = $0;
                        v->where_set = $0;
                        v->type = $<Type;
-                       v->val = NULL;
                } else {
                        v = var_ref(c, $1.txt);
                        $0->var = v;
@@ -2241,7 +2555,6 @@ link to find the primary instance.
                        v->where_decl = $0;
                        v->where_set = $0;
                        v->type = $<Type;
-                       v->val = NULL;
                        v->constant = 1;
                } else {
                        v = var_ref(c, $1.txt);
@@ -2261,7 +2574,6 @@ link to find the primary instance.
                        /* This might be a label - allocate a var just in case */
                        v = var_decl(c, $1.txt);
                        if (v) {
-                               v->val = NULL;
                                v->type = Tnone;
                                v->where_decl = $0;
                                v->where_set = $0;
@@ -2271,18 +2583,6 @@ link to find the primary instance.
        } }$
        ## variable grammar
 
-       $*type
-       Type -> IDENTIFIER ${
-               $0 = find_type(c, $1.txt);
-               if (!$0) {
-                       tok_err(c,
-                               "error: undefined type", &$1);
-
-                       $0 = Tnone;
-               }
-       }$
-       ## type grammar
-
 ###### print exec cases
        case Xvar:
        {
@@ -2296,7 +2596,7 @@ link to find the primary instance.
 
 ###### format cases
        case 'v':
-               if (loc->type == Xvar) {
+               if (loc && loc->type == Xvar) {
                        struct var *v = cast(var, loc);
                        if (v->var) {
                                struct binding *b = v->var->name;
@@ -2317,8 +2617,7 @@ link to find the primary instance.
                        type_err(c, "%d:BUG: no variable!!", prog, NULL, 0, NULL); // NOTEST
                        return Tnone;                                   // NOTEST
                }
-               if (v->merged)
-                       v = v->merged;
+               v = v->merged;
                if (v->constant && (rules & Rnoconstant)) {
                        type_err(c, "error: Cannot assign to a constant: %v",
                                 prog, NULL, 0, NULL);
@@ -2332,7 +2631,6 @@ link to find the primary instance.
                if (v->type == NULL) {
                        if (type && *ok != 0) {
                                v->type = type;
-                               v->val = NULL;
                                v->where_set = prog;
                                *ok = 2;
                        }
@@ -2355,9 +2653,8 @@ link to find the primary instance.
                struct var *var = cast(var, e);
                struct variable *v = var->var;
 
-               if (v->merged)
-                       v = v->merged;
-               lrv = v->val;
+               v = v->merged;
+               lrv = var_value(c, v);
                rvtype = v->type;
                break;
        }
@@ -2439,11 +2736,11 @@ there.
 
        case CondExpr: {
                struct binode *b2 = cast(binode, b->right);
-               left = interp_exec(b->left, &ltype);
+               left = interp_exec(c, b->left, &ltype);
                if (left.bool)
-                       rv = interp_exec(b2->left, &rvtype);
+                       rv = interp_exec(c, b2->left, &rvtype); // UNTESTED
                else
-                       rv = interp_exec(b2->right, &rvtype);
+                       rv = interp_exec(c, b2->right, &rvtype);
                }
                break;
 
@@ -2556,27 +2853,27 @@ evaluate the second expression if not necessary.
 
 ###### interp binode cases
        case And:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                rv.bool = rv.bool && right.bool;
                break;
        case AndThen:
-               rv = interp_exec(b->left, &rvtype);
+               rv = interp_exec(c, b->left, &rvtype);
                if (rv.bool)
-                       rv = interp_exec(b->right, NULL);
+                       rv = interp_exec(c, b->right, NULL);
                break;
        case Or:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                rv.bool = rv.bool || right.bool;
                break;
        case OrElse:
-               rv = interp_exec(b->left, &rvtype);
+               rv = interp_exec(c, b->left, &rvtype);
                if (!rv.bool)
-                       rv = interp_exec(b->right, NULL);
+                       rv = interp_exec(c, b->right, NULL);
                break;
        case Not:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                rv.bool = !rv.bool;
                break;
 
@@ -2666,9 +2963,9 @@ expression operator, and the `CMPop` non-terminal will match one of them.
                if (t)
                        propagate_types(b->right, c, ok, t, 0);
                else {
-                       t = propagate_types(b->right, c, ok, NULL, Rnolabel);
-                       if (t)
-                               t = propagate_types(b->left, c, ok, t, 0);
+                       t = propagate_types(b->right, c, ok, NULL, Rnolabel);   // UNTESTED
+                       if (t)  // UNTESTED
+                               t = propagate_types(b->left, c, ok, t, 0);      // UNTESTED
                }
                if (!type_compat(type, Tbool, 0))
                        type_err(c, "error: Comparison returns %1 but %2 expected", prog,
@@ -2684,8 +2981,8 @@ expression operator, and the `CMPop` non-terminal will match one of them.
        case NEql:
        {
                int cmp;
-               left = interp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               left = interp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                cmp = value_cmp(ltype, rtype, &left, &right);
                rvtype = Tbool;
                switch (b->op) {
@@ -2848,7 +3145,7 @@ should only insert brackets were needed for precedence.
                /* op must be string, result is number */
                propagate_types(b->left, c, ok, Tstr, 0);
                if (!type_compat(type, Tnum, 0))
-                       type_err(c,
+                       type_err(c,     // UNTESTED
                          "error: Can only convert string to number, not %1",
                                prog, type, 0, NULL);
                return Tnum;
@@ -2859,30 +3156,30 @@ should only insert brackets were needed for precedence.
 ###### interp binode cases
 
        case Plus:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                mpq_add(rv.num, rv.num, right.num);
                break;
        case Minus:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                mpq_sub(rv.num, rv.num, right.num);
                break;
        case Times:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                mpq_mul(rv.num, rv.num, right.num);
                break;
        case Divide:
-               rv = interp_exec(b->left, &rvtype);
-               right = interp_exec(b->right, &rtype);
+               rv = interp_exec(c, b->left, &rvtype);
+               right = interp_exec(c, b->right, &rtype);
                mpq_div(rv.num, rv.num, right.num);
                break;
        case Rem: {
                mpz_t l, r, rem;
 
-               left = interp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               left = interp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                mpz_init(l); mpz_init(r); mpz_init(rem);
                mpz_tdiv_q(l, mpq_numref(left.num), mpq_denref(left.num));
                mpz_tdiv_q(r, mpq_numref(right.num), mpq_denref(right.num));
@@ -2894,24 +3191,24 @@ should only insert brackets were needed for precedence.
                break;
        }
        case Negate:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                mpq_neg(rv.num, rv.num);
                break;
        case Absolute:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                mpq_abs(rv.num, rv.num);
                break;
        case Bracket:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                break;
        case Concat:
-               left = interp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               left = interp_exec(c, b->left, &ltype);
+               right = interp_exec(c, b->right, &rtype);
                rvtype = Tstr;
                rv.str = text_join(left.str, right.str);
                break;
        case StringConv:
-               right = interp_exec(b->right, &rvtype);
+               right = interp_exec(c, b->right, &rvtype);
                rtype = Tstr;
                rvtype = Tnum;
 
@@ -2919,16 +3216,16 @@ should only insert brackets were needed for precedence.
                char tail[3];
                int neg = 0;
                if (tx.txt[0] == '-') {
-                       neg = 1;
-                       tx.txt++;
-                       tx.len--;
+                       neg = 1;        // UNTESTED
+                       tx.txt++;       // UNTESTED
+                       tx.len--;       // UNTESTED
                }
                if (number_parse(rv.num, tail, tx) == 0)
-                       mpq_init(rv.num);
+                       mpq_init(rv.num);       // UNTESTED
                else if (neg)
-                       mpq_neg(rv.num, rv.num);
+                       mpq_neg(rv.num, rv.num);        // UNTESTED
                if (tail[0])
-                       printf("Unsupported suffix: %.*s\n", tx.len, tx.txt);
+                       printf("Unsupported suffix: %.*s\n", tx.len, tx.txt);   // UNTESTED
 
                break;
 
@@ -3082,13 +3379,13 @@ is in-place.
        case Block:
                if (indent < 0) {
                        // simple statement
-                       if (b->left == NULL)
-                               printf("pass");
+                       if (b->left == NULL)    // UNTESTED
+                               printf("pass"); // UNTESTED
                        else
-                               print_exec(b->left, indent, bracket);
-                       if (b->right) {
-                               printf("; ");
-                               print_exec(b->right, indent, bracket);
+                               print_exec(b->left, indent, bracket);   // UNTESTED
+                       if (b->right) { // UNTESTED
+                               printf("; ");   // UNTESTED
+                               print_exec(b->right, indent, bracket);  // UNTESTED
                        }
                } else {
                        // block, one per line
@@ -3133,7 +3430,7 @@ is in-place.
                while (rvtype == Tnone &&
                       b) {
                        if (b->left)
-                               rv = interp_exec(b->left, &rvtype);
+                               rv = interp_exec(c, b->left, &rvtype);
                        b = cast(binode, b->right);
                }
                break;
@@ -3222,7 +3519,7 @@ same solution.
                        if (b->left) {
                                if (sep)
                                        putchar(sep);
-                               left = interp_exec(b->left, &ltype);
+                               left = interp_exec(c, b->left, &ltype);
                                print_value(ltype, &left);
                                free_value(ltype, &left);
                                if (b->right)
@@ -3296,19 +3593,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("= ");
@@ -3353,8 +3648,8 @@ it is declared, and error will be raised as the name is created as
 ###### interp binode cases
 
        case Assign:
-               lleft = linterp_exec(b->left, &ltype);
-               right = interp_exec(b->right, &rtype);
+               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);
@@ -3365,16 +3660,18 @@ it is declared, and error will be raised as the name is created as
        case Declare:
        {
                struct variable *v = cast(var, b->left)->var;
-               if (v->merged)
-                       v = v->merged;
-               free_value(v->type, v->val);
-               free(v->val);
+               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) {
-                       right = interp_exec(b->right, &rtype);
-                       v->val = val_alloc(v->type, &right);
+                       right = interp_exec(c, b->right, &rtype);
+                       memcpy(val, &right, rtype->size);
                        rtype = Tnone;
                } else {
-                       v->val = val_alloc(v->type, NULL);
+                       val_init(v->type, val);
                }
                break;
        }
@@ -3401,9 +3698,11 @@ function.
                        struct var *v = cast(var, $0->right);
                        if (v->var->type == Tnone) {
                                /* Convert this to a label */
+                               struct value *val;
+
                                v->var->type = Tlabel;
-                               v->var->val = val_alloc(Tlabel, NULL);
-                               v->var->val->label = v->var->val;
+                               val = global_alloc(c, Tlabel, v->var, NULL);
+                               val->label = val;
                        }
                }
        }$
@@ -3426,7 +3725,7 @@ function.
 ###### interp binode cases
 
        case Use:
-               rv = interp_exec(b->right, &rvtype);
+               rv = interp_exec(c, b->right, &rvtype);
                break;
 
 ### The Conditional Statement
@@ -3491,7 +3790,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,
@@ -3504,7 +3811,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;
        };
 
@@ -3528,7 +3836,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);
@@ -3555,40 +3863,38 @@ defined.
        // may or may not end with EOL
        // WhilePart and IfPart include an appropriate Suffix
 
-
-       // Both ForPart and Whilepart open scopes, and CondSuffix only
-       // closes one - so in the first branch here we have another to close.
+       // ForPart, SwitchPart, and IfPart open scopes, o we have to close
+       // them.  WhilePart opens and closes its own scope.
        CondStatement -> ForPart OptNL ThenPart OptNL WhilePart CondSuffix ${
                        $0 = $<CS;
                        $0->forpart = $<FP;
                        $0->thenpart = $<TP;
-                       $0->condpart = $WP.condpart; $WP.condpart = NULL;
-                       $0->dopart = $WP.dopart; $WP.dopart = NULL;
+                       $0->looppart = $<WP;
                        var_block_close(c, CloseSequential);
                        }$
                | ForPart OptNL WhilePart CondSuffix ${
                        $0 = $<CS;
                        $0->forpart = $<FP;
-                       $0->condpart = $WP.condpart; $WP.condpart = NULL;
-                       $0->dopart = $WP.dopart; $WP.dopart = NULL;
+                       $0->looppart = $<WP;
                        var_block_close(c, CloseSequential);
                        }$
                | 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);
                        }$
                | SwitchPart : IN OptNL CasePart CondSuffix OUT Newlines ${
                        $0 = $<CS;
                        $0->condpart = $<SP;
                        $CP->next = $0->casepart;
                        $0->casepart = $<CP;
+                       var_block_close(c, CloseSequential);
                        }$
                | IfPart IfSuffix ${
                        $0 = $<IS;
@@ -3600,9 +3906,6 @@ defined.
 
        CondSuffix -> IfSuffix ${
                        $0 = $<1;
-                       // This is where we close scope of the whole
-                       // "for" or "while" statement
-                       var_block_close(c, CloseSequential);
                }$
                | Newlines CasePart CondSuffix ${
                        $0 = $<CS;
@@ -3639,7 +3942,7 @@ defined.
                }$
 
        $*exec
-       // These scopes are closed in CondSuffix
+       // These scopes are closed in CondStatement
        ForPart -> for OpenBlock ${
                        $0 = $<Bl;
                }$
@@ -3649,32 +3952,44 @@ defined.
                        var_block_close(c, CloseSequential);
                }$
 
-       $cond_statement
-       // This scope is closed in CondSuffix
-       WhilePart -> while UseBlock OptNL do Block ${
-                       $0.condpart = $<UB;
-                       $0.dopart = $<Bl;
+       $*binode
+       // This scope is closed in CondStatement
+       WhilePart -> while UseBlock OptNL do OpenBlock ${
+                       $0 = new(binode);
+                       $0->op = Loop;
+                       $0->left = $<UB;
+                       $0->right = $<OB;
+                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential);
                }$
-               | while OpenScope Expression ColonBlock ${
-                       $0.condpart = $<Exp;
-                       $0.dopart = $<Bl;
+               | while OpenScope Expression OpenScope ColonBlock ${
+                       $0 = new(binode);
+                       $0->op = Loop;
+                       $0->left = $<Exp;
+                       $0->right = $<CB;
+                       var_block_close(c, CloseSequential);
+                       var_block_close(c, CloseSequential);
                }$
 
-       IfPart -> if UseBlock OptNL then OpenBlock ClosePara ${
+       $cond_statement
+       IfPart -> if UseBlock OptNL then OpenBlock ${
                        $0.condpart = $<UB;
-                       $0.thenpart = $<Bl;
+                       $0.thenpart = $<OB;
+                       var_block_close(c, CloseParallel);
                }$
-               | if OpenScope Expression OpenScope ColonBlock ClosePara ${
+               | if OpenScope Expression OpenScope ColonBlock ${
                        $0.condpart = $<Ex;
-                       $0.thenpart = $<Bl;
+                       $0.thenpart = $<CB;
+                       var_block_close(c, CloseParallel);
                }$
-               | if OpenScope Expression OpenScope OptNL then Block ClosePara ${
+               | if OpenScope Expression OpenScope OptNL then Block ${
                        $0.condpart = $<Ex;
                        $0.thenpart = $<Bl;
+                       var_block_close(c, CloseParallel);
                }$
 
        $*exec
-       // This scope is closed in CondSuffix
+       // This scope is closed in CondStatement
        SwitchPart -> switch OpenScope Expression ${
                        $0 = $<Ex;
                }$
@@ -3682,6 +3997,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:
@@ -3701,33 +4045,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)
@@ -3739,12 +4058,12 @@ defined.
                                if (bracket)
                                        printf(" {\n");
                                else
-                                       printf(":\n");
+                                       printf("\n");
                                print_exec(cs->condpart, indent+1, bracket);
                                if (bracket)
                                        do_indent(indent, "}\n");
                                if (cs->thenpart) {
-                                       do_indent(indent, "then:\n");
+                                       do_indent(indent, "then\n");
                                        print_exec(cs->thenpart, indent+1, bracket);
                                }
                        } else {
@@ -3786,11 +4105,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
@@ -3804,43 +4130,45 @@ defined.
 
                t = propagate_types(cs->forpart, c, ok, Tnone, 0);
                if (!type_compat(Tnone, t, 0))
-                       *ok = 0;
-               t = propagate_types(cs->dopart, c, ok, Tnone, 0);
-               if (!type_compat(Tnone, t, 0))
-                       *ok = 0;
-               if (cs->dopart) {
+                       *ok = 0;        // UNTESTED
+
+               if (cs->looppart) {
                        t = propagate_types(cs->thenpart, c, ok, Tnone, 0);
                        if (!type_compat(Tnone, t, 0))
-                               *ok = 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;
                             cp && !t; cp = cp->next)
                                t = propagate_types(cp->value, c, ok, NULL, 0);
                        if (!t && cs->condpart)
-                               t = propagate_types(cs->condpart, c, ok, NULL, Rboolok);
+                               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);
                for (cp = cs->casepart;
                     cp && !type;
-                    cp = cp->next)
-                       type = propagate_types(cp->action, c, ok, NULL, rules);
+                    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)
@@ -3850,50 +4178,53 @@ 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:
        {
                struct value v, cnd;
                struct type *vtype, *cndtype;
                struct casepart *cp;
-               struct cond_statement *c = cast(cond_statement, e);
+               struct cond_statement *cs = cast(cond_statement, e);
 
-               if (c->forpart)
-                       interp_exec(c->forpart, NULL);
-               do {
-                       if (c->condpart)
-                               cnd = interp_exec(c->condpart, &cndtype);
-                       else
-                               cndtype = Tnone;
-                       if (!(cndtype == Tnone ||
-                             (cndtype == Tbool && cnd.bool != 0)))
-                               break;
-                       // cnd is Tnone or Tbool, doesn't need to be freed
-                       if (c->dopart)
-                               interp_exec(c->dopart, NULL);
-
-                       if (c->thenpart) {
-                               rv = interp_exec(c->thenpart, &rvtype);
-                               if (rvtype != Tnone || !c->dopart)
-                                       goto Xcond_done;
-                               free_value(rvtype, &rv);
-                               rvtype = Tnone;
+               if (cs->forpart)
+                       interp_exec(c, cs->forpart, NULL);
+               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);
+                               // skip else (and cases)
+                               goto Xcond_done;
                        }
-               } while (c->dopart);
-
-               for (cp = c->casepart; cp; cp = cp->next) {
-                       v = interp_exec(cp->value, &vtype);
+               }
+               for (cp = cs->casepart; cp; cp = cp->next) {
+                       v = interp_exec(c, cp->value, &vtype);
                        if (value_cmp(cndtype, vtype, &v, &cnd) == 0) {
                                free_value(vtype, &v);
                                free_value(cndtype, &cnd);
-                               rv = interp_exec(cp->action, &rvtype);
+                               rv = interp_exec(c, cp->action, &rvtype);
                                goto Xcond_done;
                        }
                        free_value(vtype, &v);
                }
                free_value(cndtype, &cnd);
-               if (c->elsepart)
-                       rv = interp_exec(c->elsepart, &rvtype);
+               if (cs->elsepart)
+                       rv = interp_exec(c, cs->elsepart, &rvtype);
                else
                        rvtype = Tnone;
        Xcond_done:
@@ -3909,8 +4240,8 @@ 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, and probably more.
 For now there are two sorts of things that can appear at the top
-level.  They are predefined constants, `struct` types, and the main
-program.  While the syntax will allow the main program to appear
+level.  They are predefined constants, `struct` types, and the `main`
+function.  While the syntax will allow the `main` function to appear
 multiple times, that will trigger an error if it is actually attempted.
 
 The various declarations do not return anything.  They store the
@@ -3932,15 +4263,17 @@ various declarations in the parse context.
                | DeclarationList Declaration
 
        Declaration -> ERROR Newlines ${
-                       tok_err(c,
+                       tok_err(c,      // UNTESTED
                                "error: unhandled parse error", &$1);
                }$
                | DeclareConstant
-               | DeclareProgram
+               | DeclareFunction
                | DeclareStruct
 
        ## top level grammar
 
+       ## Grammar
+
 ### The `const` section
 
 As well as being defined in with the code that uses them, constants
@@ -4009,8 +4342,8 @@ searching through for the Nth constant for decreasing N.
                if (!ok)
                        c->parse_error = 1;
                else if (v) {
-                       struct value res = interp_exec($5, &v->type);
-                       v->val = val_alloc(v->type, &res);
+                       struct value res = interp_exec(c, $5, &v->type);
+                       global_alloc(c, v->type, v, &res);
                }
        } }$
 
@@ -4033,12 +4366,13 @@ searching through for the Nth constant for decreasing N.
                                        printf("const\n");
                                target = i;
                        } else {
+                               struct value *val = var_value(&context, v);
                                printf("    %.*s :: ", v->name->name.len, v->name->name.txt);
                                type_print(v->type, stdout);
                                printf(" = ");
                                if (v->type == Tstr)
                                        printf("\"");
-                               print_value(v->type, v->val);
+                               print_value(v->type, val);
                                if (v->type == Tstr)
                                        printf("\"");
                                printf("\n");
@@ -4047,122 +4381,102 @@ searching through for the Nth constant for decreasing N.
                }
        }
 
-### Finally the whole program.
+### Finally the whole `main` function.
 
-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.  Unlike Pascal, constants and other
-declarations come *before* the program.
+An Ocean program can currently have only one function - `main` - and
+that must exist.  It expects an array of strings with a provided size.
+Following this is a `block` which is the code to execute.
 
 As this is the top level, several things are handled a bit
 differently.
-The whole program is not interpreted by `interp_exec` as that isn't
+The function is not interpreted by `interp_exec` as that isn't
 passed the argument list which the program requires.  Similarly type
 analysis is a bit more interesting at this level.
 
-###### Binode types
-       Program,
-
 ###### top level grammar
 
-       DeclareProgram -> Program ${ {
+       DeclareFunction -> MainFunction ${ {
                if (c->prog)
-                       type_err(c, "Program defined a second time",
+                       type_err(c, "\"main\" defined a second time",
                                 $1, NULL, 0, NULL);
                else
                        c->prog = $<1;
        } }$
 
-       $TERM program
-
-       $*binode
-       Program -> program OpenScope Varlist ColonBlock Newlines ${
-               $0 = new(binode);
-               $0->op = Program;
-               $0->left = reorder_bilist($<Vl);
-               $0->right = $<Bl;
-               var_block_close(c, CloseSequential);
-               if (c->scope_stack && !c->parse_error) abort();
-               }$
-
-       Varlist -> Varlist ArgDecl ${
-                       $0 = new(binode);
-                       $0->op = Program;
-                       $0->left = $<1;
-                       $0->right = $<2;
-               }$
-               | ${ $0 = NULL; }$
-
-       $*var
-       ArgDecl -> IDENTIFIER ${ {
-               struct variable *v = var_decl(c, $1.txt);
-               $0 = new(var);
-               $0->var = v;
-       } }$
-
-       ## Grammar
-
 ###### print binode cases
-       case Program:
-               do_indent(indent, "program");
+       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;
                        printf(" ");
                        print_exec(b2->left, 0, 0);
+                       printf(":");
+                       type_print(v->type, stdout);
                }
                if (bracket)
-                       printf(" {\n");
+                       printf(") {\n");
                else
-                       printf(":\n");
+                       printf(")\n");
                print_exec(b->right, indent+1, bracket);
                if (bracket)
                        do_indent(indent, "}\n");
                break;
 
 ###### propagate binode cases
-       case Program: abort();          // NOTEST
+       case List:
+       case Func: abort();             // NOTEST
 
 ###### core functions
 
        static int analyse_prog(struct exec *prog, struct parse_context *c)
        {
-               struct binode *b = cast(binode, prog);
+               struct binode *bp = cast(binode, prog);
+               struct binode *b;
                int ok = 1;
+               int arg = 0;
+               struct type *argv_type;
+               struct text argv_type_name = { " argv", 5 };
 
-               if (!b)
+               if (!bp)
                        return 0;       // NOTEST
-               do {
-                       ok = 1;
-                       propagate_types(b->right, c, &ok, Tnone, 0);
-               } while (ok == 2);
-               if (!ok)
-                       return 0;
 
-               for (b = cast(binode, b->left); b; b = cast(binode, b->right)) {
-                       struct var *v = cast(var, b->left);
-                       if (!v->var->type) {
-                               v->var->where_set = b;
-                               v->var->type = Tstr;
-                               v->var->val = NULL;
+               argv_type = add_type(c, argv_type_name, &array_prototype);
+               argv_type->array.member = Tstr;
+               argv_type->array.unspec = 1;
+
+               for (b = cast(binode, bp->left); b; b = cast(binode, b->right)) {
+                       ok = 1;
+                       switch (arg++) {
+                       case 0: /* argv */
+                               propagate_types(b->left, c, &ok, argv_type, 0);
+                               break;
+                       default: /* invalid */  // NOTEST
+                               propagate_types(b->left, c, &ok, Tnone, 0);     // NOTEST
                        }
                }
-               b = cast(binode, prog);
+
                do {
                        ok = 1;
-                       propagate_types(b->right, c, &ok, Tnone, 0);
+                       propagate_types(bp->right, c, &ok, Tnone, 0);
                } while (ok == 2);
                if (!ok)
                        return 0;
 
                /* Make sure everything is still consistent */
-               propagate_types(b->right, c, &ok, Tnone, 0);
-               return !!ok;
+               propagate_types(bp->right, c, &ok, Tnone, 0);
+               if (!ok)
+                       return 0;       // UNTESTED
+               scope_finalize(c);
+               return 1;
        }
 
-       static void interp_prog(struct exec *prog, char **argv)
+       static void interp_prog(struct parse_context *c, struct exec *prog, 
+                               int argc, char **argv)
        {
                struct binode *p = cast(binode, prog);
                struct binode *al;
+               int anum = 0;
                struct value v;
                struct type *vtype;
 
@@ -4171,31 +4485,43 @@ analysis is a bit more interesting at this level.
                al = cast(binode, p->left);
                while (al) {
                        struct var *v = cast(var, al->left);
-                       struct value *vl = v->var->val;
-
-                       if (argv[0] == NULL) {
-                               printf("Not enough args\n");
-                               exit(1);
+                       struct value *vl = var_value(c, v->var);
+                       struct value arg;
+                       struct type *t;
+                       mpq_t argcq;
+                       int i;
+
+                       switch (anum++) {
+                       case 0: /* argv */
+                               t = v->var->type;
+                               mpq_init(argcq);
+                               mpq_set_ui(argcq, argc, 1);
+                               memcpy(var_value(c, t->array.vsize), &argcq, sizeof(argcq));
+                               t->prepare_type(c, t, 0);
+                               array_init(v->var->type, vl);
+                               for (i = 0; i < argc; i++) {
+                                       struct value *vl2 = vl->array + i * v->var->type->array.member->size;
+                                       
+
+                                       arg.str.txt = argv[i];
+                                       arg.str.len = strlen(argv[i]);
+                                       free_value(Tstr, vl2);
+                                       dup_value(Tstr, &arg, vl2);
+                               }
+                               break;
                        }
                        al = cast(binode, al->right);
-                       if (vl)
-                               free_value(v->var->type, vl);
-                       if (!vl) {
-                               vl = val_alloc(v->var->type, NULL);
-                               v->var->val = vl;
-                       }
-                       free_value(v->var->type, vl);
-                       vl->str.len = strlen(argv[0]);
-                       vl->str.txt = malloc(vl->str.len);
-                       memcpy(vl->str.txt, argv[0], vl->str.len);
-                       argv++;
                }
-               v = interp_exec(p->right, &vtype);
+               v = interp_exec(c, p, &vtype);
                free_value(vtype, &v);
        }
 
 ###### interp binode cases
-       case Program: abort();  // NOTEST
+       case List: abort();     // NOTEST
+
+       case Func:
+               rv = interp_exec(c, b->right, &rvtype);
+               break;
 
 ## And now to test it out.
 
@@ -4225,12 +4551,14 @@ things which will likely grow as the languages grows.
                name:string
                alive:Boolean
 
-       program Astr Bstr:
+       func main
+               argv:[argc::]string
+       do
                print "Hello World, what lovely oceans you have!"
                print "Are there", five, "?"
                print pi, pie, "but", cake
 
-               A := $Astr; B := $Bstr
+               A := $argv[1]; B := $argv[2]
 
                /* When a variable is defined in both branches of an 'if',
                 * and used afterwards, the variables are merged.
@@ -4290,13 +4618,14 @@ things which will likely grow as the languages grows.
                        else
                                hi = mid
                        if hi - lo < 1:
+                               lo = mid
                                use GiveUp
                        use True
                do pass
                case Found:
                        print "Yay, I found", target
                case GiveUp:
-                       print "Closest I found was", mid
+                       print "Closest I found was", lo
 
                size::= 10
                list:[size]number