]> ocean-lang.org Git - ocean/commitdiff
oceani: separate types out from values
authorNeilBrown <neil@brown.name>
Wed, 29 Sep 2021 22:21:15 +0000 (08:21 +1000)
committerNeilBrown <neil@brown.name>
Thu, 30 Sep 2021 04:38:09 +0000 (14:38 +1000)
Rather than embed the type in ever value, keep it separate.
This allows arrays without duplicating the member-type information.

Also review and clean up the commentary.

Signed-off-by: NeilBrown <neil@brown.name>
csrc/oceani.mdc

index 97c195382cd8955c4e0c16e154125e5d7fa2e93b..520212be6d144365a586638bf695098689460058 100644 (file)
@@ -81,7 +81,7 @@ There will be two formats for printing the program: a default and one
 that uses bracketing.  So a `--bracket` command line option is needed
 for that.  Normally the first code section found is used, however an
 alternate section can be requested so that a file (such as this one)
 that uses bracketing.  So a `--bracket` command line option is needed
 for that.  Normally the first code section found is used, however an
 alternate section can be requested so that a file (such as this one)
-can contain multiple programs This is effected with the `--section`
+can contain multiple programs This is effected with the `--section`
 option.
 
 This code must be compiled with `-fplan9-extensions` so that anonymous
 option.
 
 This code must be compiled with `-fplan9-extensions` so that anonymous
@@ -281,7 +281,7 @@ and the program will not run.
 
 If the same variable is declared in both branchs of an 'if/else', or
 in all cases of a 'switch' then the multiple instances may be merged
 
 If the same variable is declared in both branchs of an 'if/else', or
 in all cases of a 'switch' then the multiple instances may be merged
-into just one variable if the variable is references after the
+into just one variable if the variable is referenced after the
 conditional statement.  When this happens, the types must naturally be
 consistent across all the branches.  When the variable is not used
 outside the if, the variables in the different branches are distinct
 conditional statement.  When this happens, the types must naturally be
 consistent across all the branches.  When the variable is not used
 outside the if, the variables in the different branches are distinct
@@ -340,10 +340,10 @@ errors, each language element will need to record a file location
 element where its type was set.  For now we will assume that each line
 of an error message indicates one location in the file, and up to 2
 types.  So we provide a `printf`-like function which takes a format, a
 element where its type was set.  For now we will assume that each line
 of an error message indicates one location in the file, and up to 2
 types.  So we provide a `printf`-like function which takes a format, a
-language (a `struct exec` which has not yet been introduced), and 2
+location (a `struct exec` which has not yet been introduced), and 2
 types. "`%1`" reports the first type, "`%2`" reports the second.  We
 will need a function to print the location, once we know how that is
 types. "`%1`" reports the first type, "`%2`" reports the second.  We
 will need a function to print the location, once we know how that is
-stored.  As will be explained later, there are sometimes extra rules for
+stored. e As will be explained later, there are sometimes extra rules for
 type matching and they might affect error messages, we need to pass those
 in too.
 
 type matching and they might affect error messages, we need to pass those
 in too.
 
@@ -400,9 +400,9 @@ There are various "things" that the language and/or the interpreter
 needs to know about to parse and execute a program.  These include
 types, variables, values, and executable code.  These are all lumped
 together under the term "entities" (calling them "objects" would be
 needs to know about to parse and execute a program.  These include
 types, variables, values, and executable code.  These are all lumped
 together under the term "entities" (calling them "objects" would be
-confusing) and introduced here.  These will introduced and described
-here.  The following section will present the different specific code
-elements which comprise or manipulate these various entities.
+confusing) and introduced here.  The following section will present the
+different specific code elements which comprise or manipulate these
+various entities.
 
 ### Types
 
 
 ### Types
 
@@ -416,18 +416,18 @@ better integrated into the language.
 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
 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 eventaully enable types to
+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.
 
 determine if they are compatible with other types, though such types
 have not yet been implemented.
 
-Named type are stored in a simple linked list.  Objects of each type are "values"
-which are often passed around by value.
+Named type are stored in a simple linked list.  Objects of each type are
+"values" which are often passed around by value.
 
 ###### ast
 
        struct value {
 
 ###### ast
 
        struct value {
-               struct type *type;
                union {
                union {
+                       char ptr[1];
                        ## value union fields
                };
        };
                        ## value union fields
                };
        };
@@ -435,17 +435,18 @@ which are often passed around by value.
        struct type {
                struct text name;
                struct type *next;
        struct type {
                struct text name;
                struct type *next;
-               struct value (*init)(struct type *type);
-               struct value (*prepare)(struct type *type);
-               struct value (*parse)(struct type *type, char *str);
-               void (*print)(struct value val);
+               int size, align;
+               void (*init)(struct type *type, struct value *val);
+               int (*parse)(struct type *type, char *str, struct value *val);
+               void (*print)(struct type *type, struct value *val);
                void (*print_type)(struct type *type, FILE *f);
                void (*print_type)(struct type *type, FILE *f);
-               int (*cmp_order)(struct value v1, struct value v2);
-               int (*cmp_eq)(struct value v1, struct value v2);
-               struct value (*dup)(struct value val);
-               void (*free)(struct value val);
+               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);
                void (*free_type)(struct type *t);
-               int (*compat)(struct type *this, struct type *other);
                long long (*to_int)(struct value *v);
                double (*to_float)(struct value *v);
                int (*to_mpq)(mpq_t *q, struct value *v);
                long long (*to_int)(struct value *v);
                double (*to_float)(struct value *v);
                int (*to_mpq)(mpq_t *q, struct value *v);
@@ -491,25 +492,10 @@ which are often passed around by value.
                 */
        }
 
                 */
        }
 
-       static void free_value(struct value v)
-       {
-               if (v.type)
-                       v.type->free(v);
-       }
-
-       static int type_compat(struct type *require, struct type *have, int rules)
+       static void free_value(struct type *type, struct value *v)
        {
        {
-               if ((rules & Rboolok) && have == Tbool)
-                       return 1;
-               if ((rules & Rnolabel) && have == Tlabel)
-                       return 0;
-               if (!require || !have)
-                       return 1;
-
-               if (require->compat)
-                       return require->compat(require, have);
-
-               return require == have;
+               if (type && v)
+                       type->free(type, v);
        }
 
        static void type_print(struct type *type, FILE *f)
        }
 
        static void type_print(struct type *type, FILE *f)
@@ -524,70 +510,71 @@ which are often passed around by value.
                        fputs("*invalid*type*", f);     // NOTEST
        }
 
                        fputs("*invalid*type*", f);     // NOTEST
        }
 
-       static struct value val_prepare(struct type *type)
+       static void val_init(struct type *type, struct value *val)
        {
        {
-               struct value rv;
-
-               if (type)
-                       return type->prepare(type);
-               rv.type = type;
-               return rv;
-       }
-
-       static struct value val_init(struct type *type)
-       {
-               struct value rv;
-
-               if (type)
-                       return type->init(type);
-               rv.type = type;
-               return rv;
+               if (type && type->init)
+                       type->init(type, val);
        }
 
        }
 
-       static struct value dup_value(struct value v)
+       static void dup_value(struct type *type, 
+                             struct value *vold, struct value *vnew)
        {
        {
-               if (v.type)
-                       return v.type->dup(v);
-               return v;
+               if (type && type->dup)
+                       type->dup(type, vold, vnew);
        }
 
        }
 
-       static int value_cmp(struct value left, struct value right)
+       static int value_cmp(struct type *tl, struct type *tr,
+                            struct value *left, struct value *right)
        {
        {
-               if (left.type && left.type->cmp_order)
-                       return left.type->cmp_order(left, right);
-               if (left.type && left.type->cmp_eq)
-                       return left.type->cmp_eq(left, 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;
        }
 
                return -1;
        }
 
-       static void print_value(struct value v)
+       static void print_value(struct type *type, struct value *v)
        {
        {
-               if (v.type && v.type->print)
-                       v.type->print(v);
+               if (type && type->print)
+                       type->print(type, v);
                else
                        printf("*Unknown*");            // NOTEST
        }
 
                else
                        printf("*Unknown*");            // NOTEST
        }
 
-       static struct value parse_value(struct type *type, char *arg)
+       static int parse_value(struct type *type, char *arg, 
+                              struct value *val)
        {
        {
-               struct value rv;
-
                if (type && type->parse)
                if (type && type->parse)
-                       return type->parse(type, arg);
-               rv.type = NULL;                         // NOTEST
-               return rv;                              // NOTEST
+                       return type->parse(type, arg, val);
+               return 0;                               // NOTEST
+       }
+
+       static struct value *val_alloc(struct type *t, struct value *init)
+       {
+               struct value *ret;
+
+               if (!t->size)
+                       val_init(t, NULL);
+               ret = calloc(1, t->size);
+               if (init)
+                       memcpy(ret, init, t->size);
+               else
+                       val_init(t, ret);
+               return ret;
        }
 
 ###### forward decls
 
        }
 
 ###### forward decls
 
-       static void free_value(struct value v);
+       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 int type_compat(struct type *require, struct type *have, int rules);
        static void type_print(struct type *type, FILE *f);
-       static struct value val_init(struct type *type);
-       static struct value dup_value(struct value v);
-       static int value_cmp(struct value left, struct value right);
-       static void print_value(struct value v);
-       static struct value parse_value(struct type *type, char *arg);
+       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);
+       static int parse_value(struct type *type, char *arg, struct value *val);
 
 ###### free context types
 
 
 ###### free context types
 
