]> 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,
 
        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);
        {
                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':
                        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':
                                break;
                        case '2':
-                               fputs(vtype_names[t2], stderr);
+                               fputs(t2 ? t2->name : "*unknown*", stderr);
                                break;
                        ## format cases
                        }
                                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
 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
 
 ### 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
 
 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
 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)
 
        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 {
 ###### ast
        struct value {
-               enum vtype {Vunknown, Vnone, Vstr, Vnum, Vbool, Vlabel} vtype;
+               struct type *type;
                union {
                        struct text str;
                        mpq_t num;
                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};
 
 
        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)
 ###### format cases
        case 'r':
                if (rules & Rnolabel)
@@ -447,11 +482,10 @@ to parse each type from a string.
                break;
 
 ###### ast functions
                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:
                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;
                        return 1;
-               if ((rules & Rnolabel) && have == Vlabel)
+               if ((rules & Rnolabel) && have == &Tlabel)
                        return 0;
                        return 0;
-               if (require == Vunknown || have == Vunknown)
+               if (!require || !have)
                        return 1;
 
                return require == have;
                        return 1;
 
                return require == have;
@@ -473,34 +513,46 @@ to parse each type from a string.
 
 ###### value functions
 
 
 ###### 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 Vnone:abort();
-               case Vunknown: break;
                case Vnum:
                case Vnum:
-                       mpq_init(val->num); break;
+                       mpq_init(rv.num); break;
                case Vstr:
                case Vstr:
-                       val->str.txt = malloc(1);
-                       val->str.len = 0;
+                       rv.str.txt = malloc(1);
+                       rv.str.len = 0;
                        break;
                case Vbool:
                        break;
                case Vbool:
-                       val->bool = 0;
+                       rv.bool = 0;
                        break;
                case Vlabel:
                        break;
                case Vlabel:
-                       val->label = val;
+                       rv.label = NULL;
                        break;
                }
                        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;
        {
                struct value rv;
-               rv.vtype = v.vtype;
-               switch (rv.vtype) {
+               rv.type = v.type;
+               switch (rv.type->vtype) {
                case Vnone:
                case Vnone:
-               case Vunknown: break;
+                       break;
                case Vlabel:
                        rv.label = v.label;
                        break;
                case Vlabel:
                        rv.label = v.label;
                        break;
@@ -520,22 +572,37 @@ to parse each type from a string.
                return rv;
        }
 
                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;
        {
                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 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;
        }
 
                }
                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;
        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;
        }
 
                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:
                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] = "";
 
                struct text tx;
                int neg = 0;
                char tail[3] = "";
 
-               switch(vl->vtype) {
+               val.type = type;
+               switch(type->vtype) {
                case Vlabel:
                case Vlabel:
-               case Vunknown:
                case Vnone:
                case Vnone:
-                       return 0;
+                       val.type = NULL;
+                       break;
                case Vstr:
                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 == '-') {
                        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);
                                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)
                        else if (neg)
-                               mpq_neg(vl->num, vl->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);
-                               return 0;
+                               val.type = NULL;
                        }
                        break;
                case Vbool:
                        if (strcasecmp(arg, "true") == 0 ||
                            strcmp(arg, "1") == 0)
                        }
                        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)
                        else if (strcasecmp(arg, "false") == 0 ||
                                 strcmp(arg, "0") == 0)
-                               vl->bool = 0;
+                               val.bool = 0;
                        else {
                                printf("Bad bool: %s\n", arg);
                        else {
                                printf("Bad bool: %s\n", arg);
-                               return 0;
+                               val.type = NULL;
                        }
                        break;
                }
                        }
                        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
 ### 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;
                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;
        }
 
                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->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;
                                                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)
                                        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:
                                                        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:
                                        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)
                                        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
                                                        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.
 
 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
 
 
 ###### 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)
 
                if (!prog)
