+###### interp exec cases
+ case Xvar:
+ {
+ struct var *var = cast(var, e);
+ struct variable *v = var->var;
+
+ v = v->merged;
+ lrv = var_value(c, v);
+ rvtype = v->type;
+ break;
+ }
+
+###### ast functions
+
+ static void free_var(struct var *v)
+ {
+ free(v);
+ }
+
+###### free exec cases
+ case Xvar: free_var(cast(var, e)); break;
+
+
+### 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.
+
+Being "complex" the language will naturally have syntax to access
+specifics of objects of these types. These will fit into the grammar as
+"Terms" which are the things that are combined with various operators to
+form "Expression". Where a Term is formed by some operation on another
+Term, the subordinate Term will always come first, so for example a
+member of an array will be expressed as the Term for the array followed
+by an index in square brackets. The strict rule of using postfix
+operations makes precedence irrelevant within terms. To provide a place
+to put the grammar for each terms of each type, we will start out by
+introducing the "Term" grammar production, with contains at least a
+simple "Value" (to be explained later).
+
+###### Grammar
+ $*exec
+ Term -> Value ${ $0 = $<1; }$
+ | Variable ${ $0 = $<1; }$
+ ## term grammar
+
+Thus far the complex types we have are 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 if (type->array.size)
+ fprintf(f, "%d]", type->array.size);
+ else
+ fprintf(f, "]");
+ 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 type *t;
+ int elements = 0;
+
+ 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);
+ mpq_clear(num);
+ } else {
+ elements = 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);
+ }
+
+ $0 = t = add_anon_type(c, &array_prototype, "array[%d]", elements );
+ t->array.size = elements;
+ t->array.member = $<4;
+ t->array.vsize = NULL;
+ 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);
+
+ 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;
+ } 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 .
+
+###### 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;
+ }
+
+###### 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;
+
+ $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)
+ {
+ int i;
+
+ 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, f);
+ if (fl->type == Tstr)
+ fprintf(f, "\""); // UNTESTED
+ }
+ fprintf(f, "\n");
+ }
+ }
+
+###### print type decls
+ {
+ struct type *t;
+ int target = -1;
+
+ while (target != 0) {
+ int i = 0;
+ for (t = context.typelist; t ; t=t->next)
+ if (!t->anon && 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. 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 ';' separated
+list, such as
+
+##### Example: function 1
+
+ func main(av:[ac::number]string; env:[envc::number]string)
+ code block
+
+or as an indented list of one parameter per line (though each line can
+be a ';' separated list)
+
+##### Example: function 2
+
+ func main
+ argv:[argc::number]string
+ env:[envc::number]string
+ do
+ code block
+
+In the first case a return type can follow the parentheses after a colon,
+in the second it is given on a line starting with the word `return`.
+
+##### Example: functions that return
+
+ func add(a:number; b:number): number
+ code block
+
+ func catenate
+ a: string
+ b: string
+ return string
+ do
+ code block
+
+Rather than returning a type, the function can specify a set of local
+variables to return as a struct. The values of these variables when the
+function exits will be provided to the caller. For this the return type
+is replaced with a block of result declarations, either in parentheses
+or bracketed by `return` and `do`.
+
+##### Example: functions returning multiple variables
+
+ func to_cartesian(rho:number; theta:number):(x:number; y:number)
+ x = .....
+ y = .....
+
+ func to_polar
+ x:number; y:number
+ return
+ rho:number
+ theta:number
+ do
+ rho = ....
+ theta = ....
+
+For constructing the lists we use a `List` binode, which will be
+further detailed when Expression Lists are introduced.
+
+###### type union fields
+
+ struct {
+ struct binode *params;
+ struct type *return_type;
+ struct variable *scope;
+ int inline_result; // return value is at start of 'local'
+ 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, FILE *f)
+ {
+ 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, ")");
+ if (type->function.return_type != Tnone) {
+ fprintf(f, ":");
+ if (type->function.inline_result) {
+ int i;
+ struct type *t = type->function.return_type;
+ fprintf(f, " (");
+ for (i = 0; i < t->structure.nfields; i++) {
+ struct field *fl = t->structure.fields + i;
+ if (i)
+ fprintf(f, "; ");
+ fprintf(f, "%.*s:", fl->name.len, fl->name.txt);
+ type_print(fl->type, f);
+ }
+ fprintf(f, ")");
+ } else
+ type_print(type->function.return_type, 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);
+ }
+ } }$
+
+ $*binode
+ Args -> ArgsLine NEWLINE ${ $0 = $<AL; }$
+ | Args ArgsLine NEWLINE ${ {
+ struct binode *b = $<AL;
+ struct binode **bp = &b;
+ while (*bp)
+ bp = (struct binode **)&(*bp)->left;
+ *bp = $<A;
+ $0 = b;
+ } }$
+
+ ArgsLine -> ${ $0 = NULL; }$
+ | Varlist ${ $0 = $<1; }$
+ | Varlist ; ${ $0 = $<1; }$
+
+ Varlist -> Varlist ; ArgDecl ${
+ $0 = new(binode);
+ $0->op = List;
+ $0->left = $<Vl;
+ $0->right = $<AD;
+ }$
+ | ArgDecl ${
+ $0 = new(binode);
+ $0->op = List;
+ $0->left = NULL;
+ $0->right = $<AD;
+ }$
+
+ $*var
+ ArgDecl -> IDENTIFIER : FormalType ${ {
+ struct variable *v = var_decl(c, $1.txt);
+ $0 = new(var);
+ $0->var = v;
+ v->type = $<FT;
+ } }$
+
+##### Function calls
+
+A function call can appear either as an expression or as a statement.
+We use a new 'Funcall' binode type to link the function with a list of
+arguments, form with the 'List' nodes.
+
+We have already seen the "Term" which is how a function call can appear
+in an expression. To parse a function call into a statement we include
+it in the "SimpleStatement Grammar" which will be described later.
+
+###### Binode types
+ Funcall,
+
+###### term grammar
+ | Term ( ExpressionList ) ${ {
+ struct binode *b = new(binode);
+ b->op = Funcall;
+ b->left = $<T;
+ b->right = reorder_bilist($<EL);
+ $0 = b;
+ } }$
+ | Term ( ) ${ {
+ struct binode *b = new(binode);
+ b->op = Funcall;
+ b->left = $<T;
+ b->right = NULL;
+ $0 = b;
+ } }$
+
+###### SimpleStatement Grammar
+
+ | Term ( ExpressionList ) ${ {
+ struct binode *b = new(binode);
+ b->op = Funcall;
+ b->left = $<T;
+ b->right = reorder_bilist($<EL);
+ $0 = b;
+ } }$
+
+###### print binode cases
+
+ case Funcall:
+ do_indent(indent, "");
+ print_exec(b->left, -1, bracket);
+ printf("(");
+ for (b = cast(binode, b->right); b; b = cast(binode, b->right)) {
+ if (b->left) {
+ printf(" ");
+ print_exec(b->left, -1, bracket);
+ if (b->right)
+ printf(",");
+ }
+ }
+ printf(")");
+ if (indent >= 0)
+ printf("\n");
+ break;
+
+###### propagate binode cases
+
+ case Funcall: {
+ /* Every arg must match formal parameter, and result
+ * is return type of function
+ */
+ struct binode *args = cast(binode, b->right);
+ struct var *v = cast(var, b->left);
+
+ if (!v->var->type || v->var->type->check_args == NULL) {
+ type_err(c, "error: attempt to call a non-function.",
+ prog, NULL, 0, NULL);
+ return NULL;
+ }
+ v->var->type->check_args(c, ok, v->var->type, args);
+ return v->var->type->function.return_type;
+ }
+
+###### interp binode cases
+
+ case Funcall: {
+ struct var *v = cast(var, b->left);
+ struct type *t = v->var->type;
+ void *oldlocal = c->local;
+ int old_size = c->local_size;
+ void *local = calloc(1, t->function.local_size);
+ struct value *fbody = var_value(c, v->var);
+ struct binode *arg = cast(binode, b->right);
+ struct binode *param = t->function.params;
+
+ while (param) {
+ struct var *pv = cast(var, param->left);
+ struct type *vtype = NULL;
+ struct value val = interp_exec(c, arg->left, &vtype);
+ struct value *lval;
+ c->local = local; c->local_size = t->function.local_size;
+ lval = var_value(c, pv->var);
+ c->local = oldlocal; c->local_size = old_size;
+ memcpy(lval, &val, vtype->size);
+ param = cast(binode, param->right);
+ arg = cast(binode, arg->right);
+ }
+ c->local = local; c->local_size = t->function.local_size;
+ if (t->function.inline_result && dtype) {
+ _interp_exec(c, fbody->function, NULL, NULL);
+ memcpy(dest, local, dtype->size);
+ rvtype = ret.type = NULL;
+ } else
+ rv = interp_exec(c, fbody->function, &rvtype);
+ c->local = oldlocal; c->local_size = old_size;
+ free(local);
+ break;
+ }
+
+## Complex executables: statements and expressions
+
+Now that we have types and values and variables and most of the basic
+Terms which provide access to these, we can explore the more complex
+code that combine all of these to get useful work done. Specifically
+statements and expressions.
+
+Expressions are various combinations of Terms. We will use operator
+precedence to ensure correct parsing. The simplest Expression is just a
+Term - others will follow.
+
+###### Grammar
+
+ $*exec
+ Expression -> Term ${ $0 = $<Term; }$
+ ## expression grammar
+
+### Expressions: Conditional
+
+Our first user of the `binode` will be conditional expressions, which
+is a bit odd as they actually have three components. That will be
+handled by having 2 binodes for each expression. The conditional
+expression is the lowest precedence operator which is why we define it
+first - to start the precedence list.
+
+Conditional expressions are of the form "value `if` condition `else`
+other_value". They associate to the right, so everything to the right
+of `else` is part of an else value, while only a higher-precedence to
+the left of `if` is the if values. Between `if` and `else` there is no
+room for ambiguity, so a full conditional expression is allowed in
+there.
+
+###### Binode types
+ CondExpr,
+
+###### declare terminals
+
+ $LEFT if $$ifelse
+
+###### expression grammar
+
+ | Expression if Expression else Expression $$ifelse ${ {
+ struct binode *b1 = new(binode);
+ struct binode *b2 = new(binode);
+ b1->op = CondExpr;
+ b1->left = $<3;
+ b1->right = b2;
+ b2->op = CondExpr;
+ b2->left = $<1;
+ b2->right = $<5;
+ $0 = b1;
+ } }$
+
+###### print binode cases
+
+ case CondExpr:
+ b2 = cast(binode, b->right);
+ if (bracket) printf("(");
+ print_exec(b2->left, -1, bracket);
+ printf(" if ");
+ print_exec(b->left, -1, bracket);
+ printf(" else ");
+ print_exec(b2->right, -1, bracket);
+ if (bracket) printf(")");
+ break;
+
+###### propagate binode cases
+
+ case CondExpr: {
+ /* cond must be Tbool, others must match */
+ struct binode *b2 = cast(binode, b->right);
+ struct type *t2;
+
+ propagate_types(b->left, c, ok, Tbool, 0);
+ t = propagate_types(b2->left, c, ok, type, Rnolabel);
+ t2 = propagate_types(b2->right, c, ok, type ?: t, Rnolabel);
+ return t ?: t2;
+ }
+
+###### interp binode cases
+
+ case CondExpr: {
+ struct binode *b2 = cast(binode, b->right);
+ left = interp_exec(c, b->left, <ype);
+ if (left.bool)
+ rv = interp_exec(c, b2->left, &rvtype); // UNTESTED
+ else
+ rv = interp_exec(c, b2->right, &rvtype);
+ }
+ break;
+
+### Expression list
+
+We take a brief detour, now that we have expressions, to describe lists
+of expressions. These will be needed for function parameters and
+possibly other situations. They seem generic enough to introduce here
+to be used elsewhere.
+
+And ExpressionList will use the `List` type of `binode`, building up at
+the end. And place where they are used will probably call
+`reorder_bilist()` to get a more normal first/next arrangement.
+
+###### declare terminals
+ $TERM ,
+
+`List` execs have no implicit semantics, so they are never propagated or
+interpreted. The can be printed as a comma separate list, which is how
+they are parsed. Note they are also used for function formal parameter
+lists. In that case a separate function is used to print them.