@@ -619,16 +606,30 @@ 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.
 
 primary type, and in others any type is acceptable except a label (`Vlabel`).
 A separate function encoding these cases will simplify some code later.
 
+## type functions
+
+       int (*compat)(struct type *this, struct type *other);
+
+## ast functions
+
+       static int type_compat(struct type *require, struct type *have, int rules)
+       {
+               if ((rules & Rboolok) && have == Tbool)
+                       return 1;
+               if ((rules & Rnolabel) && have == Tlabel)
+                       return 0;
+               if (!require || !have)
+                       return 1;
+
+               if (require->compat)
+                       return require->compat(require, have);
+
+               return require == have;
+       }
+
 When assigning command line arguments to variables, we need to be able
 to parse each type from a string.
 
 When assigning command line arguments to variables, we need to be able
 to parse each type from a string.
 
-The distinction beteen "prepare" and "init" needs to be explained.
-"init" sets up an initial value, such as "zero" or the empty string.
-"prepare" simply prepares the data structure so that if "free" gets
-called on it, it won't do something silly.  Normally a value will be
-stored after "prepare" but before "free", but this might not happen if
-there are errors.
-
 ###### includes
        #include <gmp.h>
        #include "parse_string.h"
 ###### includes
        #include <gmp.h>
        #include "parse_string.h"
@@ -644,16 +645,18 @@ there are errors.
 ###### value union fields
        struct text str;
        mpq_t num;
 ###### value union fields
        struct text str;
        mpq_t num;
-       int bool;
+       unsigned char bool;
        void *label;
 
 ###### ast functions
        void *label;
 
 ###### ast functions
-       static void _free_value(struct value v)
+       static void _free_value(struct type *type, struct value *v)
        {
        {
-               switch (v.type->vtype) {
+               if (!v)
+                       return;
+               switch (type->vtype) {
                case Vnone: break;
                case Vnone: break;
-               case Vstr: free(v.str.txt); break;
-               case Vnum: mpq_clear(v.num); break;
+               case Vstr: free(v->str.txt); break;
+               case Vnum: mpq_clear(v->num); break;
                case Vlabel:
                case Vbool: break;
                }
                case Vlabel:
                case Vbool: break;
                }
@@ -661,112 +664,82 @@ there are errors.
 
 ###### value functions
 
 
 ###### value functions
 
-       static struct value _val_prepare(struct type *type)
+       static void _val_init(struct type *type, struct value *val)
        {
        {
-               struct value rv;
-
-               rv.type = type;
-               switch(type->vtype) {
-               case Vnone:
-                       break;
-               case Vnum:
-                       memset(&rv.num, 0, sizeof(rv.num));
-                       break;
-               case Vstr:
-                       rv.str.txt = NULL;
-                       rv.str.len = 0;
-                       break;
-               case Vbool:
-                       rv.bool = 0;
-                       break;
-               case Vlabel:
-                       rv.label = NULL;
-                       break;
-               }
-               return rv;
-       }
-
-       static struct value _val_init(struct type *type)
-       {
-               struct value rv;
-
-               rv.type = type;
                switch(type->vtype) {
                case Vnone:             // NOTEST
                        break;          // NOTEST
                case Vnum:
                switch(type->vtype) {
                case Vnone:             // NOTEST
                        break;          // NOTEST
                case Vnum:
-                       mpq_init(rv.num); break;
+                       mpq_init(val->num); break;
                case Vstr:
                case Vstr:
-                       rv.str.txt = malloc(1);
-                       rv.str.len = 0;
+                       val->str.txt = malloc(1);
+                       val->str.len = 0;
                        break;
                case Vbool:
                        break;
                case Vbool:
-                       rv.bool = 0;
+                       val->bool = 0;
                        break;
                case Vlabel:                    // NOTEST
                        break;
                case Vlabel:                    // NOTEST
-                       rv.label = NULL;        // NOTEST
+                       val->label = NULL;      // NOTEST
                        break;                  // NOTEST
                }
                        break;                  // NOTEST
                }
-               return rv;
        }
 
        }
 
-       static struct value _dup_value(struct value v)
+       static void _dup_value(struct type *type, 
+                              struct value *vold, struct value *vnew)
        {
        {
-               struct value rv;
-               rv.type = v.type;
-               switch (rv.type->vtype) {
+               switch (type->vtype) {
                case Vnone:             // NOTEST
                        break;          // NOTEST
                case Vlabel:
                case Vnone:             // NOTEST
                        break;          // NOTEST
                case Vlabel:
-                       rv.label = v.label;
+                       vnew->label = vold->label;
                        break;
                case Vbool:
                        break;
                case Vbool:
-                       rv.bool = v.bool;
+                       vnew->bool = vold->bool;
                        break;
                case Vnum:
                        break;
                case Vnum:
-                       mpq_init(rv.num);
-                       mpq_set(rv.num, v.num);
+                       mpq_init(vnew->num);
+                       mpq_set(vnew->num, vold->num);
                        break;
                case Vstr:
                        break;
                case Vstr:
-                       rv.str.len = v.str.len;
-                       rv.str.txt = malloc(rv.str.len);
-                       memcpy(rv.str.txt, v.str.txt, v.str.len);
+                       vnew->str.len = vold->str.len;
+                       vnew->str.txt = malloc(vnew->str.len);
+                       memcpy(vnew->str.txt, vold->str.txt, vnew->str.len);
                        break;
                }
                        break;
                }
-               return rv;
        }
 
        }
 
-       static int _value_cmp(struct value left, struct value right)
+       static int _value_cmp(struct type *tl, struct type *tr,
+                             struct value *left, struct value *right)
        {
                int cmp;
        {
                int cmp;
-               if (left.type != right.type)
-                       return left.type - right.type;  // NOTEST
-               switch (left.type->vtype) {
-               case Vlabel: cmp = left.label == right.label ? 0 : 1; break;
-               case Vnum: cmp = mpq_cmp(left.num, right.num); break;
-               case Vstr: cmp = text_cmp(left.str, right.str); break;
-               case Vbool: cmp = left.bool - right.bool; break;
+               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;
        }
 
                case Vnone: cmp = 0;                    // NOTEST
                }
                return cmp;
        }
 
-       static void _print_value(struct value v)
+       static void _print_value(struct type *type, struct value *v)
        {
        {
-               switch (v.type->vtype) {
+               switch (type->vtype) {
                case Vnone:                             // NOTEST
                        printf("*no-value*"); break;    // NOTEST
                case Vlabel:                            // NOTEST
                case Vnone:                             // NOTEST
                        printf("*no-value*"); break;    // NOTEST
                case Vlabel:                            // NOTEST
-                       printf("*label-%p*", v.label); break; // NOTEST
+                       printf("*label-%p*", v->label); break; // NOTEST
                case Vstr:
                case Vstr:
-                       printf("%.*s", v.str.len, v.str.txt); break;
+                       printf("%.*s", v->str.len, v->str.txt); break;
                case Vbool:
                case Vbool:
-                       printf("%s", v.bool ? "True":"False"); break;
+                       printf("%s", v->bool ? "True":"False"); break;
                case Vnum:
                        {
                        mpf_t fl;
                        mpf_init2(fl, 20);
                case Vnum:
                        {
                        mpf_t fl;
                        mpf_init2(fl, 20);
-                       mpf_set_q(fl, v.num);
+                       mpf_set_q(fl, v->num);
                        gmp_printf("%Fg", fl);
                        mpf_clear(fl);
                        break;
                        gmp_printf("%Fg", fl);
                        mpf_clear(fl);
                        break;
@@ -774,23 +747,20 @@ there are errors.
                }
        }
 
                }
        }
 
-       static struct value _parse_value(struct type *type, char *arg)
+       static int _parse_value(struct type *type, char *arg, struct value *val)
        {
        {
-               struct value val;
                struct text tx;
                int neg = 0;
                char tail[3] = "";
 
                struct text tx;
                int neg = 0;
                char tail[3] = "";
 
-               val.type = type;
                switch(type->vtype) {
                case Vlabel:                            // NOTEST
                case Vnone:                             // NOTEST
                switch(type->vtype) {
                case Vlabel:                            // NOTEST
                case Vnone:                             // NOTEST
-                       val.type = NULL;                // NOTEST
-                       break;                          // NOTEST
+                       return 0;                       // NOTEST
                case Vstr:
                case Vstr:
-                       val.str.len = strlen(arg);
-                       val.str.txt = malloc(val.str.len);
-                       memcpy(val.str.txt, arg, val.str.len);
+                       val->str.len = strlen(arg);
+                       val->str.txt = malloc(val->str.len);
+                       memcpy(val->str.txt, arg, val->str.len);
                        break;
                case Vnum:
                        if (*arg == '-') {
                        break;
                case Vnum:
                        if (*arg == '-') {
@@ -798,36 +768,35 @@ there are errors.
                                arg++;
                        }
                        tx.txt = arg; tx.len = strlen(tx.txt);
                                arg++;
                        }
                        tx.txt = arg; tx.len = strlen(tx.txt);
-                       if (number_parse(val.num, tail, tx) == 0)
-                               mpq_init(val.num);
+                       if (number_parse(val->num, tail, tx) == 0)
+                               mpq_init(val->num);
                        else if (neg)
                        else if (neg)
-                               mpq_neg(val.num, val.num);
+                               mpq_neg(val->num, val->num);
                        if (tail[0]) {
                                printf("Unsupported suffix: %s\n", arg);
                        if (tail[0]) {
                                printf("Unsupported suffix: %s\n", arg);
-                               val.type = NULL;
+                               return 0;
                        }
                        break;
                case Vbool:
                        if (strcasecmp(arg, "true") == 0 ||
                            strcmp(arg, "1") == 0)
                        }
                        break;
                case Vbool:
                        if (strcasecmp(arg, "true") == 0 ||
                            strcmp(arg, "1") == 0)
-                               val.bool = 1;
+                               val->bool = 1;
                        else if (strcasecmp(arg, "false") == 0 ||
                                 strcmp(arg, "0") == 0)
                        else if (strcasecmp(arg, "false") == 0 ||
                                 strcmp(arg, "0") == 0)
-                               val.bool = 0;
+                               val->bool = 0;
                        else {
                                printf("Bad bool: %s\n", arg);
                        else {
                                printf("Bad bool: %s\n", arg);
-                               val.type = NULL;
+                               return 0;
                        }
                        break;
                }
                        }
                        break;
                }
-               return val;
+               return 1;
        }
 
        }
 
-       static void _free_value(struct value v);
+       static void _free_value(struct type *type, struct value *v);
 
        static struct type base_prototype = {
                .init = _val_init,
 
        static struct type base_prototype = {
                .init = _val_init,
-               .prepare = _val_prepare,
                .parse = _parse_value,
                .print = _print_value,
                .cmp_order = _value_cmp,
                .parse = _parse_value,
                .print = _print_value,
                .cmp_order = _value_cmp,
@@ -839,28 +808,33 @@ there are errors.
        static struct type *Tbool, *Tstr, *Tnum, *Tnone, *Tlabel;
 
 ###### ast functions
        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)
+       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;
        {
                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;
                return t;
        }
 
 ###### context initialization
 
                return t;
        }
 
 ###### context initialization
 
-       Tbool  = add_base_type(&context, "Boolean", Vbool);
-       Tstr   = add_base_type(&context, "string", Vstr);
-       Tnum   = add_base_type(&context, "number", Vnum);
-       Tnone  = add_base_type(&context, "none", Vnone);
-       Tlabel = add_base_type(&context, "label", Vlabel);
+       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*));
 
 ### Variables
 
 
 ### Variables
 
-Variables are scoped named values.  We store the names in a linked
-list of "bindings" sorted lexically, and use sequential search and
+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
 insertion sort.
 
 ###### ast
@@ -910,7 +884,8 @@ cannot nest, so a declaration while a name is in-scope is an error.
 ###### ast
        struct variable {
                struct variable *previous;
 ###### ast
        struct variable {
                struct variable *previous;
-               struct value val;
+               struct type *type;
+               struct value *val;
                struct binding *name;
                struct exec *where_decl;// where name was declared
                struct exec *where_set; // where type was set
                struct binding *name;
                struct exec *where_decl;// where name was declared
                struct exec *where_set; // where type was set
@@ -962,12 +937,12 @@ 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
 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 must already be conditionally scoped.
+branches, whether they may already be conditionally scoped.
 
 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
 
 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 recognized.  This can be placed, for example, between a keyword
+it is recognised.  This can be placed, for example, between a keyword
 like "if" and the code following it.
 
 ###### ast
 like "if" and the code following it.
 
 ###### ast
@@ -1047,12 +1022,11 @@ Each variable records a scope depth and is in one of four states:
        struct variable *in_scope;
 
 All variables with the same name are linked together using the
        struct variable *in_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.
+'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
 
 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
@@ -1094,7 +1068,8 @@ list of in_scope names.
                        struct variable *t = v;
 
                        v = t->previous;
                        struct variable *t = v;
 
                        v = t->previous;
-                       free_value(t->val);
+                       free_value(t->type, t->val);
+                       free(t->val);
                        if (t->depth == 0)
                                // This is a global constant
                                free_exec(t->where_decl);
                        if (t->depth == 0)
                                // This is a global constant
                                free_exec(t->where_decl);
@@ -1118,8 +1093,8 @@ depth if the name is unbound or bound to a conditionally scoped or
 pending-scope variable.  If the previous variable was conditionally
 scoped, it and its homonyms becomes out-of-scope.
 
 pending-scope variable.  If the previous variable was conditionally
 scoped, it and its homonyms becomes out-of-scope.
 
-When we parse a variable reference (including non-declarative
-assignment) we report an error if the name is not bound or is bound to
+When we parse a variable reference (including non-declarative assignment
+"foo = bar") we report an error if the name is not bound or is bound to
 a pending-scope variable; update the scope if the name is bound to a
 conditionally scoped variable; or just proceed normally if the named
 variable is in scope.
 a pending-scope variable; update the scope if the name is bound to a
 conditionally scoped variable; or just proceed normally if the named
 variable is in scope.
@@ -1174,7 +1149,7 @@ all pending-scope variables become conditionally scoped.
                v->scope = InScope;
                v->in_scope = c->in_scope;
                c->in_scope = v;
                v->scope = InScope;
                v->in_scope = c->in_scope;
                c->in_scope = v;
-               v->val = val_prepare(NULL);
+               v->val = NULL;
                return v;
        }
 
                return v;
        }
 
@@ -1226,7 +1201,7 @@ all pending-scope variables become conditionally scoped.
                                        else if (v->previous &&
                                                 v->previous->scope == PendingScope)
                                                v->scope = PendingScope;
                                        else if (v->previous &&
                                                 v->previous->scope == PendingScope)
                                                v->scope = PendingScope;
-                                       else if (v->val.type == Tlabel)
+                                       else if (v->type == Tlabel)
                                                v->scope = PendingScope;
                                        else if (v->name->var == v)
                                                v->scope = OutScope;
                                                v->scope = PendingScope;
                                        else if (v->name->var == v)
                                                v->scope = OutScope;
@@ -1243,14 +1218,14 @@ all pending-scope variables become conditionally scoped.
                                        for (v2 = v;
                                             v2 && v2->scope == PendingScope;
                                             v2 = v2->previous)
                                        for (v2 = v;
                                             v2 && v2->scope == PendingScope;
                                             v2 = v2->previous)
-                                               if (v2->val.type != Tlabel)
+                                               if (v2->type != Tlabel)
                                                        v2->scope = OutScope;
                                        break;
                                case OutScope: break;
                                }
                                break;
                        case CloseSequential:
                                                        v2->scope = OutScope;
                                        break;
                                case OutScope: break;
                                }
                                break;
                        case CloseSequential:
-                               if (v->val.type == Tlabel)
+                               if (v->type == Tlabel)
                                        v->scope = PendingScope;
                                switch (v->scope) {
                                case InScope:
                                        v->scope = PendingScope;
                                switch (v->scope) {
                                case InScope:
@@ -1265,7 +1240,7 @@ all pending-scope variables become conditionally scoped.
                                        for (v2 = v;
                                             v2 && v2->scope == PendingScope;
                                             v2 = v2->previous)
                                        for (v2 = v;
                                             v2 && v2->scope == PendingScope;
                                             v2 = v2->previous)
-                                               if (v2->val.type == Tlabel) {
+                                               if (v2->type == Tlabel) {
                                                        v2->scope = CondScope;
                                                        v2->min_depth = c->scope_depth;
                                                } else
                                                        v2->scope = CondScope;
                                                        v2->min_depth = c->scope_depth;
                                                } else
@@ -1287,13 +1262,14 @@ all pending-scope variables become conditionally scoped.
 
 Executables can be lots of different things.  In many cases an
 executable is just an operation combined with one or two other
 
 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.
+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) ({            \
 
 ###### macros
        #define cast(structname, pointer) ({            \
@@ -1351,11 +1327,10 @@ subclasses, and to access these we need to be able to `cast` the
                        fprintf(f, "??:??: ");  // NOTEST
        }
 
                        fprintf(f, "??:??: ");  // NOTEST
        }
 
-Each different type of `exec` node needs a number of functions
-defined, a bit like methods.  We must be able to 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.
+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
 
 
 #### Freeing
 
@@ -1433,8 +1408,8 @@ also want to know what sort of bracketing to use.
 
 #### Analysing
 
 
 #### Analysing
 
-As discussed, analysis involves propagating type requirements around
-the program and looking for errors.
+As discussed, analysis involves propagating type requirements around the
+program and looking for errors.
 
 So `propagate_types` is passed an expected type (being a `struct type`
 pointer together with some `val_rules` flags) that the `exec` is
 
 So `propagate_types` is passed an expected type (being a `struct type`
 pointer together with some `val_rules` flags) that the `exec` is
@@ -1498,46 +1473,53 @@ within the `exec` tree.  The exception to this is the whole `program`
 which needs to look at command line arguments.  The `program` will be
 interpreted separately.
 
 which needs to look at command line arguments.  The `program` will be
 interpreted separately.
 
-Each `exec` can return a value, which may be `Tnone` but must be
-non-NULL;  Some `exec`s will return the location of a value, which can
-be updates.  To support this, each exec case must store either a value
-in `val` or the pointer to a value in `lval`.  If `lval` is set, but a
-simple value is required, `inter_exec()` will dereference `lval` to
-get the value.
+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 {
 
 ###### core functions
 
        struct lrval {
-               struct value val, *lval;
+               struct type *type;
+               struct value rval, *lval;
        };
 
        static struct lrval _interp_exec(struct exec *e);
 
        };
 
        static struct lrval _interp_exec(struct exec *e);
 
-       static struct value interp_exec(struct exec *e)
+       static struct value interp_exec(struct exec *e, struct type **typeret)
        {
                struct lrval ret = _interp_exec(e);
 
        {
                struct lrval ret = _interp_exec(e);
 
+               if (!ret.type) abort();
+               if (typeret)
+                       *typeret = ret.type;
                if (ret.lval)
                if (ret.lval)
-                       return dup_value(*ret.lval);
-               else
-                       return ret.val;
+                       dup_value(ret.type, ret.lval, &ret.rval);
+               return ret.rval;
        }
 
        }
 
-       static struct value *linterp_exec(struct exec *e)
+       static struct value *linterp_exec(struct exec *e, struct type **typeret)
        {
                struct lrval ret = _interp_exec(e);
 
        {
                struct lrval ret = _interp_exec(e);
 
+               if (typeret)
+                       *typeret = ret.type;
                return ret.lval;
        }
 
        static struct lrval _interp_exec(struct exec *e)
        {
                struct lrval ret;
                return ret.lval;
        }
 
        static struct lrval _interp_exec(struct exec *e)
        {
                struct lrval ret;
-               struct value rv, *lrv = NULL;
-               rv.type = Tnone;
+               struct value rv = {}, *lrv = NULL;
+               struct type *rvtype;
+
+               rvtype = ret.type = Tnone;
                if (!e) {
                        ret.lval = lrv;
                if (!e) {
                        ret.lval = lrv;
-                       ret.val = rv;
+                       ret.rval = rv;
                        return ret;
                }
 
                        return ret;
                }
 
@@ -1546,17 +1528,20 @@ get the value.
                {
                        struct binode *b = cast(binode, e);
                        struct value left, right, *lleft;
                {
                        struct binode *b = cast(binode, e);
                        struct value left, right, *lleft;
-                       left.type = right.type = Tnone;
+                       struct type *ltype, *rtype;
+                       ltype = rtype = Tnone;
                        switch (b->op) {
                        ## interp binode cases
                        }
                        switch (b->op) {
                        ## interp binode cases
                        }
-                       free_value(left); free_value(right);
+                       free_value(ltype, &left);
+                       free_value(rtype, &right);
                        break;
                }
                ## interp exec cases
                }
                ret.lval = lrv;
                        break;
                }
                ## interp exec cases
                }
                ret.lval = lrv;
-               ret.val = rv;
+               ret.rval = rv;
+               ret.type = rvtype;
                return ret;
        }
 
                return ret;
        }
 
