]> ocean-lang.org Git - ocean/commitdiff
oceani: make set of types extensible.
authorNeilBrown <neil@brown.name>
Sat, 20 Apr 2019 06:18:18 +0000 (16:18 +1000)
committerNeilBrown <neil@brown.name>
Sat, 20 Apr 2019 06:18:18 +0000 (16:18 +1000)
Rather than a enum listing allowed types, we now
have a 'struct type' which can contain any type.
For now it just has an enum and function pointer
to the existing function, but that can be extended.

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

index 3fd4d74aaa68bb78b5f1880b6abdebce024c7181..062eaed69f82f7c4762b053977069609a838e4c2 100644 (file)
@@ -351,7 +351,7 @@ in too.
 
        static void type_err(struct parse_context *c,
                             char *fmt, struct exec *loc,
-                            enum vtype t1, int rules, enum vtype t2)
+                            struct type *t1, int rules, struct type *t2)
        {
                fprintf(stderr, "%s:", c->file_name);
                fput_loc(loc, stderr);
@@ -365,10 +365,10 @@ in too.
                        case '%': fputc(*fmt, stderr); break;
                        default: fputc('?', stderr); break;
                        case '1':
-                               fputs(vtype_names[t1], stderr);
+                               fputs(t1 ? t1->name : "*unknown*", stderr);
                                break;
                        case '2':
-                               fputs(vtype_names[t2], stderr);
+                               fputs(t2 ? t2->name : "*unknown*", stderr);
                                break;
                        ## format cases
                        }
@@ -392,20 +392,55 @@ to store these elements.
 There are two key objects that we need to work with: executable
 elements which comprise the program, and values which the program
 works with.  Between these are the variables in their various scopes
-which hold the values.
+which hold the values, and types which classify the values stored and
+manipulatd by executables.
+
+### Types
+
+Values come in a wide range of types, with more likely to be added.
+Each type needs to be able to parse and print its own values (for
+convenience at least) as well as to compare two values, at least for
+equality and possibly for order.  For now, values might need to be
+duplicated and freed, though eventually such manipulations will be
+better integrated into the language.
+
+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
+existance of these conversion functions enable types to determine if
+they are compatible with other types.
+
+###### ast
+
+       struct type {
+               char *name;
+               struct value (*init)(struct type *type);
+               struct value (*parse)(struct type *type, char *str);
+               void (*print)(struct value val);
+               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);
+               struct type *(*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);
+               ## type fields
+       };
 
 ### Values
 
 Values can be numbers, which we represent as multi-precision
 fractions, strings, Booleans and labels.  When analysing the program
-we also need to allow for places where no value is meaningful
-(`Vnone`) and where we don't know what type to expect yet (`Vunknown`).
+we also need to allow for places where no value is meaningful (type
+`Tnone`) and where we don't know what type to expect yet (type is
+`NULL`).
 
 Values are never shared, they are always copied when used, and freed
 when no longer needed.
 
 When propagating type information around the program, we need to
-determine if two types are compatible, where `Vunknown` is compatible
+determine if two types are compatible, where type `NULL` is compatible
 with anything.  There are two special cases with type compatibility,
 both related to the Conditional Statement which will be described
 later.  In some cases a Boolean can be accepted as well as some other
@@ -424,9 +459,12 @@ to parse each type from a string.
        myLDLIBS := libnumber.o libstring.o -lgmp
        LDLIBS := $(filter-out $(myLDLIBS),$(LDLIBS)) $(myLDLIBS)
 
