]> ocean-lang.org Git - ocean/blobdiff - csrc/oceani.mdc
oceani: rename Enoconst to Eruntime
[ocean] / csrc / oceani.mdc
index e3c8291572d9d82089c3f74538d2c5406bf86050..dca205570c9af2c1ae704e0a58c04f01bdd96782 100644 (file)
@@ -110,6 +110,7 @@ structures can be used.
        ## macros
        struct parse_context;
        ## ast
+       ## ast late
        struct parse_context {
                struct token_config config;
                char *file_name;
@@ -130,7 +131,7 @@ structures can be used.
        struct parse_context *c = config2context(config);
 
 ###### Parser: code
-
+       #define _GNU_SOURCE
        #include <unistd.h>
        #include <stdlib.h>
        #include <fcntl.h>
@@ -242,9 +243,11 @@ structures can be used.
 
                parse_oceani(ss->code, &context.config, dotrace ? stderr : NULL);
 
+               resolve_consts(&context);
+               prepare_types(&context);
                if (!context.parse_error && !analyse_funcs(&context)) {
                        fprintf(stderr, "oceani: type error in program - not running.\n");
-                       context.parse_error = 1;
+                       context.parse_error += 1;
                }
 
                if (doprint) {
@@ -265,6 +268,7 @@ structures can be used.
                while (context.scope_depth > 0)
                        scope_pop(&context);
                ## free global vars
+               ## free const decls
                ## free context types
                ## free context storage
                exit(context.parse_error ? 1 : 0);
@@ -364,6 +368,7 @@ context so indicate that parsing failed.
        static void type_err(struct parse_context *c,
                             char *fmt, struct exec *loc,
                             struct type *t1, int rules, struct type *t2);
+       static void tok_err(struct parse_context *c, char *fmt, struct token *t);
 
 ###### core functions
 
@@ -392,14 +397,14 @@ context so indicate that parsing failed.
                        }
                }
                fputs("\n", stderr);
-               c->parse_error = 1;
+               c->parse_error += 1;
        }
 
        static void tok_err(struct parse_context *c, char *fmt, struct token *t)
        {
                fprintf(stderr, "%s:%d:%d: %s: %.*s\n", c->file_name, t->line, t->col, fmt,
                        t->txt.len, t->txt.txt);
-               c->parse_error = 1;
+               c->parse_error += 1;
        }
 
 ## Entities: declared and predeclared.
@@ -412,701 +417,1350 @@ confusing) and introduced here.  The following section will present the
 different specific code elements which comprise or manipulate these
 various entities.
 
-### Types
+### Executables
 
-Values come in a wide range of types, with more likely to be added.
-Each type needs to be able to print its own values (for convenience at
-least) as well as to compare two values, at least for equality and
-possibly for order.  For now, values might need to be duplicated and
-freed, though eventually such manipulations will be better integrated
-into the language.
+Executables can be lots of different things.  In many cases an
+executable is just an operation combined with one or two other
+executables.  This allows for expressions and lists etc.  Other times an
+executable is something quite specific like a constant or variable name.
+So we define a `struct exec` to be a general executable with a type, and
+a `struct binode` which is a subclass of `exec`, forms a node in a
+binary tree, and holds an operation.  There will be other subclasses,
+and to access these we need to be able to `cast` the `exec` into the
+various other types.  The first field in any `struct exec` is the type
+from the `exec_types` enum.
 
-Rather than requiring every numeric type to support all numeric
-operations (add, multiple, etc), we allow types to be able to present
-as one of a few standard types: integer, float, and fraction.  The
-existence of these conversion functions eventually enable types to
-determine if they are compatible with other types, though such types
-have not yet been implemented.
+###### macros
+       #define cast(structname, pointer) ({            \
+               const typeof( ((struct structname *)0)->type) *__mptr = &(pointer)->type; \
+               if (__mptr && *__mptr != X##structname) abort();                \
+               (struct structname *)( (char *)__mptr);})
 
-Named type are stored in a simple linked list.  Objects of each type are
-"values" which are often passed around by value.
+       #define new(structname) ({                                              \
+               struct structname *__ptr = ((struct structname *)calloc(1,sizeof(struct structname))); \
+               __ptr->type = X##structname;                                            \
+               __ptr->line = -1; __ptr->column = -1;                                   \
+               __ptr;})
 
-###### ast
+       #define new_pos(structname, token) ({                                           \
+               struct structname *__ptr = ((struct structname *)calloc(1,sizeof(struct structname))); \
+               __ptr->type = X##structname;                                            \
+               __ptr->line = token.line; __ptr->column = token.col;                    \
+               __ptr;})
 
-       struct value {
-               union {
-                       char ptr[1];
-                       ## value union fields
-               };
+###### ast
+       enum exec_types {
+               Xbinode,
+               ## exec type
        };
-
-       struct type {
-               struct text name;
-               struct type *next;
-               int size, align;
-               void (*init)(struct type *type, struct value *val);
-               void (*prepare_type)(struct parse_context *c, struct type *type, int parse_time);
-               void (*print)(struct type *type, struct value *val, FILE *f);
-               void (*print_type)(struct type *type, FILE *f);
-               int (*cmp_order)(struct type *t1, struct type *t2,
-                                struct value *v1, struct value *v2);
-               int (*cmp_eq)(struct type *t1, struct type *t2,
-                             struct value *v1, struct value *v2);
-               void (*dup)(struct type *type, struct value *vold, struct value *vnew);
-               void (*free)(struct type *type, struct value *val);
-               void (*free_type)(struct type *t);
-               long long (*to_int)(struct value *v);
-               double (*to_float)(struct value *v);
-               int (*to_mpq)(mpq_t *q, struct value *v);
-               ## type functions
-               union {
-                       ## type union fields
-               };
+       struct exec {
+               enum exec_types type;
+               int line, column;
+               ## exec fields
+       };
+       struct binode {
+               struct exec;
+               enum Btype {
+                       ## Binode types
+               } op;
+               struct exec *left, *right;
        };
-
-###### parse context
-
-       struct type *typelist;
 
 ###### ast functions
 
-       static struct type *find_type(struct parse_context *c, struct text s)
+       static int __fput_loc(struct exec *loc, FILE *f)
        {
-               struct type *l = c->typelist;
-
-               while (l &&
-                      text_cmp(l->name, s) != 0)
-                               l = l->next;
-               return l;
+               if (!loc)
+                       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);  // NOTEST
+               return 0;       // NOTEST
        }
-
-       static struct type *add_type(struct parse_context *c, struct text s,
-                                    struct type *proto)
+       static void fput_loc(struct exec *loc, FILE *f)
        {
-               struct type *n;
-
-               n = calloc(1, sizeof(*n));
-               *n = *proto;
-               n->name = s;
-               n->next = c->typelist;
-               c->typelist = n;
-               return n;
+               if (!__fput_loc(loc, f))
+                       fprintf(f, "??:??: ");  // NOTEST
        }
 
-       static void free_type(struct type *t)
+Each different type of `exec` node needs a number of functions defined,
+a bit like methods.  We must be able to free it, print it, analyse it
+and execute it.  Once we have specific `exec` types we will need to
+parse them too.  Let's take this a bit more slowly.
+
+#### Freeing
+
+The parser generator requires a `free_foo` function for each struct
+that stores attributes and they will often be `exec`s and subtypes
+there-of.  So we need `free_exec` which can handle all the subtypes,
+and we need `free_binode`.
+
+###### ast functions
+
+       static void free_binode(struct binode *b)
        {
-               /* The type is always a reference to something in the
-                * context, so we don't need to free anything.
-                */
+               if (!b)
+                       return;
+               free_exec(b->left);
+               free_exec(b->right);
+               free(b);
        }
 
-       static void free_value(struct type *type, struct value *v)
+###### core functions
+       static void free_exec(struct exec *e)
        {
-               if (type && v) {
-                       type->free(type, v);
-                       memset(v, 0x5a, type->size);
+               if (!e)
+                       return;
+               switch(e->type) {
+                       ## free exec cases
                }
        }
 
-       static void type_print(struct type *type, FILE *f)
-       {
-               if (!type)
-                       fputs("*unknown*type*", f);     // NOTEST
-               else if (type->name.len)
-                       fprintf(f, "%.*s", type->name.len, type->name.txt);
-               else if (type->print_type)
-                       type->print_type(type, f);
-               else
-                       fputs("*invalid*type*", f);     // NOTEST
-       }
+###### forward decls
 
-       static void val_init(struct type *type, struct value *val)
-       {
-               if (type && type->init)
-                       type->init(type, val);
-       }
+       static void free_exec(struct exec *e);
 
-       static void dup_value(struct type *type,
-                             struct value *vold, struct value *vnew)
+###### free exec cases
+       case Xbinode: free_binode(cast(binode, e)); break;
+
+#### Printing
+
+Printing an `exec` requires that we know the current indent level for
+printing line-oriented components.  As will become clear later, we
+also want to know what sort of bracketing to use.
+
+###### ast functions
+
+       static void do_indent(int i, char *str)
        {
-               if (type && type->dup)
-                       type->dup(type, vold, vnew);
+               while (i-- > 0)
+                       printf("    ");
+               printf("%s", str);
        }
 
-       static int value_cmp(struct type *tl, struct type *tr,
-                            struct value *left, struct value *right)
+###### core functions
+       static void print_binode(struct binode *b, int indent, int bracket)
        {
-               if (tl && tl->cmp_order)
-                       return tl->cmp_order(tl, tr, left, right);
-               if (tl && tl->cmp_eq)                   // NOTEST
-                       return tl->cmp_eq(tl, tr, left, right); // NOTEST
-               return -1;                              // NOTEST
+               struct binode *b2;
+               switch(b->op) {
+               ## print binode cases
+               }
        }
 
-       static void print_value(struct type *type, struct value *v, FILE *f)
+       static void print_exec(struct exec *e, int indent, int bracket)
        {
-               if (type && type->print)
-                       type->print(type, v, f);
-               else
-                       fprintf(f, "*Unknown*");                // NOTEST
+               if (!e)
+                       return;
+               switch (e->type) {
+               case Xbinode:
+                       print_binode(cast(binode, e), indent, bracket); break;
+               ## print exec cases
+               }
+               if (e->to_free) {
+                       struct variable *v;
+                       do_indent(indent, "/* FREE");
+                       for (v = e->to_free; v; v = v->next_free) {
+                               printf(" %.*s", v->name->name.len, v->name->name.txt);
+                               printf("[%d,%d]", v->scope_start, v->scope_end);
+                               if (v->frame_pos >= 0)
+                                       printf("(%d+%d)", v->frame_pos,
+                                              v->type ? v->type->size:0);
+                       }
+                       printf(" */\n");
+               }
        }
 
 ###### forward decls
 
-       static void free_value(struct type *type, struct value *v);
-       static int type_compat(struct type *require, struct type *have, int rules);
-       static void type_print(struct type *type, FILE *f);
-       static void val_init(struct type *type, struct value *v);
-       static void dup_value(struct type *type,
-                             struct value *vold, struct value *vnew);
-       static int value_cmp(struct type *tl, struct type *tr,
-                            struct value *left, struct value *right);
-       static void print_value(struct type *type, struct value *v, FILE *f);
-
-###### free context types
+       static void print_exec(struct exec *e, int indent, int bracket);
 
-       while (context.typelist) {
-               struct type *t = context.typelist;
+#### Analysing
 
-               context.typelist = t->next;
-               if (t->free_type)
-                       t->free_type(t);
-               free(t);
-       }
+As discussed, analysis involves propagating type requirements around the
+program and looking for errors.
 
-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.
+So `propagate_types` is passed an expected type (being a `struct type`
+pointer together with some `val_rules` flags) that the `exec` is
+expected to return, and returns the type that it does return, either of
+which can be `NULL` signifying "unknown".  A `prop_err` flag set is
+passed by reference.  It has `Efail` set when an error is found, and
+`Eretry` when the type for some element is set via propagation.  If
+any expression cannot be evaluated a compile time, `Eruntime` is set.
+If the expression can be copied, `Emaycopy` is set.
 
-###### Grammar
+If it remains unchanged at `0`, then no more propagation is needed.
 
-       $*type
-       Type -> IDENTIFIER ${
-               $0 = find_type(c, $1.txt);
-               if (!$0) {
-                       tok_err(c,
-                               "error: undefined type", &$1);
+###### ast
 
-                       $0 = Tnone;
-               }
-       }$
-       ## type grammar
+       enum val_rules {Rboolok = 1<<1, Rnoconstant = 1<<2};
+       enum prop_err {Efail = 1<<0, Eretry = 1<<1, Eruntime = 1<<2,
+                      Emaycopy = 1<<3};
 
-       FormalType -> Type ${ $0 = $<1; }$
-       ## formal type grammar
-
-#### Base Types
-
-Values of the base types can be numbers, which we represent as
-multi-precision fractions, strings, Booleans and labels.  When
-analysing the program we also need to allow for places where no value
-is meaningful (type `Tnone`) and where we don't know what type to
-expect yet (type is `NULL`).
-
-Values are never shared, they are always copied when used, and freed
-when no longer needed.
-
-When propagating type information around the program, we need to
-determine if two types are compatible, where type `NULL` is compatible
-with anything.  There are two special cases with type compatibility,
-both related to the Conditional Statement which will be described
-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.
+###### forward decls
+       static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
+                                           struct type *type, int rules);
+###### core functions
 
-###### type functions
+       static struct type *__propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
+                                             struct type *type, int rules)
+       {
+               struct type *t;
 
-       int (*compat)(struct type *this, struct type *other);
+               if (!prog)
+                       return Tnone;
 
-###### ast functions
+               switch (prog->type) {
+               case Xbinode:
+               {
+                       struct binode *b = cast(binode, prog);
+                       switch (b->op) {
+                       ## propagate binode cases
+                       }
+                       break;
+               }
+               ## propagate exec cases
+               }
+               return Tnone;
+       }
 
-       static int type_compat(struct type *require, struct type *have, int rules)
+       static struct type *propagate_types(struct exec *prog, struct parse_context *c, enum prop_err *perr,
+                                           struct type *type, int rules)
        {
-               if ((rules & Rboolok) && have == Tbool)
-                       return 1;       // NOTEST
-               if ((rules & Rnolabel) && have == Tlabel)
-                       return 0;       // NOTEST
-               if (!require || !have)
-                       return 1;
-
-               if (require->compat)
-                       return require->compat(require, have);
+               int pre_err = c->parse_error;
+               struct type *ret = __propagate_types(prog, c, perr, type, rules);
 
-               return require == have;
+               if (c->parse_error > pre_err)
+                       *perr |= Efail;
+               return ret;
        }
 
-###### includes
-       #include <gmp.h>
-       #include "parse_string.h"
-       #include "parse_number.h"
+#### Interpreting
 
-###### libs
-       myLDLIBS := libnumber.o libstring.o -lgmp
-       LDLIBS := $(filter-out $(myLDLIBS),$(LDLIBS)) $(myLDLIBS)
+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 `main` function
+which needs to look at command line arguments.  This function will be
+interpreted separately.
 