@@ -1568,19 +1553,23 @@ different phases of parse, analyse, print, interpret.
 
 Thus far we have arrays and structs.
 
 
 Thus far we have arrays and structs.
 
+Some complex types need do not exist in a name table, so they are kept
+on a linked list in the context (`anon_typelist`).  This allows them to
+be freed when parsing is complete.
+
 #### Arrays
 
 Arrays can be declared by giving a size and a type, as `[size]type' so
 `freq:[26]number` declares `freq` to be an array of 26 numbers.  The
 #### Arrays
 
 Arrays can be declared by giving a size and a type, as `[size]type' so
 `freq:[26]number` declares `freq` to be an array of 26 numbers.  The
-size can be an arbitrary expression which is evaluated when the name
-comes into scope.
+size can be either a literal number, or a named constant.  Some day an
+arbitrary expression will be supported.
 
 Arrays cannot be assigned.  When pointers are introduced we will also
 introduce array slices which can refer to part or all of an array -
 the assignment syntax will create a slice.  For now, an array can only
 ever be referenced by the name it is declared with.  It is likely that
 a "`copy`" primitive will eventually be define which can be used to
 
 Arrays cannot be assigned.  When pointers are introduced we will also
 introduce array slices which can refer to part or all of an array -
 the assignment syntax will create a slice.  For now, an array can only
 ever be referenced by the name it is declared with.  It is likely that
 a "`copy`" primitive will eventually be define which can be used to
-make a copy of an array with controllable depth.
+make a copy of an array with controllable recursive depth.
 
 ###### type union fields
 
 
 ###### type union fields
 
@@ -1591,50 +1580,43 @@ make a copy of an array with controllable depth.
        } array;
 
 ###### value union fields
        } array;
 
 ###### value union fields
-       struct {
-               struct value *elmnts;
-       } array;
+       void *array;
 
 ###### value functions
 
 
 ###### value functions
 
-       static struct value array_prepare(struct type *type)
+       static void array_init(struct type *type, struct value *val)
        {
        {
-               struct value ret;
-
-               ret.type = type;
-               ret.array.elmnts = NULL;
-               return ret;
-       }
-
-       static struct value array_init(struct type *type)
-       {
-               struct value ret;
                int i;
 
                int i;
 
-               ret.type = type;
                if (type->array.vsize) {
                        mpz_t q;
                        mpz_init(q);
                if (type->array.vsize) {
                        mpz_t q;
                        mpz_init(q);
-                       mpz_tdiv_q(q, mpq_numref(type->array.vsize->val.num),
-                                  mpq_denref(type->array.vsize->val.num));
+                       mpz_tdiv_q(q, mpq_numref(type->array.vsize->val->num),
+                                  mpq_denref(type->array.vsize->val->num));
                        type->array.size = mpz_get_si(q);
                        mpz_clear(q);
                }
                        type->array.size = mpz_get_si(q);
                        mpz_clear(q);
                }
-               ret.array.elmnts = calloc(type->array.size,
-                                         sizeof(ret.array.elmnts[0]));
-               for (i = 0; ret.array.elmnts && i < type->array.size; i++)
-                       ret.array.elmnts[i] = val_init(type->array.member);
-               return ret;
+               type->size = type->array.size * type->array.member->size;
+               type->align = type->array.member->align;
+
+               if (!val)
+                       return;         
+               for (i = 0; i < type->array.size; i++) {
+                       struct value *v;
+                       v = (void*)val->ptr + i * type->array.member->size;
+                       val_init(type->array.member, v);
+               }
        }
 
        }
 
-       static void array_free(struct value val)
+       static void array_free(struct type *type, struct value *val)
        {
                int i;
 
        {
                int i;
 
-               if (val.array.elmnts)
-                       for (i = 0; i < val.type->array.size; i++)
-                               free_value(val.array.elmnts[i]);
-               free(val.array.elmnts);
+               for (i = 0; i < type->array.size; i++) {
+                       struct value *v;
+                       v = (void*)val->ptr + i * type->array.member->size;
+                       free_value(type->array.member, v);
+               }
        }
 
        static int array_compat(struct type *require, struct type *have)
        }
 
        static int array_compat(struct type *require, struct type *have)
@@ -1662,7 +1644,6 @@ make a copy of an array with controllable depth.
        }
 
        static struct type array_prototype = {
        }
 
        static struct type array_prototype = {
-               .prepare = array_prepare,
                .init = array_init,
                .print_type = array_print_type,
                .compat = array_compat,
                .init = array_init,
                .print_type = array_print_type,
                .compat = array_compat,
@@ -1693,7 +1674,7 @@ make a copy of an array with controllable depth.
                                        &$2);
                        mpq_clear(num);
                }
                                        &$2);
                        mpq_clear(num);
                }
-               $0->next= c->anon_typelist;
+               $0->next = c->anon_typelist;
                c->anon_typelist = $0;
                }
        }$
                c->anon_typelist = $0;
                }
        }$
@@ -1711,7 +1692,7 @@ make a copy of an array with controllable depth.
                $0->array.member = $<4;
                $0->array.size = 0;
                $0->array.vsize = v;
                $0->array.member = $<4;
                $0->array.size = 0;
                $0->array.vsize = v;
-               $0->next= c->anon_typelist;
+               $0->next = c->anon_typelist;
                c->anon_typelist = $0;
        } }$
 
                c->anon_typelist = $0;
        } }$
 
@@ -1773,17 +1754,19 @@ make a copy of an array with controllable depth.
                mpz_t q;
                long i;
 
                mpz_t q;
                long i;
 
-               lleft = linterp_exec(b->left);
-               right = interp_exec(b->right);
+               lleft = linterp_exec(b->left, &ltype);
+               right = interp_exec(b->right, &rtype);
                mpz_init(q);
                mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
                i = mpz_get_si(q);
                mpz_clear(q);
 
                mpz_init(q);
                mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
                i = mpz_get_si(q);
                mpz_clear(q);
 
-               if (i >= 0 && i < lleft->type->array.size)
-                       lrv = &lleft->array.elmnts[i];
+               rvtype = ltype->array.member;
+               if (i >= 0 && i < ltype->array.size)
+                       lrv = (void*)lleft + i * rvtype->size;
                else
                else
-                       rv = val_init(lleft->type->array.member);
+                       val_init(ltype->array.member, &rv);
+               ltype = NULL;
                break;
        }
 
                break;
        }
 
@@ -1806,7 +1789,7 @@ is not prefixed by the word `struct` as it would be in C.
 
 Structs are only treated as the same if they have the same name.
 Simply having the same fields in the same order is not enough.  This
 
 Structs are only treated as the same if they have the same name.
 Simply having the same fields in the same order is not enough.  This
-might change once we can create structure initializes from a list of
+might change once we can create structure initializers from a list of
 values.
 
 Each component datum is identified much like a variable is declared,
 values.
 
 Each component datum is identified much like a variable is declared,
@@ -1835,62 +1818,51 @@ function will be needed.
                struct field {
                        struct text name;
                        struct type *type;
                struct field {
                        struct text name;
                        struct type *type;
-                       struct value init;
+                       struct value *init;
+                       int offset;
                } *fields;
        } structure;
 
                } *fields;
        } structure;
 
-###### value union fields
-       struct {
-               struct value *fields;
-       } structure;
-
 ###### type functions
        void (*print_type_decl)(struct type *type, FILE *f);
 
 ###### value functions
 
 ###### type functions
        void (*print_type_decl)(struct type *type, FILE *f);
 
 ###### value functions
 
