}
fmt++;
switch (*fmt) {
- case '%': fputc(*fmt, stderr); break;
- default: fputc('?', stderr); break;
+ case '%': fputc(*fmt, stderr); break; // NOTEST
+ default: fputc('?', stderr); break; // NOTEST
case '1':
type_print(t1, stderr);
break;
else if (type->print_type)
type->print_type(type, f);
else
- fputs("*invalid*type*", f);
+ fputs("*invalid*type*", f); // NOTEST
}
static struct value val_prepare(struct type *type)
if (v.type && v.type->print)
v.type->print(v);
else
- printf("*Unknown*");
+ printf("*Unknown*"); // NOTEST
}
static struct value parse_value(struct type *type, char *arg)
if (type && type->parse)
return type->parse(type, arg);
- rv.type = NULL;
- return rv;
+ rv.type = NULL; // NOTEST
+ return rv; // NOTEST
}
###### forward decls
rv.type = type;
switch(type->vtype) {
- case Vnone:
- break;
+ case Vnone: // NOTEST
+ break; // NOTEST
case Vnum:
mpq_init(rv.num); break;
case Vstr:
case Vbool:
rv.bool = 0;
break;
- case Vlabel:
- rv.label = NULL;
- break;
+ case Vlabel: // NOTEST
+ rv.label = NULL; // NOTEST
+ break; // NOTEST
}
return rv;
}
struct value rv;
rv.type = v.type;
switch (rv.type->vtype) {
- case Vnone:
- break;
+ case Vnone: // NOTEST
+ break; // NOTEST
case Vlabel:
rv.label = v.label;
break;
{
int cmp;
if (left.type != right.type)
- return left.type - right.type;
+ return left.type - right.type; // NOTEST
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 Vnone: cmp = 0;
+ case Vnone: cmp = 0; // NOTEST
}
return cmp;
}
static void _print_value(struct value v)
{
switch (v.type->vtype) {
- case Vnone:
- printf("*no-value*"); break;
- case Vlabel:
- printf("*label-%p*", v.label); break;
+ case Vnone: // NOTEST
+ printf("*no-value*"); break; // NOTEST
+ case Vlabel: // NOTEST
+ printf("*label-%p*", v.label); break; // NOTEST
case Vstr:
printf("%.*s", v.str.len, v.str.txt); break;
case Vbool:
val.type = type;
switch(type->vtype) {
- case Vlabel:
- case Vnone:
- val.type = NULL;
- break;
+ case Vlabel: // NOTEST
+ case Vnone: // NOTEST
+ val.type = NULL; // NOTEST
+ break; // NOTEST
case Vstr:
val.str.len = strlen(arg);
val.str.txt = malloc(val.str.len);
static int __fput_loc(struct exec *loc, FILE *f)
{
if (!loc)
- return 0;
+ return 0; // NOTEST
if (loc->line >= 0) {
fprintf(f, "%d:%d: ", loc->line, loc->column);
return 1;
static void fput_loc(struct exec *loc, FILE *f)
{
if (!__fput_loc(loc, f))
- fprintf(f, "??:??: ");
+ fprintf(f, "??:??: "); // NOTEST
}
Each different type of `exec` node needs a number of functions
static void print_exec(struct exec *e, int indent, int bracket)
{
if (!e)
- return;
+ return; // NOTEST
switch (e->type) {
case Xbinode:
print_binode(cast(binode, e), indent, bracket); break;
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.
+
+For now, just arrays.
+
+#### 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 an arbitrary expression which is evaluated when the name
+comes into scope.
+
+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 depth.
+
+###### type union fields
+
+ struct {
+ int size;
+ struct variable *vsize;
+ struct type *member;
+ } array;
+
+###### value union fields
+ struct {
+ struct value *elmnts;
+ } array;
+
+###### value functions
+
+ static struct value array_prepare(struct type *type)
+ {
+ struct value ret;
+
+ ret.type = type;
+ ret.array.elmnts = NULL;
+ return ret;
+ }
+
+ static struct value array_init(struct type *type)
+ {
+ struct value ret;
+ int i;
+
+ ret.type = type;
+ if (type->array.vsize) {
+ mpz_t q;
+ mpz_init(q);
+ mpz_tdiv_q(q, mpq_numref(type->array.vsize->val.num),
+ mpq_denref(type->array.vsize->val.num));
+ type->array.size = mpz_get_si(q);
+ mpz_clear(q);
+ }
+ ret.array.elmnts = calloc(type->array.size,
+ sizeof(ret.array.elmnts[0]));
+ for (i = 0; ret.array.elmnts && i < type->array.size; i++)
+ ret.array.elmnts[i] = val_init(type->array.member);
+ return ret;
+ }
+
+ static void array_free(struct value val)
+ {
+ int i;
+
+ if (val.array.elmnts)
+ for (i = 0; i < val.type->array.size; i++)
+ free_value(val.array.elmnts[i]);
+ free(val.array.elmnts);
+ }
+
+ 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 (require->array.vsize == NULL && have->array.vsize == NULL)
+ return require->array.size == have->array.size;
+
+ return require->array.vsize == have->array.vsize;
+ }
+
+ 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]", b->name.len, b->name.txt);
+ } else
+ fprintf(f, "%d]", type->array.size);
+ type_print(type->array.member, f);
+ }
+
+ static struct type array_prototype = {
+ .prepare = array_prepare,
+ .init = array_init,
+ .print_type = array_print_type,
+ .compat = array_compat,
+ .free = array_free,
+ };
+
+###### type grammar
+
+ | [ NUMBER ] Type ${
+ $0 = calloc(1, sizeof(struct type));
+ *($0) = array_prototype;
+ $0->array.member = $<4;
+ $0->array.vsize = NULL;
+ {
+ struct parse_context *c = config2context(config);
+ char tail[3];
+ mpq_t num;
+ 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 {
+ $0->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);
+ }
+ $0->next= c->anon_typelist;
+ c->anon_typelist = $0;
+ }
+ }$
+
+ | [ IDENTIFIER ] Type ${ {
+ struct parse_context *c = config2context(config);
+ struct variable *v = var_ref(c, $2.txt);
+
+ if (!v)
+ tok_err(config2context(config), "error: name undeclared", &$2);
+ else if (!v->constant)
+ tok_err(config2context(config), "error: array size must be a constant", &$2);
+
+ $0 = calloc(1, sizeof(struct type));
+ *($0) = array_prototype;
+ $0->array.member = $<4;
+ $0->array.size = 0;
+ $0->array.vsize = v;
+ $0->next= c->anon_typelist;
+ c->anon_typelist = $0;
+ } }$
+
+###### parse context
+
+ struct type *anon_typelist;
+
+###### free context types
+
+ while (context.anon_typelist) {
+ struct type *t = context.anon_typelist;
+
+ context.anon_typelist = t->next;
+ free(t);
+ }
+
+###### 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, 0);
+ printf("[");
+ print_exec(b->right, -1, 0);
+ 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);
+ *ok = 0;
+ 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);
+ *ok = 0;
+ }
+ return t->array.member;
+ }
+ break;
+
+###### interp binode cases
+ case Index: {
+ mpz_t q;
+ long i;
+
+ lleft = linterp_exec(b->left);
+ right = interp_exec(b->right);
+ mpz_init(q);
+ mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
+ i = mpz_get_si(q);
+ mpz_clear(q);
+
+ if (i >= 0 && i < lleft->type->array.size)
+ lrv = &lleft->array.elmnts[i];
+ else
+ rv = val_init(lleft->type->array.member);
+ break;
+ }
+
## Language elements
Each language element needs to be parsed, printed, analysed,
struct binding *b = v->var->name;
fprintf(stderr, "%.*s", b->name.len, b->name.txt);
} else
- fputs("???", stderr);
+ fputs("???", stderr); // NOTEST
} else
- fputs("NOTVAR", stderr);
+ fputs("NOTVAR", stderr); // NOTEST
break;
###### propagate exec cases
struct var *var = cast(var, prog);
struct variable *v = var->var;
if (!v) {
- type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone);
- *ok = 0;
- return Tnone;
+ type_err(c, "%d:BUG: no variable!!", prog, Tnone, 0, Tnone); // NOTEST
+ *ok = 0; // NOTEST
+ return Tnone; // NOTEST
}
if (v->merged)
v = v->merged;
case GtrEq: printf(" >= "); break;
case Eql: printf(" == "); break;
case NEql: printf(" != "); break;
- default: abort();
+ default: abort(); // NOTEST
}
print_exec(b->right, -1, 0);
break;
case GtrEq: rv.bool = cmp >= 0; break;
case Eql: rv.bool = cmp == 0; break;
case NEql: rv.bool = cmp != 0; break;
- default: rv.bool = 0; break;
+ default: rv.bool = 0; break; // NOTEST
}
break;
}
case Divide: fputs(" / ", stdout); break;
case Rem: fputs(" % ", stdout); break;
case Concat: fputs(" ++ ", stdout); break;
- default: abort();
- }
+ default: abort(); // NOTEST
+ } // NOTEST
print_exec(b->right, indent, 0);
break;
case Absolute:
free_value(*lleft);
*lleft = right;
} else
- free_value(right);
+ free_value(right); // NOTEST
right.type = NULL;
break;
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.
-
-For now, just arrays.
-
-### 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 an arbitrary expression which is evaluated when the name
-comes into scope.
-
-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 depth.
-
-###### type union fields
-
- struct {
- int size;
- struct variable *vsize;
- struct type *member;
- } array;
-
-###### value union fields
- struct {
- struct value *elmnts;
- } array;
-
-###### value functions
-
- static struct value array_prepare(struct type *type)
- {
- struct value ret;
-
- ret.type = type;
- ret.array.elmnts = NULL;
- return ret;
- }
-
- static struct value array_init(struct type *type)
- {
- struct value ret;
- int i;
-
- ret.type = type;
- if (type->array.vsize) {
- mpz_t q;
- mpz_init(q);
- mpz_tdiv_q(q, mpq_numref(type->array.vsize->val.num),
- mpq_denref(type->array.vsize->val.num));
- type->array.size = mpz_get_si(q);
- mpz_clear(q);
- }
- ret.array.elmnts = calloc(type->array.size,
- sizeof(ret.array.elmnts[0]));
- for (i = 0; ret.array.elmnts && i < type->array.size; i++)
- ret.array.elmnts[i] = val_init(type->array.member);
- return ret;
- }
-
- static void array_free(struct value val)
- {
- int i;
-
- if (val.array.elmnts)
- for (i = 0; i < val.type->array.size; i++)
- free_value(val.array.elmnts[i]);
- free(val.array.elmnts);
- }
-
- 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 (require->array.vsize == NULL && have->array.vsize == NULL)
- return require->array.size == have->array.size;
-
- return require->array.vsize == have->array.vsize;
- }
-
- 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]", b->name.len, b->name.txt);
- } else
- fprintf(f, "%d]", type->array.size);
- type_print(type->array.member, f);
- }
-
- static struct type array_prototype = {
- .prepare = array_prepare,
- .init = array_init,
- .print_type = array_print_type,
- .compat = array_compat,
- .free = array_free,
- };
-
-###### type grammar
-
- | [ NUMBER ] Type ${
- $0 = calloc(1, sizeof(struct type));
- *($0) = array_prototype;
- $0->array.member = $<4;
- $0->array.vsize = NULL;
- {
- char tail[3];
- mpq_t num;
- if (number_parse(num, tail, $2.txt) == 0)
- tok_err(config2context(config), "error: unrecognised number", &$2);
- else if (tail[0])
- tok_err(config2context(config), "error: unsupported number suffix", &$2);
- else {
- $0->array.size = mpz_get_ui(mpq_numref(num));
- if (mpz_cmp_ui(mpq_denref(num), 1) != 0) {
- tok_err(config2context(config), "error: array size must be an integer",
- &$2);
- } else if (mpz_cmp_ui(mpq_numref(num), 1UL << 30) >= 0)
- tok_err(config2context(config), "error: array size is too large",
- &$2);
- }
- }
- }$
-
- | [ IDENTIFIER ] Type ${ {
- struct variable *v = var_ref(config2context(config), $2.txt);
-
- if (!v)
- tok_err(config2context(config), "error: name undeclared", &$2);
- else if (!v->constant)
- tok_err(config2context(config), "error: array size must be a constant", &$2);
-
- $0 = calloc(1, sizeof(struct type));
- *($0) = array_prototype;
- $0->array.member = $<4;
- $0->array.size = 0;
- $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, 0);
- printf("[");
- print_exec(b->right, -1, 0);
- 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);
- *ok = 0;
- 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);
- *ok = 0;
- }
- return t->array.member;
- }
- break;
-
-###### interp binode cases
- case Index: {
- mpz_t q;
- long i;
-
- lleft = linterp_exec(b->left);
- right = interp_exec(b->right);
- mpz_init(q);
- mpz_tdiv_q(q, mpq_numref(right.num), mpq_denref(right.num));
- i = mpz_get_si(q);
- mpz_clear(q);
-
- if (i >= 0 && i < lleft->type->array.size)
- lrv = &lleft->array.elmnts[i];
- else
- rv = val_init(lleft->type->array.member);
- break;
- }
-
### Finally the whole program.
Somewhat reminiscent of Pascal a (current) Ocean program starts with
break;
###### propagate binode cases
- case Program: abort();
+ case Program: abort(); // NOTEST
###### core functions
int ok = 1;
if (!b)
- return 0;
+ return 0; // NOTEST
do {
ok = 1;
propagate_types(b->right, c, &ok, Tnone, 0);
struct value v;
if (!prog)
- return;
+ return; // NOTEST
al = cast(binode, p->left);
while (al) {
struct var *v = cast(var, al->left);
}
###### interp binode cases
- case Program: abort();
+ case Program: abort(); // NOTEST
## And now to test it out.