-###### type union fields
-       enum vtype {Vnone, Vstr, Vnum, Vbool, Vlabel} vtype;
+Each `exec` can return a value combined with a type in `struct lrval`.
+The type may be `Tnone` but must be non-NULL.  Some `exec`s will return
+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`.
 
-###### value union fields
-       struct text str;
-       mpq_t num;
-       unsigned char bool;
-       void *label;
+###### forward decls
+       static struct value interp_exec(struct parse_context *c, struct exec *e,
+                                       struct type **typeret);
+###### core functions
 
-###### ast functions
-       static void _free_value(struct type *type, struct value *v)
-       {
-               if (!v)
-                       return;         // NOTEST
-               switch (type->vtype) {
-               case Vnone: break;
-               case Vstr: free(v->str.txt); break;
-               case Vnum: mpq_clear(v->num); break;
-               case Vlabel:
-               case Vbool: break;
-               }
-       }
+       struct lrval {
+               struct type *type;
+               struct value rval, *lval;
+       };
 
-###### value functions
+       /* If dest is passed, dtype must give the expected type, and
+        * result can go there, in which case type is returned as NULL.
+        */
+       static struct lrval _interp_exec(struct parse_context *c, struct exec *e,
+                                        struct value *dest, struct type *dtype);
 
-       static void _val_init(struct type *type, struct value *val)
+       static struct value interp_exec(struct parse_context *c, struct exec *e,
+                                       struct type **typeret)
        {
-               switch(type->vtype) {
-               case Vnone:             // NOTEST
-                       break;          // NOTEST
-               case Vnum:
-                       mpq_init(val->num); break;
-               case Vstr:
-                       val->str.txt = malloc(1);
-                       val->str.len = 0;
-                       break;
-               case Vbool:
-                       val->bool = 0;
-                       break;
-               case Vlabel:
-                       val->label = NULL;
-                       break;
-               }
+               struct lrval ret = _interp_exec(c, e, NULL, NULL);
+
+               if (!ret.type) abort();
+               if (typeret)
+                       *typeret = ret.type;
+               if (ret.lval)
+                       dup_value(ret.type, ret.lval, &ret.rval);
+               return ret.rval;
        }
 
-       static void _dup_value(struct type *type,
-                              struct value *vold, struct value *vnew)
+       static struct value *linterp_exec(struct parse_context *c, struct exec *e,
+                                         struct type **typeret)
        {
-               switch (type->vtype) {
-               case Vnone:             // NOTEST
-                       break;          // NOTEST
-               case Vlabel:
-                       vnew->label = vold->label;
-                       break;
-               case Vbool:
-                       vnew->bool = vold->bool;
-                       break;
-               case Vnum:
-                       mpq_init(vnew->num);
-                       mpq_set(vnew->num, vold->num);
-                       break;
-               case Vstr:
-                       vnew->str.len = vold->str.len;
-                       vnew->str.txt = malloc(vnew->str.len);
-                       memcpy(vnew->str.txt, vold->str.txt, vnew->str.len);
-                       break;
-               }
+               struct lrval ret = _interp_exec(c, e, NULL, NULL);
+
+               if (!ret.type) abort();
+               if (ret.lval)
+                       *typeret = ret.type;
+               else
+                       free_value(ret.type, &ret.rval);
+               return ret.lval;
        }
 
-       static int _value_cmp(struct type *tl, struct type *tr,
-                             struct value *left, struct value *right)
+       /* dinterp_exec is used when the destination type is certain and
+        * the value has a place to go.
+        */
+       static void dinterp_exec(struct parse_context *c, struct exec *e,
+                                struct value *dest, struct type *dtype,
+                                int need_free)
        {
-               int cmp;
-               if (tl != tr)
-                       return tl - tr; // NOTEST
-               switch (tl->vtype) {
-               case Vlabel: cmp = left->label == right->label ? 0 : 1; break;
-               case Vnum: cmp = mpq_cmp(left->num, right->num); break;
-               case Vstr: cmp = text_cmp(left->str, right->str); break;
-               case Vbool: cmp = left->bool - right->bool; break;
-               case Vnone: cmp = 0;                    // NOTEST
-               }
-               return cmp;
+               struct lrval ret = _interp_exec(c, e, dest, dtype);
+               if (!ret.type)
+                       return;
+               if (need_free)
+                       free_value(dtype, dest);
+               if (ret.lval)
+                       dup_value(dtype, ret.lval, dest);
+               else
+                       memcpy(dest, &ret.rval, dtype->size);
        }
 
-       static void _print_value(struct type *type, struct value *v, FILE *f)
+       static struct lrval _interp_exec(struct parse_context *c, struct exec *e,
+                                        struct value *dest, struct type *dtype)
        {
-               switch (type->vtype) {
-               case Vnone:                             // NOTEST
-                       fprintf(f, "*no-value*"); break;        // NOTEST
-               case Vlabel:                            // NOTEST
-                       fprintf(f, "*label-%p*", v->label); break; // NOTEST
-               case Vstr:
-                       fprintf(f, "%.*s", v->str.len, v->str.txt); break;
-               case Vbool:
-                       fprintf(f, "%s", v->bool ? "True":"False"); break;
-               case Vnum:
-                       {
-                       mpf_t fl;
-                       mpf_init2(fl, 20);
-                       mpf_set_q(fl, v->num);
-                       gmp_fprintf(f, "%Fg", fl);
-                       mpf_clear(fl);
-                       break;
+               /* If the result is copied to dest, ret.type is set to NULL */
+               struct lrval ret;
+               struct value rv = {}, *lrv = NULL;
+               struct type *rvtype;
+
+               rvtype = ret.type = Tnone;
+               if (!e) {
+                       ret.lval = lrv;
+                       ret.rval = rv;
+                       return ret;
+               }
+
+               switch(e->type) {
+               case Xbinode:
+               {
+                       struct binode *b = cast(binode, e);
+                       struct value left, right, *lleft;
+                       struct type *ltype, *rtype;
+                       ltype = rtype = Tnone;
+                       switch (b->op) {
+                       ## interp binode cases
                        }
+                       free_value(ltype, &left);
+                       free_value(rtype, &right);
+                       break;
+               }
+               ## interp exec cases
+               }
+               if (rvtype) {
+                       ret.lval = lrv;
+                       ret.rval = rv;
+                       ret.type = rvtype;
                }
+               ## interp exec cleanup
+               return ret;
        }
 
-       static void _free_value(struct type *type, struct value *v);
+### Types
 
-       static struct type base_prototype = {
-               .init = _val_init,
-               .print = _print_value,
-               .cmp_order = _value_cmp,
-               .cmp_eq = _value_cmp,
-               .dup = _dup_value,
-               .free = _free_value,
-       };
+Values come in a wide range of types, with more likely to be added.
+Each type needs to be able to print its own values (for convenience at
+least) as well as to compare two values, at least for equality and
+possibly for order.  For now, values might need to be duplicated and
+freed, though eventually such manipulations will be better integrated
+into the language.
 
-       static struct type *Tbool, *Tstr, *Tnum, *Tnone, *Tlabel;
-
-###### ast functions
-       static struct type *add_base_type(struct parse_context *c, char *n,
-                                         enum vtype vt, int size)
-       {
-               struct text txt = { n, strlen(n) };
-               struct type *t;
-
-               t = add_type(c, txt, &base_prototype);
-               t->vtype = vt;
-               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;       // NOTEST
-               return t;
-       }
-
-###### context initialization
-
-       Tbool  = add_base_type(&context, "Boolean", Vbool, sizeof(char));
-       Tstr   = add_base_type(&context, "string", Vstr, sizeof(struct text));
-       Tnum   = add_base_type(&context, "number", Vnum, sizeof(mpq_t));
-       Tnone  = add_base_type(&context, "none", Vnone, 0);
-       Tlabel = add_base_type(&context, "label", Vlabel, sizeof(void*));
+Rather than requiring every numeric type to support all numeric
+operations (add, multiply, etc), we allow types to be able to present
+as one of a few standard types: integer, float, and fraction.  The
+existence of these conversion functions eventually enable types to
+determine if they are compatible with other types, though such types
+have not yet been implemented.
 
-### Variables
+Named type are stored in a simple linked list.  Objects of each type are
+"values" which are often passed around by value.
 
-Variables are scoped named values.  We store the names in a linked list
-of "bindings" sorted in lexical order, and use sequential search and
-insertion sort.
+There are both explicitly named types, and anonymous types.  Anonymous
+cannot be accessed by name, but are used internally and have a name
+which might be reported in error messages.
 
 ###### ast
 
-       struct binding {
-               struct text name;
-               struct binding *next;   // in lexical order
-               ## binding fields
+       struct value {
+               union {
+                       char ptr[1];
+                       ## value union fields
+               };
        };
 
-This linked list is stored in the parse context so that "reduce"
-functions can find or add variables, and so the analysis phase can
-ensure that every variable gets a type.
+###### ast late
+       struct type {
+               struct text name;
+               struct type *next;
+               struct token first_use;
+               int size, align;
+               int anon;
+               void (*init)(struct type *type, struct value *val);
+               int (*prepare_type)(struct parse_context *c, struct type *type, int parse_time);
+               void (*print)(struct type *type, struct value *val, FILE *f);
+               void (*print_type)(struct type *type, FILE *f);
+               int (*cmp_order)(struct type *t1, struct type *t2,
+                                struct value *v1, struct value *v2);
+               int (*cmp_eq)(struct type *t1, struct type *t2,
+                             struct value *v1, struct value *v2);
+               void (*dup)(struct type *type, struct value *vold, struct value *vnew);
+               int (*test)(struct type *type, struct value *val);
+               void (*free)(struct type *type, struct value *val);
+               void (*free_type)(struct type *t);
+               long long (*to_int)(struct value *v);
+               double (*to_float)(struct value *v);
+               int (*to_mpq)(mpq_t *q, struct value *v);
+               ## type functions
+               union {
+                       ## type union fields
+               };
+       };
 
 ###### parse context
 
-       struct binding *varlist;  // In lexical order
+       struct type *typelist;
+
+###### includes
+       #include <stdarg.h>
 
 ###### ast functions
 
-       static struct binding *find_binding(struct parse_context *c, struct text s)
+       static struct type *find_type(struct parse_context *c, struct text s)
        {
-               struct binding **l = &c->varlist;
-               struct binding *n;
-               int cmp = 1;
+               struct type *t = c->typelist;
+
+               while (t && (t->anon ||
+                            text_cmp(t->name, s) != 0))
+                               t = t->next;
+               return t;
+       }
+
+       static struct type *_add_type(struct parse_context *c, struct text s,
+                                    struct type *proto, int anon)
+       {
+               struct type *n;
 
-               while (*l &&
-                       (cmp = text_cmp((*l)->name, s)) < 0)
-                               l = & (*l)->next;
-               if (cmp == 0)
-                       return *l;
                n = calloc(1, sizeof(*n));
+               if (proto)
+                       *n = *proto;
+               else
+                       n->size = -1;
                n->name = s;
-               n->next = *l;
-               *l = n;
+               n->anon = anon;
+               n->next = c->typelist;
+               c->typelist = n;
                return n;
        }
 
-Each name can be linked to multiple variables defined in different
-scopes.  Each scope starts where the name is declared and continues
-until the end of the containing code block.  Scopes of a given name
-cannot nest, so a declaration while a name is in-scope is an error.
-
-###### binding fields
-       struct variable *var;
-
-###### ast
-       struct variable {
-               struct variable *previous;
-               struct type *type;
-               struct binding *name;
-               struct exec *where_decl;// where name was declared
-               struct exec *where_set; // where type was set
-               ## variable fields
-       };
+       static struct type *add_type(struct parse_context *c, struct text s,
+                                     struct type *proto)
+       {
+               return _add_type(c, s, proto, 0);
+       }
 
-When a scope closes, the values of the variables might need to be freed.
-This happens in the context of some `struct exec` and each `exec` will
-need to know which variables need to be freed when it completes.
+       static struct type *add_anon_type(struct parse_context *c,
+                                         struct type *proto, char *name, ...)
+       {
+               struct text t;
+               va_list ap;
+
+               va_start(ap, name);
+               vasprintf(&t.txt, name, ap);
+               va_end(ap);
+               t.len = strlen(t.txt);
+               return _add_type(c, t, proto, 1);
+       }
 
-####### exec fields
-       struct variable *to_free;
+       static struct type *find_anon_type(struct parse_context *c,
+                                          struct type *proto, char *name, ...)
+       {
+               struct type *t = c->typelist;
+               struct text nm;
+               va_list ap;
+
+               va_start(ap, name);
+               vasprintf(&nm.txt, name, ap);
+               va_end(ap);
+               nm.len = strlen(name);
+
+               while (t && (!t->anon ||
+                            text_cmp(t->name, nm) != 0))
+                               t = t->next;
+               if (t) {
+                       free(nm.txt);
+                       return t;
+               }
+               return _add_type(c, nm, proto, 1);
+       }
 
-####### variable fields
-       struct exec *cleanup_exec;
-       struct variable *next_free;
+       static void free_type(struct type *t)
+       {
+               /* The type is always a reference to something in the
+                * context, so we don't need to free anything.
+                */
+       }
 
-####### interp exec cleanup
+       static void free_value(struct type *type, struct value *v)
        {
-               struct variable *v;
-               for (v = e->to_free; v; v = v->next_free) {
-                       struct value *val = var_value(c, v);
-                       free_value(v->type, val);
+               if (type && v) {
+                       type->free(type, v);
+                       memset(v, 0x5a, type->size);
                }
        }
 
-###### ast functions
-       static void variable_unlink_exec(struct variable *v)
+       static void type_print(struct type *type, FILE *f)
        {
-               struct variable **vp;
-               if (!v->cleanup_exec)
-                       return;
-               for (vp = &v->cleanup_exec->to_free;
-                   *vp; vp = &(*vp)->next_free) {
-                       if (*vp != v)
-                               continue;
-                       *vp = v->next_free;
-                       v->cleanup_exec = NULL;
-                       break;
-               }
+               if (!type)
+                       fputs("*unknown*type*", f);     // NOTEST
+               else if (type->name.len && !type->anon)
+                       fprintf(f, "%.*s", type->name.len, type->name.txt);
+               else if (type->print_type)
+                       type->print_type(type, f);
+               else if (type->name.len && type->anon)
+                       fprintf(f, "\"%.*s\"", type->name.len, type->name.txt);
+               else
+                       fputs("*invalid*type*", f);     // NOTEST
        }
 
-While the naming seems strange, we include local constants in the
-definition of variables.  A name declared `var := value` can
-subsequently be changed, but a name declared `var ::= value` cannot -
-it is constant
+       static void val_init(struct type *type, struct value *val)
+       {
+               if (type && type->init)
+                       type->init(type, val);
+       }
 
-###### variable fields
-       int constant;
+       static void dup_value(struct type *type,
+                             struct value *vold, struct value *vnew)
+       {
+               if (type && type->dup)
+                       type->dup(type, vold, vnew);
+       }
 
-Scopes in parallel branches can be partially merged.  More
-specifically, if a given name is declared in both branches of an
-if/else then its scope is a candidate for merging.  Similarly if
-every branch of an exhaustive switch (e.g. has an "else" clause)
-declares a given name, then the scopes from the branches are
-candidates for merging.
+       static int value_cmp(struct type *tl, struct type *tr,
+                            struct value *left, struct value *right)
+       {
+               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;                              // NOTEST
+       }
 
-Note that names declared inside a loop (which is only parallel to
-itself) are never visible after the loop.  Similarly names defined in
-scopes which are not parallel, such as those started by `for` and
-`switch`, are never visible after the scope.  Only variables defined in
-both `then` and `else` (including the implicit then after an `if`, and
-excluding `then` used with `for`) and in all `case`s and `else` of a
-`switch` or `while` can be visible beyond the `if`/`switch`/`while`.
+       static void print_value(struct type *type, struct value *v, FILE *f)
+       {
+               if (type && type->print)
+                       type->print(type, v, f);
+               else
+                       fprintf(f, "*Unknown*");                // NOTEST
+       }
 
-Labels, which are a bit like variables, follow different rules.
-Labels are not explicitly declared, but if an undeclared name appears
-in a context where a label is legal, that effectively declares the
-name as a label.  The declaration remains in force (or in scope) at
-least to the end of the immediately containing block and conditionally
-in any larger containing block which does not declare the name in some
-other way.  Importantly, the conditional scope extension happens even
-if the label is only used in one parallel branch of a conditional --
-when used in one branch it is treated as having been declared in all
-branches.
+       static void prepare_types(struct parse_context *c)
+       {
+               struct type *t;
+               int retry = 1;
+               enum { none, some, cannot } progress = none;
+
+               while (retry) {
+                       retry = 0;
+
+                       for (t = c->typelist; t; t = t->next) {
+                               if (t->size < 0)
+                                       tok_err(c, "error: type used but not declared",
+                                                &t->first_use);
+                               if (t->size == 0 && t->prepare_type) {
+                                       if (t->prepare_type(c, t, 1))
+                                               progress = some;
+                                       else if (progress == cannot)
+                                               tok_err(c, "error: type has recursive definition",
+                                                       &t->first_use);
+                                       else
+                                               retry = 1;
+                               }
+                       }
+                       switch (progress) {
+                       case cannot:
+                               retry = 0; break;
+                       case none:
+                               progress = cannot; break;
+                       case some:
+                               progress = none; break;
+                       }
+               }
+       }
 
-Merge candidates are tentatively visible beyond the end of the
-branching statement which creates them.  If the name is used, the
-merge is affirmed and they become a single variable visible at the
-outer layer.  If not - if it is redeclared first - the merge lapses.
+###### forward decls
 
-To track scopes we have an extra stack, implemented as a linked list,
-which roughly parallels the parse stack and which is used exclusively
-for scoping.  When a new scope is opened, a new frame is pushed and
-the child-count of the parent frame is incremented.  This child-count
-is used to distinguish between the first of a set of parallel scopes,
-in which declared variables must not be in scope, and subsequent
-branches, whether they may already be conditionally scoped.
+       static void free_value(struct type *type, struct value *v);
+       static int type_compat(struct type *require, struct type *have, int rules);
+       static void type_print(struct type *type, FILE *f);
+       static void val_init(struct type *type, struct value *v);
+       static void dup_value(struct type *type,
+                             struct value *vold, struct value *vnew);
+       static int value_cmp(struct type *tl, struct type *tr,
+                            struct value *left, struct value *right);
+       static void print_value(struct type *type, struct value *v, FILE *f);
 
-We need a total ordering of scopes so we can easily compare to variables
-to see if they are concurrently in scope.  To achieve this we record a
-`scope_count` which is actually a count of both beginnings and endings
-of scopes.  Then each variable has a record of the scope count where it
-enters scope, and where it leaves.
+###### free context types
 
-To push a new frame *before* any code in the frame is parsed, we need a
-grammar reduction.  This is most easily achieved with a grammar
-element which derives the empty string, and creates the new scope when
-it is recognised.  This can be placed, for example, between a keyword
-like "if" and the code following it.
-
-###### ast
-       struct scope {
-               struct scope *parent;
-               int child_count;
-       };
-
-###### parse context
-       int scope_depth;
-       int scope_count;
-       struct scope *scope_stack;
-
-###### variable fields
-       int scope_start, scope_end;
-
-###### ast functions
-       static void scope_pop(struct parse_context *c)
-       {
-               struct scope *s = c->scope_stack;
+       while (context.typelist) {
+               struct type *t = context.typelist;
 
-               c->scope_stack = s->parent;
-               free(s);
-               c->scope_depth -= 1;
-               c->scope_count += 1;
+               context.typelist = t->next;
+               if (t->free_type)
+                       t->free_type(t);
+               if (t->anon)
+                       free(t->name.txt);
+               free(t);
        }
 
-       static void scope_push(struct parse_context *c)
-       {
-               struct scope *s = calloc(1, sizeof(*s));
-               if (c->scope_stack)
-                       c->scope_stack->child_count += 1;
-               s->parent = c->scope_stack;
-               c->scope_stack = s;
-               c->scope_depth += 1;
-               c->scope_count += 1;
-       }
+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
 
-       $void
-       OpenScope -> ${ scope_push(c); }$
+       $*type
+       Type -> IDENTIFIER ${
+               $0 = find_type(c, $ID.txt);
+               if (!$0) {
+                       $0 = add_type(c, $ID.txt, NULL);
+                       $0->first_use = $ID;
+               }
+       }$
+       ## type grammar
 
-Each variable records a scope depth and is in one of four states:
+       FormalType -> Type ${ $0 = $<1; }$
+       ## formal type grammar
 
-- "in scope".  This is the case between the declaration of the
-  variable and the end of the containing block, and also between
-  the usage with affirms a merge and the end of that block.
+#### Base Types
 
-  The scope depth is not greater than the current parse context scope
-  nest depth.  When the block of that depth closes, the state will
-  change.  To achieve this, all "in scope" variables are linked
-  together as a stack in nesting order.
+Values of the base types can be numbers, which we represent as
+multi-precision fractions, strings, Booleans and labels.  When
+analysing the program we also need to allow for places where no value
+is meaningful (type `Tnone`) and where we don't know what type to
+expect yet (type is `NULL`).
 
-- "pending".  The "in scope" block has closed, but other parallel
-  scopes are still being processed.  So far, every parallel block at
-  the same level that has closed has declared the name.
+Values are never shared, they are always copied when used, and freed
+when no longer needed.
 
-  The scope depth is the depth of the last parallel block that
-  enclosed the declaration, and that has closed.
+When propagating type information around the program, we need to
+determine if two types are compatible, where type `NULL` is compatible
+with anything.  There are two special cases with type compatibility,
+both related to the Conditional Statement which will be described
+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.
 
-- "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 (`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.
+###### type functions
 
-- "out of scope".  The variable is neither in scope nor conditionally
-  in scope.  It is permanently out of scope now and can be removed from
-  the "in scope" stack.  When a variable becomes out-of-scope it is
-  moved to a separate list (`out_scope`) of variables which have fully
-  known scope.  This will be used at the end of each function to assign
-  each variable a place in the stack frame.
+       int (*compat)(struct type *this, struct type *other);
 
-###### variable fields
-       int depth, min_depth;
-       enum { OutScope, PendingScope, CondScope, InScope } scope;
-       struct variable *in_scope;
+###### ast functions
 
-###### parse context
+       static int type_compat(struct type *require, struct type *have, int rules)
+       {
+               if ((rules & Rboolok) && have == Tbool)
+                       return 1;       // NOTEST
+               if (!require || !have)
+                       return 1;
 
-       struct variable *in_scope;
-       struct variable *out_scope;
+               if (require->compat)
+                       return require->compat(require, have);
 
-All variables with the same name are linked together using the
-'previous' link.  Those variable that have been affirmatively merged all
-have a 'merged' pointer that points to one primary variable - the most
-recently declared instance.  When merging variables, we need to also
-adjust the 'merged' pointer on any other variables that had previously
-been merged with the one that will no longer be primary.
+               return require == have;
+       }
 
-A variable that is no longer the most recent instance of a name may
-still have "pending" scope, if it might still be merged with most
-recent instance.  These variables don't really belong in the
-"in_scope" list, but are not immediately removed when a new instance
-is found.  Instead, they are detected and ignored when considering the
-list of in_scope names.
+###### includes
+       #include <gmp.h>
+       #include "parse_string.h"
+       #include "parse_number.h"
 
-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.
+###### libs
+       myLDLIBS := libnumber.o libstring.o -lgmp
+       LDLIBS := $(filter-out $(myLDLIBS),$(LDLIBS)) $(myLDLIBS)
 
-###### variable fields
-       struct variable *merged;
+###### type union fields
+       enum vtype {Vnone, Vstr, Vnum, Vbool, Vlabel} vtype;
 
-###### ast functions
+###### value union fields
+       struct text str;
+       mpq_t num;
+       unsigned char bool;
+       int label;
 
-       static void variable_merge(struct variable *primary, struct variable *secondary)
+###### ast functions
+       static void _free_value(struct type *type, struct value *v)
        {
-               struct variable *v;
-
-               primary = primary->merged;
-
-               for (v = primary->previous; v; v=v->previous)
-                       if (v == secondary || v == secondary->merged ||
-                           v->merged == secondary ||
-                           v->merged == secondary->merged) {
-                               v->scope = OutScope;
-                               v->merged = primary;
-                               if (v->scope_start < primary->scope_start)
-                                       primary->scope_start = v->scope_start;
-                               if (v->scope_end > primary->scope_end)
-                                       primary->scope_end = v->scope_end;      // NOTEST
-                               variable_unlink_exec(v);
-                       }
+               if (!v)
+                       return;         // NOTEST
+               switch (type->vtype) {
+               case Vnone: break;
+               case Vstr: free(v->str.txt); break;
+               case Vnum: mpq_clear(v->num); break;
+               case Vlabel:
+               case Vbool: break;
+               }
        }
 
-###### forward decls
-       static struct value *var_value(struct parse_context *c, struct variable *v);
-
-###### free global vars
+###### value functions
 