-       static struct value structure_prepare(struct type *type)
-       {
-               struct value ret;
-
-               ret.type = type;
-               ret.structure.fields = NULL;
-               return ret;
-       }
-
-       static struct value structure_init(struct type *type)
+       static void structure_init(struct type *type, struct value *val)
        {
        {
-               struct value ret;
                int i;
 
                int i;
 
-               ret.type = type;
-               ret.structure.fields = calloc(type->structure.nfields,
-                                             sizeof(ret.structure.fields[0]));
-               for (i = 0; ret.structure.fields && i < type->structure.nfields; i++)
-                       ret.structure.fields[i] = val_init(type->structure.fields[i].type);
-               return ret;
+               for (i = 0; i < type->structure.nfields; i++) {
+                       struct value *v;
+                       v = (void*) val->ptr + type->structure.fields[i].offset;
+                       val_init(type->structure.fields[i].type, v);
+               }
        }
 
        }
 
-       static void structure_free(struct value val)
+       static void structure_free(struct type *type, struct value *val)
        {
                int i;
 
        {
                int i;
 
-               if (val.structure.fields)
-                       for (i = 0; i < val.type->structure.nfields; i++)
-                               free_value(val.structure.fields[i]);
-               free(val.structure.fields);
+               for (i = 0; i < type->structure.nfields; i++) {
+                       struct value *v;
+                       v = (void*)val->ptr + type->structure.fields[i].offset;
+                       free_value(type->structure.fields[i].type, v);
+               }
        }
 
        static void structure_free_type(struct type *t)
        {
                int i;
                for (i = 0; i < t->structure.nfields; i++)
        }
 
        static void structure_free_type(struct type *t)
        {
                int i;
                for (i = 0; i < t->structure.nfields; i++)
-                       free_value(t->structure.fields[i].init);
+                       if (t->structure.fields[i].init) {
+                               free_value(t->structure.fields[i].type,
+                                          t->structure.fields[i].init);
+                               free(t->structure.fields[i].init);
+                       }
                free(t->structure.fields);
        }
 
        static struct type structure_prototype = {
                free(t->structure.fields);
        }
 
        static struct type structure_prototype = {
-               .prepare = structure_prepare,
                .init = structure_init,
                .free = structure_free,
                .free_type = structure_free_type,
                .init = structure_init,
                .free = structure_free,
                .free_type = structure_free_type,
@@ -1954,7 +1926,7 @@ function will be needed.
                if (!st)
                        type_err(c, "error: unknown type for field access", f->left,
                                 NULL, 0, NULL);
                if (!st)
                        type_err(c, "error: unknown type for field access", f->left,
                                 NULL, 0, NULL);
-               else if (st->prepare != structure_prepare)
+               else if (st->init != structure_init)
                        type_err(c, "error: field reference attempted on %1, not a struct",
                                 f->left, st, 0, NULL);
                else if (f->index == -2) {
                        type_err(c, "error: field reference attempted on %1, not a struct",
                                 f->left, st, 0, NULL);
                else if (f->index == -2) {
@@ -1977,8 +1949,10 @@ function will be needed.
        case Xfieldref:
        {
                struct fieldref *f = cast(fieldref, e);
        case Xfieldref:
        {
                struct fieldref *f = cast(fieldref, e);
-               struct value *lleft = linterp_exec(f->left);
-               lrv = &lleft->structure.fields[f->index];
+               struct type *ltype;
+               struct value *lleft = linterp_exec(f->left, &ltype);
+               lrv = (void*)lleft->ptr + ltype->structure.fields[f->index].offset;
+               rvtype = ltype->structure.fields[f->index].type;
                break;
        }
 
                break;
        }
 
@@ -1994,7 +1968,10 @@ function will be needed.
                if (!f)
                        return;
                free_fieldlist(f->prev);
                if (!f)
                        return;
                free_fieldlist(f->prev);
-               free_value(f->f.init);
+               if (f->f.init) {
+                       free_value(f->f.type, f->f.init);
+                       free(f->f.init);
+               }
                free(f);
        }
 
                free(f);
        }
 
@@ -2012,9 +1989,16 @@ function will be needed.
                        t->structure.fields = calloc(cnt, sizeof(struct field));
                        f = $3;
                        while (cnt > 0) {
                        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;
                                cnt -= 1;
                                t->structure.fields[cnt] = f->f;
-                               f->f.init = val_prepare(Tnone);
+                               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;
                        }
                } }$
                                f = f->prev;
                        }
                } }$
@@ -2047,21 +2031,23 @@ function will be needed.
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
                        $0->f.type = $<3;
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
                        $0->f.type = $<3;
-                       $0->f.init = val_prepare($0->f.type);
+                       $0->f.init = NULL;
                        do {
                                ok = 1;
                                propagate_types($<5, c, &ok, $3, 0);
                        } while (ok == 2);
                        if (!ok)
                                c->parse_error = 1;
                        do {
                                ok = 1;
                                propagate_types($<5, c, &ok, $3, 0);
                        } while (ok == 2);
                        if (!ok)
                                c->parse_error = 1;
-                       else
-                               $0->f.init = interp_exec($5);
+                       else {
+                               struct value vl = interp_exec($5, NULL);
+                               $0->f.init = val_alloc($0->f.type, &vl);
+                       }
                } }$
                | IDENTIFIER : Type ${
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
                        $0->f.type = $<3;
                } }$
                | IDENTIFIER : Type ${
                        $0 = calloc(1, sizeof(struct fieldlist));
                        $0->f.name = $1.txt;
                        $0->f.type = $<3;
-                       $0->f.init = val_init($3);
+                       $0->f.init = val_alloc($0->f.type, NULL);
                }$
 
 ###### forward decls
                }$
 
 ###### forward decls
@@ -2078,12 +2064,12 @@ function will be needed.
                        struct field *fl = t->structure.fields + i;
                        fprintf(f, "    %.*s : ", fl->name.len, fl->name.txt);
                        type_print(fl->type, f);
                        struct field *fl = t->structure.fields + i;
                        fprintf(f, "    %.*s : ", fl->name.len, fl->name.txt);
                        type_print(fl->type, f);
-                       if (fl->init.type->print) {
+                       if (fl->type->print && fl->init) {
                                fprintf(f, " = ");
                                fprintf(f, " = ");
-                               if (fl->init.type == Tstr)
+                               if (fl->type == Tstr)
                                        fprintf(f, "\"");
                                        fprintf(f, "\"");
-                               print_value(fl->init);
-                               if (fl->init.type == Tstr)
+                               print_value(fl->type, fl->init);
+                               if (fl->type == Tstr)
                                        fprintf(f, "\"");
                        }
                        printf("\n");
                                        fprintf(f, "\"");
                        }
                        printf("\n");