+###### type fields
+       enum vtype {Vnone, Vstr, Vnum, Vbool, Vlabel} vtype;
+
 ###### ast
        struct value {
-               enum vtype {Vunknown, Vnone, Vstr, Vnum, Vbool, Vlabel} vtype;
+               struct type *type;
                union {
                        struct text str;
                        mpq_t num;
@@ -437,9 +475,6 @@ to parse each type from a string.
 
        enum val_rules {Rnolabel = 1<<0, Rboolok = 1<<1};
 
-       char *vtype_names[] = {"unknown", "none", "string",
-                              "number", "Boolean", "label"};
-
 ###### format cases
        case 'r':
                if (rules & Rnolabel)
@@ -447,11 +482,10 @@ to parse each type from a string.
                break;
 
 ###### ast functions
-       static void free_value(struct value v)
+       static void _free_value(struct value v)
        {
-               switch (v.vtype) {
-               case Vnone:
-               case Vunknown: break;
+               switch (v.type->vtype) {
+               case Vnone: break;
                case Vstr: free(v.str.txt); break;
                case Vnum: mpq_clear(v.num); break;
                case Vlabel:
@@ -459,13 +493,19 @@ to parse each type from a string.
                }
        }
 
-       static int vtype_compat(enum vtype require, enum vtype have, int rules)
+       static void free_value(struct value v)
+       {
+               if (v.type)
+                       v.type->free(v);
+       }
+
+       static int vtype_compat(struct type *require, struct type *have, int rules)
        {
-               if ((rules & Rboolok) && have == Vbool)
+               if ((rules & Rboolok) && have == &Tbool)
                        return 1;
-               if ((rules & Rnolabel) && have == Vlabel)
+               if ((rules & Rnolabel) && have == &Tlabel)
                        return 0;
-               if (require == Vunknown || have == Vunknown)
+               if (!require || !have)
                        return 1;
 
                return require == have;
@@ -473,34 +513,46 @@ to parse each type from a string.
 
 ###### value functions
 
-       static void val_init(struct value *val, enum vtype type)
+       static struct value _val_init(struct type *type)
        {
-               val->vtype = type;
-               switch(type) {
+               struct value rv;
+
+               rv.type = type;
+               switch(type->vtype) {
                case Vnone:abort();
-               case Vunknown: break;
                case Vnum:
-                       mpq_init(val->num); break;
+                       mpq_init(rv.num); break;
                case Vstr:
-                       val->str.txt = malloc(1);
-                       val->str.len = 0;
+                       rv.str.txt = malloc(1);
+                       rv.str.len = 0;
                        break;
                case Vbool:
-                       val->bool = 0;
+                       rv.bool = 0;
                        break;
                case Vlabel:
-                       val->label = val;
+                       rv.label = NULL;
                        break;
                }
+               return rv;
        }
 
-       static struct value dup_value(struct value v)
+       static struct value val_init(struct type *type)
+       {
+               struct value rv;
+
+               if (type)
+                       return type->init(type);
+               rv.type = type;
+               return rv;
+       }
+
+       static struct value _dup_value(struct value v)
        {
                struct value rv;
-               rv.vtype = v.vtype;
-               switch (rv.vtype) {
+               rv.type = v.type;
+               switch (rv.type->vtype) {
                case Vnone:
-               case Vunknown: break;
+                       break;
                case Vlabel:
                        rv.label = v.label;
                        break;
@@ -520,22 +572,37 @@ to parse each type from a string.
                return rv;
        }
 
-       static int value_cmp(struct value left, struct value right)
+       static struct value dup_value(struct value v)
+       {
+               if (v.type)
+                       return v.type->dup(v);
+               return v;
+       }
+
+       static int _value_cmp(struct value left, struct value right)
        {
                int cmp;
-               if (left.vtype != right.vtype)
-                       return left.vtype - right.vtype;
-               switch (left.vtype) {
+               if (left.type != right.type)
+                       return left.type - right.type;
+               switch (left.type->vtype) {
                case Vlabel: cmp = left.label == right.label ? 0 : 1; break;
                case Vnum: cmp = mpq_cmp(left.num, right.num); break;
                case Vstr: cmp = text_cmp(left.str, right.str); break;
                case Vbool: cmp = left.bool - right.bool; break;
-               case Vnone:
-               case Vunknown: cmp = 0;
+               case Vnone: cmp = 0;
                }
                return cmp;
        }
 
+       static int value_cmp(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);
+               return -1;
+       }
+
        static struct text text_join(struct text a, struct text b)
        {
                struct text rv;
@@ -546,11 +613,9 @@ to parse each type from a string.
                return rv;
        }
 
-       static void print_value(struct value v)
+       static void _print_value(struct value v)
        {
-               switch (v.vtype) {
-               case Vunknown:
-                       printf("*Unknown*"); break;
+               switch (v.type->vtype) {
                case Vnone:
                        printf("*no-value*"); break;
                case Vlabel:
@@ -571,21 +636,31 @@ to parse each type from a string.
                }
        }
 
-       static int parse_value(struct value *vl, char *arg)
+       static void print_value(struct value v)
+       {
+               if (v.type && v.type->print)
+                       v.type->print(v);
+               else
+                       printf("*Unknown*");
+       }
+
+       static struct value _parse_value(struct type *type, char *arg)
        {
+               struct value val;
                struct text tx;
                int neg = 0;
                char tail[3] = "";
 
-               switch(vl->vtype) {
+               val.type = type;
+               switch(type->vtype) {
                case Vlabel:
-               case Vunknown:
                case Vnone:
-                       return 0;
+                       val.type = NULL;
+                       break;
                case Vstr:
-                       vl->str.len = strlen(arg);
-                       vl->str.txt = malloc(vl->str.len);
-                       memcpy(vl->str.txt, arg, vl->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 == '-') {
@@ -593,31 +668,82 @@ to parse each type from a string.
                                arg++;
                        }
                        tx.txt = arg; tx.len = strlen(tx.txt);
-                       if (number_parse(vl->num, tail, tx) == 0)
-                               mpq_init(vl->num);
+                       if (number_parse(val.num, tail, tx) == 0)
+                               mpq_init(val.num);
                        else if (neg)
-                               mpq_neg(vl->num, vl->num);
+                               mpq_neg(val.num, val.num);
                        if (tail[0]) {
                                printf("Unsupported suffix: %s\n", arg);
-                               return 0;
+                               val.type = NULL;
                        }
                        break;
                case Vbool:
                        if (strcasecmp(arg, "true") == 0 ||
                            strcmp(arg, "1") == 0)
-                               vl->bool = 1;
+                               val.bool = 1;
                        else if (strcasecmp(arg, "false") == 0 ||
                                 strcmp(arg, "0") == 0)
-                               vl->bool = 0;
+                               val.bool = 0;
                        else {
                                printf("Bad bool: %s\n", arg);
-                               return 0;
+                               val.type = NULL;
                        }
                        break;
                }
-               return 1;
+               return val;
        }
 
+       static struct value parse_value(struct type *type, char *arg)
+       {
+               struct value rv;
+
+               if (type && type->parse)
+                       return type->parse(type, arg);
+               rv.type = NULL;
+               return rv;
+       }
+
+       static void _free_value(struct value v);
+
+       #define BaseType \
+               .init = _val_init,              \
+               .parse = _parse_value,          \
+               .print = _print_value,          \
+               .cmp_order = _value_cmp,        \
+               .cmp_eq = _value_cmp,           \
+               .dup = _dup_value,              \
+               .free = _free_value,            \
+
+       static struct type Tbool = {
+               BaseType
+               .name = "Boolean",
+               .vtype = Vbool,
+       };
+
+       static struct type Tstr = {
+               BaseType
+               .name = "string",
+               .vtype = Vstr,
+       };
+
+       static struct type Tnum = {
+               BaseType
+               .name = "number",
+               .vtype = Vnum,
+       };
+
+       static struct type Tnone = {
+               BaseType
+               .name = "none",
+               .vtype = Vnone,
+       };
+
+       static struct type Tlabel = {
+               BaseType
+               .name = "label",
+               .vtype = Vlabel,
+       };
+
 ### Variables
 
 Variables are scoped named values.  We store the names in a linked
@@ -924,7 +1050,7 @@ all pending-scope variables become conditionally scoped.
                v->scope = InScope;
                v->in_scope = c->in_scope;
                c->in_scope = v;
-               val_init(&v->val, Vunknown);
+               v->val = val_init(NULL);
                return v;
        }
 
@@ -976,7 +1102,7 @@ all pending-scope variables become conditionally scoped.
                                        else if (v->previous &&
                                                 v->previous->scope == PendingScope)
                                                v->scope = PendingScope;
-                                       else if (v->val.vtype == Vlabel)
+                                       else if (v->val.type == &Tlabel)
                                                v->scope = PendingScope;
                                        else if (v->name->var == v)
                                                v->scope = OutScope;
@@ -993,14 +1119,14 @@ all pending-scope variables become conditionally scoped.
                                        for (v2 = v;
                                             v2 && v2->scope == PendingScope;
                                             v2 = v2->previous)
-                                               if (v2->val.vtype != Vlabel)
+                                               if (v2->val.type != &Tlabel)
                                                        v2->scope = OutScope;
                                        break;
                                case OutScope: break;
                                }
                                break;
                        case CloseSequential:
-                               if (v->val.vtype == Vlabel)
+                               if (v->val.type == &Tlabel)
                                        v->scope = PendingScope;
                                switch (v->scope) {
                                case InScope:
@@ -1015,7 +1141,7 @@ all pending-scope variables become conditionally scoped.
                                        for (v2 = v;
                                             v2 && v2->scope == PendingScope;
                                             v2 = v2->previous)
-                                               if (v2->val.vtype == Vlabel) {
+                                               if (v2->val.type == &Tlabel) {
                                                        v2->scope = CondScope;
                                                        v2->min_depth = c->scope_depth;
                                                } else
@@ -1184,22 +1310,23 @@ also want to know what sort of bracketing to use.
 As discussed, analysis involves propagating type requirements around
 the program and looking for errors.
 
-So `propagate_types` is passed an expected type (being a `vtype`
-together with some `val_rules` flags) that the `exec` is expected to
-return, and returns the type that it does return, either of which can
-be `Vunknown`.  An `ok` flag is passed by reference. It is set to `0`
-when an error is found, and `2` when any change is made.  If it
-remains unchanged at `1`, then no more propagation is needed.
+So `propagate_types` is passed an expected type (being a `struct type`
+pointer together with some `val_rules` flags) that the `exec` is
+expected to return, and returns the type that it does return, either
+of which can be `NULL` signifying "unknown".  An `ok` flag is passed
+by reference. It is set to `0` when an error is found, and `2` when
+any change is made.  If it remains unchanged at `1`, then no more
+propagation is needed.
 
 ###### core functions
 
-       static enum vtype propagate_types(struct exec *prog, struct parse_context *c, int *ok,
-                                         enum vtype type, int rules)
+       static struct type *propagate_types(struct exec *prog, struct parse_context *c, int *ok,
+                                           struct type *type, int rules)
        {
-               enum vtype t;
+               struct type *t;
 
                if (!prog)
-                       return Vnone;
+                       return &Tnone;
 
                switch (prog->type) {
                case Xbinode:
@@ -1212,7 +1339,7 @@ remains unchanged at `1`, then no more propagation is needed.
                }
                ## propagate exec cases
                }
-               return Vnone;
+               return &Tnone;
        }
 
 #### Interpreting
@@ -1223,14 +1350,14 @@ 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.
 
-Each `exec` can return a value, which may be `Vnone` but shouldn't be `Vunknown`.
+Each `exec` can return a value, which may be `Tnone` but must be non-NULL;
 
 ###### core functions
 
        static struct value interp_exec(struct exec *e)
        {
                struct value rv;
-               rv.vtype = Vnone;
+               rv.type = &Tnone;
                if (!e)
                        return rv;
 
@@ -1239,7 +1366,7 @@ Each `exec` can return a value, which may be `Vnone` but shouldn't be `Vunknown`
                {
                        struct binode *b = cast(binode, e);
                        struct value left, right;
-                       left.vtype = right.vtype = Vnone;
+                       left.type = right.type = &Tnone;
                        switch (b->op) {
                        ## interp binode cases
                        }
@@ -1278,17 +1405,17 @@ an executable.
        $*val
        Value ->  True ${
                        $0 = new_pos(val, $1);
-                       $0->val.vtype = Vbool;
+                       $0->val.type = &Tbool;
                        $0->val.bool = 1;
                        }$
                | False ${
                        $0 = new_pos(val, $1);
-                       $0->val.vtype = Vbool;
+                       $0->val.type = &Tbool;
                        $0->val.bool = 0;
                        }$
                | NUMBER ${
                        $0 = new_pos(val, $1);
-                       $0->val.vtype = Vnum;
+                       $0->val.type = &Tnum;
                        {
                        char tail[3];
                        if (number_parse($0->val.num, tail, $1.txt) == 0)
@@ -1300,7 +1427,7 @@ an executable.
                        }$
                | STRING ${
                        $0 = new_pos(val, $1);
-                       $0->val.vtype = Vstr;
+                       $0->val.type = &Tstr;
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
@@ -1311,7 +1438,7 @@ an executable.
                        }$
                | MULTI_STRING ${
                        $0 = new_pos(val, $1);
-                       $0->val.vtype = Vstr;
+                       $0->val.type = &Tstr;
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
@@ -1325,10 +1452,10 @@ an executable.
        case Xval:
        {
                struct val *v = cast(val, e);
-               if (v->val.vtype == Vstr)
+               if (v->val.type == &Tstr)
                        printf("\"");
                print_value(v->val);
-               if (v->val.vtype == Vstr)
+               if (v->val.type == &Tstr)
                        printf("\"");
                break;
        }
@@ -1337,12 +1464,12 @@ an executable.
                case Xval:
                {
                        struct val *val = cast(val, prog);
-                       if (!vtype_compat(type, val->val.vtype, rules)) {
+                       if (!vtype_compat(type, val->val.type, rules)) {
                                type_err(c, "error: expected %1%r found %2",
-                                          prog, type, rules, val->val.vtype);
+                                          prog, type, rules, val->val.type);
                                *ok = 0;
                        }
-                       return val->val.vtype;
+                       return val->val.type;
                }
 
 ###### interp exec cases
@@ -1413,9 +1540,9 @@ link to find the primary instance.
                        v = var_ref(config2context(config), $1.txt);
                        $0->var = v;
                        type_err(config2context(config), "error: variable '%v' redeclared",
-                                $0, Vnone, 0, Vnone);
+                                $0, &Tnone, 0, &Tnone);
                        type_err(config2context(config), "info: this is where '%v' was first declared",
-                                v->where_decl, Vnone, 0, Vnone);
+                                v->where_decl, &Tnone, 0, &Tnone);
                }
        } }$
            | IDENTIFIER ::= ${ {
@@ -1429,9 +1556,9 @@ link to find the primary instance.
                        v = var_ref(config2context(config), $1.txt);
                        $0->var = v;
                        type_err(config2context(config), "error: variable '%v' redeclared",
-                                $0, Vnone, 0, Vnone);
+                                $0, &Tnone, 0, &Tnone);
                        type_err(config2context(config), "info: this is where '%v' was first declared",
-                                v->where_decl, Vnone, 0, Vnone);
+                                v->where_decl, &Tnone, 0, &Tnone);
                }
        } }$
 
@@ -1442,7 +1569,8 @@ link to find the primary instance.
                        /* This might be a label - allocate a var just in case */
                        v = var_decl(config2context(config), $1.txt);
                        if (v) {
-                               val_init(&v->val, Vlabel);
+                               v->val = val_init(&Tlabel);
+                               v->val.label = &v->val;
                                v->where_set = $0;
                        }
                }
@@ -1480,29 +1608,29 @@ link to find the primary instance.
                struct var *var = cast(var, prog);
                struct variable *v = var->var;
                if (!v) {
-                       type_err(c, "%d:BUG: no variable!!", prog, Vnone, 0, Vnone);
+                       type_err(c, "%d:BUG: no variable!!", prog, &Tnone, 0, &Tnone);
                        *ok = 0;
-                       return Vnone;
+                       return &Tnone;
                }
                if (v->merged)
                        v = v->merged;
-               if (v->val.vtype == Vunknown) {
-                       if (type != Vunknown && *ok != 0) {
-                               val_init(&v->val, type);
+               if (v->val.type == NULL) {
+                       if (type && *ok != 0) {
+                               v->val = val_init(type);
                                v->where_set = prog;
                                *ok = 2;
                        }
                        return type;
                }
-               if (!vtype_compat(type, v->val.vtype, rules)) {
+               if (!vtype_compat(type, v->val.type, rules)) {
                        type_err(c, "error: expected %1%r but variable '%v' is %2", prog,
-                                type, rules, v->val.vtype);
+                                type, rules, v->val.type);
                        type_err(c, "info: this is where '%v' was set to %1", v->where_set,
-                                v->val.vtype, rules, Vnone);
+                                v->val.type, rules, &Tnone);
                        *ok = 0;
                }
-               if (type == Vunknown)
-                       return v->val.vtype;
+               if (!type)
+                       return v->val.type;
                return type;
        }
 
@@ -1590,15 +1718,15 @@ and `BFact`s.
        case And:
        case Or:
        case Not:
-               /* both must be Vbool, result is Vbool */
-               propagate_types(b->left, c, ok, Vbool, 0);
-               propagate_types(b->right, c, ok, Vbool, 0);
-               if (type != Vbool && type != Vunknown) {
+               /* both must be Tbool, result is Tbool */
+               propagate_types(b->left, c, ok, &Tbool, 0);
+               propagate_types(b->right, c, ok, &Tbool, 0);
+               if (type && type != &Tbool) {
                        type_err(c, "error: %1 operation found where %2 expected", prog,
-                                  Vbool, 0, type);
+                                  &Tbool, 0, type);
                        *ok = 0;
                }
-               return Vbool;
+               return &Tbool;
 
 ###### interp binode cases
        case And:
@@ -1694,21 +1822,21 @@ expression operator.
        case GtrEq:
        case Eql:
        case NEql:
-               /* Both must match but not labels, result is Vbool */
-               t = propagate_types(b->left, c, ok, Vunknown, Rnolabel);
-               if (t != Vunknown)
+               /* Both must match but not labels, result is Tbool */
+               t = propagate_types(b->left, c, ok, NULL, Rnolabel);
+               if (t)
                        propagate_types(b->right, c, ok, t, 0);
                else {
-                       t = propagate_types(b->right, c, ok, Vunknown, Rnolabel);
-                       if (t != Vunknown)
+                       t = propagate_types(b->right, c, ok, NULL, Rnolabel);
+                       if (t)
                                t = propagate_types(b->left, c, ok, t, 0);
                }
-               if (!vtype_compat(type, Vbool, 0)) {
+               if (!vtype_compat(type, &Tbool, 0)) {
                        type_err(c, "error: Comparison returns %1 but %2 expected", prog,
-                                   Vbool, rules, type);
+                                   &Tbool, rules, type);
                        *ok = 0;
                }
-               return Vbool;
+               return &Tbool;
 
 ###### interp binode cases
        case Less:
@@ -1722,7 +1850,7 @@ expression operator.
                left = interp_exec(b->left);
                right = interp_exec(b->right);
                cmp = value_cmp(left, right);
-               rv.vtype = Vbool;
+               rv.type = &Tbool;
                switch (b->op) {
                case Less:      rv.bool = cmp <  0; break;
                case LessEq:    rv.bool = cmp <= 0; break;
@@ -1839,30 +1967,30 @@ precedence is handled better I might be able to discard this.
        case Minus:
        case Times:
        case Divide:
-               /* both must be numbers, result is Vnum */
+               /* both must be numbers, result is Tnum */
        case Absolute:
        case Negate:
                /* as propagate_types ignores a NULL,
                 * unary ops fit here too */
-               propagate_types(b->left, c, ok, Vnum, 0);
-               propagate_types(b->right, c, ok, Vnum, 0);
-               if (!vtype_compat(type, Vnum, 0)) {
+               propagate_types(b->left, c, ok, &Tnum, 0);
+               propagate_types(b->right, c, ok, &Tnum, 0);
+               if (!vtype_compat(type, &Tnum, 0)) {
                        type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
-                                  Vnum, rules, type);
+                                  &Tnum, rules, type);
                        *ok = 0;
                }
-               return Vnum;
+               return &Tnum;
 
        case Concat:
-               /* both must be Vstr, result is Vstr */
-               propagate_types(b->left, c, ok, Vstr, 0);
-               propagate_types(b->right, c, ok, Vstr, 0);
-               if (!vtype_compat(type, Vstr, 0)) {
+               /* both must be Tstr, result is Tstr */
+               propagate_types(b->left, c, ok, &Tstr, 0);
+               propagate_types(b->right, c, ok, &Tstr, 0);
+               if (!vtype_compat(type, &Tstr, 0)) {
                        type_err(c, "error: Concat returns %1 but %2 expected", prog,
-                                  Vstr, rules, type);
+                                  &Tstr, rules, type);
                        *ok = 0;
                }
-               return Vstr;
+               return &Tstr;
 
        case Bracket:
                return propagate_types(b->right, c, ok, type, 0);
@@ -1903,7 +2031,7 @@ precedence is handled better I might be able to discard this.
        case Concat:
                left = interp_exec(b->left);
                right = interp_exec(b->right);
-               rv.vtype = Vstr;
+               rv.type = &Tstr;
                rv.str = text_join(left.str, right.str);
                break;
 
@@ -2049,21 +2177,21 @@ list.
 ###### propagate binode cases
        case Block:
        {
-               /* If any statement returns something other then Vnone
-                * or Vbool then all such must return same type.
-                * As each statement may be Vnone or something else,
-                * we must always pass Vunknown down, otherwise an incorrect
-                * error might occur.  We never return Vnone unless it is
+               /* If any statement returns something other then Tnone
+                * or Tbool then all such must return same type.
+                * As each statement may be Tnone or something else,
+                * we must always pass NULL (unknown) down, otherwise an incorrect
+                * error might occur.  We never return Tnone unless it is
                 * passed in.
                 */
                struct binode *e;
 
                for (e = b; e; e = cast(binode, e->right)) {
-                       t = propagate_types(e->left, c, ok, Vunknown, rules);
-                       if ((rules & Rboolok) && t == Vbool)
-                               t = Vunknown;
-                       if (t != Vunknown && t != Vnone && t != Vbool) {
-                               if (type == Vunknown)
+                       t = propagate_types(e->left, c, ok, NULL, rules);
+                       if ((rules & Rboolok) && t == &Tbool)
+                               t = NULL;
+                       if (t && t != &Tnone && t != &Tbool) {
+                               if (!type)
                                        type = t;
                                else if (t != type) {
                                        type_err(c, "error: expected %1%r, found %2",
@@ -2077,7 +2205,7 @@ list.
 
 ###### interp binode cases
        case Block:
-               while (rv.vtype == Vnone &&
+               while (rv.type == &Tnone &&
                       b) {
                        if (b->left)
                                rv = interp_exec(b->left);
@@ -2152,8 +2280,8 @@ same solution.
 
        case Print:
                /* don't care but all must be consistent */
-               propagate_types(b->left, c, ok, Vunknown, Rnolabel);
-               propagate_types(b->right, c, ok, Vunknown, Rnolabel);
+               propagate_types(b->left, c, ok, NULL, Rnolabel);
+               propagate_types(b->right, c, ok, NULL, Rnolabel);
                break;
 
 ###### interp binode cases
@@ -2173,7 +2301,7 @@ same solution.
                                        sep = ' ';
                        } else if (sep)
                                eol = 0;
-               left.vtype = Vnone;
+               left.type = &Tnone;
                if (eol)
                        printf("\n");
                break;
@@ -2187,7 +2315,7 @@ will be correct so the interpreter just needs to perform the
 calculation.  There is a form of assignment which declares a new
 variable as well as assigning a value.  If a name is assigned before
 it is declared, and error will be raised as the name is created as
-`Vlabel` and it is illegal to assign to such names.
+`Tlabel` and it is illegal to assign to such names.
 
 ###### Binode types
        Assign,
@@ -2239,19 +2367,19 @@ it is declared, and error will be raised as the name is created as
 
        case Assign:
        case Declare:
-               /* Both must match and not be labels, result is Vnone */
-               t = propagate_types(b->left, c, ok, Vunknown, Rnolabel);
-               if (t != Vunknown) {
+               /* Both must match and not be labels, result is Tnone */
+               t = propagate_types(b->left, c, ok, NULL, Rnolabel);
+               if (t) {
                        if (propagate_types(b->right, c, ok, t, 0) != t)
                                if (b->left->type == Xvar)
                                        type_err(c, "info: variable '%v' was set as %1 here.",
-                                                cast(var, b->left)->var->where_set, t, rules, Vnone);
+                                                cast(var, b->left)->var->where_set, t, rules, &Tnone);
                } else {
-                       t = propagate_types(b->right, c, ok, Vunknown, Rnolabel);
-                       if (t != Vunknown)
+                       t = propagate_types(b->right, c, ok, NULL, Rnolabel);
+                       if (t)
                                propagate_types(b->left, c, ok, t, 0);
                }
-               return Vnone;
+               return &Tnone;
 
                break;
 
@@ -2266,7 +2394,7 @@ it is declared, and error will be raised as the name is created as
                right = interp_exec(b->right);
                free_value(v->val);
                v->val = right;
-               right.vtype = Vunknown;
+               right.type = NULL;
                break;
        }
 
@@ -2677,8 +2805,8 @@ defined.
 ###### propagate exec cases
        case Xcond_statement:
        {
-               // forpart and dopart must return Vnone
-               // thenpart must return Vnone if there is a dopart,
+               // forpart and dopart must return Tnone
+               // thenpart must return Tnone if there is a dopart,
                // otherwise it is like elsepart.
                // condpart must:
                //    be bool if there is not casepart
@@ -2690,44 +2818,44 @@ defined.
                struct cond_statement *cs = cast(cond_statement, prog);
                struct casepart *cp;
 
-               t = propagate_types(cs->forpart, c, ok, Vnone, 0);
-               if (!vtype_compat(Vnone, t, 0))
+               t = propagate_types(cs->forpart, c, ok, &Tnone, 0);
+               if (!vtype_compat(&Tnone, t, 0))
                        *ok = 0;
-               t = propagate_types(cs->dopart, c, ok, Vnone, 0);
-               if (!vtype_compat(Vnone, t, 0))
+               t = propagate_types(cs->dopart, c, ok, &Tnone, 0);
+               if (!vtype_compat(&Tnone, t, 0))
                        *ok = 0;
                if (cs->dopart) {
-                       t = propagate_types(cs->thenpart, c, ok, Vnone, 0);
-                       if (!vtype_compat(Vnone, t, 0))
+                       t = propagate_types(cs->thenpart, c, ok, &Tnone, 0);
+                       if (!vtype_compat(&Tnone, t, 0))
                                *ok = 0;
                }
                if (cs->casepart == NULL)
-                       propagate_types(cs->condpart, c, ok, Vbool, 0);
+                       propagate_types(cs->condpart, c, ok, &Tbool, 0);
                else {
                        /* Condpart must match case values, with bool permitted */
-                       t = Vunknown;
+                       t = NULL;
                        for (cp = cs->casepart;
-                            cp && (t == Vunknown); cp = cp->next)
-                               t = propagate_types(cp->value, c, ok, Vunknown, 0);
-                       if (t == Vunknown && cs->condpart)
-                               t = propagate_types(cs->condpart, c, ok, Vunknown, Rboolok);
+                            cp && !t; cp = cp->next)
+                               t = propagate_types(cp->value, c, ok, NULL, 0);
+                       if (!t && cs->condpart)
+                               t = propagate_types(cs->condpart, c, ok, NULL, Rboolok);
                        // Now we have a type (I hope) push it down
-                       if (t != Vunknown) {
+                       if (t) {
                                for (cp = cs->casepart; cp; cp = cp->next)
                                        propagate_types(cp->value, c, ok, t, 0);
                                propagate_types(cs->condpart, c, ok, t, Rboolok);
                        }
                }
                // (if)then, else, and case parts must return expected type.
-               if (!cs->dopart && type == Vunknown)
-                       type = propagate_types(cs->thenpart, c, ok, Vunknown, rules);
-               if (type == Vunknown)
-                       type = propagate_types(cs->elsepart, c, ok, Vunknown, rules);
+               if (!cs->dopart && !type)
+                       type = propagate_types(cs->thenpart, c, ok, NULL, rules);
+               if (!type)
+                       type = propagate_types(cs->elsepart, c, ok, NULL, rules);
                for (cp = cs->casepart;
-                    cp && type == Vunknown;
+                    cp && !type;
                     cp = cp->next)
-                       type = propagate_types(cp->action, c, ok, Vunknown, rules);
-               if (type != Vunknown) {
+                       type = propagate_types(cp->action, c, ok, NULL, rules);
+               if (type) {
                        if (!cs->dopart)
                                propagate_types(cs->thenpart, c, ok, type, rules);
                        propagate_types(cs->elsepart, c, ok, type, rules);
@@ -2735,7 +2863,7 @@ defined.
                                propagate_types(cp->action, c, ok, type, rules);
                        return type;
                } else
-                       return Vunknown;
+                       return NULL;
        }
 
 ###### interp exec cases
@@ -2751,17 +2879,17 @@ defined.
                        if (c->condpart)
                                cnd = interp_exec(c->condpart);
                        else
-                               cnd.vtype = Vnone;
-                       if (!(cnd.vtype == Vnone ||
-                             (cnd.vtype == Vbool && cnd.bool != 0)))
+                               cnd.type = &Tnone;
+                       if (!(cnd.type == &Tnone ||
+                             (cnd.type == &Tbool && cnd.bool != 0)))
                                break;
-                       // cnd is Vnone or Vbool, doesn't need to be freed
+                       // cnd is Tnone or Tbool, doesn't need to be freed
                        if (c->dopart)
                                interp_exec(c->dopart);
 
                        if (c->thenpart) {
                                v = interp_exec(c->thenpart);
-                               if (v.vtype != Vnone || !c->dopart)
+                               if (v.type != &Tnone || !c->dopart)
                                        return v;
                                free_value(v);
                        }
@@ -2779,7 +2907,7 @@ defined.
                free_value(cnd);
                if (c->elsepart)
                        return interp_exec(c->elsepart);
-               v.vtype = Vnone;
+               v.type = &Tnone;
                return v;
        }
 
@@ -2862,28 +2990,28 @@ analysis is a bit more interesting at this level.
                        return 0;
                do {
                        ok = 1;
-                       propagate_types(b->right, c, &ok, Vnone, 0);
+                       propagate_types(b->right, c, &ok, &Tnone, 0);
                } while (ok == 2);
                if (!ok)
                        return 0;
 
                for (b = cast(binode, b->left); b; b = cast(binode, b->right)) {
                        struct var *v = cast(var, b->left);
-                       if (v->var->val.vtype == Vunknown) {
+                       if (!v->var->val.type) {
                                v->var->where_set = b;
-                               val_init(&v->var->val, Vstr);
+                               v->var->val = val_init(&Tstr);
                        }
                }
                b = cast(binode, prog);
                do {
                        ok = 1;
-                       propagate_types(b->right, c, &ok, Vnone, 0);
+                       propagate_types(b->right, c, &ok, &Tnone, 0);
                } while (ok == 2);
                if (!ok)
                        return 0;
 
                /* Make sure everything is still consistent */
-               propagate_types(b->right, c, &ok, Vnone, 0);
+               propagate_types(b->right, c, &ok, &Tnone, 0);
                return !!ok;
        }
 
@@ -2906,7 +3034,8 @@ analysis is a bit more interesting at this level.
                        }
                        al = cast(binode, al->right);
                        free_value(*vl);
-                       if (!parse_value(vl, argv[0]))
+                       *vl = parse_value(vl->type, argv[0]);
+                       if (vl->type == NULL)
                                exit(1);
                        argv++;
                }