-       while (context.varlist) {
-               struct binding *b = context.varlist;
-               struct variable *v = b->var;
-               context.varlist = b->next;
-               free(b);
-               while (v) {
-                       struct variable *next = v->previous;
+       static void _val_init(struct type *type, struct value *val)
+       {
+               switch(type->vtype) {
+               case Vnone:             // NOTEST
+                       break;          // NOTEST
+               case Vnum:
+                       mpq_init(val->num); break;
+               case Vstr:
+                       val->str.txt = malloc(1);
+                       val->str.len = 0;
+                       break;
+               case Vbool:
+                       val->bool = 0;
+                       break;
+               case Vlabel:
+                       val->label = 0; // NOTEST
+                       break;          // NOTEST
+               }
+       }
 
-                       if (v->global) {
-                               free_value(v->type, var_value(&context, v));
-                               if (v->depth == 0)
-                                       // This is a global constant
-                                       free_exec(v->where_decl);
-                       }
-                       free(v);
-                       v = next;
+       static void _dup_value(struct type *type,
+                              struct value *vold, struct value *vnew)
+       {
+               switch (type->vtype) {
+               case Vnone:             // NOTEST
+                       break;          // NOTEST
+               case Vlabel:
+                       vnew->label = vold->label;      // NOTEST
+                       break;          // NOTEST
+               case Vbool:
+                       vnew->bool = vold->bool;
+                       break;
+               case Vnum:
+                       mpq_init(vnew->num);
+                       mpq_set(vnew->num, vold->num);
+                       break;
+               case Vstr:
+                       vnew->str.len = vold->str.len;
+                       vnew->str.txt = malloc(vnew->str.len);
+                       memcpy(vnew->str.txt, vold->str.txt, vnew->str.len);
+                       break;
                }
        }
 
-#### Manipulating Bindings
+       static int _value_cmp(struct type *tl, struct type *tr,
+                             struct value *left, struct value *right)
+       {
+               int cmp;
+               if (tl != tr)
+                       return tl - tr; // NOTEST
+               switch (tl->vtype) {
+               case Vlabel: cmp = left->label == right->label ? 0 : 1; break;
+               case Vnum: cmp = mpq_cmp(left->num, right->num); break;
+               case Vstr: cmp = text_cmp(left->str, right->str); break;
+               case Vbool: cmp = left->bool - right->bool; break;
+               case Vnone: cmp = 0;                    // NOTEST
+               }
+               return cmp;
+       }
 
-When a name is conditionally visible, a new declaration discards the old
-binding - the condition lapses.  Similarly when we reach the end of a
-function (outermost non-global scope) any conditional scope must lapse.
-Conversely a usage of the name affirms the visibility and extends it to
-the end of the containing block - i.e.  the block that contains both the
+       static void _print_value(struct type *type, struct value *v, FILE *f)
+       {
+               switch (type->vtype) {
+               case Vnone:                             // NOTEST
+                       fprintf(f, "*no-value*"); break;        // NOTEST
+               case Vlabel:                            // NOTEST
+                       fprintf(f, "*label-%d*", v->label); break; // NOTEST
+               case Vstr:
+                       fprintf(f, "%.*s", v->str.len, v->str.txt); break;
+               case Vbool:
+                       fprintf(f, "%s", v->bool ? "True":"False"); break;
+               case Vnum:
+                       {
+                       mpf_t fl;
+                       mpf_init2(fl, 20);
+                       mpf_set_q(fl, v->num);
+                       gmp_fprintf(f, "%.10Fg", fl);
+                       mpf_clear(fl);
+                       break;
+                       }
+               }
+       }
+
+       static void _free_value(struct type *type, struct value *v);
+
+       static int bool_test(struct type *type, struct value *v)
+       {
+               return v->bool;
+       }
+
+       static struct type base_prototype = {
+               .init = _val_init,
+               .print = _print_value,
+               .cmp_order = _value_cmp,
+               .cmp_eq = _value_cmp,
+               .dup = _dup_value,
+               .free = _free_value,
+       };
+
+       static struct type *Tbool, *Tstr, *Tnum, *Tnone, *Tlabel;
+
+###### ast functions
+       static struct type *add_base_type(struct parse_context *c, char *n,
+                                         enum vtype vt, int size)
+       {
+               struct text txt = { n, strlen(n) };
+               struct type *t;
+
+               t = add_type(c, txt, &base_prototype);
+               t->vtype = vt;
+               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;       // NOTEST
+               return t;
+       }
+
+###### context initialization
+
+       Tbool  = add_base_type(&context, "Boolean", Vbool, sizeof(char));
+       Tbool->test = bool_test;
+       Tstr   = add_base_type(&context, "string", Vstr, sizeof(struct text));
+       Tnum   = add_base_type(&context, "number", Vnum, sizeof(mpq_t));
+       Tnone  = add_base_type(&context, "none", Vnone, 0);
+       Tlabel = add_base_type(&context, "label", Vlabel, sizeof(void*));
+
+##### Base Values
+
+We have already met values as separate objects.  When manifest constants
+appear in the program text, that must result in an executable which has
+a constant value.  So the `val` structure embeds a value in an
+executable.
+
+###### exec type
+       Xval,
+
+###### ast
+       struct val {
+               struct exec;
+               struct type *vtype;
+               struct value val;
+       };
+
+###### ast functions
+       struct val *new_val(struct type *T, struct token tk)
+       {
+               struct val *v = new_pos(val, tk);
+               v->vtype = T;
+               return v;
+       }
+
+###### declare terminals
+       $TERM True False
+
+###### Grammar
+
+       $*val
+       Value ->  True ${
+               $0 = new_val(Tbool, $1);
+               $0->val.bool = 1;
+       }$
+       | False ${
+               $0 = new_val(Tbool, $1);
+               $0->val.bool = 0;
+       }$
+       | NUMBER ${ {
+               char tail[3];
+               $0 = new_val(Tnum, $1);
+               if (number_parse($0->val.num, tail, $1.txt) == 0)
+                       mpq_init($0->val.num);  // UNTESTED
+                       if (tail[0])
+                               tok_err(c, "error: unsupported number suffix",
+                                       &$1);
+       } }$
+       | STRING ${ {
+               char tail[3];
+               $0 = new_val(Tstr, $1);
+               string_parse(&$1, '\\', &$0->val.str, tail);
+               if (tail[0])
+                       tok_err(c, "error: unsupported string suffix",
+                               &$1);
+       } }$
+       | MULTI_STRING ${ {
+               char tail[3];
+               $0 = new_val(Tstr, $1);
+               string_parse(&$1, '\\', &$0->val.str, tail);
+               if (tail[0])
+                       tok_err(c, "error: unsupported string suffix",
+                               &$1);
+       } }$
+
+###### print exec cases
+       case Xval:
+       {
+               struct val *v = cast(val, e);
+               if (v->vtype == Tstr)
+                       printf("\"");
+               // FIXME how to ensure numbers have same precision.
+               print_value(v->vtype, &v->val, stdout);
+               if (v->vtype == Tstr)
+                       printf("\"");
+               break;
+       }
+
+###### propagate exec cases
+       case Xval:
+       {
+               struct val *val = cast(val, prog);
+               if (!type_compat(type, val->vtype, rules))
+                       type_err(c, "error: expected %1 found %2",
+                                  prog, type, rules, val->vtype);
+               return val->vtype;
+       }
+
+###### interp exec cases
+       case Xval:
+               rvtype = cast(val, e)->vtype;
+               dup_value(rvtype, &cast(val, e)->val, &rv);
+               break;
+
+###### ast functions
+       static void free_val(struct val *v)
+       {
+               if (v)
+                       free_value(v->vtype, &v->val);
+               free(v);
+       }
+
+###### free exec cases
+       case Xval: free_val(cast(val, e)); break;
+
+###### ast functions
+       // Move all nodes from 'b' to 'rv', reversing their order.
+       // In 'b' 'left' is a list, and 'right' is the last node.
+       // In 'rv', left' is the first node and 'right' is a list.
+       static struct binode *reorder_bilist(struct binode *b)
+       {
+               struct binode *rv = NULL;
+
+               while (b) {
+                       struct exec *t = b->right;
+                       b->right = rv;
+                       rv = b;
+                       if (b->left)
+                               b = cast(binode, b->left);
+                       else
+                               b = NULL;
+                       rv->left = t;
+               }
+               return rv;
+       }
+
+#### Labels
+
+Labels are a temporary concept until I implement enums.  There are an
+anonymous enum which is declared by usage.  Thet are only allowed in
+`use` statements and corresponding `case` entries.  They appear as a
+period followed by an identifier.  All identifiers that are "used" must
+have a "case".
+
+For now, we have a global list of labels, and don't check that all "use"
+match "case".
+
+###### exec type
+       Xlabel,
+
+###### ast
+       struct label {
+               struct exec;
+               struct text name;
+               int value;
+       };
+###### free exec cases
+       case Xlabel:
+               free(e);
+               break;
+###### print exec cases
+       case Xlabel: {
+               struct label *l = cast(label, e);
+               printf(".%.*s", l->name.len, l->name.txt);
+               break;
+       }
+
+###### ast
+       struct labels {
+               struct labels *next;
+               struct text name;
+               int value;
+       };
+###### parse context
+       struct labels *labels;
+       int next_label;
+###### ast functions
+       static int label_lookup(struct parse_context *c, struct text name)
+       {
+               struct labels *l, **lp = &c->labels;
+               while (*lp && text_cmp((*lp)->name, name) < 0)
+                       lp = &(*lp)->next;
+               if (*lp && text_cmp((*lp)->name, name) == 0)
+                       return (*lp)->value;
+               l = calloc(1, sizeof(*l));
+               l->next = *lp;
+               l->name = name;
+               if (c->next_label == 0)
+                       c->next_label = 2;
+               l->value = c->next_label;
+               c->next_label += 1;
+               *lp = l;
+               return l->value;
+       }
+
+###### free context storage
+       while (context.labels) {
+               struct labels *l = context.labels;
+               context.labels = l->next;
+               free(l);
+       }
+
+###### declare terminals
+       $TERM .
+###### term grammar
+       | . IDENTIFIER ${ {
+               struct label *l = new_pos(label, $ID);
+               l->name = $ID.txt;
+               $0 = l;
+       } }$
+###### propagate exec cases
+       case Xlabel: {
+               struct label *l = cast(label, prog);
+               l->value = label_lookup(c, l->name);
+               if (!type_compat(type, Tlabel, rules))
+                       type_err(c, "error: expected %1 found %2",
+                                prog, type, rules, Tlabel);
+               return Tlabel;
+       }
+###### interp exec cases
+       case Xlabel : {
+               struct label *l = cast(label, e);
+               rv.label = l->value;
+               rvtype = Tlabel;
+               break;
+       }
+
+
+### Variables
+
+Variables are scoped named values.  We store the names in a linked list
+of "bindings" sorted in lexical order, and use sequential search and
+insertion sort.
+
+###### ast
+
+       struct binding {
+               struct text name;
+               struct binding *next;   // in lexical order
+               ## binding fields
+       };
+
+This linked list is stored in the parse context so that "reduce"
+functions can find or add variables, and so the analysis phase can
+ensure that every variable gets a type.
+
+###### parse context
+
+       struct binding *varlist;  // In lexical order
+
+###### ast functions
+
+       static struct binding *find_binding(struct parse_context *c, struct text s)
+       {
+               struct binding **l = &c->varlist;
+               struct binding *n;
+               int cmp = 1;
+
+               while (*l &&
+                       (cmp = text_cmp((*l)->name, s)) < 0)
+                               l = & (*l)->next;
+               if (cmp == 0)
+                       return *l;
+               n = calloc(1, sizeof(*n));
+               n->name = s;
+               n->next = *l;
+               *l = n;
+               return n;
+       }
+
+Each name can be linked to multiple variables defined in different
+scopes.  Each scope starts where the name is declared and continues
+until the end of the containing code block.  Scopes of a given name
+cannot nest, so a declaration while a name is in-scope is an error.
+
+###### binding fields
+       struct variable *var;
+
+###### ast
+       struct variable {
+               struct variable *previous;
+               struct type *type;
+               struct binding *name;
+               struct exec *where_decl;// where name was declared
+               struct exec *where_set; // where type was set
+               ## variable fields
+       };
+
+When a scope closes, the values of the variables might need to be freed.
+This happens in the context of some `struct exec` and each `exec` will
+need to know which variables need to be freed when it completes.
+
+####### exec fields
+       struct variable *to_free;
+
+####### variable fields
+       struct exec *cleanup_exec;
+       struct variable *next_free;
+
+####### interp exec cleanup
+       {
+               struct variable *v;
+               for (v = e->to_free; v; v = v->next_free) {
+                       struct value *val = var_value(c, v);
+                       free_value(v->type, val);
+               }
+       }
+
+###### ast functions
+       static void variable_unlink_exec(struct variable *v)
+       {
+               struct variable **vp;
+               if (!v->cleanup_exec)
+                       return;
+               for (vp = &v->cleanup_exec->to_free;
+                   *vp; vp = &(*vp)->next_free) {
+                       if (*vp != v)
+                               continue;
+                       *vp = v->next_free;
+                       v->cleanup_exec = NULL;
+                       break;
+               }
+       }
+
+While the naming seems strange, we include local constants in the
+definition of variables.  A name declared `var := value` can
+subsequently be changed, but a name declared `var ::= value` cannot -
+it is constant
+
+###### variable fields
+       int constant;
+
+Scopes in parallel branches can be partially merged.  More
+specifically, if a given name is declared in both branches of an
+if/else then its scope is a candidate for merging.  Similarly if
+every branch of an exhaustive switch (e.g. has an "else" clause)
+declares a given name, then the scopes from the branches are
+candidates for merging.
+
+Note that names declared inside a loop (which is only parallel to
+itself) are never visible after the loop.  Similarly names defined in
+scopes which are not parallel, such as those started by `for` and
+`switch`, are never visible after the scope.  Only variables defined in
+both `then` and `else` (including the implicit then after an `if`, and
+excluding `then` used with `for`) and in all `case`s and `else` of a
+`switch` or `while` can be visible beyond the `if`/`switch`/`while`.
+
+Labels, which are a bit like variables, follow different rules.
+Labels are not explicitly declared, but if an undeclared name appears
+in a context where a label is legal, that effectively declares the
+name as a label.  The declaration remains in force (or in scope) at
+least to the end of the immediately containing block and conditionally
+in any larger containing block which does not declare the name in some
+other way.  Importantly, the conditional scope extension happens even
+if the label is only used in one parallel branch of a conditional --
+when used in one branch it is treated as having been declared in all
+branches.
+
+Merge candidates are tentatively visible beyond the end of the
+branching statement which creates them.  If the name is used, the
+merge is affirmed and they become a single variable visible at the
+outer layer.  If not - if it is redeclared first - the merge lapses.
+
+To track scopes we have an extra stack, implemented as a linked list,
+which roughly parallels the parse stack and which is used exclusively
+for scoping.  When a new scope is opened, a new frame is pushed and
+the child-count of the parent frame is incremented.  This child-count
+is used to distinguish between the first of a set of parallel scopes,
+in which declared variables must not be in scope, and subsequent
+branches, whether they may already be conditionally scoped.
+
+We need a total ordering of scopes so we can easily compare to variables
+to see if they are concurrently in scope.  To achieve this we record a
+`scope_count` which is actually a count of both beginnings and endings
+of scopes.  Then each variable has a record of the scope count where it
+enters scope, and where it leaves.
+
+To push a new frame *before* any code in the frame is parsed, we need a
+grammar reduction.  This is most easily achieved with a grammar
+element which derives the empty string, and creates the new scope when
+it is recognised.  This can be placed, for example, between a keyword
+like "if" and the code following it.
+
+###### ast
+       struct scope {
+               struct scope *parent;
+               int child_count;
+       };
+
+###### parse context
+       int scope_depth;
+       int scope_count;
+       struct scope *scope_stack;
+
+###### variable fields
+       int scope_start, scope_end;
+
+###### ast functions
+       static void scope_pop(struct parse_context *c)
+       {
+               struct scope *s = c->scope_stack;
+
+               c->scope_stack = s->parent;
+               free(s);
+               c->scope_depth -= 1;
+               c->scope_count += 1;
+       }
+
+       static void scope_push(struct parse_context *c)
+       {
+               struct scope *s = calloc(1, sizeof(*s));
+               if (c->scope_stack)
+                       c->scope_stack->child_count += 1;
+               s->parent = c->scope_stack;
+               c->scope_stack = s;
+               c->scope_depth += 1;
+               c->scope_count += 1;
+       }
+
+###### Grammar
+
+       $void
+       OpenScope -> ${ scope_push(c); }$
+
+Each variable records a scope depth and is in one of four states:
+
+- "in scope".  This is the case between the declaration of the
+  variable and the end of the containing block, and also between
+  the usage with affirms a merge and the end of that block.
+
+  The scope depth is not greater than the current parse context scope
+  nest depth.  When the block of that depth closes, the state will
+  change.  To achieve this, all "in scope" variables are linked
+  together as a stack in nesting order.
+
+- "pending".  The "in scope" block has closed, but other parallel
+  scopes are still being processed.  So far, every parallel block at
+  the same level that has closed has declared the name.
+
+  The scope depth is the depth of the last parallel block that
+  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 (`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
+  the "in scope" stack.  When a variable becomes out-of-scope it is
+  moved to a separate list (`out_scope`) of variables which have fully
+  known scope.  This will be used at the end of each function to assign
+  each variable a place in the stack frame.
+
+###### variable fields
+       int depth, min_depth;
+       enum { OutScope, PendingScope, CondScope, InScope } scope;
+       struct variable *in_scope;
+
+###### parse context
+
+       struct variable *in_scope;
+       struct variable *out_scope;
+
+All variables with the same name are linked together using the
+'previous' link.  Those variable that have been affirmatively merged all
+have a 'merged' pointer that points to one primary variable - the most
+recently declared instance.  When merging variables, we need to also
+adjust the 'merged' pointer on any other variables that had previously
+been merged with the one that will no longer be primary.
+
+A variable that is no longer the most recent instance of a name may
+still have "pending" scope, if it might still be merged with most
+recent instance.  These variables don't really belong in the
+"in_scope" list, but are not immediately removed when a new instance
+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;
+
+###### ast functions
+
+       static void variable_merge(struct variable *primary, struct variable *secondary)
+       {
+               struct variable *v;
+
+               primary = primary->merged;
+
+               for (v = primary->previous; v; v=v->previous)
+                       if (v == secondary || v == secondary->merged ||
+                           v->merged == secondary ||
+                           v->merged == secondary->merged) {
+                               v->scope = OutScope;
+                               v->merged = primary;
+                               if (v->scope_start < primary->scope_start)
+                                       primary->scope_start = v->scope_start;
+                               if (v->scope_end > primary->scope_end)
+                                       primary->scope_end = v->scope_end;      // NOTEST
+                               variable_unlink_exec(v);
+                       }
+       }
+
+###### forward decls
+       static struct value *var_value(struct parse_context *c, struct variable *v);
+
+###### free global vars
+
+       while (context.varlist) {
+               struct binding *b = context.varlist;
+               struct variable *v = b->var;
+               context.varlist = b->next;
+               free(b);
+               while (v) {
+                       struct variable *next = v->previous;
+
+                       if (v->global && v->frame_pos >= 0) {
+                               free_value(v->type, var_value(&context, v));
+                               if (v->depth == 0 && v->type->free == function_free)
+                                       // This is a function constant
+                                       free_exec(v->where_decl);
+                       }
+                       free(v);
+                       v = next;
+               }
+       }
+
+#### Manipulating Bindings
+
+When a name is conditionally visible, a new declaration discards the old
+binding - the condition lapses.  Similarly when we reach the end of a
+function (outermost non-global scope) any conditional scope must lapse.
+Conversely a usage of the name affirms the visibility and extends it to
+the end of the containing block - i.e.  the block that contains both the
 original declaration and the latest usage.  This is determined from
 `min_depth`.  When a conditionally visible variable gets affirmed like
 this, it is also merged with other conditionally visible variables with
@@ -1269,9 +1923,6 @@ all pending-scope variables become conditionally scoped.
                                                 v->previous->scope == PendingScope)
                                                /* all previous branches used name */
                                                v->scope = PendingScope;
-                                       else if (v->type == Tlabel)
-                                               /* Labels remain pending even when not used */
-                                               v->scope = PendingScope;        // UNTESTED
                                        else
                                                v->scope = OutScope;
                                        if (ct == CloseElse) {
@@ -1302,8 +1953,6 @@ all pending-scope variables become conditionally scoped.
                                        v->scope = InScope;
                                /* fallthrough */
                        case CloseSequential:
-                               if (v->type == Tlabel)
-                                       v->scope = PendingScope;
                                switch (v->scope) {
                                case InScope:
                                        v->scope = OutScope;
@@ -1317,10 +1966,7 @@ all pending-scope variables become conditionally scoped.
                                        for (v2 = v;
                                             v2 && v2->scope == PendingScope;
                                             v2 = v2->previous)
-                                               if (v2->type == Tlabel)
-                                                       v2->scope = CondScope;
-                                               else
-                                                       v2->scope = OutScope;
+                                               v2->scope = OutScope;
                                        break;
                                case CondScope:
                                case OutScope: break;
@@ -1359,13 +2005,17 @@ tell if it was set or not later.
        short local_size;
        void *global, *local;
 
+###### forward decls
+       static struct value *global_alloc(struct parse_context *c, struct type *t,
+                                         struct variable *v, struct value *init);
+
 ###### ast functions
 
        static struct value *var_value(struct parse_context *c, struct variable *v)
        {
                if (!v->global) {
                        if (!c->local || !v->type)
-                               return NULL;                    // NOTEST
+                               return NULL;    // UNTESTED
                        if (v->frame_pos + v->type->size > c->local_size) {
                                printf("INVALID frame_pos\n");  // NOTEST
                                exit(2);                        // NOTEST
@@ -1391,7 +2041,7 @@ tell if it was set or not later.
                        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);
+                       c->global_size = (c->global_size + t->align) & ~(t->align-1);   // NOTEST
                if (!v) {
                        v = &scratch;
                        v->type = t;
@@ -1403,7 +2053,7 @@ tell if it was set or not later.
                if (init)
                        memcpy(ret, init, t->size);
                else
-                       val_init(t, ret);
+                       val_init(t, ret);       // NOTEST
                return ret;
        }
 
@@ -1426,9 +2076,10 @@ stack.
 
        static void scope_finalize(struct parse_context *c, struct type *ft)
        {
-               int size = 0;
+               int size = ft->function.local_size;
                struct variable *next = ft->function.scope;
                struct variable *done = NULL;
+
                while (next) {
                        struct variable *v = next;
                        struct type *t = v->type;
@@ -1438,366 +2089,260 @@ stack.
                                continue;
                        if (!t)
                                continue;
+                       if (v->frame_pos >= 0)
+                               continue;
                        while (done && done->scope_end < v->scope_start)
                                done = done->in_scope;
                        if (done)
                                pos = done->frame_pos + done->type->size;
                        else
-                               pos = 0;
+                               pos = ft->function.local_size;
                        if (pos & (t->align - 1))
                                pos = (pos + t->align) & ~(t->align-1);
                        v->frame_pos = pos;
                        if (size < pos + v->type->size)
                                size = pos + v->type->size;
-                       v->in_scope = done;
-                       done = v;
-               }
-               c->out_scope = NULL;
-               ft->function.local_size = size;
-       }
-
-###### free context storage
-       free(context.global);
-
-### Executables
-
-Executables can be lots of different things.  In many cases an
-executable is just an operation combined with one or two other
-executables.  This allows for expressions and lists etc.  Other times an
-executable is something quite specific like a constant or variable name.
-So we define a `struct exec` to be a general executable with a type, and
-a `struct binode` which is a subclass of `exec`, forms a node in a
-binary tree, and holds an operation.  There will be other subclasses,
-and to access these we need to be able to `cast` the `exec` into the
-various other types.  The first field in any `struct exec` is the type
-from the `exec_types` enum.
-
-###### macros
-       #define cast(structname, pointer) ({            \
-               const typeof( ((struct structname *)0)->type) *__mptr = &(pointer)->type; \
-               if (__mptr && *__mptr != X##structname) abort();                \
-               (struct structname *)( (char *)__mptr);})
-
-       #define new(structname) ({                                              \
-               struct structname *__ptr = ((struct structname *)calloc(1,sizeof(struct structname))); \
-               __ptr->type = X##structname;                                            \
-               __ptr->line = -1; __ptr->column = -1;                                   \
-               __ptr;})
-
-       #define new_pos(structname, token) ({                                           \
-               struct structname *__ptr = ((struct structname *)calloc(1,sizeof(struct structname))); \
-               __ptr->type = X##structname;                                            \
-               __ptr->line = token.line; __ptr->column = token.col;                    \
-               __ptr;})
-
-###### ast
-       enum exec_types {
-               Xbinode,
-               ## exec type
-       };
-       struct exec {
-               enum exec_types type;
-               int line, column;
-               ## exec fields
-       };
-       struct binode {
-               struct exec;
-               enum Btype {
-                       ## Binode types
-               } op;
-               struct exec *left, *right;
-       };
-
-###### ast functions
-
-       static int __fput_loc(struct exec *loc, FILE *f)
-       {
-               if (!loc)
-                       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);  // NOTEST
-               return 0;
-       }
-       static void fput_loc(struct exec *loc, FILE *f)
-       {
-               if (!__fput_loc(loc, f))
-                       fprintf(f, "??:??: ");
-       }
-
-Each different type of `exec` node needs a number of functions defined,
-a bit like methods.  We must be able to free it, print it, analyse it
-and execute it.  Once we have specific `exec` types we will need to
-parse them too.  Let's take this a bit more slowly.
-
-#### Freeing
-
-The parser generator requires a `free_foo` function for each struct
-that stores attributes and they will often be `exec`s and subtypes
-there-of.  So we need `free_exec` which can handle all the subtypes,
-and we need `free_binode`.
-
-###### ast functions
-
-       static void free_binode(struct binode *b)
-       {
-               if (!b)
-                       return;
-               free_exec(b->left);
-               free_exec(b->right);
-               free(b);
-       }
-
-###### core functions
-       static void free_exec(struct exec *e)
-       {
-               if (!e)
-                       return;
-               switch(e->type) {
-                       ## free exec cases
-               }
-       }
-
-###### forward decls
-
-       static void free_exec(struct exec *e);
-
-###### free exec cases
-       case Xbinode: free_binode(cast(binode, e)); break;
-
-#### Printing
-
-Printing an `exec` requires that we know the current indent level for
-printing line-oriented components.  As will become clear later, we
-also want to know what sort of bracketing to use.
-
-###### ast functions
-
-       static void do_indent(int i, char *str)
-       {
-               while (i-- > 0)
-                       printf("    ");
-               printf("%s", str);
-       }
-
-###### core functions
-       static void print_binode(struct binode *b, int indent, int bracket)
-       {
-               struct binode *b2;
-               switch(b->op) {
-               ## print binode cases
-               }
-       }
-
-       static void print_exec(struct exec *e, int indent, int bracket)
-       {
-               if (!e)
-                       return;
-               switch (e->type) {
-               case Xbinode:
-                       print_binode(cast(binode, e), indent, bracket); break;
-               ## print exec cases
-               }
-               if (e->to_free) {
-                       struct variable *v;
-                       do_indent(indent, "/* FREE");
-                       for (v = e->to_free; v; v = v->next_free) {
-                               printf(" %.*s", v->name->name.len, v->name->name.txt);
-                               printf("[%d,%d]", v->scope_start, v->scope_end);
-                               if (v->frame_pos >= 0)
-                                       printf("(%d+%d)", v->frame_pos,
-                                              v->type ? v->type->size:0);
-                       }
-                       printf(" */\n");
+                       v->in_scope = done;
+                       done = v;
                }
+               c->out_scope = NULL;
+               ft->function.local_size = size;
        }
 
-###### forward decls
+###### free context storage
+       free(context.global);
 
-       static void print_exec(struct exec *e, int indent, int bracket);
+#### Variables as executables
 
-#### Analysing
+Just as we used a `val` to wrap a value into an `exec`, we similarly
+need a `var` to wrap a `variable` into an exec.  While each `val`
+contained a copy of the value, each `var` holds a link to the variable
+because it really is the same variable no matter where it appears.
+When a variable is used, we need to remember to follow the `->merged`
+link to find the primary instance.
 
-As discussed, analysis involves propagating type requirements around the
-program and looking for errors.
+When a variable is declared, it may or may not be given an explicit
+type.  We need to record which so that we can report the parsed code
+correctly.
 
-So `propagate_types` is passed an expected type (being a `struct type`
-pointer together with some `val_rules` flags) that the `exec` is
-expected to return, and returns the type that it does return, either
-of which can be `NULL` signifying "unknown".  An `ok` flag is passed
-by reference. It is set to `0` when an error is found, and `2` when
-any change is made.  If it remains unchanged at `1`, then no more
-propagation is needed.
+###### exec type
+       Xvar,
 
 ###### ast
+       struct var {
+               struct exec;
+               struct variable *var;
+       };
 
-       enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1, Rnoconstant = 1<<2};
-
-###### format cases
-       case 'r':
-               if (rules & Rnolabel)
-                       fputs(" (labels not permitted)", stderr);
-               break;
+###### variable fields
+       int explicit_type;
 
-###### forward decls
-       static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok,
-                                           struct type *type, int rules);
-###### core functions
+###### Grammar
 
-       static struct type *__propagate_types(struct exec *prog, struct parse_context *c, int *ok,
-                                             struct type *type, int rules)
-       {
-               struct type *t;
+       $TERM : ::
 
-               if (!prog)
-                       return Tnone;
+       $*var
+       VariableDecl -> IDENTIFIER : ${ {
+               struct variable *v = var_decl(c, $1.txt);
+               $0 = new_pos(var, $1);
+               $0->var = v;
+               if (v)
+                       v->where_decl = $0;
+               else {
+                       v = var_ref(c, $1.txt);
+                       $0->var = v;
+                       type_err(c, "error: variable '%v' redeclared",
+                                $0, NULL, 0, NULL);
+                       type_err(c, "info: this is where '%v' was first declared",
+                                v->where_decl, NULL, 0, NULL);
+               }
+       } }$
+       | IDENTIFIER :: ${ {
+               struct variable *v = var_decl(c, $1.txt);
+               $0 = new_pos(var, $1);
+               $0->var = v;
+               if (v) {
+                       v->where_decl = $0;
+                       v->constant = 1;
+               } else {
+                       v = var_ref(c, $1.txt);
+                       $0->var = v;
+                       type_err(c, "error: variable '%v' redeclared",
+                                $0, NULL, 0, NULL);
+                       type_err(c, "info: this is where '%v' was first declared",
+                                v->where_decl, NULL, 0, NULL);
+               }
+       } }$
+       | IDENTIFIER : Type ${ {
+               struct variable *v = var_decl(c, $1.txt);
+               $0 = new_pos(var, $1);
+               $0->var = v;
+               if (v) {
+                       v->where_decl = $0;
+                       v->where_set = $0;
+                       v->type = $<Type;
+                       v->explicit_type = 1;
+               } else {
+                       v = var_ref(c, $1.txt);
+                       $0->var = v;
+                       type_err(c, "error: variable '%v' redeclared",
+                                $0, NULL, 0, NULL);
+                       type_err(c, "info: this is where '%v' was first declared",
+                                v->where_decl, NULL, 0, NULL);
+               }
+       } }$
+       | IDENTIFIER :: Type ${ {
+               struct variable *v = var_decl(c, $1.txt);
+               $0 = new_pos(var, $1);
+               $0->var = v;
+               if (v) {
+                       v->where_decl = $0;
+                       v->where_set = $0;
+                       v->type = $<Type;
+                       v->constant = 1;
+                       v->explicit_type = 1;
+               } else {
+                       v = var_ref(c, $1.txt);
+                       $0->var = v;
+                       type_err(c, "error: variable '%v' redeclared",
+                                $0, NULL, 0, NULL);
+                       type_err(c, "info: this is where '%v' was first declared",
+                                v->where_decl, NULL, 0, NULL);
+               }
+       } }$
 
-               switch (prog->type) {
-               case Xbinode:
-               {
-                       struct binode *b = cast(binode, prog);
-                       switch (b->op) {
-                       ## propagate binode cases
+       $*exec
+       Variable -> IDENTIFIER ${ {
+               struct variable *v = var_ref(c, $1.txt);
+               $0 = new_pos(var, $1);
+               if (v == NULL) {
+                       /* This might be a global const or a label
+                        * Allocate a var with impossible type Tnone,
+                        * which will be adjusted when we find out what it is,
+                        * or will trigger an error.
+                        */
+                       v = var_decl(c, $1.txt);
+                       if (v) {
+                               v->type = Tnone;
+                               v->where_decl = $0;
+                               v->where_set = $0;
                        }
-                       break;
-               }
-               ## propagate exec cases
                }
-               return Tnone;
-       }
+               cast(var, $0)->var = v;
+       } }$
 
-       static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok,
-                                           struct type *type, int rules)
+###### print exec cases
+       case Xvar:
        {
-               struct type *ret = __propagate_types(prog, c, ok, type, rules);
-
-               if (c->parse_error)
-                       *ok = 0;
-               return ret;
+               struct var *v = cast(var, e);
+               if (v->var) {
+                       struct binding *b = v->var->name;
+                       printf("%.*s", b->name.len, b->name.txt);
+               }
+               break;
        }
 
-#### Interpreting
-
-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 `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`.
-The type may be `Tnone` but must be non-NULL.  Some `exec`s will return
-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 {
-               struct type *type;
-               struct value rval, *lval;
-       };
-
-       /* If dest is passed, dtype must give the expected type, and
-        * result can go there, in which case type is returned as NULL.
-        */
-       static struct lrval _interp_exec(struct parse_context *c, struct exec *e,
-                                        struct value *dest, struct type *dtype);
-
-       static struct value interp_exec(struct parse_context *c, struct exec *e,
-                                       struct type **typeret)
-       {
-               struct lrval ret = _interp_exec(c, e, NULL, NULL);
+###### format cases
+       case 'v':
+               if (loc && loc->type == Xvar) {
+                       struct var *v = cast(var, loc);
+                       if (v->var) {
+                               struct binding *b = v->var->name;
+                               fprintf(stderr, "%.*s", b->name.len, b->name.txt);
+                       } else
+                               fputs("???", stderr);   // NOTEST
+               } else
+                       fputs("NOTVAR", stderr);        // NOTEST
+               break;
 
-               if (!ret.type) abort();
-               if (typeret)
-                       *typeret = ret.type;
-               if (ret.lval)
-                       dup_value(ret.type, ret.lval, &ret.rval);
-               return ret.rval;
-       }
+###### propagate exec cases
 
-       static struct value *linterp_exec(struct parse_context *c, struct exec *e,
-                                         struct type **typeret)
+       case Xvar:
        {
-               struct lrval ret = _interp_exec(c, e, NULL, NULL);
-
-               if (!ret.type) abort();
-               if (ret.lval)
-                       *typeret = ret.type;
-               else
-                       free_value(ret.type, &ret.rval);
-               return ret.lval;
+               struct var *var = cast(var, prog);
+               struct variable *v = var->var;
+               if (!v) {
+                       type_err(c, "%d:BUG: no variable!!", prog, NULL, 0, NULL); // NOTEST
+                       return Tnone;                                   // NOTEST
+               }
+               v = v->merged;
+               if (v->constant && (rules & Rnoconstant)) {
+                       type_err(c, "error: Cannot assign to a constant: %v",
+                                prog, NULL, 0, NULL);
+                       type_err(c, "info: name was defined as a constant here",
+                                v->where_decl, NULL, 0, NULL);
+                       return v->type;
+               }
+               if (v->type == Tnone && v->where_decl == prog)
+                       type_err(c, "error: variable used but not declared: %v",
+                                prog, NULL, 0, NULL);
+               if (v->type == NULL) {
+                       if (type && !(*perr & Efail)) {
+                               v->type = type;
+                               v->where_set = prog;
+                               *perr |= Eretry;
+                       }
+               } else if (!type_compat(type, v->type, rules)) {
+                       type_err(c, "error: expected %1 but variable '%v' is %2", prog,
+                                type, rules, v->type);
+                       type_err(c, "info: this is where '%v' was set to %1", v->where_set,
+                                v->type, rules, NULL);
+               }
+               if (!v->global || v->frame_pos < 0)
+                       *perr |= Eruntime;
+               if (!type)
+                       return v->type;
+               return type;
        }
 
-       /* dinterp_exec is used when the destination type is certain and
-        * the value has a place to go.
-        */
-       static void dinterp_exec(struct parse_context *c, struct exec *e,
-                                struct value *dest, struct type *dtype,
-                                int need_free)
+###### interp exec cases
+       case Xvar:
        {
-               struct lrval ret = _interp_exec(c, e, dest, dtype);
-               if (!ret.type)
-                       return; // NOTEST
-               if (need_free)
-                       free_value(dtype, dest);
-               if (ret.lval)
-                       dup_value(dtype, ret.lval, dest);
-               else
-                       memcpy(dest, &ret.rval, dtype->size);
-       }
+               struct var *var = cast(var, e);
+               struct variable *v = var->var;
 
-       static struct lrval _interp_exec(struct parse_context *c, struct exec *e,
-                                        struct value *dest, struct type *dtype)
-       {
-               /* If the result is copied to dest, ret.type is set to NULL */
-               struct lrval ret;
-               struct value rv = {}, *lrv = NULL;
-               struct type *rvtype;
+               v = v->merged;
+               lrv = var_value(c, v);
+               rvtype = v->type;
+               break;
+       }
 
-               rvtype = ret.type = Tnone;
-               if (!e) {
-                       ret.lval = lrv;
-                       ret.rval = rv;
-                       return ret;
-               }
+###### ast functions
 
-               switch(e->type) {
-               case Xbinode:
-               {
-                       struct binode *b = cast(binode, e);
-                       struct value left, right, *lleft;
-                       struct type *ltype, *rtype;
-                       ltype = rtype = Tnone;
-                       switch (b->op) {
-                       ## interp binode cases
-                       }
-                       free_value(ltype, &left);
-                       free_value(rtype, &right);
-                       break;
-               }
-               ## interp exec cases
-               }
-               if (rvtype) {
-                       ret.lval = lrv;
-                       ret.rval = rv;
-                       ret.type = rvtype;
-               }
-               ## interp exec cleanup
-               return ret;
+       static void free_var(struct var *v)
+       {
+               free(v);
        }
 
+###### free exec cases
+       case Xvar: free_var(cast(var, e)); break;
+
+
 ### Complex types
 
 Now that we have the shape of the interpreter in place we can add some
 complex types and connected them in to the data structures and the
 different phases of parse, analyse, print, interpret.
 
-Thus far we have arrays and structs.
+Being "complex" the language will naturally have syntax to access
+specifics of objects of these types.  These will fit into the grammar as
+"Terms" which are the things that are combined with various operators to
+form "Expression".  Where a Term is formed by some operation on another
+Term, the subordinate Term will always come first, so for example a
+member of an array will be expressed as the Term for the array followed
+by an index in square brackets.  The strict rule of using postfix
+operations makes precedence irrelevant within terms.  To provide a place
+to put the grammar for each terms of each type, we will start out by
+introducing the "Term" grammar production, with contains at least a
+simple "Value" (to be explained later).
+
+###### Grammar
+       $*exec
+       Term ->  Value ${ $0 = $<1; }$
+       | Variable ${ $0 = $<1; }$
+       ## term grammar
+
+Thus far the complex types we have are arrays and structs.
 
 #### Arrays
 
@@ -1846,25 +2391,37 @@ with a const size by whether they are prepared at parse time or not.
 
 ###### value functions
 
-       static void array_prepare_type(struct parse_context *c, struct type *type,
+       static int array_prepare_type(struct parse_context *c, struct type *type,
                                       int parse_time)
        {
                struct value *vsize;
                mpz_t q;
-               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(vsize->num), mpq_denref(vsize->num));
-               type->array.size = mpz_get_si(q);
-               mpz_clear(q);
+               if (type->array.static_size)
+                       return 1;       // UNTESTED
+               if (type->array.unspec && parse_time)
+                       return 1;       // UNTESTED
+               if (parse_time && type->array.vsize && !type->array.vsize->global)
+                       return 1;       // UNTESTED
 
-               if (parse_time) {
-                       type->array.static_size = 1;
-                       type->size = type->array.size * type->array.member->size;
-                       type->align = type->array.member->align;
+               if (type->array.vsize) {
+                       vsize = var_value(c, type->array.vsize);
+                       if (!vsize)
+                               return 1;       // UNTESTED
+                       mpz_init(q);
+                       mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
+                       type->array.size = mpz_get_si(q);
+                       mpz_clear(q);
                }
+               if (!parse_time)
+                       return 1;
+               if (type->array.member->size <= 0)
+                       return 0;       // UNTESTED
+
+               type->array.static_size = 1;
+               type->size = type->array.size * type->array.member->size;
+               type->align = type->array.member->align;
+
+               return 1;
        }
 
        static void array_init(struct type *type, struct value *val)
@@ -1931,8 +2488,10 @@ with a const size by whether they are prepared at parse time or not.
                        struct binding *b = type->array.vsize->name;
                        fprintf(f, "%.*s%s]", b->name.len, b->name.txt,
                                type->array.unspec ? "::" : "");
-               } else
+               } else if (type->array.size)
                        fprintf(f, "%d]", type->array.size);
+               else
+                       fprintf(f, "]");
                type_print(type->array.member, f);
        }
 
@@ -1954,19 +2513,16 @@ with a const size by whether they are prepared at parse time or not.
        | [ NUMBER ] Type ${ {
                char tail[3];
                mpq_t num;
-               struct text noname = { "", 0 };
                struct type *t;
+               int elements = 0;
 
-               $0 = t = add_type(c, noname, &array_prototype);
-               t->array.member = $<4;
-               t->array.vsize = NULL;
                if (number_parse(num, tail, $2.txt) == 0)
                        tok_err(c, "error: unrecognised number", &$2);
                else if (tail[0]) {
                        tok_err(c, "error: unsupported number suffix", &$2);
                        mpq_clear(num);
                } else {
-                       t->array.size = mpz_get_ui(mpq_numref(num));
+                       elements = mpz_get_ui(mpq_numref(num));
                        if (mpz_cmp_ui(mpq_denref(num), 1) != 0) {
                                tok_err(c, "error: array size must be an integer",
                                        &$2);
@@ -1975,21 +2531,22 @@ with a const size by whether they are prepared at parse time or not.
                                        &$2);
                        mpq_clear(num);
                }
-               t->array.static_size = 1;
-               t->size = t->array.size * t->array.member->size;
-               t->align = t->array.member->align;
+
+               $0 = t = add_anon_type(c, &array_prototype, "array[%d]", elements );
+               t->array.size = elements;
+               t->array.member = $<4;
+               t->array.vsize = NULL;
        } }$
 
        | [ IDENTIFIER ] Type ${ {
                struct variable *v = var_ref(c, $2.txt);
-               struct text noname = { "", 0 };
 
                if (!v)
                        tok_err(c, "error: name undeclared", &$2);
                else if (!v->constant)
                        tok_err(c, "error: array size must be a constant", &$2);
 
-               $0 = add_type(c, noname, &array_prototype);
+               $0 = add_anon_type(c, &array_prototype, "array[%.*s]", $2.txt.len, $2.txt.txt);
                $0->array.member = $<4;
                $0->array.size = 0;
                $0->array.vsize = v;
@@ -2004,13 +2561,12 @@ with a const size by whether they are prepared at parse time or not.
 
        | [ 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 = add_anon_type(c, &array_prototype, "array[var]");
                $0->array.member = $<6;
                $0->array.size = 0;
                $0->array.unspec = 1;
@@ -2020,9 +2576,9 @@ with a const size by whether they are prepared at parse time or not.
 ###### Binode types
        Index,
 
-###### variable grammar
+###### term grammar
 
-       | Variable [ Expression ] ${ {
+       | Term [ Expression ] ${ {
                struct binode *b = new(binode);
                b->op = Index;
                b->left = $<1;
@@ -2043,8 +2599,8 @@ with a const size by whether they are prepared at parse time or not.
                /* left must be an array, right must be a number,
                 * result is the member type of the array
                 */
-               propagate_types(b->right, c, ok, Tnum, 0);
-               t = propagate_types(b->left, c, ok, NULL, rules & Rnoconstant);
+               propagate_types(b->right, c, perr, Tnum, 0);
+               t = propagate_types(b->left, c, perr, NULL, rules & Rnoconstant);
                if (!t || t->compat != array_compat) {
                        type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL);
                        return NULL;
@@ -2133,11 +2689,18 @@ function will be needed.
                        struct type *type;
                        struct value *init;
                        int offset;
-               } *fields;
+               } *fields; // This is created when field_list is analysed.
+               struct fieldlist {
+                       struct fieldlist *prev;
+                       struct field f;
+                       struct exec *init;
+               } *field_list; // This is created during parsing
        } structure;
 
 ###### type functions
        void (*print_type_decl)(struct type *type, FILE *f);
+       struct type *(*fieldref)(struct type *t, struct parse_context *c,
+                                struct fieldref *f, struct value **vp);
 
 ###### value functions
 
@@ -2168,6 +2731,15 @@ function will be needed.
                }
        }
 
+       static void free_fieldlist(struct fieldlist *f)
+       {
+               if (!f)
+                       return;
+               free_fieldlist(f->prev);
+               free_exec(f->init);
+               free(f);
+       }
+
        static void structure_free_type(struct type *t)
        {
                int i;
@@ -2177,6 +2749,88 @@ function will be needed.
                                           t->structure.fields[i].init);
                        }
                free(t->structure.fields);
+               free_fieldlist(t->structure.field_list);
+       }
+
+       static int structure_prepare_type(struct parse_context *c,
+                                         struct type *t, int parse_time)
+       {
+               int cnt = 0;
+               struct fieldlist *f;
+
+               if (!parse_time || t->structure.fields)
+                       return 1;
+
+               for (f = t->structure.field_list; f; f=f->prev) {
+                       enum prop_err perr;
+                       cnt += 1;
+
+                       if (f->f.type->size <= 0)
+                               return 0;
+                       if (f->f.type->prepare_type)
+                               f->f.type->prepare_type(c, f->f.type, parse_time);
+
+                       if (f->init == NULL)
+                               continue;
+                       do {
+                               perr = 0;
+                               propagate_types(f->init, c, &perr, f->f.type, 0);
+                       } while (perr & Eretry);
+                       if (perr & Efail)
+                               c->parse_error += 1;    // NOTEST
+               }
+
+               t->structure.nfields = cnt;
+               t->structure.fields = calloc(cnt, sizeof(struct field));
+               f = t->structure.field_list;
+               while (cnt > 0) {
+                       int a = f->f.type->align;
+                       cnt -= 1;
+                       t->structure.fields[cnt] = f->f;
+                       if (t->size & (a-1))
+                               t->size = (t->size | (a-1)) + 1;
+                       t->structure.fields[cnt].offset = t->size;
+                       t->size += ((f->f.type->size - 1) | (a-1)) + 1;
+                       if (a > t->align)
+                               t->align = a;
+
+                       if (f->init && !c->parse_error) {
+                               struct value vl = interp_exec(c, f->init, NULL);
+                               t->structure.fields[cnt].init =
+                                       global_alloc(c, f->f.type, NULL, &vl);
+                       }
+
+                       f = f->prev;
+               }
+               return 1;
+       }
+
+       static int find_struct_index(struct type *type, struct text field)
+       {
+               int i;
+               for (i = 0; i < type->structure.nfields; i++)
+                       if (text_cmp(type->structure.fields[i].name, field) == 0)
+                               return i;
+               return IndexInvalid;
+       }
+
+       static struct type *structure_fieldref(struct type *t, struct parse_context *c,
+                                              struct fieldref *f, struct value **vp)
+       {
+               if (f->index == IndexUnknown) {
+                       f->index = find_struct_index(t, f->name);
+                       if (f->index < 0)
+                               type_err(c, "error: cannot find requested field in %1",
+                                        f->left, t, 0, NULL);
+               }
+               if (f->index < 0)
+                       return NULL;
+               if (vp) {
+                       struct value *v = *vp;
+                       v = (void*)v->ptr + t->structure.fields[f->index].offset;
+                       *vp = v;
+               }
+               return t->structure.fields[f->index].type;
        }
 
        static struct type structure_prototype = {
@@ -2184,6 +2838,8 @@ function will be needed.
                .free = structure_free,
                .free_type = structure_free_type,
                .print_type_decl = structure_print_type,
+               .prepare_type = structure_prepare_type,
+               .fieldref = structure_fieldref,
        };
 
 ###### exec type
@@ -2196,6 +2852,7 @@ function will be needed.
                int index;
                struct text name;
        };
+       enum { IndexUnknown = -1, IndexInvalid = -2 };
 
 ###### free exec cases
        case Xfieldref:
@@ -2204,15 +2861,15 @@ function will be needed.
                break;
 
 ###### declare terminals
-       $TERM struct .
+       $TERM struct
 
-###### variable grammar
+###### term grammar
 
-       | Variable . IDENTIFIER ${ {
+       | Term . IDENTIFIER ${ {
                struct fieldref *fr = new_pos(fieldref, $2);
                fr->left = $<1;
                fr->name = $3.txt;
-               fr->index = -2;
+               fr->index = IndexUnknown;
                $0 = fr;
        } }$
 
@@ -2226,41 +2883,22 @@ function will be needed.
                break;
        }
 
-###### ast functions
-       static int find_struct_index(struct type *type, struct text field)
-       {
-               int i;
-               for (i = 0; i < type->structure.nfields; i++)
-                       if (text_cmp(type->structure.fields[i].name, field) == 0)
-                               return i;
-               return -1;
-       }
-
 ###### propagate exec cases
 
        case Xfieldref:
        {
                struct fieldref *f = cast(fieldref, prog);
-               struct type *st = propagate_types(f->left, c, ok, NULL, 0);
+               struct type *st = propagate_types(f->left, c, perr, NULL, 0);
 
-               if (!st)
-                       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",
+               if (!st || !st->fieldref)
+                       type_err(c, "error: field reference on %1 is not supported",
                                 f->left, st, 0, NULL);
-               else if (f->index == -2) {
-                       f->index = find_struct_index(st, f->name);
-                       if (f->index < 0)
-                               type_err(c, "error: cannot find requested field in %1",
-                                        f->left, st, 0, NULL);
-               }
-               if (f->index >= 0) {
-                       struct type *ft = st->structure.fields[f->index].type;
-                       if (!type_compat(type, ft, rules))
+               else {
+                       t = st->fieldref(st, c, f, NULL);
+                       if (t && !type_compat(type, t, rules))
                                type_err(c, "error: have %1 but need %2", prog,
-                                        ft, rules, type);
-                       return ft;
+                                        t, rules, type);
+                       return t;
                }
                break;
        }
@@ -2271,105 +2909,66 @@ function will be needed.
                struct fieldref *f = cast(fieldref, e);
                struct type *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;
+               lrv = lleft;
+               rvtype = ltype->fieldref(ltype, c, f, &lrv);
                break;
        }
 
-###### ast
-       struct fieldlist {
-               struct fieldlist *prev;
-               struct field f;
-       };
-
-###### ast functions
-       static void free_fieldlist(struct fieldlist *f)
-       {
-               if (!f)
-                       return;
-               free_fieldlist(f->prev);
-               if (f->f.init) {
-                       free_value(f->f.type, f->f.init);       // UNTESTED
-                       free(f->f.init);        // UNTESTED
-               }
-               free(f);
-       }
-
 ###### top level grammar
        DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ {
-                       struct type *t =
-                               add_type(c, $2.txt, &structure_prototype);
-                       int cnt = 0;
-                       struct fieldlist *f;
-
-                       for (f = $3; f; f=f->prev)
-                               cnt += 1;
-
-                       t->structure.nfields = cnt;
-                       t->structure.fields = calloc(cnt, sizeof(struct field));
-                       f = $3;
-                       while (cnt > 0) {
-                               int a = f->f.type->align;
-                               cnt -= 1;
-                               t->structure.fields[cnt] = f->f;
-                               if (t->size & (a-1))
-                                       t->size = (t->size | (a-1)) + 1;
-                               t->structure.fields[cnt].offset = t->size;
-                               t->size += ((f->f.type->size - 1) | (a-1)) + 1;
-                               if (a > t->align)
-                                       t->align = a;
-                               f->f.init = NULL;
-                               f = f->prev;
-                       }
-               } }$
+               struct type *t;
+               t = find_type(c, $ID.txt);
+               if (!t)
+                       t = add_type(c, $ID.txt, &structure_prototype);
+               else if (t->size >= 0) {
+                       tok_err(c, "error: type already declared", &$ID);
+                       tok_err(c, "info: this is location of declartion", &t->first_use);
+                       /* Create a new one - duplicate */
+                       t = add_type(c, $ID.txt, &structure_prototype);
+               } else {
+                       struct type tmp = *t;
+                       *t = structure_prototype;
+                       t->name = tmp.name;
+                       t->next = tmp.next;
+               }
+               t->structure.field_list = $<FB;
+               t->first_use = $ID;
+       } }$
 
        $*fieldlist
        FieldBlock -> { IN OptNL FieldLines OUT OptNL } ${ $0 = $<FL; }$
-               | { SimpleFieldList } ${ $0 = $<SFL; }$
-               | IN OptNL FieldLines OUT ${ $0 = $<FL; }$
-               | SimpleFieldList EOL ${ $0 = $<SFL; }$
+       | { SimpleFieldList } ${ $0 = $<SFL; }$
+       | IN OptNL FieldLines OUT ${ $0 = $<FL; }$
+       | SimpleFieldList EOL ${ $0 = $<SFL; }$
 
        FieldLines -> SimpleFieldList Newlines ${ $0 = $<SFL; }$
-               | FieldLines SimpleFieldList Newlines ${
-                       $SFL->prev = $<FL;
-                       $0 = $<SFL;
-               }$
+       | FieldLines SimpleFieldList Newlines ${
+               $SFL->prev = $<FL;
+               $0 = $<SFL;
+       }$
 
        SimpleFieldList -> Field ${ $0 = $<F; }$
-               | SimpleFieldList ; Field ${
-                       $F->prev = $<SFL;
-                       $0 = $<F;
-               }$
-               | SimpleFieldList ; ${
-                       $0 = $<SFL;
-               }$
-               | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
+       | SimpleFieldList ; Field ${
+               $F->prev = $<SFL;
+               $0 = $<F;
+       }$
+       | SimpleFieldList ; ${
+               $0 = $<SFL;
+       }$
+       | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
 
        Field -> IDENTIFIER : Type = Expression ${ {
-                       int ok;
-
-                       $0 = calloc(1, sizeof(struct fieldlist));
-                       $0->f.name = $1.txt;
-                       $0->f.type = $<3;
-                       $0->f.init = NULL;
-                       do {
-                               ok = 1;
-                               propagate_types($<5, c, &ok, $3, 0);
-                       } while (ok == 2);
-                       if (!ok)
-                               c->parse_error = 1;     // UNTESTED
-                       else {
-                               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;
-                       if ($0->f.type->prepare_type)
-                               $0->f.type->prepare_type(c, $0->f.type, 1);
-               }$
+               $0 = calloc(1, sizeof(struct fieldlist));
+               $0->f.name = $ID.txt;
+               $0->f.type = $<Type;
+               $0->f.init = NULL;
+               $0->init = $<Expr;
+       } }$
+       | IDENTIFIER : Type ${
+               $0 = calloc(1, sizeof(struct fieldlist));
+               $0->f.name = $ID.txt;
+               $0->f.type = $<Type;
+       }$
 
 ###### forward decls
        static void structure_print_type(struct type *t, FILE *f);
@@ -2397,29 +2996,360 @@ function will be needed.
                }
        }
 
-###### print type decls
-       {
-               struct type *t;
-               int target = -1;
+###### print type decls
+       {
+               struct type *t;
+               int target = -1;
+
+               while (target != 0) {
+                       int i = 0;
+                       for (t = context.typelist; t ; t=t->next)
+                               if (!t->anon && t->print_type_decl &&
+                                   !t->check_args) {
+                                       i += 1;
+                                       if (i == target)
+                                               break;
+                               }
+
+                       if (target == -1) {
+                               target = i;
+                       } else {
+                               t->print_type_decl(t, stdout);
+                               target -= 1;
+                       }
+               }
+       }
+
+#### References
+
+References, or pointers, are values that refer to another value.  They
+can only refer to a `struct`, though as a struct can embed anything they
+can effectively refer to anything.
+
+References are potentially dangerous as they might refer to some
+variable which no longer exists - either because a stack frame
+containing it has been discarded or because the value was allocated on
+the heap and has now been free.  Ocean does not yet provide any
+protection against these problems.  It will in due course.
+
+With references comes the opportunity and the need to explicitly
+allocate values on the "heap" and to free them.  We currently provide
+fairly basic support for this.
+
+Reference make use of the `@` symbol in various ways.  A type that starts
+with `@` is a reference to whatever follows.  A reference value
+followed by an `@` acts as the referred value, though the `@` is often
+not needed.  Finally, an expression that starts with `@` is a special
+reference related expression.  Some examples might help.
+
+##### Example: Reference examples
+
+       struct foo
+               a: number
+               b: string
+       ref: @foo
+       bar: foo
+       bar.number = 23; bar.string = "hello"
+       baz: foo
+       ref = bar
+       baz = @ref
+       baz.a = ref.a * 2
+
+       ref = @new()
+       ref@ = baz
+       @free = ref
+       ref = @nil
+
+Obviously this is very contrived.  `ref` is a reference to a `foo` which
+is initially set to refer to the value stored in `bar` - no extra syntax
+is needed to "Take the address of" `bar` - the fact that `ref` is a
+reference means that only the address make sense.
+
+When `ref.a` is accessed, that is whatever value is stored in `bar.a`.
+The same syntax is used for accessing fields both in structs and in
+references to structs.  It would be correct to use `ref@.a`, but not
+necessary.
+
+`@new()` creates an object of whatever type is needed for the program
+to by type-correct.  In future iterations of Ocean, arguments a
+constructor will access arguments, so the the syntax now looks like a
+function call.  `@free` can be assigned any reference that was returned
+by `@new()`, and it will be freed.  `@nil` is a value of whatever
+reference type is appropriate, and is stable and never the address of
+anything in the heap or on the stack.  A reference can be assigned
+`@nil` or compared against that value.
+
+###### declare terminals
+       $TERM @
+
+###### type union fields
+
+       struct {
+               struct type *referent;
+       } reference;
+
+###### value union fields
+       struct value *ref;
+
+###### value functions
+
+       static void reference_print_type(struct type *t, FILE *f)
+       {
+               fprintf(f, "@");
+               type_print(t->reference.referent, f);
+       }
+
+       static int reference_cmp(struct type *tl, struct type *tr,
+                                struct value *left, struct value *right)
+       {
+               return left->ref == right->ref ? 0 : 1;
+       }
+
+       static void reference_dup(struct type *t,
+                                 struct value *vold, struct value *vnew)
+       {
+               vnew->ref = vold->ref;
+       }
+
+       static void reference_free(struct type *t, struct value *v)
+       {
+               /* Nothing to do here */
+       }
+
+       static int reference_compat(struct type *require, struct type *have)
+       {
+               if (have->compat != require->compat)
+                       return 0;
+               if (have->reference.referent != require->reference.referent)
+                       return 0;
+               return 1;
+       }
+
+       static int reference_test(struct type *type, struct value *val)
+       {
+               return val->ref != NULL;
+       }
+
+       static struct type *reference_fieldref(struct type *t, struct parse_context *c,
+                                              struct fieldref *f, struct value **vp)
+       {
+               struct type *rt = t->reference.referent;
+
+               if (rt->fieldref) {
+                       if (vp)
+                               *vp = (*vp)->ref;
+                       return rt->fieldref(rt, c, f, vp);
+               }
+               type_err(c, "error: field reference on %1 is not supported",
+                                f->left, rt, 0, NULL);
+               return Tnone;
+       }
+
+
+       static struct type reference_prototype = {
+               .print_type = reference_print_type,
+               .cmp_eq = reference_cmp,
+               .dup = reference_dup,
+               .test = reference_test,
+               .free = reference_free,
+               .compat = reference_compat,
+               .fieldref = reference_fieldref,
+               .size = sizeof(void*),
+               .align = sizeof(void*),
+       };
+
+###### type grammar
+
+       | @ IDENTIFIER ${ {
+               struct type *t = find_type(c, $ID.txt);
+               if (!t) {
+                       t = add_type(c, $ID.txt, NULL);
+                       t->first_use = $ID;
+               }
+               $0 = find_anon_type(c, &reference_prototype, "@%.*s",
+                                   $ID.txt.len, $ID.txt.txt);
+               $0->reference.referent = t;
+       } }$
+
+###### core functions
+       static int text_is(struct text t, char *s)
+       {
+               return (strlen(s) == t.len &&
+                       strncmp(s, t.txt, t.len) == 0);
+       }
+
+###### exec type
+       Xref,
+
+###### ast
+       struct ref {
+               struct exec;
+               enum ref_func { RefNew, RefFree, RefNil } action;
+               struct type *reftype;
+               struct exec *right;
+       };
+
+###### SimpleStatement Grammar
+
+       | @ IDENTIFIER = Expression ${ {
+               struct ref *r = new_pos(ref, $ID);
+               // Must be "free"
+               if (!text_is($ID.txt, "free"))
+                       tok_err(c, "error: only \"@free\" makes sense here",
+                               &$ID);
+
+               $0 = r;
+               r->action = RefFree;
+               r->right = $<Exp;
+       } }$
+
+###### expression grammar
+       | @ IDENTIFIER ( ) ${
+               // Only 'new' valid here
+               if (!text_is($ID.txt, "new")) {
+                       tok_err(c, "error: Only reference function is \"@new()\"",
+                               &$ID);
+               } else {
+                       struct ref *r = new_pos(ref,$ID);
+                       $0 = r;
+                       r->action = RefNew;
+               }
+       }$
+       | @ IDENTIFIER ${
+               // Only 'nil' valid here
+               if (!text_is($ID.txt, "nil")) {
+                       tok_err(c, "error: Only reference value is \"@nil\"",
+                               &$ID);
+               } else {
+                       struct ref *r = new_pos(ref,$ID);
+                       $0 = r;
+                       r->action = RefNil;
+               }
+       }$
+
+###### print exec cases
+       case Xref: {
+               struct ref *r = cast(ref, e);
+               switch (r->action) {
+               case RefNew:
+                       printf("@new()"); break;
+               case RefNil:
+                       printf("@nil"); break;
+               case RefFree:
+                       do_indent(indent, "@free = ");
+                       print_exec(r->right, indent, bracket);
+                       break;
+               }
+               break;
+       }
+
+###### propagate exec cases
+       case Xref: {
+               struct ref *r = cast(ref, prog);
+               switch (r->action) {
+               case RefNew:
+                       if (type && type->free != reference_free) {
+                               type_err(c, "error: @new() can only be used with references, not %1",
+                                        prog, type, 0, NULL);
+                               return NULL;
+                       }
+                       if (type && !r->reftype) {
+                               r->reftype = type;
+                               *perr |= Eretry;
+                       }
+                       return type;
+               case RefNil:
+                       if (type && type->free != reference_free)
+                               type_err(c, "error: @nil can only be used with reference, not %1",
+                                        prog, type, 0, NULL);
+                       if (type && !r->reftype) {
+                               r->reftype = type;
+                               *perr |= Eretry;
+                       }
+                       return type;
+               case RefFree:
+                       t = propagate_types(r->right, c, perr, NULL, 0);
+                       if (t && t->free != reference_free)
+                               type_err(c, "error: @free can only be assigned a reference, not %1",
+                                        prog, t, 0, NULL);
+                       r->reftype = Tnone;
+                       return Tnone;
+               }
+               break;  // NOTEST
+       }
+
+
+###### interp exec cases
+       case Xref: {
+               struct ref *r = cast(ref, e);
+               switch (r->action) {
+               case RefNew:
+                       if (r->reftype)
+                               rv.ref = calloc(1, r->reftype->reference.referent->size);
+                       rvtype = r->reftype;
+                       break;
+               case RefNil:
+                       rv.ref = NULL;
+                       rvtype = r->reftype;
+                       break;
+               case RefFree:
+                       rv = interp_exec(c, r->right, &rvtype);
+                       free_value(rvtype->reference.referent, rv.ref);
+                       free(rv.ref);
+                       rvtype = Tnone;
+                       break;
+               }
+               break;
+       }
+
+###### free exec cases
+       case Xref: {
+               struct ref *r = cast(ref, e);
+               free_exec(r->right);
+               free(r);
+               break;
+       }
 
-               while (target != 0) {
-                       int i = 0;
-                       for (t = context.typelist; t ; t=t->next)
-                               if (t->print_type_decl && !t->check_args) {
-                                       i += 1;
-                                       if (i == target)
-                                               break;
-                               }
+###### Expressions: dereference
 
-                       if (target == -1) {
-                               target = i;
-                       } else {
-                               t->print_type_decl(t, stdout);
-                               target -= 1;
-                       }
-               }
+###### Binode types
+       Deref,
+
+###### term grammar
+
+       | Term @ ${ {
+               struct binode *b = new(binode);
+               b->op = Deref;
+               b->left = $<Trm;
+               $0 = b;
+       } }$
+
+###### print binode cases
+       case Deref:
+               print_exec(b->left, -1, bracket);
+               printf("@");
+               break;
+
+###### propagate binode cases
+       case Deref:
+               /* left must be a reference, and we return what it refers to */
+               /* FIXME how can I pass the expected type down? */
+               t = propagate_types(b->left, c, perr, NULL, 0);
+               if (!t || t->free != reference_free)
+                       type_err(c, "error: Cannot dereference %1", b, t, 0, NULL);
+               else
+                       return t->reference.referent;
+               break;
+
+###### interp binode cases
+       case Deref: {
+               left = interp_exec(c, b->left, &ltype);
+               lrv = left.ref;
+               rvtype = ltype->reference.referent;
+               break;
        }
 
+
 #### Functions
 
 A function is a chunk of code which can be passed parameters and can
@@ -2446,7 +3376,7 @@ be a ';' separated list)
        do
                code block
 
-In the first case a return type can follow the paentheses after a colon,
+In the first case a return type can follow the parentheses after a colon,
 in the second it is given on a line starting with the word `return`.
 
 ##### Example: functions that return
@@ -2461,8 +3391,28 @@ in the second it is given on a line starting with the word `return`.
        do
                code block
 
+Rather than returning a type, the function can specify a set of local
+variables to return as a struct.  The values of these variables when the
+function exits will be provided to the caller.  For this the return type
+is replaced with a block of result declarations, either in parentheses
+or bracketed by `return` and `do`.
+
+##### Example: functions returning multiple variables
+
+       func to_cartesian(rho:number; theta:number):(x:number; y:number)
+               x = .....
+               y = .....
+
+       func to_polar
+               x:number; y:number
+       return
+               rho:number
+               theta:number
+       do
+               rho = ....
+               theta = ....
 
-For constructing these lists we use a `List` binode, which will be
+For constructing the lists we use a `List` binode, which will be
 further detailed when Expression Lists are introduced.
 
 ###### type union fields
@@ -2471,6 +3421,7 @@ further detailed when Expression Lists are introduced.
                struct binode *params;
                struct type *return_type;
                struct variable *scope;
+               int inline_result;      // return value is at start of 'local'
                int local_size;
        } function;
 
@@ -2478,7 +3429,7 @@ further detailed when Expression Lists are introduced.
        struct exec *function;
 
 ###### type functions
-       void (*check_args)(struct parse_context *c, int *ok,
+       void (*check_args)(struct parse_context *c, enum prop_err *perr,
                           struct type *require, struct exec *args);
 
 ###### value functions
@@ -2495,7 +3446,7 @@ further detailed when Expression Lists are introduced.
                return 0;
        }
 
-       static void function_check_args(struct parse_context *c, int *ok,
+       static void function_check_args(struct parse_context *c, enum prop_err *perr,
                                        struct type *require, struct exec *args)
        {
                /* This should be 'compat', but we don't have a 'tuple' type to
@@ -2511,8 +3462,8 @@ further detailed when Expression Lists are introduced.
                                         args, NULL, 0, NULL);
                                break;
                        }
-                       *ok = 1;
-                       propagate_types(arg->left, c, ok, pv->var->type, 0);
+                       *perr = 0;
+                       propagate_types(arg->left, c, perr, pv->var->type, 0);
                        param = cast(binode, param->right);
                        arg = cast(binode, arg->right);
                }
@@ -2541,7 +3492,20 @@ further detailed when Expression Lists are introduced.
                fprintf(f, ")");
                if (type->function.return_type != Tnone) {
                        fprintf(f, ":");
-                       type_print(type->function.return_type, f);
+                       if (type->function.inline_result) {
+                               int i;
+                               struct type *t = type->function.return_type;
+                               fprintf(f, " (");
+                               for (i = 0; i < t->structure.nfields; i++) {
+                                       struct field *fl = t->structure.fields + i;
+                                       if (i)
+                                               fprintf(f, "; ");
+                                       fprintf(f, "%.*s:", fl->name.len, fl->name.txt);
+                                       type_print(fl->type, f);
+                               }
+                               fprintf(f, ")");
+                       } else
+                               type_print(type->function.return_type, f);
                }
                fprintf(f, "\n");
        }
@@ -2573,387 +3537,193 @@ further detailed when Expression Lists are introduced.
 
        $*variable
        FuncName -> IDENTIFIER ${ {
-                       struct variable *v = var_decl(c, $1.txt);
-                       struct var *e = new_pos(var, $1);
-                       e->var = v;
-                       if (v) {
-                               v->where_decl = e;
-                               $0 = v;
-                       } else {
-                               v = var_ref(c, $1.txt);
-                               e->var = v;
-                               type_err(c, "error: function '%v' redeclared",
-                                       e, NULL, 0, NULL);
-                               type_err(c, "info: this is where '%v' was first declared",
-                                       v->where_decl, NULL, 0, NULL);
-                               free_exec(e);
-                       }
-               } }$
-
-       $*binode
-       Args -> ArgsLine NEWLINE ${ $0 = $<AL; }$
-               | Args ArgsLine NEWLINE ${ {
-                       struct binode *b = $<AL;
-                       struct binode **bp = &b;
-                       while (*bp)
-                               bp = (struct binode **)&(*bp)->left;
-                       *bp = $<A;
-                       $0 = b;
-               } }$
-
-       ArgsLine -> ${ $0 = NULL; }$
-               | Varlist ${ $0 = $<1; }$
-               | Varlist ; ${ $0 = $<1; }$
-
-       Varlist -> Varlist ; ArgDecl ${
-                       $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,
-interpreted, and freed.  There are several, so let's just start with
-the easy ones and work our way up.
-
-### Values
-
-We have already met values as separate objects.  When manifest
-constants appear in the program text, that must result in an executable
-which has a constant value.  So the `val` structure embeds a value in
-an executable.
-
-###### exec type
-       Xval,
-
-###### ast
-       struct val {
-               struct exec;
-               struct type *vtype;
-               struct value val;
-       };
-
-###### ast functions
-       struct val *new_val(struct type *T, struct token tk)
-       {
-               struct val *v = new_pos(val, tk);
-               v->vtype = T;
-               return v;
-       }
-
-###### Grammar
-
-       $TERM True False
-
-       $*val
-       Value ->  True ${
-                       $0 = new_val(Tbool, $1);
-                       $0->val.bool = 1;
-                       }$
-               | False ${
-                       $0 = new_val(Tbool, $1);
-                       $0->val.bool = 0;
-                       }$
-               | NUMBER ${
-                       $0 = new_val(Tnum, $1);
-                       {
-                       char tail[3];
-                       if (number_parse($0->val.num, tail, $1.txt) == 0)
-                               mpq_init($0->val.num);  // UNTESTED
-                               if (tail[0])
-                                       tok_err(c, "error: unsupported number suffix",
-                                               &$1);
-                       }
-                       }$
-               | STRING ${
-                       $0 = new_val(Tstr, $1);
-                       {
-                       char tail[3];
-                       string_parse(&$1, '\\', &$0->val.str, tail);
-                       if (tail[0])
-                               tok_err(c, "error: unsupported string suffix",
-                                       &$1);
-                       }
-                       }$
-               | MULTI_STRING ${
-                       $0 = new_val(Tstr, $1);
-                       {
-                       char tail[3];
-                       string_parse(&$1, '\\', &$0->val.str, tail);
-                       if (tail[0])
-                               tok_err(c, "error: unsupported string suffix",
-                                       &$1);
-                       }
-                       }$
-
-###### print exec cases
-       case Xval:
-       {
-               struct val *v = cast(val, e);
-               if (v->vtype == Tstr)
-                       printf("\"");
-               print_value(v->vtype, &v->val, stdout);
-               if (v->vtype == Tstr)
-                       printf("\"");
-               break;
-       }
-
-###### propagate exec cases
-       case Xval:
-       {
-               struct val *val = cast(val, prog);
-               if (!type_compat(type, val->vtype, rules))
-                       type_err(c, "error: expected %1%r found %2",
-                                  prog, type, rules, val->vtype);
-               return val->vtype;
-       }
-
-###### interp exec cases
-       case Xval:
-               rvtype = cast(val, e)->vtype;
-               dup_value(rvtype, &cast(val, e)->val, &rv);
-               break;
-
-###### ast functions
-       static void free_val(struct val *v)
-       {
-               if (v)
-                       free_value(v->vtype, &v->val);
-               free(v);
-       }
-
-###### free exec cases
-       case Xval: free_val(cast(val, e)); break;
-
-###### ast functions
-       // Move all nodes from 'b' to 'rv', reversing their order.
-       // In 'b' 'left' is a list, and 'right' is the last node.
-       // In 'rv', left' is the first node and 'right' is a list.
-       static struct binode *reorder_bilist(struct binode *b)
-       {
-               struct binode *rv = NULL;
-
-               while (b) {
-                       struct exec *t = b->right;
-                       b->right = rv;
-                       rv = b;
-                       if (b->left)
-                               b = cast(binode, b->left);
-                       else
-                               b = NULL;
-                       rv->left = t;
-               }
-               return rv;
-       }
-
-### Variables
-
-Just as we used a `val` to wrap a value into an `exec`, we similarly
-need a `var` to wrap a `variable` into an exec.  While each `val`
-contained a copy of the value, each `var` holds a link to the variable
-because it really is the same variable no matter where it appears.
-When a variable is used, we need to remember to follow the `->merged`
-link to find the primary instance.
-
-###### exec type
-       Xvar,
-
-###### ast
-       struct var {
-               struct exec;
-               struct variable *var;
-       };
-
-###### Grammar
-
-       $TERM : ::
-
-       $*var
-       VariableDecl -> IDENTIFIER : ${ {
-               struct variable *v = var_decl(c, $1.txt);
-               $0 = new_pos(var, $1);
-               $0->var = v;
-               if (v)
-                       v->where_decl = $0;
-               else {
-                       v = var_ref(c, $1.txt);
-                       $0->var = v;
-                       type_err(c, "error: variable '%v' redeclared",
-                                $0, NULL, 0, NULL);
-                       type_err(c, "info: this is where '%v' was first declared",
-                                v->where_decl, NULL, 0, NULL);
-               }
-       } }$
-           | IDENTIFIER :: ${ {
-               struct variable *v = var_decl(c, $1.txt);
-               $0 = new_pos(var, $1);
-               $0->var = v;
-               if (v) {
-                       v->where_decl = $0;
-                       v->constant = 1;
-               } else {
-                       v = var_ref(c, $1.txt);
-                       $0->var = v;
-                       type_err(c, "error: variable '%v' redeclared",
-                                $0, NULL, 0, NULL);
-                       type_err(c, "info: this is where '%v' was first declared",
-                                v->where_decl, NULL, 0, NULL);
-               }
-       } }$
-           | IDENTIFIER : Type ${ {
-               struct variable *v = var_decl(c, $1.txt);
-               $0 = new_pos(var, $1);
-               $0->var = v;
-               if (v) {
-                       v->where_decl = $0;
-                       v->where_set = $0;
-                       v->type = $<Type;
-               } else {
-                       v = var_ref(c, $1.txt);
-                       $0->var = v;
-                       type_err(c, "error: variable '%v' redeclared",
-                                $0, NULL, 0, NULL);
-                       type_err(c, "info: this is where '%v' was first declared",
-                                v->where_decl, NULL, 0, NULL);
-               }
-       } }$
-           | IDENTIFIER :: Type ${ {
                struct variable *v = var_decl(c, $1.txt);
-               $0 = new_pos(var, $1);
-               $0->var = v;
+               struct var *e = new_pos(var, $1);
+               e->var = v;
                if (v) {
-                       v->where_decl = $0;
-                       v->where_set = $0;
-                       v->type = $<Type;
-                       v->constant = 1;
+                       v->where_decl = e;
+                       v->where_set = e;
+                       $0 = v;
                } else {
                        v = var_ref(c, $1.txt);
-                       $0->var = v;
-                       type_err(c, "error: variable '%v' redeclared",
-                                $0, NULL, 0, NULL);
+                       e->var = v;
+                       type_err(c, "error: function '%v' redeclared",
+                               e, NULL, 0, NULL);
                        type_err(c, "info: this is where '%v' was first declared",
-                                v->where_decl, NULL, 0, NULL);
+                               v->where_decl, NULL, 0, NULL);
+                       free_exec(e);
                }
        } }$
 
-       $*exec
-       Variable -> IDENTIFIER ${ {
-               struct variable *v = var_ref(c, $1.txt);
-               $0 = new_pos(var, $1);
-               if (v == NULL) {
-                       /* This might be a label - allocate a var just in case */
-                       v = var_decl(c, $1.txt);
-                       if (v) {
-                               v->type = Tnone;
-                               v->where_decl = $0;
-                               v->where_set = $0;
-                       }
-               }
-               cast(var, $0)->var = v;
+       $*binode
+       Args -> ArgsLine NEWLINE ${ $0 = $<AL; }$
+       | Args ArgsLine NEWLINE ${ {
+               struct binode *b = $<AL;
+               struct binode **bp = &b;
+               while (*bp)
+                       bp = (struct binode **)&(*bp)->left;
+               *bp = $<A;
+               $0 = b;
        } }$
-       ## variable grammar
 
-###### print exec cases
-       case Xvar:
-       {
-               struct var *v = cast(var, e);
-               if (v->var) {
-                       struct binding *b = v->var->name;
-                       printf("%.*s", b->name.len, b->name.txt);
-               }
-               break;
-       }
+       ArgsLine -> ${ $0 = NULL; }$
+       | Varlist ${ $0 = $<1; }$
+       | Varlist ; ${ $0 = $<1; }$
 
-###### format cases
-       case 'v':
-               if (loc && loc->type == Xvar) {
-                       struct var *v = cast(var, loc);
-                       if (v->var) {
-                               struct binding *b = v->var->name;
-                               fprintf(stderr, "%.*s", b->name.len, b->name.txt);
-                       } else
-                               fputs("???", stderr);   // NOTEST
-               } else
-                       fputs("NOTVAR", stderr);
-               break;
+       Varlist -> Varlist ; ArgDecl ${
+               $0 = new_pos(binode, $2);
+               $0->op = List;
+               $0->left = $<Vl;
+               $0->right = $<AD;
+       }$
+       | ArgDecl ${
+               $0 = new(binode);
+               $0->op = List;
+               $0->left = NULL;
+               $0->right = $<AD;
+       }$
 
-###### propagate exec cases
+       $*var
+       ArgDecl -> IDENTIFIER : FormalType ${ {
+               struct variable *v = var_decl(c, $ID.txt);
+               $0 = new_pos(var, $ID);
+               $0->var = v;
+               v->where_decl = $0;
+               v->where_set = $0;
+               v->type = $<FT;
+       } }$
 
-       case Xvar:
-       {
-               struct var *var = cast(var, prog);
-               struct variable *v = var->var;
-               if (!v) {
-                       type_err(c, "%d:BUG: no variable!!", prog, NULL, 0, NULL); // NOTEST
-                       return Tnone;                                   // NOTEST
-               }
-               v = v->merged;
-               if (v->constant && (rules & Rnoconstant)) {
-                       type_err(c, "error: Cannot assign to a constant: %v",
-                                prog, NULL, 0, NULL);
-                       type_err(c, "info: name was defined as a constant here",
-                                v->where_decl, NULL, 0, NULL);
-                       return v->type;
-               }
-               if (v->type == Tnone && v->where_decl == prog)
-                       type_err(c, "error: variable used but not declared: %v",
-                                prog, NULL, 0, NULL);
-               if (v->type == NULL) {
-                       if (type && *ok != 0) {
-                               v->type = type;
-                               v->where_set = prog;
-                               *ok = 2;
+##### Function calls
+
+A function call can appear either as an expression or as a statement.
+We use a new 'Funcall' binode type to link the function with a list of
+arguments, form with the 'List' nodes.
+
+We have already seen the "Term" which is how a function call can appear
+in an expression.  To parse a function call into a statement we include
+it in the "SimpleStatement Grammar" which will be described later.
+
+###### Binode types
+       Funcall,
+
+###### term grammar
+       | Term ( ExpressionList ) ${ {
+               struct binode *b = new(binode);
+               b->op = Funcall;
+               b->left = $<T;
+               b->right = reorder_bilist($<EL);
+               $0 = b;
+       } }$
+       | Term ( ) ${ {
+               struct binode *b = new(binode);
+               b->op = Funcall;
+               b->left = $<T;
+               b->right = NULL;
+               $0 = b;
+       } }$
+
+###### SimpleStatement Grammar
+
+       | Term ( ExpressionList ) ${ {
+               struct binode *b = new(binode);
+               b->op = Funcall;
+               b->left = $<T;
+               b->right = reorder_bilist($<EL);
+               $0 = b;
+       } }$
+
+###### print binode cases
+
+       case Funcall:
+               do_indent(indent, "");
+               print_exec(b->left, -1, bracket);
+               printf("(");
+               for (b = cast(binode, b->right); b; b = cast(binode, b->right)) {
+                       if (b->left) {
+                               printf(" ");
+                               print_exec(b->left, -1, bracket);
+                               if (b->right)
+                                       printf(",");
                        }
-                       return type;
                }
-               if (!type_compat(type, v->type, rules)) {
-                       type_err(c, "error: expected %1%r but variable '%v' is %2", prog,
-                                type, rules, v->type);
-                       type_err(c, "info: this is where '%v' was set to %1", v->where_set,
-                                v->type, rules, NULL);
+               printf(")");
+               if (indent >= 0)
+                       printf("\n");
+               break;
+
+###### propagate binode cases
+
+       case Funcall: {
+               /* Every arg must match formal parameter, and result
+                * is return type of function
+                */
+               struct binode *args = cast(binode, b->right);
+               struct var *v = cast(var, b->left);
+
+               if (!v->var->type || v->var->type->check_args == NULL) {
+                       type_err(c, "error: attempt to call a non-function.",
+                                prog, NULL, 0, NULL);
+                       return NULL;
                }
-               if (!type)
-                       return v->type;
-               return type;
+               *perr |= Eruntime;
+               v->var->type->check_args(c, perr, v->var->type, args);
+               if (v->var->type->function.inline_result)
+                       *perr |= Emaycopy;
+               return v->var->type->function.return_type;
        }
 
-###### interp exec cases
-       case Xvar:
-       {
-               struct var *var = cast(var, e);
-               struct variable *v = var->var;
+###### interp binode cases
 
-               v = v->merged;
-               lrv = var_value(c, v);
-               rvtype = v->type;
+       case Funcall: {
+               struct var *v = cast(var, b->left);
+               struct type *t = v->var->type;
+               void *oldlocal = c->local;
+               int old_size = c->local_size;
+               void *local = calloc(1, t->function.local_size);
+               struct value *fbody = var_value(c, v->var);
+               struct binode *arg = cast(binode, b->right);
+               struct binode *param = t->function.params;
+
+               while (param) {
+                       struct var *pv = cast(var, param->left);
+                       struct type *vtype = NULL;
+                       struct value val = interp_exec(c, arg->left, &vtype);
+                       struct value *lval;
+                       c->local = local; c->local_size = t->function.local_size;
+                       lval = var_value(c, pv->var);
+                       c->local = oldlocal; c->local_size = old_size;
+                       memcpy(lval, &val, vtype->size);
+                       param = cast(binode, param->right);
+                       arg = cast(binode, arg->right);
+               }
+               c->local = local; c->local_size = t->function.local_size;
+               if (t->function.inline_result && dtype) {
+                       _interp_exec(c, fbody->function, NULL, NULL);
+                       memcpy(dest, local, dtype->size);
+                       rvtype = ret.type = NULL;
+               } else
+                       rv = interp_exec(c, fbody->function, &rvtype);
+               c->local = oldlocal; c->local_size = old_size;
+               free(local);
                break;
        }
 
-###### ast functions
+## Complex executables: statements and expressions
 
-       static void free_var(struct var *v)
-       {
-               free(v);
-       }
+Now that we have types and values and variables and most of the basic
+Terms which provide access to these, we can explore the more complex
+code that combine all of these to get useful work done.  Specifically
+statements and expressions.
 
-###### free exec cases
-       case Xvar: free_var(cast(var, e)); break;
+Expressions are various combinations of Terms.  We will use operator
+precedence to ensure correct parsing.  The simplest Expression is just a
+Term - others will follow.
+
+###### Grammar
+
+       $*exec
+       Expression -> Term ${ $0 = $<Term; }$
+       ## expression grammar
 
 ### Expressions: Conditional
 
@@ -2973,24 +3743,23 @@ there.
 ###### Binode types
        CondExpr,
 
-###### Grammar
+###### declare terminals
 
        $LEFT if $$ifelse
-       ## expr precedence
 
-       $*exec
-       Expression -> Expression if Expression else Expression $$ifelse ${ {
-                       struct binode *b1 = new(binode);
-                       struct binode *b2 = new(binode);
-                       b1->op = CondExpr;
-                       b1->left = $<3;
-                       b1->right = b2;
-                       b2->op = CondExpr;
-                       b2->left = $<1;
-                       b2->right = $<5;
-                       $0 = b1;
-               } }$
-               ## expression grammar
+###### expression grammar
+
+       | Expression if Expression else Expression $$ifelse ${ {
+               struct binode *b1 = new(binode);
+               struct binode *b2 = new(binode);
+               b1->op = CondExpr;
+               b1->left = $<3;
+               b1->right = b2;
+               b2->op = CondExpr;
+               b2->left = $<1;
+               b2->right = $<5;
+               $0 = b1;
+       } }$
 
 ###### print binode cases
 
@@ -3012,9 +3781,9 @@ there.
                struct binode *b2 = cast(binode, b->right);
                struct type *t2;
 
-               propagate_types(b->left, c, ok, Tbool, 0);
-               t = propagate_types(b2->left, c, ok, type, Rnolabel);
-               t2 = propagate_types(b2->right, c, ok, type ?: t, Rnolabel);
+               propagate_types(b->left, c, perr, Tbool, 0);
+               t = propagate_types(b2->left, c, perr, type, 0);
+               t2 = propagate_types(b2->right, c, perr, type ?: t, 0);
                return t ?: t2;
        }
 
@@ -3069,17 +3838,17 @@ lists.  In that case a separate function is used to print them.
 
        $*binode
        ExpressionList -> ExpressionList , Expression ${
-                       $0 = new(binode);
-                       $0->op = List;
-                       $0->left = $<1;
-                       $0->right = $<3;
-               }$
-               | Expression ${
-                       $0 = new(binode);
-                       $0->op = List;
-                       $0->left = NULL;
-                       $0->right = $<1;
-               }$
+               $0 = new(binode);
+               $0->op = List;
+               $0->left = $<1;
+               $0->right = $<3;
+       }$
+       | Expression ${
+               $0 = new(binode);
+               $0->op = List;
+               $0->left = NULL;
+               $0->right = $<1;
+       }$
 
 ### Expressions: Boolean
 
@@ -3095,48 +3864,48 @@ evaluate the second expression if not necessary.
        OrElse,
        Not,
 
-###### expr precedence
+###### declare terminals
        $LEFT or
        $LEFT and
        $LEFT not
 
 ###### expression grammar
-               | Expression or Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = Or;
-                       b->left = $<1;
-                       b->right = $<3;
-                       $0 = b;
-               } }$
-               | Expression or else Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = OrElse;
-                       b->left = $<1;
-                       b->right = $<4;
-                       $0 = b;
-               } }$
+       | Expression or Expression ${ {
+               struct binode *b = new(binode);
+               b->op = Or;
+               b->left = $<1;
+               b->right = $<3;
+               $0 = b;
+       } }$
+       | Expression or else Expression ${ {
+               struct binode *b = new(binode);
+               b->op = OrElse;
+               b->left = $<1;
+               b->right = $<4;
+               $0 = b;
+       } }$
 
-               | Expression and Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = And;
-                       b->left = $<1;
-                       b->right = $<3;
-                       $0 = b;
-               } }$
-               | Expression and then Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = AndThen;
-                       b->left = $<1;
-                       b->right = $<4;
-                       $0 = b;
-               } }$
+       | Expression and Expression ${ {
+               struct binode *b = new(binode);
+               b->op = And;
+               b->left = $<1;
+               b->right = $<3;
+               $0 = b;
+       } }$
+       | Expression and then Expression ${ {
+               struct binode *b = new(binode);
+               b->op = AndThen;
+               b->left = $<1;
+               b->right = $<4;
+               $0 = b;
+       } }$
 
-               | not Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = Not;
-                       b->right = $<2;
-                       $0 = b;
-               } }$
+       | not Expression ${ {
+               struct binode *b = new(binode);
+               b->op = Not;
+               b->right = $<2;
+               $0 = b;
+       } }$
 
 ###### print binode cases
        case And:
@@ -3181,8 +3950,8 @@ evaluate the second expression if not necessary.
        case OrElse:
        case Not:
                /* both must be Tbool, result is Tbool */
-               propagate_types(b->left, c, ok, Tbool, 0);
-               propagate_types(b->right, c, ok, Tbool, 0);
+               propagate_types(b->left, c, perr, Tbool, 0);
+               propagate_types(b->right, c, perr, Tbool, 0);
                if (type && type != Tbool)
                        type_err(c, "error: %1 operation found where %2 expected", prog,
                                   Tbool, 0, type);
@@ -3243,7 +4012,7 @@ expression operator, and the `CMPop` non-terminal will match one of them.
        Eql,
        NEql,
 
-###### expr precedence
+###### declare terminals
        $LEFT < > <= >= == != CMPop
 
 ###### expression grammar
@@ -3258,12 +4027,12 @@ expression operator, and the `CMPop` non-terminal will match one of them.
 ###### Grammar
 
        $eop
-       CMPop ->   < ${ $0.op = Less; }$
-               |  > ${ $0.op = Gtr; }$
-               |  <= ${ $0.op = LessEq; }$
-               |  >= ${ $0.op = GtrEq; }$
-               |  == ${ $0.op = Eql; }$
-               |  != ${ $0.op = NEql; }$
+       CMPop ->  < ${ $0.op = Less; }$
+       |         > ${ $0.op = Gtr; }$
+       |         <= ${ $0.op = LessEq; }$
+       |         >= ${ $0.op = GtrEq; }$
+       |         == ${ $0.op = Eql; }$
+       |         != ${ $0.op = NEql; }$
 
 ###### print binode cases
 
@@ -3296,13 +4065,13 @@ expression operator, and the `CMPop` non-terminal will match one of them.
        case Eql:
        case NEql:
                /* Both must match but not be labels, result is Tbool */
-               t = propagate_types(b->left, c, ok, NULL, Rnolabel);
+               t = propagate_types(b->left, c, perr, NULL, 0);
                if (t)
-                       propagate_types(b->right, c, ok, t, 0);
+                       propagate_types(b->right, c, perr, t, 0);
                else {
-                       t = propagate_types(b->right, c, ok, NULL, Rnolabel);   // UNTESTED
+                       t = propagate_types(b->right, c, perr, NULL, 0);        // UNTESTED
                        if (t)  // UNTESTED
-                               t = propagate_types(b->left, c, ok, t, 0);      // UNTESTED
+                               t = propagate_types(b->left, c, perr, t, 0);    // UNTESTED
                }
                if (!type_compat(type, Tbool, 0))
                        type_err(c, "error: Comparison returns %1 but %2 expected", prog,
@@ -3337,10 +4106,17 @@ expression operator, and the `CMPop` non-terminal will match one of them.
 ### Expressions: Arithmetic etc.
 
 The remaining expressions with the highest precedence are arithmetic,
-string concatenation, and string conversion.  String concatenation
+string concatenation, string conversion, and testing.  String concatenation
 (`++`) has the same precedence as multiplication and division, but lower
 than the uniary.
 
+Testing comes in two forms.  A single question mark (`?`) is a uniary
+operator which converts come types into Boolean.  The general meaning is
+"is this a value value" and there will be more uses as the language
+develops.  A double questionmark (`??`) is a binary operator (Choose),
+with same precedence as multiplication, which returns the LHS if it
+tests successfully, else returns the RHS.
+
 String conversion is a temporary feature until I get a better type
 system.  `$` is a prefix operator which expects a string and returns
 a number.
@@ -3350,68 +4126,72 @@ absolute value and negation).  These have different operator names.
 
 We also have a 'Bracket' operator which records where parentheses were
 found.  This makes it easy to reproduce these when printing.  Possibly I
-should only insert brackets were needed for precedence.
+should only insert brackets were needed for precedence.  Putting
+parentheses around an expression converts it into a Term,
 
 ###### Binode types
        Plus, Minus,
        Times, Divide, Rem,
-       Concat,
-       Absolute, Negate,
+       Concat, Choose,
+       Absolute, Negate, Test,
        StringConv,
        Bracket,
 
-###### expr precedence
+###### declare terminals
        $LEFT + - Eop
-       $LEFT * / % ++ Top
-       $LEFT Uop $
+       $LEFT * / % ++ ?? Top
+       $LEFT Uop $ ?
        $TERM ( )
 
 ###### expression grammar
-               | Expression Eop Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = $2.op;
-                       b->left = $<1;
-                       b->right = $<3;
-                       $0 = b;
-               } }$
+       | Expression Eop Expression ${ {
+               struct binode *b = new(binode);
+               b->op = $2.op;
+               b->left = $<1;
+               b->right = $<3;
+               $0 = b;
+       } }$
 
-               | Expression Top Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = $2.op;
-                       b->left = $<1;
-                       b->right = $<3;
-                       $0 = b;
-               } }$
-
-               | ( Expression ) ${ {
-                       struct binode *b = new_pos(binode, $1);
-                       b->op = Bracket;
-                       b->right = $<2;
-                       $0 = b;
-               } }$
-               | Uop Expression ${ {
-                       struct binode *b = new(binode);
-                       b->op = $1.op;
-                       b->right = $<2;
-                       $0 = b;
-               } }$
-               | Value ${ $0 = $<1; }$
-               | Variable ${ $0 = $<1; }$
+       | Expression Top Expression ${ {
+               struct binode *b = new(binode);
+               b->op = $2.op;
+               b->left = $<1;
+               b->right = $<3;
+               $0 = b;
+       } }$
+
+       | Uop Expression ${ {
+               struct binode *b = new(binode);
+               b->op = $1.op;
+               b->right = $<2;
+               $0 = b;
+       } }$
+
+###### term grammar
+
+       | ( Expression ) ${ {
+               struct binode *b = new_pos(binode, $1);
+               b->op = Bracket;
+               b->right = $<2;
+               $0 = b;
+       } }$
 
 ###### Grammar
 
        $eop
-       Eop ->    + ${ $0.op = Plus; }$
-               | - ${ $0.op = Minus; }$
+       Eop ->   + ${ $0.op = Plus; }$
+       |        - ${ $0.op = Minus; }$
 
-       Uop ->    + ${ $0.op = Absolute; }$
-               | - ${ $0.op = Negate; }$
-               | $ ${ $0.op = StringConv; }$
+       Uop ->   + ${ $0.op = Absolute; }$
+       |        - ${ $0.op = Negate; }$
+       |        $ ${ $0.op = StringConv; }$
+       |        ? ${ $0.op = Test; }$
 
-       Top ->    * ${ $0.op = Times; }$
-               | / ${ $0.op = Divide; }$
-               | % ${ $0.op = Rem; }$
-               | ++ ${ $0.op = Concat; }$
+       Top ->   * ${ $0.op = Times; }$
+       |        / ${ $0.op = Divide; }$
+       |        % ${ $0.op = Rem; }$
+       |        ++ ${ $0.op = Concat; }$
+       |        ?? ${ $0.op = Choose; }$
 
 ###### print binode cases
        case Plus:
@@ -3420,6 +4200,7 @@ should only insert brackets were needed for precedence.
        case Divide:
        case Concat:
        case Rem:
+       case Choose:
                if (bracket) printf("(");
                print_exec(b->left, indent, bracket);
                switch(b->op) {
@@ -3429,6 +4210,7 @@ should only insert brackets were needed for precedence.
                case Divide: fputs(" / ", stdout); break;
                case Rem:    fputs(" % ", stdout); break;
                case Concat: fputs(" ++ ", stdout); break;
+               case Choose: fputs(" ?? ", stdout); break;
                default: abort();       // NOTEST
                }                       // NOTEST
                print_exec(b->right, indent, bracket);
@@ -3437,11 +4219,13 @@ should only insert brackets were needed for precedence.
        case Absolute:
        case Negate:
        case StringConv:
+       case Test:
                if (bracket) printf("(");
                switch (b->op) {
                case Absolute:   fputs("+", stdout); break;
                case Negate:     fputs("-", stdout); break;
                case StringConv: fputs("$", stdout); break;
+               case Test:       fputs("?", stdout); break;
                default: abort();       // NOTEST
                }                       // NOTEST
                print_exec(b->right, indent, bracket);
@@ -3464,8 +4248,8 @@ should only insert brackets were needed for precedence.
        case Negate:
                /* as propagate_types ignores a NULL,
                 * unary ops fit here too */
-               propagate_types(b->left, c, ok, Tnum, 0);
-               propagate_types(b->right, c, ok, Tnum, 0);
+               propagate_types(b->left, c, perr, Tnum, 0);
+               propagate_types(b->right, c, perr, Tnum, 0);
                if (!type_compat(type, Tnum, 0))
                        type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
                                   Tnum, rules, type);
@@ -3473,8 +4257,8 @@ should only insert brackets were needed for precedence.
 
        case Concat:
                /* both must be Tstr, result is Tstr */
-               propagate_types(b->left, c, ok, Tstr, 0);
-               propagate_types(b->right, c, ok, Tstr, 0);
+               propagate_types(b->left, c, perr, Tstr, 0);
+               propagate_types(b->right, c, perr, Tstr, 0);
                if (!type_compat(type, Tstr, 0))
                        type_err(c, "error: Concat returns %1 but %2 expected", prog,
                                   Tstr, rules, type);
@@ -3482,15 +4266,34 @@ should only insert brackets were needed for precedence.
 
        case StringConv:
                /* op must be string, result is number */
-               propagate_types(b->left, c, ok, Tstr, 0);
+               propagate_types(b->left, c, perr, Tstr, 0);
                if (!type_compat(type, Tnum, 0))
                        type_err(c,     // UNTESTED
                          "error: Can only convert string to number, not %1",
                                prog, type, 0, NULL);
                return Tnum;
 
+       case Test:
+               /* LHS must support ->test, result is Tbool */
+               t = propagate_types(b->right, c, perr, NULL, 0);
+               if (!t || !t->test)
+                       type_err(c, "error: '?' requires a testable value, not %1",
+                                prog, t, 0, NULL);
+               return Tbool;
+
+       case Choose:
+               /* LHS and RHS must match and are returned. Must support
+                * ->test
+                */
+               t = propagate_types(b->left, c, perr, type, rules);
+               t = propagate_types(b->right, c, perr, t, rules);
+               if (t && t->test == NULL)
+                       type_err(c, "error: \"??\" requires a testable value, not %1",
+                                prog, t, 0, NULL);
+               return t;
+
        case Bracket:
-               return propagate_types(b->right, c, ok, type, 0);
+               return propagate_types(b->right, c, perr, type, 0);
 
 ###### interp binode cases
 
@@ -3567,6 +4370,20 @@ should only insert brackets were needed for precedence.
                        printf("Unsupported suffix: %.*s\n", tx.len, tx.txt);   // UNTESTED
 
                break;
+       case Test:
+               right = interp_exec(c, b->right, &rtype);
+               rvtype = Tbool;
+               rv.bool = !!rtype->test(rtype, &right);
+               break;
+       case Choose:
+               left = interp_exec(c, b->left, &ltype);
+               if (ltype->test(ltype, &left)) {
+                       rv = left;
+                       rvtype = ltype;
+                       ltype = NULL;
+               } else
+                       rv = interp_exec(c, b->right, &rvtype);
+               break;
 
 ###### value functions
 
@@ -3580,110 +4397,6 @@ should only insert brackets were needed for precedence.
                return rv;
        }
 
-### Function calls
-
-A function call can appear either as an expression or as a statement.
-As functions cannot yet return values, only the statement version will work.
-We use a new 'Funcall' binode type to link the function with a list of
-arguments, form with the 'List' nodes.
-
-###### Binode types
-       Funcall,
-
-###### expression grammar
-       | Variable ( ExpressionList ) ${ {
-               struct binode *b = new(binode);
-               b->op = Funcall;
-               b->left = $<V;
-               b->right = reorder_bilist($<EL);
-               $0 = b;
-       } }$
-       | Variable ( ) ${ {
-               struct binode *b = new(binode);
-               b->op = Funcall;
-               b->left = $<V;
-               b->right = NULL;
-               $0 = b;
-       } }$
-
-###### SimpleStatement Grammar
-
-       | Variable ( ExpressionList ) ${ {
-               struct binode *b = new(binode);
-               b->op = Funcall;
-               b->left = $<V;
-               b->right = reorder_bilist($<EL);
-               $0 = b;
-       } }$
-
-###### print binode cases
-
-       case Funcall:
-               do_indent(indent, "");
-               print_exec(b->left, -1, bracket);
-               printf("(");
-               for (b = cast(binode, b->right); b; b = cast(binode, b->right)) {
-                       if (b->left) {
-                               printf(" ");
-                               print_exec(b->left, -1, bracket);
-                               if (b->right)
-                                       printf(",");
-                       }
-               }
-               printf(")");
-               if (indent >= 0)
-                       printf("\n");
-               break;
-
-###### propagate binode cases
-
-       case Funcall: {
-               /* Every arg must match formal parameter, and result
-                * is return type of function
-                */
-               struct binode *args = cast(binode, b->right);
-               struct var *v = cast(var, b->left);
-
-               if (!v->var->type || v->var->type->check_args == NULL) {
-                       type_err(c, "error: attempt to call a non-function.",
-                                prog, NULL, 0, NULL);
-                       return NULL;
-               }
-               v->var->type->check_args(c, ok, v->var->type, args);
-               return v->var->type->function.return_type;
-       }
-
-###### interp binode cases
-
-       case Funcall: {
-               struct var *v = cast(var, b->left);
-               struct type *t = v->var->type;
-               void *oldlocal = c->local;
-               int old_size = c->local_size;
-               void *local = calloc(1, t->function.local_size);
-               struct value *fbody = var_value(c, v->var);
-               struct binode *arg = cast(binode, b->right);
-               struct binode *param = t->function.params;
-
-               while (param) {
-                       struct var *pv = cast(var, param->left);
-                       struct type *vtype = NULL;
-                       struct value val = interp_exec(c, arg->left, &vtype);
-                       struct value *lval;
-                       c->local = local; c->local_size = t->function.local_size;
-                       lval = var_value(c, pv->var);
-                       c->local = oldlocal; c->local_size = old_size;
-                       memcpy(lval, &val, vtype->size);
-                       param = cast(binode, param->right);
-                       arg = cast(binode, arg->right);
-               }
-               c->local = local; c->local_size = t->function.local_size;
-               rv = interp_exec(c, fbody->function, &rvtype);
-               c->local = oldlocal; c->local_size = old_size;
-               free(local);
-               break;
-       }
-
 ### Blocks, Statements, and Statement lists.
 
 Now that we have expressions out of the way we need to turn to
@@ -3737,6 +4450,12 @@ which does nothing and is represented as a `NULL` pointer in a `Block`
 list.  Other stand-alone statements will follow once the infrastructure
 is in-place.
 
+As many statements will use binodes, we declare a binode pointer 'b' in
+the common header for all reductions to use.
+
+###### Parser: reduce
+       struct binode *b;
+
 ###### Binode types
        Block,
 
@@ -3746,77 +4465,78 @@ is in-place.
 
        $*binode
        Block -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
-               | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
-               | SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
-               | SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
-               | IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
+       |        { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+       |        SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
+       |        SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+       |        IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
 
        OpenBlock -> OpenScope { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
-               | OpenScope { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
-               | OpenScope SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
-               | OpenScope SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
-               | IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
+       |        OpenScope { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+       |        OpenScope SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
+       |        OpenScope SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+       |        IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
 
        UseBlock -> { OpenScope IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
-               | { OpenScope SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
-               | IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
+       |        { OpenScope SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+       |        IN OpenScope OptNL Statementlist OUT ${ $0 = $<Sl; }$
 
        ColonBlock -> { IN OptNL Statementlist OUT OptNL } ${ $0 = $<Sl; }$
-               | { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
-               | : SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
-               | : SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
-               | : IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
+       |        { SimpleStatements } ${ $0 = reorder_bilist($<SS); }$
+       |        : SimpleStatements ; ${ $0 = reorder_bilist($<SS); }$
+       |        : SimpleStatements EOL ${ $0 = reorder_bilist($<SS); }$
+       |        : IN OptNL Statementlist OUT ${ $0 = $<Sl; }$
 
        Statementlist -> ComplexStatements ${ $0 = reorder_bilist($<CS); }$
 
        ComplexStatements -> ComplexStatements ComplexStatement ${
-                       if ($2 == NULL) {
-                               $0 = $<1;
-                       } else {
-                               $0 = new(binode);
-                               $0->op = Block;
-                               $0->left = $<1;
-                               $0->right = $<2;
-                       }
-               }$
-               | ComplexStatement ${
-                       if ($1 == NULL) {
-                               $0 = NULL;
-                       } else {
-                               $0 = new(binode);
-                               $0->op = Block;
-                               $0->left = NULL;
-                               $0->right = $<1;
-                       }
-               }$
-
-       $*exec
-       ComplexStatement -> SimpleStatements Newlines ${
-                       $0 = reorder_bilist($<SS);
-                       }$
-               |  SimpleStatements ; Newlines ${
-                       $0 = reorder_bilist($<SS);
-                       }$
-               ## ComplexStatement Grammar
-
-       $*binode
-       SimpleStatements -> SimpleStatements ; SimpleStatement ${
+               if ($2 == NULL) {
+                       $0 = $<1;
+               } else {
                        $0 = new(binode);
                        $0->op = Block;
                        $0->left = $<1;
-                       $0->right = $<3;
-                       }$
-               | SimpleStatement ${
+                       $0->right = $<2;
+               }
+       }$
+       | ComplexStatement ${
+               if ($1 == NULL) {
+                       $0 = NULL;
+               } else {
                        $0 = new(binode);
                        $0->op = Block;
                        $0->left = NULL;
                        $0->right = $<1;
-                       }$
+               }
+       }$
+
+       $*exec
+       ComplexStatement -> SimpleStatements Newlines ${
+               $0 = reorder_bilist($<SS);
+       }$
+       |  SimpleStatements ; Newlines ${
+               $0 = reorder_bilist($<SS);
+       }$
+       ## ComplexStatement Grammar
+
+       $*binode
+       SimpleStatements -> SimpleStatements ; SimpleStatement ${
+               $0 = new(binode);
+               $0->op = Block;
+               $0->left = $<1;
+               $0->right = $<3;
+       }$
+       | SimpleStatement ${
+               $0 = new(binode);
+               $0->op = Block;
+               $0->left = NULL;
+               $0->right = $<1;
+       }$
 
        $TERM pass
+       $*exec
        SimpleStatement -> pass ${ $0 = NULL; }$
-               | ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$
-               ## SimpleStatement Grammar
+       | ERROR ${ tok_err(c, "Syntax error in statement", &$1); }$
+       ## SimpleStatement Grammar
 
 ###### print binode cases
        case Block:
@@ -3854,7 +4574,7 @@ is in-place.
                struct binode *e;
 
                for (e = b; e; e = cast(binode, e->right)) {
-                       t = propagate_types(e->left, c, ok, NULL, rules);
+                       t = propagate_types(e->left, c, perr, NULL, rules);
                        if ((rules & Rboolok) && (t == Tbool || t == Tnone))
                                t = NULL;
                        if (t == Tnone && e->right)
@@ -3866,7 +4586,7 @@ is in-place.
                                if (!type)
                                        type = t;
                                else if (t != type)
-                                       type_err(c, "error: expected %1%r, found %2",
+                                       type_err(c, "error: expected %1, found %2",
                                                 e->left, type, rules, t);
                        }
                }
@@ -3897,28 +4617,28 @@ printed.
 ###### Binode types
        Print,
 
-##### expr precedence
+##### declare terminals
        $TERM print
 
 ###### SimpleStatement Grammar
 
        | print ExpressionList ${
-               $0 = new(binode);
-               $0->op = Print;
-               $0->right = NULL;
-               $0->left = reorder_bilist($<EL);
+               $0 = b = new_pos(binode, $1);
+               b->op = Print;
+               b->right = NULL;
+               b->left = reorder_bilist($<EL);
        }$
        | print ExpressionList , ${ {
-               $0 = new(binode);
-               $0->op = Print;
-               $0->right = reorder_bilist($<EL);
-               $0->left = NULL;
+               $0 = b = new_pos(binode, $1);
+               b->op = Print;
+               b->right = reorder_bilist($<EL);
+               b->left = NULL;
        } }$
        | print ${
-               $0 = new(binode);
-               $0->op = Print;
-               $0->left = NULL;
-               $0->right = NULL;
+               $0 = b = new_pos(binode, $1);
+               b->op = Print;
+               b->left = NULL;
+               b->right = NULL;
        }$
 
 ###### print binode cases
@@ -3943,7 +4663,7 @@ printed.
                else
                        b = cast(binode, b->right);
                while (b) {
-                       propagate_types(b->left, c, ok, NULL, Rnolabel);
+                       propagate_types(b->left, c, perr, NULL, 0);
                        b = cast(binode, b->right);
                }
                break;
@@ -3974,9 +4694,9 @@ An assignment will assign a value to a variable, providing it hasn't
 been declared as a constant.  The analysis phase ensures that the type
 will be correct so the interpreter just needs to perform the
 calculation.  There is a form of assignment which declares a new
-variable as well as assigning a value.  If a name is assigned before
-it is declared, and error will be raised as the name is created as
-`Tlabel` and it is illegal to assign to such names.
+variable as well as assigning a value.  If a name is used before
+it is declared, it is assumed to be a global constant which are allowed to
+be declared at any time.
 
 ###### Binode types
        Assign,
@@ -3986,40 +4706,40 @@ it is declared, and error will be raised as the name is created as
        $TERM =
 
 ###### SimpleStatement Grammar
-       | Variable = Expression ${
-                       $0 = new(binode);
-                       $0->op = Assign;
-                       $0->left = $<1;
-                       $0->right = $<3;
-               }$
+       | Term = Expression ${
+               $0 = b= new(binode);
+               b->op = Assign;
+               b->left = $<1;
+               b->right = $<3;
+       }$
        | VariableDecl = Expression ${
-                       $0 = new(binode);
-                       $0->op = Declare;
-                       $0->left = $<1;
-                       $0->right =$<3;
-               }$
+               $0 = b= new(binode);
+               b->op = Declare;
+               b->left = $<1;
+               b->right =$<3;
+       }$
 
        | VariableDecl ${
-                       if ($1->var->where_set == NULL) {
-                               type_err(c,
-                                        "Variable declared with no type or value: %v",
-                                        $1, NULL, 0, NULL);
-                               free_var($1);
-                       } else {
-                               $0 = new(binode);
-                               $0->op = Declare;
-                               $0->left = $<1;
-                               $0->right = NULL;
-                       }
-               }$
+               if ($1->var->where_set == NULL) {
+                       type_err(c,
+                                "Variable declared with no type or value: %v",
+                                $1, NULL, 0, NULL);
+                       free_var($1);
+               } else {
+                       $0 = b = new(binode);
+                       b->op = Declare;
+                       b->left = $<1;
+                       b->right = NULL;
+               }
+       }$
 
 ###### print binode cases
 
        case Assign:
                do_indent(indent, "");
-               print_exec(b->left, indent, bracket);
+               print_exec(b->left, -1, bracket);
                printf(" = ");
-               print_exec(b->right, indent, bracket);
+               print_exec(b->right, -1, bracket);
                if (indent >= 0)
                        printf("\n");
                break;
@@ -4028,23 +4748,23 @@ it is declared, and error will be raised as the name is created as
                {
                struct variable *v = cast(var, b->left)->var;
                do_indent(indent, "");
-               print_exec(b->left, indent, bracket);
+               print_exec(b->left, -1, bracket);
                if (cast(var, b->left)->var->constant) {
                        printf("::");
-                       if (v->where_decl == v->where_set) {
+                       if (v->explicit_type) {
                                type_print(v->type, stdout);
                                printf(" ");
                        }
                } else {
                        printf(":");
-                       if (v->where_decl == v->where_set) {
+                       if (v->explicit_type) {
                                type_print(v->type, stdout);
                                printf(" ");
                        }
                }
                if (b->right) {
                        printf("= ");
-                       print_exec(b->right, indent, bracket);
+                       print_exec(b->right, -1, bracket);
                }
                if (indent >= 0)
                        printf("\n");
@@ -4060,23 +4780,23 @@ it is declared, and error will be raised as the name is created as
                 * For Assign, left must not be constant.
                 * result is Tnone
                 */
-               t = propagate_types(b->left, c, ok, NULL,
-                                   Rnolabel | (b->op == Assign ? Rnoconstant : 0));
+               t = propagate_types(b->left, c, perr, NULL,
+                                   (b->op == Assign ? Rnoconstant : 0));
                if (!b->right)
                        return Tnone;
 
                if (t) {
-                       if (propagate_types(b->right, c, ok, t, 0) != t)
+                       if (propagate_types(b->right, c, perr, t, 0) != t)
                                if (b->left->type == Xvar)
                                        type_err(c, "info: variable '%v' was set as %1 here.",
                                                 cast(var, b->left)->var->where_set, t, rules, NULL);
                } else {
-                       t = propagate_types(b->right, c, ok, NULL, Rnolabel);
+                       t = propagate_types(b->right, c, perr, NULL, 0);
                        if (t)
-                               propagate_types(b->left, c, ok, t,
+                               propagate_types(b->left, c, perr, t,
                                                (b->op == Assign ? Rnoconstant : 0));
                }
-               if (t && t->dup == NULL)
+               if (t && t->dup == NULL && !(*perr & Emaycopy))
                        type_err(c, "error: cannot assign value of type %1", b, t, 0, NULL);
                return Tnone;
 
@@ -4116,25 +4836,14 @@ function which has a return type, and the "condition" code blocks in
 ###### Binode types
        Use,
 
-###### expr precedence
+###### declare terminals
        $TERM use
 
 ###### SimpleStatement Grammar
        | use Expression ${
-               $0 = new_pos(binode, $1);
-               $0->op = Use;
-               $0->right = $<2;
-               if ($0->right->type == Xvar) {
-                       struct var *v = cast(var, $0->right);
-                       if (v->var->type == Tnone) {
-                               /* Convert this to a label */
-                               struct value *val;
-
-                               v->var->type = Tlabel;
-                               val = global_alloc(c, Tlabel, v->var, NULL);
-                               val->label = val;
-                       }
-               }
+               $0 = b = new_pos(binode, $1);
+               b->op = Use;
+               b->right = $<2;
        }$
 
 ###### print binode cases
@@ -4150,7 +4859,7 @@ function which has a return type, and the "condition" code blocks in
 
        case Use:
                /* result matches value */
-               return propagate_types(b->right, c, ok, type, 0);
+               return propagate_types(b->right, c, perr, type, 0);
 
 ###### interp binode cases
 
@@ -4279,7 +4988,7 @@ casepart` to track a list of case parts.
 ###### ComplexStatement Grammar
        | CondStatement ${ $0 = $<1; }$
 
-###### expr precedence
+###### declare terminals
        $TERM for then while do
        $TERM else
        $TERM switch case
@@ -4296,136 +5005,136 @@ casepart` to track a list of case parts.
        // 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->looppart = $<WP;
-                       var_block_close(c, CloseSequential, $0);
-                       }$
-               | ForPart OptNL WhilePart CondSuffix ${
-                       $0 = $<CS;
-                       $0->forpart = $<FP;
-                       $0->looppart = $<WP;
-                       var_block_close(c, CloseSequential, $0);
-                       }$
-               | WhilePart CondSuffix ${
-                       $0 = $<CS;
-                       $0->looppart = $<WP;
-                       }$
-               | SwitchPart OptNL CasePart CondSuffix ${
-                       $0 = $<CS;
-                       $0->condpart = $<SP;
-                       $CP->next = $0->casepart;
-                       $0->casepart = $<CP;
-                       var_block_close(c, CloseSequential, $0);
-                       }$
-               | SwitchPart : IN OptNL CasePart CondSuffix OUT Newlines ${
-                       $0 = $<CS;
-                       $0->condpart = $<SP;
-                       $CP->next = $0->casepart;
-                       $0->casepart = $<CP;
-                       var_block_close(c, CloseSequential, $0);
-                       }$
-               | IfPart IfSuffix ${
-                       $0 = $<IS;
-                       $0->condpart = $IP.condpart; $IP.condpart = NULL;
-                       $0->thenpart = $IP.thenpart; $IP.thenpart = NULL;
-                       // This is where we close an "if" statement
-                       var_block_close(c, CloseSequential, $0);
-                       }$
+               $0 = $<CS;
+               $0->forpart = $<FP;
+               $0->thenpart = $<TP;
+               $0->looppart = $<WP;
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | ForPart OptNL WhilePart CondSuffix ${
+               $0 = $<CS;
+               $0->forpart = $<FP;
+               $0->looppart = $<WP;
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | WhilePart CondSuffix ${
+               $0 = $<CS;
+               $0->looppart = $<WP;
+       }$
+       | SwitchPart OptNL CasePart CondSuffix ${
+               $0 = $<CS;
+               $0->condpart = $<SP;
+               $CP->next = $0->casepart;
+               $0->casepart = $<CP;
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | SwitchPart : IN OptNL CasePart CondSuffix OUT Newlines ${
+               $0 = $<CS;
+               $0->condpart = $<SP;
+               $CP->next = $0->casepart;
+               $0->casepart = $<CP;
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | IfPart IfSuffix ${
+               $0 = $<IS;
+               $0->condpart = $IP.condpart; $IP.condpart = NULL;
+               $0->thenpart = $IP.thenpart; $IP.thenpart = NULL;
+               // This is where we close an "if" statement
+               var_block_close(c, CloseSequential, $0);
+       }$
 
        CondSuffix -> IfSuffix ${
-                       $0 = $<1;
-               }$
-               | Newlines CasePart CondSuffix ${
-                       $0 = $<CS;
-                       $CP->next = $0->casepart;
-                       $0->casepart = $<CP;
-               }$
-               | CasePart CondSuffix ${
-                       $0 = $<CS;
-                       $CP->next = $0->casepart;
-                       $0->casepart = $<CP;
-               }$
+               $0 = $<1;
+       }$
+       | Newlines CasePart CondSuffix ${
+               $0 = $<CS;
+               $CP->next = $0->casepart;
+               $0->casepart = $<CP;
+       }$
+       | CasePart CondSuffix ${
+               $0 = $<CS;
+               $CP->next = $0->casepart;
+               $0->casepart = $<CP;
+       }$
 
        IfSuffix -> Newlines ${ $0 = new(cond_statement); }$
-               | Newlines ElsePart ${ $0 = $<EP; }$
-               | ElsePart ${$0 = $<EP; }$
+       | Newlines ElsePart ${ $0 = $<EP; }$
+       | ElsePart ${$0 = $<EP; }$
 
        ElsePart -> else OpenBlock Newlines ${
-                       $0 = new(cond_statement);
-                       $0->elsepart = $<OB;
-                       var_block_close(c, CloseElse, $0->elsepart);
-               }$
-               | else OpenScope CondStatement ${
-                       $0 = new(cond_statement);
-                       $0->elsepart = $<CS;
-                       var_block_close(c, CloseElse, $0->elsepart);
-               }$
+               $0 = new(cond_statement);
+               $0->elsepart = $<OB;
+               var_block_close(c, CloseElse, $0->elsepart);
+       }$
+       | else OpenScope CondStatement ${
+               $0 = new(cond_statement);
+               $0->elsepart = $<CS;
+               var_block_close(c, CloseElse, $0->elsepart);
+       }$
 
        $*casepart
        CasePart -> case Expression OpenScope ColonBlock ${
-                       $0 = calloc(1,sizeof(struct casepart));
-                       $0->value = $<Ex;
-                       $0->action = $<Bl;
-                       var_block_close(c, CloseParallel, $0->action);
-               }$
+               $0 = calloc(1,sizeof(struct casepart));
+               $0->value = $<Ex;
+               $0->action = $<Bl;
+               var_block_close(c, CloseParallel, $0->action);
+       }$
 
        $*exec
        // These scopes are closed in CondStatement
        ForPart -> for OpenBlock ${
-                       $0 = $<Bl;
-               }$
+               $0 = $<Bl;
+       }$
 
        ThenPart -> then OpenBlock ${
-                       $0 = $<OB;
-                       var_block_close(c, CloseSequential, $0);
-               }$
+               $0 = $<OB;
+               var_block_close(c, CloseSequential, $0);
+       }$
 
        $*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, $0->right);
-                       var_block_close(c, CloseSequential, $0);
-               }$
-               | while OpenScope Expression OpenScope ColonBlock ${
-                       $0 = new(binode);
-                       $0->op = Loop;
-                       $0->left = $<Exp;
-                       $0->right = $<CB;
-                       var_block_close(c, CloseSequential, $0->right);
-                       var_block_close(c, CloseSequential, $0);
-               }$
+               $0 = new(binode);
+               $0->op = Loop;
+               $0->left = $<UB;
+               $0->right = $<OB;
+               var_block_close(c, CloseSequential, $0->right);
+               var_block_close(c, CloseSequential, $0);
+       }$
+       | while OpenScope Expression OpenScope ColonBlock ${
+               $0 = new(binode);
+               $0->op = Loop;
+               $0->left = $<Exp;
+               $0->right = $<CB;
+               var_block_close(c, CloseSequential, $0->right);
+               var_block_close(c, CloseSequential, $0);
+       }$
 
        $cond_statement
        IfPart -> if UseBlock OptNL then OpenBlock ${
-                       $0.condpart = $<UB;
-                       $0.thenpart = $<OB;
-                       var_block_close(c, CloseParallel, $0.thenpart);
-               }$
-               | if OpenScope Expression OpenScope ColonBlock ${
-                       $0.condpart = $<Ex;
-                       $0.thenpart = $<CB;
-                       var_block_close(c, CloseParallel, $0.thenpart);
-               }$
-               | if OpenScope Expression OpenScope OptNL then Block ${
-                       $0.condpart = $<Ex;
-                       $0.thenpart = $<Bl;
-                       var_block_close(c, CloseParallel, $0.thenpart);
-               }$
+               $0.condpart = $<UB;
+               $0.thenpart = $<OB;
+               var_block_close(c, CloseParallel, $0.thenpart);
+       }$
+       | if OpenScope Expression OpenScope ColonBlock ${
+               $0.condpart = $<Ex;
+               $0.thenpart = $<CB;
+               var_block_close(c, CloseParallel, $0.thenpart);
+       }$
+       | if OpenScope Expression OpenScope OptNL then Block ${
+               $0.condpart = $<Ex;
+               $0.thenpart = $<Bl;
+               var_block_close(c, CloseParallel, $0.thenpart);
+       }$
 
        $*exec
        // This scope is closed in CondStatement
        SwitchPart -> switch OpenScope Expression ${
-                       $0 = $<Ex;
-               }$
-               | switch UseBlock ${
-                       $0 = $<Bl;
-               }$
+               $0 = $<Ex;
+       }$
+       | switch UseBlock ${
+               $0 = $<Bl;
+       }$
 
 ###### print binode cases
        case Loop:
@@ -4537,10 +5246,10 @@ casepart` to track a list of case parts.
 
 ###### propagate binode cases
        case Loop:
-               t = propagate_types(b->right, c, ok, Tnone, 0);
+               t = propagate_types(b->right, c, perr, Tnone, 0);
                if (!type_compat(Tnone, t, 0))
-                       *ok = 0;        // UNTESTED
-               return propagate_types(b->left, c, ok, type, rules);
+                       *perr |= Efail; // UNTESTED
+               return propagate_types(b->left, c, perr, type, rules);
 
 ###### propagate exec cases
        case Xcond_statement:
@@ -4558,51 +5267,51 @@ casepart` to track a list of case parts.
                struct cond_statement *cs = cast(cond_statement, prog);
                struct casepart *cp;
 
-               t = propagate_types(cs->forpart, c, ok, Tnone, 0);
+               t = propagate_types(cs->forpart, c, perr, Tnone, 0);
                if (!type_compat(Tnone, t, 0))
-                       *ok = 0;        // UNTESTED
+                       *perr |= Efail; // UNTESTED
 
                if (cs->looppart) {
-                       t = propagate_types(cs->thenpart, c, ok, Tnone, 0);
+                       t = propagate_types(cs->thenpart, c, perr, Tnone, 0);
                        if (!type_compat(Tnone, t, 0))
-                               *ok = 0;        // UNTESTED
+                               *perr |= Efail; // UNTESTED
                }
                if (cs->casepart == NULL) {
-                       propagate_types(cs->condpart, c, ok, Tbool, 0);
-                       propagate_types(cs->looppart, c, ok, Tbool, 0);
+                       propagate_types(cs->condpart, c, perr, Tbool, 0);
+                       propagate_types(cs->looppart, c, perr, 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);
+                               t = propagate_types(cp->value, c, perr, NULL, 0);
                        if (!t && cs->condpart)
-                               t = propagate_types(cs->condpart, c, ok, NULL, Rboolok);        // UNTESTED
+                               t = propagate_types(cs->condpart, c, perr, NULL, Rboolok);      // UNTESTED
                        if (!t && cs->looppart)
-                               t = propagate_types(cs->looppart, c, ok, NULL, Rboolok);        // UNTESTED
+                               t = propagate_types(cs->looppart, c, perr, 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);
+                                       propagate_types(cp->value, c, perr, t, 0);
+                               propagate_types(cs->condpart, c, perr, t, Rboolok);
+                               propagate_types(cs->looppart, c, perr, t, Rboolok);
                        }
                }
                // (if)then, else, and case parts must return expected type.
                if (!cs->looppart && !type)
-                       type = propagate_types(cs->thenpart, c, ok, NULL, rules);
+                       type = propagate_types(cs->thenpart, c, perr, NULL, rules);
                if (!type)
-                       type = propagate_types(cs->elsepart, c, ok, NULL, rules);
+                       type = propagate_types(cs->elsepart, c, perr, NULL, rules);
                for (cp = cs->casepart;
                     cp && !type;
                     cp = cp->next)     // UNTESTED
-                       type = propagate_types(cp->action, c, ok, NULL, rules); // UNTESTED
+                       type = propagate_types(cp->action, c, perr, NULL, rules);       // UNTESTED
                if (type) {
                        if (!cs->looppart)
-                               propagate_types(cs->thenpart, c, ok, type, rules);
-                       propagate_types(cs->elsepart, c, ok, type, rules);
+                               propagate_types(cs->thenpart, c, perr, type, rules);
+                       propagate_types(cs->elsepart, c, perr, type, rules);
                        for (cp = cs->casepart; cp ; cp = cp->next)
-                               propagate_types(cp->action, c, ok, type, rules);
+                               propagate_types(cp->action, c, perr, type, rules);
                        return type;
                } else
                        return NULL;
@@ -4685,20 +5394,21 @@ various declarations in the parse context.
        ## declare terminals
 
        OptNL ->
-               | OptNL NEWLINE
+       | OptNL NEWLINE
+
        Newlines -> NEWLINE
-               | Newlines NEWLINE
+       | Newlines NEWLINE
 
        DeclarationList -> Declaration
-               | DeclarationList Declaration
+       | DeclarationList Declaration
 
        Declaration -> ERROR Newlines ${
-                       tok_err(c,      // UNTESTED
-                               "error: unhandled parse error", &$1);
-               }$
-               | DeclareConstant
-               | DeclareFunction
-               | DeclareStruct
+               tok_err(c,      // UNTESTED
+                       "error: unhandled parse error", &$1);
+       }$
+       | DeclareConstant
+       | DeclareFunction
+       | DeclareStruct
 
        ## top level grammar
 
@@ -4706,13 +5416,15 @@ various declarations in the parse context.
 
 ### The `const` section
 
-As well as being defined in with the code that uses them, constants
-can be declared at the top level.  These have full-file scope, so they
-are always `InScope`.  The value of a top level constant can be given
-as an expression, and this is evaluated immediately rather than in the
-later interpretation stage.  Once we add functions to the language, we
-will need rules concern which, if any, can be used to define a top
-level constant.
+As well as being defined in with the code that uses them, constants can
+be declared at the top level.  These have full-file scope, so they are
+always `InScope`, even before(!) they have been declared.  The value of
+a top level constant can be given as an expression, and this is
+evaluated after parsing and before execution.
+
+A function call can be used to evaluate a constant, but it will not have
+access to any program state, once such statement becomes meaningful.
+e.g.  arguments and filesystem will not be visible.
 
 Constants are defined in a section that starts with the reserved word
 `const` and then has a block with a list of assignment statements.
@@ -4721,97 +5433,147 @@ make it clear that they are constants.  Type can also be given: if
 not, the type will be determined during analysis, as with other
 constants.
 
-As the types constants are inserted at the head of a list, printing
-them in the same order that they were read is not straight forward.
-We take a quadratic approach here and count the number of constants
-(variables of depth 0), then count down from there, each time
-searching through for the Nth constant for decreasing N.
+###### parse context
+       struct binode *constlist;
 
 ###### top level grammar
 
        $TERM const
 
        DeclareConstant -> const { IN OptNL ConstList OUT OptNL } Newlines
-               | const { SimpleConstList } Newlines
-               | const IN OptNL ConstList OUT Newlines
-               | const SimpleConstList Newlines
+       | const { SimpleConstList } Newlines
+       | const IN OptNL ConstList OUT Newlines
+       | const SimpleConstList Newlines
 
        ConstList -> ConstList SimpleConstLine
-               | SimpleConstLine
+       | SimpleConstLine
+
        SimpleConstList -> SimpleConstList ; Const
-               | Const
-               | SimpleConstList ;
+       | Const
+       | SimpleConstList ;
+
        SimpleConstLine -> SimpleConstList Newlines
-               | ERROR Newlines ${ tok_err(c, "Syntax error in constant", &$1); }$
+       | ERROR Newlines ${ tok_err(c, "Syntax error in constant", &$1); }$
 
        $*type
        CType -> Type   ${ $0 = $<1; }$
-               |       ${ $0 = NULL; }$
+       |               ${ $0 = NULL; }$
+
        $void
        Const -> IDENTIFIER :: CType = Expression ${ {
-               int ok;
                struct variable *v;
+               struct binode *bl, *bv;
+               struct var *var = new_pos(var, $ID);
 
-               v = var_decl(c, $1.txt);
+               v = var_decl(c, $ID.txt);
                if (v) {
-                       struct var *var = new_pos(var, $1);
                        v->where_decl = var;
                        v->where_set = var;
-                       var->var = v;
+                       v->type = $<CT;
                        v->constant = 1;
                        v->global = 1;
                } else {
-                       struct variable *vorig = var_ref(c, $1.txt);
-                       tok_err(c, "error: name already declared", &$1);
-                       type_err(c, "info: this is where '%v' was first declared",
-                                vorig->where_decl, NULL, 0, NULL);
-               }
-               do {
-                       ok = 1;
-                       propagate_types($5, c, &ok, $3, 0);
-               } while (ok == 2);
-               if (!ok)
-                       c->parse_error = 1;
-               else if (v) {
-                       struct value res = interp_exec(c, $5, &v->type);
-                       global_alloc(c, v->type, v, &res);
+                       v = var_ref(c, $1.txt);
+                       if (v->type == Tnone) {
+                               v->where_decl = var;
+                               v->where_set = var;
+                               v->type = $<CT;
+                               v->constant = 1;
+                               v->global = 1;
+                       } else {
+                               tok_err(c, "error: name already declared", &$1);
+                               type_err(c, "info: this is where '%v' was first declared",
+                                        v->where_decl, NULL, 0, NULL);
+                       }
                }
+               var->var = v;
+
+               bv = new(binode);
+               bv->op = Declare;
+               bv->left = var;
+               bv->right= $<Exp;
+
+               bl = new(binode);
+               bl->op = List;
+               bl->left = c->constlist;
+               bl->right = bv;
+               c->constlist = bl;
        } }$
 
-###### print const decls
+###### core functions
+       static void resolve_consts(struct parse_context *c)
        {
-               struct variable *v;
-               int target = -1;
-
-               while (target != 0) {
-                       int i = 0;
-                       for (v = context.in_scope; v; v=v->in_scope)
-                               if (v->depth == 0 && v->constant) {
-                                       i += 1;
-                                       if (i == target)
-                                               break;
+               struct binode *b;
+               int retry = 1;
+               enum { none, some, cannot } progress = none;
+
+               c->constlist = reorder_bilist(c->constlist);
+               while (retry) {
+                       retry = 0;
+                       for (b = cast(binode, c->constlist); b;
+                            b = cast(binode, b->right)) {
+                               enum prop_err perr;
+                               struct binode *vb = cast(binode, b->left);
+                               struct var *v = cast(var, vb->left);
+                               if (v->var->frame_pos >= 0)
+                                       continue;
+                               do {
+                                       perr = 0;
+                                       propagate_types(vb->right, c, &perr,
+                                                       v->var->type, 0);
+                               } while (perr & Eretry);
+                               if (perr & Efail)
+                                       c->parse_error += 1;
+                               else if (!(perr & Eruntime)) {
+                                       progress = some;
+                                       struct value res = interp_exec(
+                                               c, vb->right, &v->var->type);
+                                       global_alloc(c, v->var->type, v->var, &res);
+                               } else {
+                                       if (progress == cannot)
+                                               type_err(c, "error: const %v cannot be resolved.",
+                                                        v, NULL, 0, NULL);
+                                       else
+                                               retry = 1;
                                }
-
-                       if (target == -1) {
-                               if (i)
-                                       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, val, stdout);
-                               if (v->type == Tstr)
-                                       printf("\"");
-                               printf("\n");
-                               target -= 1;
                        }
+                       switch (progress) {
+                       case cannot:
+                               retry = 0; break;
+                       case none:
+                               progress = cannot; break;
+                       case some:
+                               progress = none; break;
+                       }
+               }
+       }
+
+###### print const decls
+       {
+               struct binode *b;
+               int first = 1;
+
+               for (b = cast(binode, context.constlist); b;
+                    b = cast(binode, b->right)) {
+                       struct binode *vb = cast(binode, b->left);
+                       struct var *vr = cast(var, vb->left);
+                       struct variable *v = vr->var;
+
+                       if (first)
+                               printf("const\n");
+                       first = 0;
+
+                       printf("    %.*s :: ", v->name->name.len, v->name->name.txt);
+                       type_print(v->type, stdout);
+                       printf(" = ");
+                       print_exec(vb->right, -1, 0);
+                       printf("\n");
                }
        }
 
+###### free const decls
+       free_binode(context.constlist);
+
 ### Function declarations
 
 The code in an Ocean program is all stored in function declarations.
@@ -4825,20 +5587,63 @@ is a bit more interesting at this level.
 
 ###### ast functions
 
+       static struct type *handle_results(struct parse_context *c,
+                                          struct binode *results)
+       {
+               /* Create a 'struct' type from the results list, which
+                * is a list for 'struct var'
+                */
+               struct type *t = add_anon_type(c, &structure_prototype,
+                                              "function result");
+               int cnt = 0;
+               struct binode *b;
+
+               for (b = results; b; b = cast(binode, b->right))
+                       cnt += 1;
+               t->structure.nfields = cnt;
+               t->structure.fields = calloc(cnt, sizeof(struct field));
+               cnt = 0;
+               for (b = results; b; b = cast(binode, b->right)) {
+                       struct var *v = cast(var, b->left);
+                       struct field *f = &t->structure.fields[cnt++];
+                       int a = v->var->type->align;
+                       f->name = v->var->name->name;
+                       f->type = v->var->type;
+                       f->init = NULL;
+                       f->offset = t->size;
+                       v->var->frame_pos = f->offset;
+                       t->size += ((f->type->size - 1) | (a-1)) + 1;
+                       if (a > t->align)
+                               t->align = a;
+                       variable_unlink_exec(v->var);
+               }
+               free_binode(results);
+               return t;
+       }
+
        static struct variable *declare_function(struct parse_context *c,
                                                struct variable *name,
                                                struct binode *args,
                                                struct type *ret,
+                                               struct binode *results,
                                                struct exec *code)
        {
-               struct text funcname = {" func", 5};
                if (name) {
                        struct value fn = {.function = code};
-                       name->type = add_type(c, funcname, &function_prototype);
-                       name->type->function.params = reorder_bilist(args);
-                       name->type->function.return_type = ret;
-                       global_alloc(c, name->type, name, &fn);
+                       struct type *t;
                        var_block_close(c, CloseFunction, code);
+                       t = add_anon_type(c, &function_prototype, 
+                                         "func %.*s", name->name->name.len, 
+                                         name->name->name.txt);
+                       name->type = t;
+                       t->function.params = reorder_bilist(args);
+                       if (!ret) {
+                               ret = handle_results(c, reorder_bilist(results));
+                               t->function.inline_result = 1;
+                               t->function.local_size = ret->size;
+                       }
+                       t->function.return_type = ret;
+                       global_alloc(c, t, name, &fn);
                        name->type->function.scope = c->out_scope;
                } else {
                        free_binode(args);
@@ -4857,23 +5662,32 @@ is a bit more interesting at this level.
 
        $*variable
        DeclareFunction -> func FuncName ( OpenScope ArgsLine ) Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
-               }$
-               | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, Tnone, $<Bl);
-               }$
-               | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
-                       $0 = declare_function(c, $<FN, NULL, Tnone, $<Bl);
-               }$
-               | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, $<Bl);
-               }$
-               | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
-                       $0 = declare_function(c, $<FN, $<Ar, $<Ty, $<Bl);
-               }$
-               | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
-                       $0 = declare_function(c, $<FN, NULL, $<Ty, $<Bl);
-               }$
+               $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
+       }$
+       | func FuncName IN OpenScope Args OUT OptNL do Block Newlines ${
+               $0 = declare_function(c, $<FN, $<Ar, Tnone, NULL, $<Bl);
+       }$
+       | func FuncName NEWLINE OpenScope OptNL do Block Newlines ${
+               $0 = declare_function(c, $<FN, NULL, Tnone, NULL, $<Bl);
+       }$
+       | func FuncName ( OpenScope ArgsLine ) : Type Block Newlines ${
+               $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
+       }$
+       | func FuncName ( OpenScope ArgsLine ) : ( ArgsLine ) Block Newlines ${
+               $0 = declare_function(c, $<FN, $<AL, NULL, $<AL2, $<Bl);
+       }$
+       | func FuncName IN OpenScope Args OUT OptNL return Type Newlines do Block Newlines ${
+               $0 = declare_function(c, $<FN, $<Ar, $<Ty, NULL, $<Bl);
+       }$
+       | func FuncName NEWLINE OpenScope return Type Newlines do Block Newlines ${
+               $0 = declare_function(c, $<FN, NULL, $<Ty, NULL, $<Bl);
+       }$
+       | func FuncName IN OpenScope Args OUT OptNL return IN Args OUT OptNL do Block Newlines ${
+               $0 = declare_function(c, $<FN, $<Ar, NULL, $<Ar2, $<Bl);
+       }$
+       | func FuncName NEWLINE OpenScope return IN Args OUT OptNL do Block Newlines ${
+               $0 = declare_function(c, $<FN, NULL, NULL, $<Ar, $<Bl);
+       }$
 
 ###### print func decls
        {
@@ -4913,22 +5727,24 @@ is a bit more interesting at this level.
                int all_ok = 1;
                for (v = c->in_scope; v; v = v->in_scope) {
                        struct value *val;
-                       int ok = 1;
+                       struct type *ret;
+                       enum prop_err perr;
                        if (v->depth != 0 || !v->type || !v->type->check_args)
                                continue;
+                       ret = v->type->function.inline_result ?
+                               Tnone : v->type->function.return_type;
                        val = var_value(c, v);
                        do {
-                               ok = 1;
-                               propagate_types(val->function, c, &ok,
-                                               v->type->function.return_type, 0);
-                       } while (ok == 2);
-                       if (ok)
+                               perr = 0;
+                               propagate_types(val->function, c, &perr, ret, 0);
+                       } while (!(perr & Efail) && (perr & Eretry));
+                       if (!(perr & Efail))
                                /* Make sure everything is still consistent */
-                               propagate_types(val->function, c, &ok,
-                                               v->type->function.return_type, 0);
-                       if (!ok)
+                               propagate_types(val->function, c, &perr, ret, 0);
+                       if (perr & Efail)
                                all_ok = 0;
-                       if (!v->type->function.return_type->dup) {
+                       if (!v->type->function.inline_result &&
+                           !v->type->function.return_type->dup) {
                                type_err(c, "error: function cannot return value of type %1", 
                                         v->where_decl, v->type->function.return_type, 0, NULL);
                        }
@@ -4942,26 +5758,25 @@ is a bit more interesting at this level.
        {
                struct binode *bp = type->function.params;
                struct binode *b;
-               int ok = 1;
+               enum prop_err perr;
                int arg = 0;
                struct type *argv_type;
-               struct text argv_type_name = { " argv", 5 };
 
-               argv_type = add_type(c, argv_type_name, &array_prototype);
+               argv_type = add_anon_type(c, &array_prototype, "argv");
                argv_type->array.member = Tstr;
                argv_type->array.unspec = 1;
 
                for (b = bp; b; b = cast(binode, b->right)) {
-                       ok = 1;
+                       perr = 0;
                        switch (arg++) {
                        case 0: /* argv */
-                               propagate_types(b->left, c, &ok, argv_type, 0);
+                               propagate_types(b->left, c, &perr, argv_type, 0);
                                break;
                        default: /* invalid */  // NOTEST
-                               propagate_types(b->left, c, &ok, Tnone, 0);     // NOTEST
+                               propagate_types(b->left, c, &perr, Tnone, 0);   // NOTEST
                        }
-                       if (!ok)
-                               c->parse_error = 1;
+                       if (perr & Efail)
+                               c->parse_error += 1;
                }
 
                return !c->parse_error;
@@ -4982,12 +5797,12 @@ is a bit more interesting at this level.
                        progp = var_value(c, mainv);
                if (!progp || !progp->function) {
                        fprintf(stderr, "oceani: no main function found.\n");
-                       c->parse_error = 1;
+                       c->parse_error += 1;
                        return;
                }
                if (!analyse_main(mainv->type, c)) {
                        fprintf(stderr, "oceani: main has wrong type.\n");
-                       c->parse_error = 1;
+                       c->parse_error += 1;
                        return;
                }
                al = mainv->type->function.params;
@@ -5120,19 +5935,19 @@ things which will likely grow as the languages grows.
                while
                        mid := (lo + hi) / 2
                        if mid == target:
-                               use Found
+                               use .Found
                        if mid < target:
                                lo = mid
                        else
                                hi = mid
                        if hi - lo < 1:
                                lo = mid
-                               use GiveUp
+                               use .GiveUp
                        use True
                do pass
-               case Found:
+               case .Found:
                        print "Yay, I found", target
-               case GiveUp:
+               case .GiveUp:
                        print "Closest I found was", lo
 
                size::= 10