@@ -2132,25 +2118,31 @@ an executable.
 ###### ast
        struct val {
                struct exec;
 ###### ast
        struct val {
                struct exec;
+               struct type *vtype;
                struct value val;
        };
 
                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
 
        $*val
        Value ->  True ${
 ###### Grammar
 
        $*val
        Value ->  True ${
-                       $0 = new_pos(val, $1);
-                       $0->val.type = Tbool;
+                       $0 = new_val(Tbool, $1);
                        $0->val.bool = 1;
                        }$
                | False ${
                        $0->val.bool = 1;
                        }$
                | False ${
-                       $0 = new_pos(val, $1);
-                       $0->val.type = Tbool;
+                       $0 = new_val(Tbool, $1);
                        $0->val.bool = 0;
                        }$
                | NUMBER ${
                        $0->val.bool = 0;
                        }$
                | NUMBER ${
-                       $0 = new_pos(val, $1);
-                       $0->val.type = Tnum;
+                       $0 = new_val(Tnum, $1);
                        {
                        char tail[3];
                        if (number_parse($0->val.num, tail, $1.txt) == 0)
                        {
                        char tail[3];
                        if (number_parse($0->val.num, tail, $1.txt) == 0)
@@ -2161,8 +2153,7 @@ an executable.
                        }
                        }$
                | STRING ${
                        }
                        }$
                | STRING ${
-                       $0 = new_pos(val, $1);
-                       $0->val.type = Tstr;
+                       $0 = new_val(Tstr, $1);
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
@@ -2172,8 +2163,7 @@ an executable.
                        }
                        }$
                | MULTI_STRING ${
                        }
                        }$
                | MULTI_STRING ${
-                       $0 = new_pos(val, $1);
-                       $0->val.type = Tstr;
+                       $0 = new_val(Tstr, $1);
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
@@ -2187,10 +2177,10 @@ an executable.
        case Xval:
        {
                struct val *v = cast(val, e);
        case Xval:
        {
                struct val *v = cast(val, e);
-               if (v->val.type == Tstr)
+               if (v->vtype == Tstr)
                        printf("\"");
                        printf("\"");
-               print_value(v->val);
-               if (v->val.type == Tstr)
+               print_value(v->vtype, &v->val);
+               if (v->vtype == Tstr)
                        printf("\"");
                break;
        }
                        printf("\"");
                break;
        }
@@ -2199,23 +2189,23 @@ an executable.
        case Xval:
        {
                struct val *val = cast(val, prog);
        case Xval:
        {
                struct val *val = cast(val, prog);
-               if (!type_compat(type, val->val.type, rules))
+               if (!type_compat(type, val->vtype, rules))
                        type_err(c, "error: expected %1%r found %2",
                        type_err(c, "error: expected %1%r found %2",
-                                  prog, type, rules, val->val.type);
-               return val->val.type;
+                                  prog, type, rules, val->vtype);
+               return val->vtype;
        }
 
 ###### interp exec cases
        case Xval:
        }
 
 ###### interp exec cases
        case Xval:
-               rv = dup_value(cast(val, e)->val);
+               rvtype = cast(val, e)->vtype;
+               dup_value(rvtype, &cast(val, e)->val, &rv);
                break;
 
 ###### ast functions
        static void free_val(struct val *v)
        {
                break;
 
 ###### ast functions
        static void free_val(struct val *v)
        {
-               if (!v)
-                       return;
-               free_value(v->val);
+               if (v)
+                       free_value(v->vtype, &v->val);
                free(v);
        }
 
                free(v);
        }
 
@@ -2223,7 +2213,7 @@ an executable.
        case Xval: free_val(cast(val, e)); break;
 
 ###### ast functions
        case Xval: free_val(cast(val, e)); break;
 
 ###### ast functions
-       // Move all nodes from 'b' to 'rv', reversing the order.
+       // 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)
        // 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)
@@ -2247,7 +2237,7 @@ an executable.
 
 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`
 
 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` hold a link to the variable
+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.
 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.
@@ -2302,7 +2292,8 @@ link to find the primary instance.
                if (v) {
                        v->where_decl = $0;
                        v->where_set = $0;
                if (v) {
                        v->where_decl = $0;
                        v->where_set = $0;
-                       v->val = val_prepare($<3);
+                       v->type = $<Type;
+                       v->val = NULL;
                } else {
                        v = var_ref(c, $1.txt);
                        $0->var = v;
                } else {
                        v = var_ref(c, $1.txt);
                        $0->var = v;
@@ -2319,7 +2310,8 @@ link to find the primary instance.
                if (v) {
                        v->where_decl = $0;
                        v->where_set = $0;
                if (v) {
                        v->where_decl = $0;
                        v->where_set = $0;
-                       v->val = val_prepare($<3);
+                       v->type = $<Type;
+                       v->val = NULL;
                        v->constant = 1;
                } else {
                        v = var_ref(c, $1.txt);
                        v->constant = 1;
                } else {
                        v = var_ref(c, $1.txt);
@@ -2339,7 +2331,8 @@ link to find the primary instance.
                        /* This might be a label - allocate a var just in case */
                        v = var_decl(c, $1.txt);
                        if (v) {
                        /* This might be a label - allocate a var just in case */
                        v = var_decl(c, $1.txt);
                        if (v) {
-                               v->val = val_prepare(Tnone);
+                               v->val = NULL;
+                               v->type = Tnone;
                                v->where_decl = $0;
                                v->where_set = $0;
                        }
                                v->where_decl = $0;
                                v->where_set = $0;
                        }
@@ -2401,27 +2394,28 @@ link to find the primary instance.
                                 prog, NULL, 0, NULL);
                        type_err(c, "info: name was defined as a constant here",
                                 v->where_decl, NULL, 0, NULL);
                                 prog, NULL, 0, NULL);
                        type_err(c, "info: name was defined as a constant here",
                                 v->where_decl, NULL, 0, NULL);
-                       return v->val.type;
+                       return v->type;
                }
                }
-               if (v->val.type == Tnone && v->where_decl == prog)
+               if (v->type == Tnone && v->where_decl == prog)
                        type_err(c, "error: variable used but not declared: %v",
                                 prog, NULL, 0, NULL);
                        type_err(c, "error: variable used but not declared: %v",
                                 prog, NULL, 0, NULL);
-               if (v->val.type == NULL) {
+               if (v->type == NULL) {
                        if (type && *ok != 0) {
                        if (type && *ok != 0) {
-                               v->val = val_prepare(type);
+                               v->type = type;
+                               v->val = NULL;
                                v->where_set = prog;
                                *ok = 2;
                        }
                        return type;
                }
                                v->where_set = prog;
                                *ok = 2;
                        }
                        return type;
                }
-               if (!type_compat(type, v->val.type, rules)) {
+               if (!type_compat(type, v->type, rules)) {
                        type_err(c, "error: expected %1%r but variable '%v' is %2", prog,
                        type_err(c, "error: expected %1%r but variable '%v' is %2", prog,
-                                type, rules, v->val.type);
+                                type, rules, v->type);
                        type_err(c, "info: this is where '%v' was set to %1", v->where_set,
                        type_err(c, "info: this is where '%v' was set to %1", v->where_set,
-                                v->val.type, rules, NULL);
+                                v->type, rules, NULL);
                }
                if (!type)
                }
                if (!type)
-                       return v->val.type;
+                       return v->type;
                return type;
        }
 
                return type;
        }
 
@@ -2433,7 +2427,8 @@ link to find the primary instance.
 
                if (v->merged)
                        v = v->merged;
 
                if (v->merged)
                        v = v->merged;
-               lrv = &v->val;
+               lrv = v->val;
+               rvtype = v->type;
                break;
        }
 
                break;
        }
 
@@ -2452,15 +2447,15 @@ link to find the primary instance.
 Our first user of the `binode` will be conditional expressions, which
 is a bit odd as they actually have three components.  That will be
 handled by having 2 binodes for each expression.  The conditional
 Our first user of the `binode` will be conditional expressions, which
 is a bit odd as they actually have three components.  That will be
 handled by having 2 binodes for each expression.  The conditional
-expression is the lowest precedence operatior, so it gets to define
-what an "Expression" is.  The next level up is "BoolExpr", which
-comes next.
+expression is the lowest precedence operator which is why we define it
+first - to start the precedence list.
 
 Conditional expressions are of the form "value `if` condition `else`
 other_value".  They associate to the right, so everything to the right
 
 Conditional expressions are of the form "value `if` condition `else`
 other_value".  They associate to the right, so everything to the right
-of `else` is part of an else value, while only the BoolExpr to the
-left of `if` is the if values.  Between `if` and `else` there is no
-room for ambiguity, so a full conditional expression is allowed in there.
+of `else` is part of an else value, while only a higher-precedence to
+the left of `if` is the if values.  Between `if` and `else` there is no
+room for ambiguity, so a full conditional expression is allowed in
+there.
 
 ###### Binode types
        CondExpr,
 
 ###### Binode types
        CondExpr,
@@ -2514,24 +2509,20 @@ room for ambiguity, so a full conditional expression is allowed in there.
 
        case CondExpr: {
                struct binode *b2 = cast(binode, b->right);
 
        case CondExpr: {
                struct binode *b2 = cast(binode, b->right);
-               left = interp_exec(b->left);
+               left = interp_exec(b->left, &ltype);
                if (left.bool)
                if (left.bool)
-                       rv = interp_exec(b2->left);
+                       rv = interp_exec(b2->left, &rvtype);
                else
                else
-                       rv = interp_exec(b2->right);
+                       rv = interp_exec(b2->right, &rvtype);
                }
                break;
 
 ### Expressions: Boolean
 
 The next class of expressions to use the `binode` will be Boolean
                }
                break;
 
 ### Expressions: Boolean
 
 The next class of expressions to use the `binode` will be Boolean
-expressions.  As I haven't implemented precedence in the parser
-generator yet, we need different names for each precedence level used
-by expressions.  The outer most or lowest level precedence after
-conditional expressions are Boolean operators which form an `BoolExpr`
-out of `BTerm`s and `BFact`s.  As well as `or` `and`, and `not` we
-have `and then` and `or else` which only evaluate the second operand
-if the result would make a difference.
+expressions.  "`and then`" and "`or else`" are similar to `and` and `or`
+have same corresponding precendence.  The difference is that they don't
+evaluate the second expression if not necessary.
 
 ###### Binode types
        And,
 
 ###### Binode types
        And,
@@ -2635,39 +2626,38 @@ if the result would make a difference.
 
 ###### interp binode cases
        case And:
 
 ###### interp binode cases
        case And:
-               rv = interp_exec(b->left);
-               right = interp_exec(b->right);
+               rv = interp_exec(b->left, &rvtype);
+               right = interp_exec(b->right, &rtype);
                rv.bool = rv.bool && right.bool;
                break;
        case AndThen:
                rv.bool = rv.bool && right.bool;
                break;
        case AndThen:
-               rv = interp_exec(b->left);
+               rv = interp_exec(b->left, &rvtype);
                if (rv.bool)
                if (rv.bool)
-                       rv = interp_exec(b->right);
+                       rv = interp_exec(b->right, NULL);
                break;
        case Or:
                break;
        case Or:
-               rv = interp_exec(b->left);
-               right = interp_exec(b->right);
+               rv = interp_exec(b->left, &rvtype);
+               right = interp_exec(b->right, &rtype);
                rv.bool = rv.bool || right.bool;
                break;
        case OrElse:
                rv.bool = rv.bool || right.bool;
                break;
        case OrElse:
-               rv = interp_exec(b->left);
+               rv = interp_exec(b->left, &rvtype);
                if (!rv.bool)
                if (!rv.bool)
-                       rv = interp_exec(b->right);
+                       rv = interp_exec(b->right, NULL);
                break;
        case Not:
                break;
        case Not:
-               rv = interp_exec(b->right);
+               rv = interp_exec(b->right, &rvtype);
                rv.bool = !rv.bool;
                break;
 
 ### Expressions: Comparison
 
                rv.bool = !rv.bool;
                break;
 
 ### Expressions: Comparison
 