-                       return Vnone;
+                       return &Tnone;
 
                switch (prog->type) {
                case Xbinode:
 
                switch (prog->type) {
                case Xbinode:
@@ -1212,7 +1339,7 @@ remains unchanged at `1`, then no more propagation is needed.
                }
                ## propagate exec cases
                }
                }
                ## propagate exec cases
                }
-               return Vnone;
+               return &Tnone;
        }
 
 #### Interpreting
        }
 
 #### 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.
 
 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;
 
 ###### core functions
 
        static struct value interp_exec(struct exec *e)
        {
                struct value rv;
-               rv.vtype = Vnone;
+               rv.type = &Tnone;
                if (!e)
                        return rv;
 
                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;
                {
                        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
                        }
                        switch (b->op) {
                        ## interp binode cases
                        }
@@ -1278,17 +1405,17 @@ an executable.
        $*val
        Value ->  True ${
                        $0 = new_pos(val, $1);
        $*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.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.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)
                        {
                        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);
                        }$
                | STRING ${
                        $0 = new_pos(val, $1);
-                       $0->val.vtype = Vstr;
+                       $0->val.type = &Tstr;
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
@@ -1311,7 +1438,7 @@ an executable.
                        }$
                | MULTI_STRING ${
                        $0 = new_pos(val, $1);
                        }$
                | 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);
                        {
                        char tail[3];
                        string_parse(&$1, '\\', &$0->val.str, tail);
@@ -1325,10 +1452,10 @@ an executable.
        case Xval:
        {
                struct val *v = cast(val, e);
        case Xval:
        {
                struct val *v = cast(val, e);
-               if (v->val.vtype == Vstr)
+               if (v->val.type == &Tstr)
                        printf("\"");
                print_value(v->val);
                        printf("\"");
                print_value(v->val);
-               if (v->val.vtype == Vstr)
+               if (v->val.type == &Tstr)
                        printf("\"");
                break;
        }
                        printf("\"");
                break;
        }
@@ -1337,12 +1464,12 @@ an executable.
                case Xval:
                {
                        struct val *val = cast(val, prog);
                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",
                                type_err(c, "error: expected %1%r found %2",
-                                          prog, type, rules, val->val.vtype);
+                                          prog, type, rules, val->val.type);
                                *ok = 0;
                        }
                                *ok = 0;
                        }
-                       return val->val.vtype;
+                       return val->val.type;
                }
 
 ###### interp exec cases
                }
 
 ###### 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",
                        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",
                        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 ::= ${ {
                }
        } }$
            | 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",
                        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",
                        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) {
                        /* 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;
                        }
                }
                                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) {
                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;
                        *ok = 0;
-                       return Vnone;
+                       return &Tnone;
                }
                if (v->merged)
                        v = v->merged;
                }
                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;
                }
                                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_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,
                        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;
                }
                        *ok = 0;
                }
-               if (type == Vunknown)
-                       return v->val.vtype;
+               if (!type)
+                       return v->val.type;
                return type;
        }
 
                return type;
        }
 
@@ -1590,15 +1718,15 @@ and `BFact`s.
        case And:
        case Or:
        case Not:
        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,
                        type_err(c, "error: %1 operation found where %2 expected", prog,
-                                  Vbool, 0, type);
+                                  &Tbool, 0, type);
                        *ok = 0;
                }
                        *ok = 0;
                }
-               return Vbool;
+               return &Tbool;
 
 ###### interp binode cases
        case And:
 
 ###### interp binode cases
        case And:
@@ -1694,21 +1822,21 @@ expression operator.
        case GtrEq:
        case Eql:
        case NEql:
        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 {
                        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);
                }
                                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,
                        type_err(c, "error: Comparison returns %1 but %2 expected", prog,
-                                   Vbool, rules, type);
+                                   &Tbool, rules, type);
                        *ok = 0;
                }
                        *ok = 0;
                }
-               return Vbool;
+               return &Tbool;
 
 ###### interp binode cases
        case Less:
 
 ###### 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);
                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;
                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:
        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 */
        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,
                        type_err(c, "error: Arithmetic returns %1 but %2 expected", prog,
-                                  Vnum, rules, type);
+                                  &Tnum, rules, type);
                        *ok = 0;
                }
                        *ok = 0;
                }
