+ ## interp exec cases
+ }
+ if (rvtype) {
+ ret.lval = lrv;
+ ret.rval = rv;
+ ret.type = rvtype;
+ }
+ ## interp exec cleanup
+ return ret;
+ }
+
+### Types
+
+Values come in a wide range of types, with more likely to be added.
+Each type needs to be able to print its own values (for convenience at
+least) as well as to compare two values, at least for equality and
+possibly for order. For now, values might need to be duplicated and
+freed, though eventually such manipulations will be better integrated
+into the language.
+
+Rather than requiring every numeric type to support all numeric
+operations (add, multiply, etc), we allow types to be able to present
+as one of a few standard types: integer, float, and fraction. The
+existence of these conversion functions eventually enable types to
+determine if they are compatible with other types, though such types
+have not yet been implemented.
+
+Named type are stored in a simple linked list. Objects of each type are
+"values" which are often passed around by value.
+
+There are both explicitly named types, and anonymous types. Anonymous
+cannot be accessed by name, but are used internally and have a name
+which might be reported in error messages.
+
+###### ast
+
+ struct value {
+ union {
+ char ptr[1];
+ ## value union fields
+ };
+ };
+
+###### ast late
+ struct type {
+ struct text name;
+ struct type *next;
+ struct token first_use;
+ int size, align;
+ int anon;
+ void (*init)(struct type *type, struct value *val);
+ int (*prepare_type)(struct parse_context *c, struct type *type, int parse_time);
+ void (*print)(struct type *type, struct value *val, FILE *f);
+ void (*print_type)(struct type *type, FILE *f);
+ int (*cmp_order)(struct type *t1, struct type *t2,
+ struct value *v1, struct value *v2);
+ int (*cmp_eq)(struct type *t1, struct type *t2,
+ struct value *v1, struct value *v2);
+ void (*dup)(struct type *type, struct value *vold, struct value *vnew);
+ int (*test)(struct type *type, struct value *val);
+ void (*free)(struct type *type, struct value *val);
+ void (*free_type)(struct type *t);
+ long long (*to_int)(struct value *v);
+ double (*to_float)(struct value *v);
+ int (*to_mpq)(mpq_t *q, struct value *v);
+ ## type functions
+ union {
+ ## type union fields
+ };
+ };
+
+###### parse context
+
+ struct type *typelist;
+
+###### includes
+ #include <stdarg.h>
+
+###### ast functions
+
+ static struct type *find_type(struct parse_context *c, struct text s)
+ {
+ struct type *t = c->typelist;
+
+ while (t && (t->anon ||
+ text_cmp(t->name, s) != 0))
+ t = t->next;
+ return t;
+ }
+
+ static struct type *_add_type(struct parse_context *c, struct text s,
+ struct type *proto, int anon)
+ {
+ struct type *n;
+
+ n = calloc(1, sizeof(*n));
+ if (proto)
+ *n = *proto;
+ else
+ n->size = -1;
+ n->name = s;
+ n->anon = anon;
+ n->next = c->typelist;
+ c->typelist = n;
+ return n;
+ }
+
+ static struct type *add_type(struct parse_context *c, struct text s,
+ struct type *proto)
+ {
+ return _add_type(c, s, proto, 0);
+ }
+
+ static struct type *add_anon_type(struct parse_context *c,
+ struct type *proto, char *name, ...)
+ {
+ struct text t;
+ va_list ap;
+
+ va_start(ap, name);
+ vasprintf(&t.txt, name, ap);
+ va_end(ap);
+ t.len = strlen(t.txt);
+ return _add_type(c, t, proto, 1);
+ }
+
+ static struct type *find_anon_type(struct parse_context *c,
+ struct type *proto, char *name, ...)
+ {
+ struct type *t = c->typelist;
+ struct text nm;
+ va_list ap;
+
+ va_start(ap, name);
+ vasprintf(&nm.txt, name, ap);
+ va_end(ap);
+ nm.len = strlen(name);
+
+ while (t && (!t->anon ||
+ text_cmp(t->name, nm) != 0))
+ t = t->next;
+ if (t) {
+ free(nm.txt);
+ return t;
+ }
+ return _add_type(c, nm, proto, 1);
+ }
+
+ static void free_type(struct type *t)
+ {
+ /* The type is always a reference to something in the
+ * context, so we don't need to free anything.
+ */
+ }
+
+ static void free_value(struct type *type, struct value *v)
+ {
+ if (type && v) {
+ type->free(type, v);
+ memset(v, 0x5a, type->size);
+ }
+ }
+
+ static void type_print(struct type *type, FILE *f)
+ {
+ if (!type)
+ fputs("*unknown*type*", f); // NOTEST
+ else if (type->name.len && !type->anon)
+ fprintf(f, "%.*s", type->name.len, type->name.txt);
+ else if (type->print_type)
+ type->print_type(type, f);
+ else if (type->name.len && type->anon)
+ fprintf(f, "\"%.*s\"", type->name.len, type->name.txt);
+ else
+ fputs("*invalid*type*", f); // NOTEST
+ }
+
+ static void val_init(struct type *type, struct value *val)
+ {
+ if (type && type->init)
+ type->init(type, val);
+ }
+
+ static void dup_value(struct type *type,
+ struct value *vold, struct value *vnew)
+ {
+ if (type && type->dup)
+ type->dup(type, vold, vnew);
+ }
+
+ static int value_cmp(struct type *tl, struct type *tr,
+ struct value *left, struct value *right)
+ {
+ if (tl && tl->cmp_order)
+ return tl->cmp_order(tl, tr, left, right);
+ if (tl && tl->cmp_eq)
+ return tl->cmp_eq(tl, tr, left, right);
+ return -1; // NOTEST
+ }
+
+ static void print_value(struct type *type, struct value *v, FILE *f)
+ {
+ if (type && type->print)
+ type->print(type, v, f);
+ else
+ fprintf(f, "*Unknown*"); // NOTEST
+ }
+
+ static void prepare_types(struct parse_context *c)
+ {
+ struct type *t;
+ int retry = 1;
+ enum { none, some, cannot } progress = none;
+
+ while (retry) {
+ retry = 0;
+
+ for (t = c->typelist; t; t = t->next) {
+ if (t->size < 0)
+ tok_err(c, "error: type used but not declared",
+ &t->first_use);
+ if (t->size == 0 && t->prepare_type) {
+ if (t->prepare_type(c, t, 1))
+ progress = some;
+ else if (progress == cannot)
+ tok_err(c, "error: type has recursive definition",
+ &t->first_use);
+ else
+ retry = 1;
+ }
+ }
+ switch (progress) {
+ case cannot:
+ retry = 0; break;
+ case none:
+ progress = cannot; break;
+ case some:
+ progress = none; break;
+ }
+ }