-Of slightly higher precedence that Boolean expressions are
-Comparisons.
-A comparison takes arguments of any comparable type, but the two types must be
-the same.
+Of slightly higher precedence that Boolean expressions are Comparisons.
+A comparison takes arguments of any comparable type, but the two types
+must be the same.
 
 To simplify the parsing we introduce an `eop` which can record an
 
 To simplify the parsing we introduce an `eop` which can record an
-expression operator.
+expression operator, and the `CMPop` non-terminal will match one of them.
 
 ###### ast
        struct eop {
 
 ###### ast
        struct eop {
@@ -2764,10 +2754,10 @@ expression operator.
        case NEql:
        {
                int cmp;
        case NEql:
        {
                int cmp;
-               left = interp_exec(b->left);
-               right = interp_exec(b->right);
-               cmp = value_cmp(left, right);
-               rv.type = Tbool;
+               left = interp_exec(b->left, &ltype);
+               right = interp_exec(b->right, &rtype);
+               cmp = value_cmp(ltype, rtype, &left, &right);
+               rvtype = Tbool;
                switch (b->op) {
                case Less:      rv.bool = cmp <  0; break;
                case LessEq:    rv.bool = cmp <= 0; break;
                switch (b->op) {
                case Less:      rv.bool = cmp <  0; break;
                case LessEq:    rv.bool = cmp <= 0; break;
@@ -2775,24 +2765,23 @@ expression operator.
                case GtrEq:     rv.bool = cmp >= 0; break;
                case Eql:       rv.bool = cmp == 0; break;
                case NEql:      rv.bool = cmp != 0; break;
                case GtrEq:     rv.bool = cmp >= 0; break;
                case Eql:       rv.bool = cmp == 0; break;
                case NEql:      rv.bool = cmp != 0; break;
-               default: rv.bool = 0; break;    // NOTEST
+               default:        rv.bool = 0; break;     // NOTEST
                }
                break;
        }
 
 ### Expressions: The rest
 
                }
                break;
        }
 
 ### Expressions: The rest
 
-The remaining expressions with the highest precedence are arithmetic
-and string concatenation.  They are `Expr`, `Term`, and `Factor`.
-The `Factor` is where the `Value` and `Variable` that we already have
-are included.
+The remaining expressions with the highest precedence are arithmetic and
+string concatenation.  String concatenation (`++`) has the same
+precedence as multiplication and division, but lower than the uniary.
 
 `+` and `-` are both infix and prefix operations (where they are
 absolute value and negation).  These have different operator names.
 
 We also have a 'Bracket' operator which records where parentheses were
 
 `+` and `-` are both infix and prefix operations (where they are
 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.  Once
-precedence is handled better I might be able to discard this.
+found.  This makes it easy to reproduce these when printing.  Possibly I
+should only insert brackets were needed for precedence.
 
 ###### Binode types
        Plus, Minus,
 
 ###### Binode types
        Plus, Minus,
@@ -2923,54 +2912,55 @@ precedence is handled better I might be able to discard this.
 ###### interp binode cases
 
        case Plus:
 ###### interp binode cases
 
        case Plus:
-               rv = interp_exec(b->left);
-               right = interp_exec(b->right);
+               rv = interp_exec(b->left, &rvtype);
+               right = interp_exec(b->right, &rtype);
                mpq_add(rv.num, rv.num, right.num);
                break;
        case Minus:
                mpq_add(rv.num, rv.num, right.num);
                break;
        case Minus:
-               rv = interp_exec(b->left);
-               right = interp_exec(b->right);
+               rv = interp_exec(b->left, &rvtype);
+               right = interp_exec(b->right, &rtype);
                mpq_sub(rv.num, rv.num, right.num);
                break;
        case Times:
                mpq_sub(rv.num, rv.num, right.num);
                break;
        case Times:
-               rv = interp_exec(b->left);
-               right = interp_exec(b->right);
+               rv = interp_exec(b->left, &rvtype);
+               right = interp_exec(b->right, &rtype);
                mpq_mul(rv.num, rv.num, right.num);
                break;
        case Divide:
                mpq_mul(rv.num, rv.num, right.num);
                break;
        case Divide:
-               rv = interp_exec(b->left);
-               right = interp_exec(b->right);
+               rv = interp_exec(b->left, &rvtype);
+               right = interp_exec(b->right, &rtype);
                mpq_div(rv.num, rv.num, right.num);
                break;
        case Rem: {
                mpz_t l, r, rem;
 
                mpq_div(rv.num, rv.num, right.num);
                break;
        case Rem: {
                mpz_t l, r, rem;
 
-               left = interp_exec(b->left);
-               right = interp_exec(b->right);
+               left = interp_exec(b->left, &ltype);
+               right = interp_exec(b->right, &rtype);
                mpz_init(l); mpz_init(r); mpz_init(rem);
                mpz_tdiv_q(l, mpq_numref(left.num), mpq_denref(left.num));
                mpz_tdiv_q(r, mpq_numref(right.num), mpq_denref(right.num));
                mpz_tdiv_r(rem, l, r);
                mpz_init(l); mpz_init(r); mpz_init(rem);
                mpz_tdiv_q(l, mpq_numref(left.num), mpq_denref(left.num));
                mpz_tdiv_q(r, mpq_numref(right.num), mpq_denref(right.num));
                mpz_tdiv_r(rem, l, r);
-               rv = val_init(Tnum);
+               val_init(Tnum, &rv);
                mpq_set_z(rv.num, rem);
                mpz_clear(r); mpz_clear(l); mpz_clear(rem);
                mpq_set_z(rv.num, rem);
                mpz_clear(r); mpz_clear(l); mpz_clear(rem);
+               rvtype = ltype;
                break;
        }
        case Negate:
                break;
        }
        case Negate:
-               rv = interp_exec(b->right);
+               rv = interp_exec(b->right, &rvtype);
                mpq_neg(rv.num, rv.num);
                break;
        case Absolute:
                mpq_neg(rv.num, rv.num);
                break;
        case Absolute:
-               rv = interp_exec(b->right);
+               rv = interp_exec(b->right, &rvtype);
                mpq_abs(rv.num, rv.num);
                break;
        case Bracket:
                mpq_abs(rv.num, rv.num);
                break;
        case Bracket:
-               rv = interp_exec(b->right);
+               rv = interp_exec(b->right, &rvtype);
                break;
        case Concat:
                break;
        case Concat:
-               left = interp_exec(b->left);
-               right = interp_exec(b->right);
-               rv.type = Tstr;
+               left = interp_exec(b->left, &ltype);
+               right = interp_exec(b->right, &rtype);
+               rvtype = Tstr;
                rv.str = text_join(left.str, right.str);
                break;
 
                rv.str = text_join(left.str, right.str);
                break;
 
@@ -3014,7 +3004,7 @@ confusion, so I'm not set on it yet.
 
 A simple statement list needs no extra syntax.  A complex statement
 list has two syntactic forms.  It can be enclosed in braces (much like
 
 A simple statement list needs no extra syntax.  A complex statement
 list has two syntactic forms.  It can be enclosed in braces (much like
-C blocks), or it can be introduced by a colon and continue until an
+C blocks), or it can be introduced by an indent and continue until an
 unindented newline (much like Python blocks).  With this extra syntax
 it is referred to as a block.
 
 unindented newline (much like Python blocks).  With this extra syntax
 it is referred to as a block.
 
@@ -3172,10 +3162,10 @@ is in-place.
 
 ###### interp binode cases
        case Block:
 
 ###### interp binode cases
        case Block:
-               while (rv.type == Tnone &&
+               while (rvtype == Tnone &&
                       b) {
                        if (b->left)
                       b) {
                        if (b->left)
-                               rv = interp_exec(b->left);
+                               rv = interp_exec(b->left, &rvtype);
                        b = cast(binode, b->right);
                }
                break;
                        b = cast(binode, b->right);
                }
                break;
@@ -3264,14 +3254,14 @@ same solution.
                        if (b->left) {
                                if (sep)
                                        putchar(sep);
                        if (b->left) {
                                if (sep)
                                        putchar(sep);
-                               left = interp_exec(b->left);
-                               print_value(left);
-                               free_value(left);
+                               left = interp_exec(b->left, &ltype);
+                               print_value(ltype, &left);
+                               free_value(ltype, &left);
                                if (b->right)
                                        sep = ' ';
                        } else if (sep)
                                eol = 0;
                                if (b->right)
                                        sep = ' ';
                        } else if (sep)
                                eol = 0;
-               left.type = Tnone;
+               ltype = Tnone;
                if (eol)
                        printf("\n");
                break;
                if (eol)
                        printf("\n");
                break;
@@ -3280,7 +3270,7 @@ same solution.
 ###### Assignment statement
 
 An assignment will assign a value to a variable, providing it hasn't
 ###### Assignment statement
 
 An assignment will assign a value to a variable, providing it hasn't
-be declared as a constant.  The analysis phase ensures that the type
+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
 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
@@ -3337,14 +3327,14 @@ it is declared, and error will be raised as the name is created as
                if (cast(var, b->left)->var->constant) {
                        if (v->where_decl == v->where_set) {
                                printf("::");
                if (cast(var, b->left)->var->constant) {
                        if (v->where_decl == v->where_set) {
                                printf("::");
-                               type_print(v->val.type, stdout);
+                               type_print(v->type, stdout);
                                printf(" ");
                        } else
                                printf(" ::");
                } else {
                        if (v->where_decl == v->where_set) {
                                printf(":");
                                printf(" ");
                        } else
                                printf(" ::");
                } else {
                        if (v->where_decl == v->where_set) {
                                printf(":");
-                               type_print(v->val.type, stdout);
+                               type_print(v->type, stdout);
                                printf(" ");
                        } else
                                printf(" :");
                                printf(" ");
                        } else
                                printf(" :");
@@ -3392,14 +3382,13 @@ it is declared, and error will be raised as the name is created as
 ###### interp binode cases
 
        case Assign:
 ###### interp binode cases
 
        case Assign:
-               lleft = linterp_exec(b->left);
-               right = interp_exec(b->right);
+               lleft = linterp_exec(b->left, &ltype);
+               right = interp_exec(b->right, &rtype);
                if (lleft) {
                if (lleft) {
-                       free_value(*lleft);
-                       *lleft = right;
-               } else
-                       free_value(right);      // NOTEST
-               right.type = NULL;
+                       free_value(ltype, lleft);
+                       dup_value(ltype, &right, lleft);
+                       ltype = NULL;
+               }
                break;
 
        case Declare:
                break;
 
        case Declare:
@@ -3407,13 +3396,16 @@ it is declared, and error will be raised as the name is created as
                struct variable *v = cast(var, b->left)->var;
                if (v->merged)
                        v = v->merged;
                struct variable *v = cast(var, b->left)->var;
                if (v->merged)
                        v = v->merged;
-               if (b->right)
-                       right = interp_exec(b->right);
-               else
-                       right = val_init(v->val.type);
-               free_value(v->val);
-               v->val = right;
-               right.type = NULL;
+               if (b->right) {
+                       right = interp_exec(b->right, &rtype);
+                       free_value(v->type, v->val);
+                       free(v->val);
+                       v->val = val_alloc(v->type, &right);
+                       rtype = Tnone;
+               } else {
+                       free_value(v->type, v->val);
+                       v->val = val_alloc(v->type, NULL);
+               }
                break;
        }
 
                break;
        }
 
@@ -3437,10 +3429,11 @@ function.
                $0->right = $<2;
                if ($0->right->type == Xvar) {
                        struct var *v = cast(var, $0->right);
                $0->right = $<2;
                if ($0->right->type == Xvar) {
                        struct var *v = cast(var, $0->right);
-                       if (v->var->val.type == Tnone) {
+                       if (v->var->type == Tnone) {
                                /* Convert this to a label */
                                /* Convert this to a label */
-                               v->var->val = val_prepare(Tlabel);
-                               v->var->val.label = &v->var->val;
+                               v->var->type = Tlabel;
+                               v->var->val = val_alloc(Tlabel, NULL);
+                               v->var->val->label = v->var->val;
                        }
                }
        }$
                        }
                }
        }$
@@ -3463,7 +3456,7 @@ function.
 ###### interp binode cases
 
        case Use:
 ###### interp binode cases
 
        case Use:
-               rv = interp_exec(b->right);
+               rv = interp_exec(b->right, &rvtype);
                break;
 
 ### The Conditional Statement
                break;
 
 ### The Conditional Statement
@@ -3891,46 +3884,48 @@ defined.
        case Xcond_statement:
        {
                struct value v, cnd;
        case Xcond_statement:
        {
                struct value v, cnd;
+               struct type *vtype, *cndtype;
                struct casepart *cp;
                struct cond_statement *c = cast(cond_statement, e);
 
                if (c->forpart)
                struct casepart *cp;
                struct cond_statement *c = cast(cond_statement, e);
 
                if (c->forpart)
-                       interp_exec(c->forpart);
+                       interp_exec(c->forpart, NULL);
                do {
                        if (c->condpart)
                do {
                        if (c->condpart)
-                               cnd = interp_exec(c->condpart);
+                               cnd = interp_exec(c->condpart, &cndtype);
                        else
                        else
-                               cnd.type = Tnone;
-                       if (!(cnd.type == Tnone ||
-                             (cnd.type == Tbool && cnd.bool != 0)))
+                               cndtype = Tnone;
+                       if (!(cndtype == Tnone ||
+                             (cndtype == Tbool && cnd.bool != 0)))
                                break;
                        // cnd is Tnone or Tbool, doesn't need to be freed
                        if (c->dopart)
                                break;
                        // cnd is Tnone or Tbool, doesn't need to be freed
                        if (c->dopart)
-                               interp_exec(c->dopart);
+                               interp_exec(c->dopart, NULL);
 
                        if (c->thenpart) {
 
                        if (c->thenpart) {
-                               rv = interp_exec(c->thenpart);
-                               if (rv.type != Tnone || !c->dopart)
+                               rv = interp_exec(c->thenpart, &rvtype);
+                               if (rvtype != Tnone || !c->dopart)
                                        goto Xcond_done;
                                        goto Xcond_done;
-                               free_value(rv);
+                               free_value(rvtype, &rv);
+                               rvtype = Tnone;
                        }
                } while (c->dopart);
 
                for (cp = c->casepart; cp; cp = cp->next) {
                        }
                } while (c->dopart);
 
                for (cp = c->casepart; cp; cp = cp->next) {
-                       v = interp_exec(cp->value);
-                       if (value_cmp(v, cnd) == 0) {
-                               free_value(v);
-                               free_value(cnd);
-                               rv = interp_exec(cp->action);
+                       v = interp_exec(cp->value, &vtype);
+                       if (value_cmp(cndtype, vtype, &v, &cnd) == 0) {
+                               free_value(vtype, &v);
+                               free_value(cndtype, &cnd);
+                               rv = interp_exec(cp->action, &rvtype);
                                goto Xcond_done;
                        }
                                goto Xcond_done;
                        }
-                       free_value(v);
+                       free_value(vtype, &v);
                }
                }
-               free_value(cnd);
+               free_value(cndtype, &cnd);
                if (c->elsepart)
                if (c->elsepart)
-                       rv = interp_exec(c->elsepart);
+                       rv = interp_exec(c->elsepart, &rvtype);
                else
                else
-                       rv.type = Tnone;
+                       rvtype = Tnone;
        Xcond_done:
                break;
        }
        Xcond_done:
                break;
        }
@@ -4040,7 +4035,8 @@ searching through for the Nth constant for decreasing N.
                if (!ok)
                        c->parse_error = 1;
                else if (v) {
                if (!ok)
                        c->parse_error = 1;
                else if (v) {
-                       v->val = interp_exec($5);
+                       struct value res = interp_exec($5, &v->type);
+                       v->val = val_alloc(v->type, &res);
                }
        } }$
 
                }
        } }$
 