-               return Vnum;
+               return &Tnum;
 
        case Concat:
 
        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,
                        type_err(c, "error: Concat returns %1 but %2 expected", prog,
-                                  Vstr, rules, type);
+                                  &Tstr, rules, type);
                        *ok = 0;
                }
                        *ok = 0;
                }
-               return Vstr;
+               return &Tstr;
 
        case Bracket:
                return propagate_types(b->right, c, ok, type, 0);
 
        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);
        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;
 
                rv.str = text_join(left.str, right.str);
                break;
 
@@ -2049,21 +2177,21 @@ list.
 ###### propagate binode cases
        case Block:
        {
 ###### 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)) {
                 * 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",
                                        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:
 
 ###### interp binode cases
        case Block:
-               while (rv.vtype == Vnone &&
+               while (rv.type == &Tnone &&
                       b) {
                        if (b->left)
                                rv = interp_exec(b->left);
                       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 */
 
        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
                break;
 
 ###### interp binode cases
@@ -2173,7 +2301,7 @@ same solution.
                                        sep = ' ';
                        } else if (sep)
                                eol = 0;
                                        sep = ' ';
                        } else if (sep)
                                eol = 0;
-               left.vtype = Vnone;
+               left.type = &Tnone;
                if (eol)
                        printf("\n");
                break;
                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
 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,
 
 ###### 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:
 
        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.",
                        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 {
                } 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);
                }
                                propagate_types(b->left, c, ok, t, 0);
                }
-               return Vnone;
+               return &Tnone;
 
                break;
 
 
                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 = interp_exec(b->right);
                free_value(v->val);
                v->val = right;
-               right.vtype = Vunknown;
+               right.type = NULL;
                break;
        }
 
                break;
        }
 
@@ -2677,8 +2805,8 @@ defined.
 ###### propagate exec cases
        case Xcond_statement:
        {
 ###### 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
                // 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;
 
                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;
                        *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) {
                        *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)
                                *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 */
                else {
                        /* Condpart must match case values, with bool permitted */
-                       t = Vunknown;
+                       t = NULL;
                        for (cp = cs->casepart;
                        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
                        // 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.
                                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;
                for (cp = cs->casepart;
-                    cp && type == Vunknown;
+                    cp && !type;
                     cp = cp->next)
                     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);
                        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
                                propagate_types(cp->action, c, ok, type, rules);
                        return type;
                } else
-                       return Vunknown;
+                       return NULL;
        }
 
 ###### interp exec cases
        }
 
 ###### interp exec cases
@@ -2751,17 +2879,17 @@ defined.
                        if (c->condpart)
                                cnd = interp_exec(c->condpart);
                        else
                        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;
                                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 (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);
                        }
                                        return v;
                                free_value(v);
                        }
@@ -2779,7 +2907,7 @@ defined.
                free_value(cnd);
                if (c->elsepart)
                        return interp_exec(c->elsepart);
                free_value(cnd);
                if (c->elsepart)
                        return interp_exec(c->elsepart);
-               v.vtype = Vnone;
+               v.type = &Tnone;
                return v;
        }
 
                return v;
        }
 
@@ -2862,28 +2990,28 @@ analysis is a bit more interesting at this level.
                        return 0;
                do {
                        ok = 1;
                        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);
                } 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;
                                v->var->where_set = b;
-                               val_init(&v->var->val, Vstr);
+                               v->var->val = val_init(&Tstr);
                        }
                }
                b = cast(binode, prog);
                do {
                        ok = 1;
                        }
                }
                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 */
                } 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;
        }
 
                return !!ok;
        }
 
@@ -2906,7 +3034,8 @@ analysis is a bit more interesting at this level.
                        }
                        al = cast(binode, al->right);
                        free_value(*vl);
                        }
                        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++;
                }
                                exit(1);
                        argv++;
                }