+Each `exec` can return a value combined with a type in `struct lrval`.
+The type may be `Tnone` but must be non-NULL. Some `exec`s will return
+the location of a value, which can be updated, in `lval`. Others will
+set `lval` to NULL indicating that there is a value of appropriate type
+in `rval`.
+
+###### core functions
+
+ struct lrval {
+ struct type *type;
+ struct value rval, *lval;
+ };
+
+ static struct lrval _interp_exec(struct parse_context *c, struct exec *e);
+
+ static struct value interp_exec(struct parse_context *c, struct exec *e,
+ struct type **typeret)
+ {
+ struct lrval ret = _interp_exec(c, e);
+
+ if (!ret.type) abort();
+ if (typeret)
+ *typeret = ret.type;
+ if (ret.lval)
+ dup_value(ret.type, ret.lval, &ret.rval);
+ return ret.rval;
+ }
+
+ static struct value *linterp_exec(struct parse_context *c, struct exec *e,
+ struct type **typeret)
+ {
+ struct lrval ret = _interp_exec(c, e);
+
+ if (ret.lval)
+ *typeret = ret.type;
+ else
+ free_value(ret.type, &ret.rval);
+ return ret.lval;
+ }
+
+ static struct lrval _interp_exec(struct parse_context *c, struct exec *e)
+ {
+ struct lrval ret;
+ struct value rv = {}, *lrv = NULL;
+ struct type *rvtype;
+
+ rvtype = ret.type = Tnone;
+ if (!e) {
+ ret.lval = lrv;
+ ret.rval = rv;
+ return ret;
+ }
+
+ switch(e->type) {
+ case Xbinode:
+ {
+ struct binode *b = cast(binode, e);
+ struct value left, right, *lleft;
+ struct type *ltype, *rtype;
+ ltype = rtype = Tnone;
+ switch (b->op) {
+ ## interp binode cases
+ }
+ free_value(ltype, &left);
+ free_value(rtype, &right);
+ break;
+ }
+ ## interp exec cases
+ }
+ ret.lval = lrv;
+ ret.rval = rv;
+ ret.type = rvtype;
+ ## interp exec cleanup
+ return ret;
+ }
+
+### Complex types
+
+Now that we have the shape of the interpreter in place we can add some
+complex types and connected them in to the data structures and the
+different phases of parse, analyse, print, interpret.
+
+Thus far we have arrays and structs.
+
+#### Arrays
+
+Arrays can be declared by giving a size and a type, as `[size]type' so
+`freq:[26]number` declares `freq` to be an array of 26 numbers. The
+size can be either a literal number, or a named constant. Some day an
+arbitrary expression will be supported.
+
+As a formal parameter to a function, the array can be declared with a
+new variable as the size: `name:[size::number]string`. The `size`
+variable is set to the size of the array and must be a constant. As
+`number` is the only supported type, it can be left out:
+`name:[size::]string`.
+
+Arrays cannot be assigned. When pointers are introduced we will also
+introduce array slices which can refer to part or all of an array -
+the assignment syntax will create a slice. For now, an array can only
+ever be referenced by the name it is declared with. It is likely that
+a "`copy`" primitive will eventually be define which can be used to
+make a copy of an array with controllable recursive depth.
+
+For now we have two sorts of array, those with fixed size either because
+it is given as a literal number or because it is a struct member (which
+cannot have a runtime-changing size), and those with a size that is
+determined at runtime - local variables with a const size. The former
+have their size calculated at parse time, the latter at run time.
+
+For the latter type, the `size` field of the type is the size of a
+pointer, and the array is reallocated every time it comes into scope.
+
+We differentiate struct fields with a const size from local variables
+with a const size by whether they are prepared at parse time or not.
+
+###### type union fields
+
+ struct {
+ int unspec; // size is unspecified - vsize must be set.
+ short size;
+ short static_size;
+ struct variable *vsize;
+ struct type *member;
+ } array;
+
+###### value union fields
+ void *array; // used if not static_size
+
+###### value functions
+
+ static void array_prepare_type(struct parse_context *c, struct type *type,
+ int parse_time)
+ {
+ struct value *vsize;
+ mpz_t q;
+ if (!type->array.vsize || type->array.static_size)
+ return;
+
+ vsize = var_value(c, type->array.vsize);
+ mpz_init(q);
+ mpz_tdiv_q(q, mpq_numref(vsize->num), mpq_denref(vsize->num));
+ type->array.size = mpz_get_si(q);
+ mpz_clear(q);
+
+ if (parse_time) {
+ type->array.static_size = 1;
+ type->size = type->array.size * type->array.member->size;
+ type->align = type->array.member->align;
+ }
+ }
+
+ static void array_init(struct type *type, struct value *val)
+ {
+ int i;
+ void *ptr = val->ptr;
+
+ if (!val)
+ return; // NOTEST
+ if (!type->array.static_size) {
+ val->array = calloc(type->array.size,
+ type->array.member->size);
+ ptr = val->array;
+ }
+ for (i = 0; i < type->array.size; i++) {
+ struct value *v;
+ v = (void*)ptr + i * type->array.member->size;
+ val_init(type->array.member, v);
+ }
+ }
+
+ static void array_free(struct type *type, struct value *val)
+ {
+ int i;
+ void *ptr = val->ptr;
+
+ if (!type->array.static_size)
+ ptr = val->array;
+ for (i = 0; i < type->array.size; i++) {
+ struct value *v;
+ v = (void*)ptr + i * type->array.member->size;
+ free_value(type->array.member, v);
+ }
+ if (!type->array.static_size)
+ free(ptr);
+ }
+
+ static int array_compat(struct type *require, struct type *have)
+ {
+ if (have->compat != require->compat)
+ return 0;
+ /* Both are arrays, so we can look at details */
+ if (!type_compat(require->array.member, have->array.member, 0))
+ return 0;
+ if (have->array.unspec && require->array.unspec) {
+ if (have->array.vsize && require->array.vsize &&
+ have->array.vsize != require->array.vsize) // UNTESTED
+ /* sizes might not be the same */
+ return 0; // UNTESTED
+ return 1;
+ }
+ if (have->array.unspec || require->array.unspec)
+ return 1; // UNTESTED
+ if (require->array.vsize == NULL && have->array.vsize == NULL)
+ return require->array.size == have->array.size;
+
+ return require->array.vsize == have->array.vsize; // UNTESTED
+ }
+
+ static void array_print_type(struct type *type, FILE *f)
+ {
+ fputs("[", f);
+ if (type->array.vsize) {
+ struct binding *b = type->array.vsize->name;
+ fprintf(f, "%.*s%s]", b->name.len, b->name.txt,
+ type->array.unspec ? "::" : "");
+ } else
+ fprintf(f, "%d]", type->array.size);
+ type_print(type->array.member, f);
+ }
+
+ static struct type array_prototype = {
+ .init = array_init,
+ .prepare_type = array_prepare_type,
+ .print_type = array_print_type,
+ .compat = array_compat,
+ .free = array_free,
+ .size = sizeof(void*),
+ .align = sizeof(void*),
+ };
+
+###### declare terminals
+ $TERM [ ]
+
+###### type grammar
+
+ | [ NUMBER ] Type ${ {
+ char tail[3];
+ mpq_t num;
+ struct text noname = { "", 0 };
+ struct type *t;
+
+ $0 = t = add_type(c, noname, &array_prototype);
+ t->array.member = $<4;
+ t->array.vsize = NULL;
+ if (number_parse(num, tail, $2.txt) == 0)
+ tok_err(c, "error: unrecognised number", &$2);
+ else if (tail[0])
+ tok_err(c, "error: unsupported number suffix", &$2);
+ else {
+ t->array.size = mpz_get_ui(mpq_numref(num));
+ if (mpz_cmp_ui(mpq_denref(num), 1) != 0) {
+ tok_err(c, "error: array size must be an integer",
+ &$2);
+ } else if (mpz_cmp_ui(mpq_numref(num), 1UL << 30) >= 0)
+ tok_err(c, "error: array size is too large",
+ &$2);
+ mpq_clear(num);
+ }
+ t->array.static_size = 1;
+ t->size = t->array.size * t->array.member->size;
+ t->align = t->array.member->align;
+ } }$
+
+ | [ IDENTIFIER ] Type ${ {
+ struct variable *v = var_ref(c, $2.txt);
+ struct text noname = { "", 0 };
+
+ 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_type(c, noname, &array_prototype);
+ $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);
+ struct text noname = { "", 0 };
+
+ v->type = $<OT;
+ v->constant = 1;
+ if (!v->type)
+ v->type = Tnum;
+ $0 = add_type(c, noname, &array_prototype);
+ $0->array.member = $<6;
+ $0->array.size = 0;
+ $0->array.unspec = 1;
+ $0->array.vsize = v;
+ } }$
+
+###### Binode types
+ Index,
+
+###### variable grammar
+
+ | Variable [ 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);
+ 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;
+ } 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 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);
+ }
+
+ static struct type structure_prototype = {
+ .init = structure_init,
+ .free = structure_free,
+ .free_type = structure_free_type,
+ .print_type_decl = structure_print_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 .
+
+###### variable grammar
+
+ | Variable . 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;
+ }
+
+###### ast
+ struct fieldlist {
+ struct fieldlist *prev;
+ struct field f;
+ };
+
+###### ast functions
+ static void free_fieldlist(struct fieldlist *f)
+ {
+ if (!f)
+ return;
+ free_fieldlist(f->prev);
+ if (f->f.init) {
+ free_value(f->f.type, f->f.init); // UNTESTED
+ free(f->f.init); // UNTESTED
+ }
+ free(f);
+ }
+
+###### top level grammar
+ DeclareStruct -> struct IDENTIFIER FieldBlock Newlines ${ {
+ struct type *t =
+ add_type(c, $2.txt, &structure_prototype);
+ int cnt = 0;
+ struct fieldlist *f;
+
+ for (f = $3; f; f=f->prev)
+ cnt += 1;
+
+ t->structure.nfields = cnt;
+ t->structure.fields = calloc(cnt, sizeof(struct field));
+ f = $3;
+ while (cnt > 0) {
+ int a = f->f.type->align;
+ cnt -= 1;
+ t->structure.fields[cnt] = f->f;
+ if (t->size & (a-1))
+ t->size = (t->size | (a-1)) + 1;
+ t->structure.fields[cnt].offset = t->size;
+ t->size += ((f->f.type->size - 1) | (a-1)) + 1;
+ if (a > t->align)
+ t->align = a;
+ f->f.init = NULL;
+ f = f->prev;
+ }
+ } }$
+
+ $*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;
+ }$
+
+ SimpleFieldList -> Field ${ $0 = $<F; }$
+ | SimpleFieldList ; Field ${
+ $F->prev = $<SFL;
+ $0 = $<F;
+ }$
+ | SimpleFieldList ; ${
+ $0 = $<SFL;
+ }$
+ | ERROR ${ tok_err(c, "Syntax error in struct field", &$1); }$
+
+ Field -> IDENTIFIER : Type = Expression ${ {
+ int ok; // UNTESTED
+
+ $0 = calloc(1, sizeof(struct fieldlist));
+ $0->f.name = $1.txt;
+ $0->f.type = $<3;
+ $0->f.init = NULL;
+ do {
+ ok = 1;
+ propagate_types($<5, c, &ok, $3, 0);
+ } while (ok == 2);
+ if (!ok)
+ c->parse_error = 1; // UNTESTED
+ else {
+ struct value vl = interp_exec(c, $5, NULL);
+ $0->f.init = global_alloc(c, $0->f.type, NULL, &vl);
+ }
+ } }$
+ | IDENTIFIER : Type ${
+ $0 = calloc(1, sizeof(struct fieldlist));
+ $0->f.name = $1.txt;
+ $0->f.type = $<3;
+ if ($0->f.type->prepare_type)
+ $0->f.type->prepare_type(c, $0->f.type, 1);
+ }$
+
+###### forward decls
+ static void structure_print_type(struct type *t, FILE *f);
+
+###### value functions
+ static void structure_print_type(struct type *t, FILE *f) // UNTESTED
+ { // UNTESTED
+ int i; // UNTESTED
+
+ fprintf(f, "struct %.*s\n", t->name.len, t->name.txt);
+
+ for (i = 0; i < t->structure.nfields; i++) {
+ struct field *fl = t->structure.fields + i;
+ fprintf(f, " %.*s : ", fl->name.len, fl->name.txt);
+ type_print(fl->type, f);
+ if (fl->type->print && fl->init) {
+ fprintf(f, " = ");
+ if (fl->type == Tstr)
+ fprintf(f, "\""); // UNTESTED
+ print_value(fl->type, fl->init);
+ if (fl->type == Tstr)
+ fprintf(f, "\""); // UNTESTED
+ }
+ printf("\n");
+ }
+ }
+
+###### print type decls
+ { // UNTESTED
+ struct type *t; // UNTESTED
+ int target = -1;
+
+ while (target != 0) {
+ int i = 0;
+ for (t = context.typelist; t ; t=t->next)
+ if (t->print_type_decl && !t->check_args) {
+ i += 1;
+ if (i == target)
+ break;
+ }
+
+ if (target == -1) {
+ target = i;
+ } else {
+ t->print_type_decl(t, stdout);
+ target -= 1;
+ }
+ }
+ }
+
+#### Functions
+
+A function is a chunk of code which can be passed parameters and can
+return results (though results are not yet implemented). Each function
+has a type which includes the set of parameters and the return value.
+As yet these types cannot be declared separately from the function
+itself.
+
+The parameters can be specified either in parentheses as a list, such as
+
+##### Example: function 1
+
+ func main(av:[ac::number]string)
+ code block
+
+or as an indented list of one parameter per line
+
+##### Example: function 2
+
+ func main
+ argv:[argc::number]string
+ do
+ code block
+
+For constructing these lists we use a `List` binode, which will be
+further detailed when Expression Lists are introduced.
+
+###### type union fields
+
+ struct {
+ struct binode *params;
+ int local_size;
+ } function;
+
+###### value union fields
+ struct exec *function;
+
+###### type functions
+ void (*check_args)(struct parse_context *c, int *ok,
+ struct type *require, struct exec *args);
+
+###### value functions
+
+ static void function_free(struct type *type, struct value *val)
+ {
+ free_exec(val->function);
+ val->function = NULL;
+ }
+
+ static int function_compat(struct type *require, struct type *have)
+ {
+ // FIXME can I do anything here yet?
+ return 0;
+ }
+
+ static void function_check_args(struct parse_context *c, int *ok,
+ struct type *require, struct exec *args)
+ {
+ /* This should be 'compat', but we don't have a 'tuple' type to
+ * hold the type of 'args'
+ */
+ struct binode *arg = cast(binode, args);
+ struct binode *param = require->function.params;
+
+ while (param) {
+ struct var *pv = cast(var, param->left);
+ if (!arg) {
+ type_err(c, "error: insufficient arguments to function.",
+ args, NULL, 0, NULL);
+ break;
+ }
+ *ok = 1;
+ propagate_types(arg->left, c, ok, pv->var->type, 0);
+ param = cast(binode, param->right);
+ arg = cast(binode, arg->right);
+ }
+ if (arg)
+ type_err(c, "error: too many arguments to function.",
+ args, NULL, 0, NULL);
+ }
+
+ static void function_print(struct type *type, struct value *val)
+ {
+ print_exec(val->function, 1, 0);
+ }
+
+ static void function_print_type_decl(struct type *type, FILE *f)
+ {
+ struct binode *b;
+ fprintf(f, "(");
+ for (b = type->function.params; b; b = cast(binode, b->right)) {
+ struct variable *v = cast(var, b->left)->var;
+ fprintf(f, "%.*s%s", v->name->name.len, v->name->name.txt,
+ v->constant ? "::" : ":");
+ type_print(v->type, f);
+ if (b->right)
+ fprintf(f, "; ");
+ }
+ fprintf(f, ")\n");
+ }
+
+ static void function_free_type(struct type *t)
+ {
+ free_exec(t->function.params);
+ }
+
+ static struct type function_prototype = {
+ .size = sizeof(void*),
+ .align = sizeof(void*),
+ .free = function_free,
+ .compat = function_compat,
+ .check_args = function_check_args,
+ .print = function_print,
+ .print_type_decl = function_print_type_decl,
+ .free_type = function_free_type,
+ };
+
+###### declare terminals
+
+ $TERM func
+
+###### Binode types
+ List,
+
+###### Grammar
+
+ $*variable
+ FuncName -> IDENTIFIER ${ {
+ struct variable *v = var_decl(c, $1.txt);
+ struct var *e = new_pos(var, $1);
+ e->var = v;
+ if (v) {
+ v->where_decl = e;
+ $0 = v;
+ } else {
+ v = var_ref(c, $1.txt);
+ e->var = v;
+ type_err(c, "error: function '%v' redeclared",
+ e, NULL, 0, NULL);
+ type_err(c, "info: this is where '%v' was first declared",
+ v->where_decl, NULL, 0, NULL);
+ free_exec(e);
+ }
+ } }$