@@ -4064,12 +4060,12 @@ searching through for the Nth constant for decreasing N.
                                target = i;
                        } else {
                                printf("    %.*s :: ", v->name->name.len, v->name->name.txt);
                                target = i;
                        } else {
                                printf("    %.*s :: ", v->name->name.len, v->name->name.txt);
-                               type_print(v->val.type, stdout);
+                               type_print(v->type, stdout);
                                printf(" = ");
                                printf(" = ");
-                               if (v->val.type == Tstr)
+                               if (v->type == Tstr)
                                        printf("\"");
                                        printf("\"");
-                               print_value(v->val);
-                               if (v->val.type == Tstr)
+                               print_value(v->type, v->val);
+                               if (v->type == Tstr)
                                        printf("\"");
                                printf("\n");
                                target -= 1;
                                        printf("\"");
                                printf("\n");
                                target -= 1;
@@ -4168,9 +4164,10 @@ analysis is a bit more interesting at this level.
 
                for (b = cast(binode, b->left); b; b = cast(binode, b->right)) {
                        struct var *v = cast(var, b->left);
 
                for (b = cast(binode, b->left); b; b = cast(binode, b->right)) {
                        struct var *v = cast(var, b->left);
-                       if (!v->var->val.type) {
+                       if (!v->var->type) {
                                v->var->where_set = b;
                                v->var->where_set = b;
-                               v->var->val = val_prepare(Tstr);
+                               v->var->type = Tstr;
+                               v->var->val = NULL;
                        }
                }
                b = cast(binode, prog);
                        }
                }
                b = cast(binode, prog);
@@ -4191,27 +4188,33 @@ analysis is a bit more interesting at this level.
                struct binode *p = cast(binode, prog);
                struct binode *al;
                struct value v;
                struct binode *p = cast(binode, prog);
                struct binode *al;
                struct value v;
+               struct type *vtype;
 
                if (!prog)
                        return;         // NOTEST
                al = cast(binode, p->left);
                while (al) {
                        struct var *v = cast(var, al->left);
 
                if (!prog)
                        return;         // NOTEST
                al = cast(binode, p->left);
                while (al) {
                        struct var *v = cast(var, al->left);
-                       struct value *vl = &v->var->val;
+                       struct value *vl = v->var->val;
 
                        if (argv[0] == NULL) {
                                printf("Not enough args\n");
                                exit(1);
                        }
                        al = cast(binode, al->right);
 
                        if (argv[0] == NULL) {
                                printf("Not enough args\n");
                                exit(1);
                        }
                        al = cast(binode, al->right);
-                       free_value(*vl);
-                       *vl = parse_value(vl->type, argv[0]);
-                       if (vl->type == NULL)
+                       if (vl)
+                               free_value(v->var->type, vl);
+                       if (!vl) {
+                               vl = val_alloc(v->var->type, NULL);
+                               v->var->val = vl;
+                       }
+                       free_value(v->var->type, vl);
+                       if (!parse_value(v->var->type, argv[0], vl))
                                exit(1);
                        argv++;
                }
                                exit(1);
                        argv++;
                }
-               v = interp_exec(p->right);
-               free_value(v);
+               v = interp_exec(p->right, &vtype);
+               free_value(vtype, &v);
        }
 
 ###### interp binode cases
        }
 
 ###### interp binode cases