+ if (!v)
+ tok_err(c, "error: name undeclared", &$2);
+ else if (!v->constant)
+ tok_err(c, "error: array size must be a constant", &$2);
+
+ $0 = add_anon_type(c, &array_prototype, "array[%.*s]", $2.txt.len, $2.txt.txt);
+ $0->array.member = $<4;
+ $0->array.size = 0;
+ $0->array.vsize = v;
+ } }$
+
+###### Grammar
+ $*type
+ OptType -> Type ${ $0 = $<1; }$
+ | ${ $0 = NULL; }$
+
+###### formal type grammar
+
+ | [ IDENTIFIER :: OptType ] Type ${ {
+ struct variable *v = var_decl(c, $ID.txt);
+
+ v->type = $<OT;
+ v->constant = 1;
+ if (!v->type)
+ v->type = Tnum;
+ $0 = add_anon_type(c, &array_prototype, "array[var]");
+ $0->array.member = $<6;
+ $0->array.size = 0;
+ $0->array.unspec = 1;
+ $0->array.vsize = v;
+ } }$
+
+###### Binode types
+ Index,
+
+###### term grammar
+
+ | Term [ Expression ] ${ {
+ struct binode *b = new(binode);
+ b->op = Index;
+ b->left = $<1;
+ b->right = $<3;
+ $0 = b;
+ } }$
+
+###### print binode cases
+ case Index:
+ print_exec(b->left, -1, bracket);
+ printf("[");
+ print_exec(b->right, -1, bracket);
+ printf("]");
+ break;
+
+###### propagate binode cases
+ case Index:
+ /* left must be an array, right must be a number,
+ * result is the member type of the array
+ */
+ propagate_types(b->right, c, ok, Tnum, 0);
+ t = propagate_types(b->left, c, ok, NULL, rules & Rnoconstant);
+ if (!t || t->compat != array_compat) {
+ type_err(c, "error: %1 cannot be indexed", prog, t, 0, NULL);
+ return NULL;
+ } else {
+ if (!type_compat(type, t->array.member, rules)) {
+ type_err(c, "error: have %1 but need %2", prog,
+ t->array.member, rules, type);
+ }
+ return t->array.member;
+ }
+ break;
+
+###### interp binode cases
+ case Index: {
+ mpz_t q;
+ long i;
+ void *ptr;
+
+ lleft = linterp_exec(c, b->left, <ype);
+ right = interp_exec(c, b->right, &rtype);
+ mpz_init(q);
+ mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
+ i = mpz_get_si(q);
+ mpz_clear(q);
+
+ if (ltype->array.static_size)
+ ptr = lleft;
+ else
+ ptr = *(void**)lleft;
+ rvtype = ltype->array.member;
+ if (i >= 0 && i < ltype->array.size)
+ lrv = ptr + i * rvtype->size;
+ else
+ val_init(ltype->array.member, &rv); // UNSAFE
+ ltype = NULL;
+ break;
+ }
+
+#### Structs
+
+A `struct` is a data-type that contains one or more other data-types.
+It differs from an array in that each member can be of a different
+type, and they are accessed by name rather than by number. Thus you
+cannot choose an element by calculation, you need to know what you
+want up-front.
+
+The language makes no promises about how a given structure will be
+stored in memory - it is free to rearrange fields to suit whatever
+criteria seems important.
+
+Structs are declared separately from program code - they cannot be
+declared in-line in a variable declaration like arrays can. A struct
+is given a name and this name is used to identify the type - the name
+is not prefixed by the word `struct` as it would be in C.
+
+Structs are only treated as the same if they have the same name.
+Simply having the same fields in the same order is not enough. This
+might change once we can create structure initializers from a list of
+values.
+
+Each component datum is identified much like a variable is declared,
+with a name, one or two colons, and a type. The type cannot be omitted
+as there is no opportunity to deduce the type from usage. An initial
+value can be given following an equals sign, so
+
+##### Example: a struct type
+
+ struct complex:
+ x:number = 0
+ y:number = 0
+
+would declare a type called "complex" which has two number fields,
+each initialised to zero.
+
+Struct will need to be declared separately from the code that uses
+them, so we will need to be able to print out the declaration of a
+struct when reprinting the whole program. So a `print_type_decl` type
+function will be needed.
+
+###### type union fields
+
+ struct {
+ int nfields;
+ struct field {
+ struct text name;
+ struct type *type;
+ struct value *init;
+ int offset;
+ } *fields; // This is created when field_list is analysed.
+ struct fieldlist {
+ struct fieldlist *prev;
+ struct field f;
+ struct exec *init;
+ } *field_list; // This is created during parsing
+ } structure;
+
+###### type functions
+ void (*print_type_decl)(struct type *type, FILE *f);
+
+###### value functions
+
+ static void structure_init(struct type *type, struct value *val)
+ {
+ int i;
+
+ for (i = 0; i < type->structure.nfields; i++) {
+ struct value *v;
+ v = (void*) val->ptr + type->structure.fields[i].offset;
+ if (type->structure.fields[i].init)
+ dup_value(type->structure.fields[i].type,
+ type->structure.fields[i].init,
+ v);
+ else
+ val_init(type->structure.fields[i].type, v);
+ }
+ }
+
+ static void structure_free(struct type *type, struct value *val)
+ {
+ int i;
+
+ for (i = 0; i < type->structure.nfields; i++) {
+ struct value *v;
+ v = (void*)val->ptr + type->structure.fields[i].offset;
+ free_value(type->structure.fields[i].type, v);
+ }
+ }
+
+ static void free_fieldlist(struct fieldlist *f)
+ {
+ if (!f)
+ return;
+ free_fieldlist(f->prev);
+ free_exec(f->init);
+ free(f);
+ }
+
+ static void structure_free_type(struct type *t)
+ {
+ int i;
+ for (i = 0; i < t->structure.nfields; i++)
+ if (t->structure.fields[i].init) {
+ free_value(t->structure.fields[i].type,
+ t->structure.fields[i].init);
+ }
+ free(t->structure.fields);
+ free_fieldlist(t->structure.field_list);
+ }
+
+ static void structure_prepare_type(struct parse_context *c,
+ struct type *t, int parse_time)
+ {
+ int cnt = 0;
+ struct fieldlist *f;
+
+ if (!parse_time || t->structure.fields)
+ return;
+
+ for (f = t->structure.field_list; f; f=f->prev) {
+ int ok;
+ cnt += 1;
+
+ if (f->f.type->prepare_type)
+ f->f.type->prepare_type(c, f->f.type, 1);
+ if (f->init == NULL)
+ continue;
+ do {
+ ok = 1;
+ propagate_types(f->init, c, &ok, f->f.type, 0);
+ } while (ok == 2);
+ if (!ok)
+ c->parse_error = 1; // NOTEST
+ }
+
+ t->structure.nfields = cnt;
+ t->structure.fields = calloc(cnt, sizeof(struct field));
+ f = t->structure.field_list;
+ while (cnt > 0) {
+ int a = f->f.type->align;
+ cnt -= 1;
+ t->structure.fields[cnt] = f->f;
+ if (t->size & (a-1))
+ t->size = (t->size | (a-1)) + 1;
+ t->structure.fields[cnt].offset = t->size;
+ t->size += ((f->f.type->size - 1) | (a-1)) + 1;
+ if (a > t->align)
+ t->align = a;
+
+ if (f->init && !c->parse_error) {
+ struct value vl = interp_exec(c, f->init, NULL);
+ t->structure.fields[cnt].init =
+ global_alloc(c, f->f.type, NULL, &vl);
+ }
+
+ f = f->prev;
+ }
+ }
+
+ static struct type structure_prototype = {
+ .init = structure_init,
+ .free = structure_free,
+ .free_type = structure_free_type,
+ .print_type_decl = structure_print_type,
+ .prepare_type = structure_prepare_type,
+ };
+
+###### exec type
+ Xfieldref,
+
+###### ast
+ struct fieldref {
+ struct exec;
+ struct exec *left;
+ int index;
+ struct text name;
+ };
+
+###### free exec cases
+ case Xfieldref:
+ free_exec(cast(fieldref, e)->left);
+ free(e);
+ break;
+
+###### declare terminals
+ $TERM struct .
+
+###### term grammar
+
+ | Term . IDENTIFIER ${ {
+ struct fieldref *fr = new_pos(fieldref, $2);
+ fr->left = $<1;
+ fr->name = $3.txt;
+ fr->index = -2;
+ $0 = fr;
+ } }$
+
+###### print exec cases
+
+ case Xfieldref:
+ {
+ struct fieldref *f = cast(fieldref, e);
+ print_exec(f->left, -1, bracket);
+ printf(".%.*s", f->name.len, f->name.txt);
+ break;
+ }
+
+###### ast functions
+ static int find_struct_index(struct type *type, struct text field)
+ {
+ int i;
+ for (i = 0; i < type->structure.nfields; i++)
+ if (text_cmp(type->structure.fields[i].name, field) == 0)
+ return i;
+ return -1;
+ }
+
+###### propagate exec cases
+
+ case Xfieldref:
+ {
+ struct fieldref *f = cast(fieldref, prog);
+ struct type *st = propagate_types(f->left, c, ok, NULL, 0);
+
+ if (!st)
+ type_err(c, "error: unknown type for field access", f->left, // UNTESTED
+ NULL, 0, NULL);
+ else if (st->init != structure_init)
+ type_err(c, "error: field reference attempted on %1, not a struct",
+ f->left, st, 0, NULL);
+ else if (f->index == -2) {
+ f->index = find_struct_index(st, f->name);
+ if (f->index < 0)
+ type_err(c, "error: cannot find requested field in %1",
+ f->left, st, 0, NULL);
+ }
+ if (f->index >= 0) {
+ struct type *ft = st->structure.fields[f->index].type;
+ if (!type_compat(type, ft, rules))
+ type_err(c, "error: have %1 but need %2", prog,
+ ft, rules, type);
+ return ft;
+ }
+ break;
+ }
+
+###### interp exec cases
+ case Xfieldref:
+ {
+ struct fieldref *f = cast(fieldref, e);
+ struct type *ltype;
+ struct value *lleft = linterp_exec(c, f->left, <ype);
+ lrv = (void*)lleft->ptr + ltype->structure.fields[f->index].offset;
+ rvtype = ltype->structure.fields[f->index].type;
+ break;
+ }
+
+###### top level grammar
+ DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ {
+ struct type *t =
+ add_type(c, $2.txt, &structure_prototype);
+ t->structure.field_list = $<FB;
+ } }$
+
+ $*fieldlist
+ FieldBlock -> { IN OptNL FieldLines OUT OptNL } ${ $0 = $<FL; }$
+ | { SimpleFieldList } ${ $0 = $<SFL; }$
+ | IN OptNL FieldLines OUT ${ $0 = $<FL; }$
+ | SimpleFieldList EOL ${ $0 = $<SFL; }$
+
+ FieldLines -> SimpleFieldList Newlines ${ $0 = $<SFL; }$
+ | FieldLines SimpleFieldList Newlines ${
+ $SFL->prev = $<FL;
+ $0 = $<SFL;
+ }$