chiark / gitweb /
@@@ BROKEN wip
authorMark Wooding <mdw@distorted.org.uk>
Sat, 3 Jun 2023 09:31:39 +0000 (10:31 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 3 Jun 2023 09:32:26 +0000 (10:32 +0100)
struct/t/dstr-putf-test.c
struct/tests.at
test/t/tvec-test.c
test/tests.at
test/tvec-core.c
test/tvec-output.c
test/tvec-types.c
test/tvec.h
utils/t/control-test.c
utils/tests.at

index 462371d5e8d37b92908da475246093d3b499500e..416b164bd2509c3f4cf58f4d2e0859bc2b69c536 100644 (file)
@@ -9,21 +9,14 @@
 
 #include "dstr.h"
 #include "macros.h"
+#include "report.h"
+#include "tvec.h"
 
-static int win = 0, lose = 0;
+static struct tvec_state tvstate;
 static dstr d = DSTR_INIT;
-static char buf[1024];
+static char strbuf[1024];
 
-static void check(const char *what, const char *want)
-{
-  if (STRCMP(want, ==, d.buf))
-    win++;
-  else {
-    lose++;
-    fprintf(stderr, "test failed: %s\n  expected: %s\n     found: %s\n",
-           what, want, d.buf);
-  }
-}
+#define TESTGROUP(name) TVEC_TESTGROUP_TAG(grp, &tvstate, name)
 
 static void PRINTF_LIKE(1, 2) format(const char *fmt, ...)
 {
@@ -41,54 +34,77 @@ static void PRINTF_LIKE(1, 2) prepare(const char *fmt, ...)
 
   va_start(ap, fmt);
 #ifdef HAVE_SNPRINTF
-  n = vsnprintf(buf, sizeof(buf), fmt, ap);
+  n = vsnprintf(strbuf, sizeof(strbuf), fmt, ap);
 #else
-  n = vsprintf(buf, fmt, ap);
+  n = vsprintf(strbuf, fmt, ap);
 #endif
-  assert(0 <= n && n < sizeof(buf));
+  assert(0 <= n && n < sizeof(strbuf));
 }
 
-#define TEST1(fmtargs) do {                                            \
+#define TEST_KNOWN(fmtargs, want) do {                                 \
   format fmtargs;                                                      \
-  prepare fmtargs;                                                     \
-  check(#fmtargs, buf);                                                        \
+  tvec_claimeq_string(&tvstate, d.buf, d.len, want, strlen(want),      \
+                     __FILE__, __LINE__, "format " #fmtargs);          \
 } while (0)
 
-#define TEST2(fmtargs, want) do {                                      \
+#define TEST_REF(fmtargs) do {                                         \
   format fmtargs;                                                      \
-  check(#fmtargs, want);                                               \
+  prepare fmtargs;                                                     \
+  tvec_claimeq_string(&tvstate, d.buf, d.len, strbuf, strlen(strbuf),  \
+                     __FILE__, __LINE__, "format " #fmtargs);          \
 } while (0)
 
 #define LENGTHY \
   "This is a rather longer string than the code is expecting: will it fit?"
 
-int main(void)
+int main(int argc, char *argv[])
 {
-  TEST2(("Hello, world!"), "Hello, world!");
-  TEST2(("just a ->%%<- sign"), "just a ->%<- sign");
-  TEST2(("Testing, testing, %d, %d, %d.", 1, 2, 3),
-       "Testing, testing, 1, 2, 3.");
-  TEST2(("->%5d<-", 138), "->  138<-");
-  TEST2(("->%*d<-", 5, 138), "->  138<-");
-  TEST2(("->%-*d<-", 5, 138), "->138  <-");
-  TEST2(("->%*d<-", -5, 138), "->138  <-");
-  TEST2(("->%-*d<-", -5, 138), "->138  <-");
-  TEST2(("->%.*s<-", 5, "truncate me"), "->trunc<-");
-  TEST2(("->%.*s<-", -5, "don't truncate me"), "->don't truncate me<-");
-  TEST2(("Truncation indirect: ->%.*s<-", 10, "a long string to be chopped"),
-       "Truncation indirect: ->a long str<-");
-  TEST2(("%08lx:%s", 0x65604204ul, "tripe-ec"), "65604204:tripe-ec");
-  TEST2(("%s", LENGTHY), LENGTHY);
-
-  TEST1(("big float: ->%f<- and integer %d\n", DBL_MAX, 42));
-
-  TEST2(("Testing, testing, %3$d, %2$d, %1$d.", 3, 2, 1),
-       "Testing, testing, 1, 2, 3.");
-  TEST2(("Truncation indirect: ->%1$.*2$s<-",
-        "a long string to be chopped", 10),
-       "Truncation indirect: ->a long str<-");
-
-  if (!lose) printf("All tests successful.\n");
-  else printf("FAILED %d of %d tests.\n", lose, win + lose);
-  return (!!lose);
+  struct tvec_test test;
+  int argpos;
+
+  tvec_parseargs(argc, argv, &tvstate, &argpos, &tvec_adhocconfig);
+  if (argpos < argc) die(2, "no input files expected");
+  tvec_adhoc(&tvstate, &test);
+
+  TESTGROUP("basics") {
+    TEST_KNOWN(("Hello, world!"), "Hello, world!");
+    TEST_KNOWN(("just a ->%%<- sign"), "just a ->%<- sign");
+  }
+
+  TESTGROUP("integers") {
+    TEST_KNOWN(("Testing, testing, %d, %d, %d.", 1, 2, 3),
+              "Testing, testing, 1, 2, 3.");
+    TEST_KNOWN(("->%5d<-", 138), "->  138<-");
+    TEST_KNOWN(("->%*d<-", 5, 138), "->  138<-");
+    TEST_KNOWN(("->%-*d<-", 5, 138), "->138  <-");
+    TEST_KNOWN(("->%*d<-", -5, 138), "->138  <-");
+    TEST_KNOWN(("->%-*d<-", -5, 138), "->138  <-");
+  }
+
+  TESTGROUP("strings") {
+    TEST_KNOWN(("->%.*s<-", 5, "truncate me"), "->trunc<-");
+    TEST_KNOWN(("->%.*s<-", -5, "don't truncate me"),
+              "->don't truncate me<-");
+    TEST_KNOWN(("Truncation indirect: ->%.*s<-", 10,
+               "a long string to be chopped"),
+              "Truncation indirect: ->a long str<-");
+  }
+
+  TESTGROUP("combinations") {
+    TEST_KNOWN(("%08lx:%s", 0x65604204ul, "tripe-ec"), "65604204:tripe-ec");
+    TEST_REF(("big float: ->%f<- and integer %d\n", DBL_MAX, 42));
+  }
+
+  TESTGROUP("argument-order") {
+    TEST_KNOWN(("Testing, testing, %3$d, %2$d, %1$d.", 3, 2, 1),
+              "Testing, testing, 1, 2, 3.");
+    TEST_KNOWN(("Truncation indirect: ->%1$.*2$s<-",
+               "a long string to be chopped", 10),
+              "Truncation indirect: ->a long str<-");
+  }
+
+  TESTGROUP("misc")
+    TEST_KNOWN(("%s", LENGTHY), LENGTHY);
+
+  return (tvec_end(&tvstate));
 }
index f54deb5ba717b24ad86ac51dc74c60b28311730d..63d5fc40a803d4da42164fe80979867ddd70d743 100644 (file)
@@ -50,8 +50,7 @@ AT_CLEANUP
 ## dstr
 AT_SETUP([struct: dstr-putf])
 AT_KEYWORDS([struct dstr putf dstr_putf])
-AT_CHECK([BUILDDIR/t/dstr-putf.t], [0], [All tests successful.
-])
+AT_CHECK([BUILDDIR/t/dstr-putf.t -fh], [0], [ignore])
 AT_CLEANUP
 
 ## sym
index b4ef9a45005063cfa303cb500e69e47d15a35511..78f730f34534eb588d7e76e704180f1a3b36be3d 100644 (file)
@@ -70,7 +70,7 @@ static const struct tvec_floatinfo fenum_fltinfo =
 
 #define DEFENUM(tag, ty, slot)                                         \
   static const struct tvec_##slot##enuminfo slot##enum_info =          \
-    { { slot##enum_NAME, TVMISC_##tag }, slot##enum_assocs slot##enum_ARGS };
+    { slot##enum_NAME, slot##enum_assocs slot##enum_ARGS };
 #define ienum_NAME "order"
 #define ienum_ARGS , &tvrange_i16
 #define uenum_NAME "fruit"
@@ -122,10 +122,10 @@ static const struct tvec_urange range_32 = { 0, 31 };
   _(float,     RFP,    float,                  p, 0)                   \
   _(fltish,    RFISH,  float,                  p, &fltish_info)        \
   _(char,      RCH,    char,                   p, 0)                   \
-  _(ienum,     RIE,    enum,                   p, &ienum_info)         \
-  _(uenum,     RUE,    enum,                   p, &uenum_info)         \
-  _(fenum,     RFE,    enum,                   p, &fenum_info)         \
-  _(penum,     RPE,    enum,                   p, &penum_info)         \
+  _(ienum,     RIE,    ienum,                  p, &ienum_info)         \
+  _(uenum,     RUE,    uenum,                  p, &uenum_info)         \
+  _(fenum,     RFE,    fenum,                  p, &fenum_info)         \
+  _(penum,     RPE,    penum,                  p, &penum_info)         \
   _(flags,     RF,     flags,                  p, &attr_info)          \
   _(string,    RSTR,   string,                 p, &range_32)           \
   _(bytes,     RBY,    bytes,                  p, &tvrange_byte)       \
@@ -199,30 +199,25 @@ static void test_serialization
   if (BLEFT(&b._b))
     { out[RRC].v.i = -3; goto end; }
 
-  if (in[RSAB].f&TVRF_LIVE) {
-    for (rd = tv->test->regs; rd->name; rd++)
-      if (STRCMP(in[RSAB].v.str.p, ==, rd->name)) {
-       rv = &out[rd->i].v;
-       if (rd->ty == &tvty_int ||
-           (rd->ty == &tvty_enum &&
-            ((const struct tvec_enuminfo *)rd->arg.p)->mv == TVMISC_INT))
-         rv->i ^= 1;
-       else if (rd->ty == &tvty_uint || rd->ty == &tvty_flags ||
-                (rd->ty == &tvty_enum &&
-                 ((const struct tvec_enuminfo *)rd->arg.p)->mv ==
-                   TVMISC_INT))
-         rv->u ^= 1;
-       else if (rd->ty == &tvty_enum &&
-                ((const struct tvec_enuminfo *)rd->arg.p)->mv == TVMISC_PTR)
-         rv->p = rv->p
-           ? 0
-           : (/*unconst*/ void *)
-               ((const struct tvec_penuminfo *)rd->arg.p)->av[0].p;
-       else if (rd->ty == &tvty_string)
-         { if (rv->str.sz) rv->str.p[0] ^= 1; }
-       else if (rd->ty == &tvty_bytes)
-         { if (rv->bytes.sz) rv->bytes.p[0] ^= 1; }
-      }
+  if ((in[RSAB].f&TVRF_LIVE) && in[RSAB].v.i >= 0) {
+    rd = &tv->test->regs[in[RSAB].v.i]; rv = &out[in[RSAB].v.i].v;
+    if (rd->ty == &tvty_int || rd->ty == &tvty_ienum)
+      rv->i ^= 1;
+    else if (rd->ty == &tvty_uint ||
+            rd->ty == &tvty_flags || rd->ty == &tvty_uenum)
+      rv->u ^= 1;
+    else if (rd->ty == &tvty_float || rd->ty == &tvty_fenum) {
+      if (rv->f == rv->f) rv->f = -rv->f;
+      else rv->f = 1.0;
+    } else if (rd->ty == &tvty_penum)
+      rv->p = rv->p
+       ? 0
+       : (/*unconst*/ void *)
+       ((const struct tvec_penuminfo *)rd->arg.p)->av[0].p;
+    else if (rd->ty == &tvty_string)
+      { if (rv->str.sz) rv->str.p[0] ^= 1; }
+    else if (rd->ty == &tvty_bytes)
+      { if (rv->bytes.sz) rv->bytes.p[0] ^= 1; }
   }
 
   out[RRC].v.i = 0;
@@ -230,6 +225,15 @@ end:
   dbuf_destroy(&b);
 }
 
+static const struct tvec_iassoc type_assocs[] = {
+  { "none",            -1 },
+#define DEFASSOC(name, i, ty, argslot, argval) { #name, i },
+  TYPEREGS(DEFASSOC)
+#undef DEFASSOC
+  { 0,                 0 }
+};
+static const struct tvec_ienuminfo type_enum = { "regty", type_assocs, 0 };
+
 static DSGINIT(const) struct tvec_regdef test_regs[] = {
 #define DEFREG(name, i, ty, argslot, argval)                           \
   { #name,     i,      &tvty_##ty,     TVRF_OPT,                       \
@@ -238,7 +242,7 @@ static DSGINIT(const) struct tvec_regdef test_regs[] = {
 #undef DEFREG
   { "rc",      RRC,    &tvty_int,      TVRF_OPT,       { &tvrange_int } },
   { "serialized", RSER,        &tvty_bytes,    TVRF_OPT },
-  { "sabotage",        RSAB,   &tvty_string,   TVRF_OPT,       { &tvrange_byte } },
+  { "sabotage",        RSAB,   &tvty_ienum,    TVRF_OPT,       { &type_enum } },
 
   { 0 }
 };
@@ -298,10 +302,10 @@ static int single_set(struct tvec_state *tv, const char *name,
   struct singlectx *s = ctx;
   union tvec_regval rv;
   static const struct tvec_regdef rd =
-    { "@show", -1, &tvty_enum, 0, { &tvenum_bool } };
+    { "@show", -1, &tvty_ienum, 0, { &tvenum_bool } };
 
   if (STRCMP(name, ==, "@show")) {
-    if (tvty_enum.parse(&rv, &rd, tv)) return (-1);
+    if (tvty_ienum.parse(&rv, &rd, tv)) return (-1);
     if (s) {
       if (rv.i) s->f |= SF_SHOW;
       else s->f &= ~SF_SHOW;
index 30c898a3429c2f049d5e0a875d4f12b02465c5be..d823872ab3ce17a321d69c9dc235359744259bd1 100644 (file)
@@ -85,31 +85,48 @@ test_parse([int], [4], [4 ; = 0x04 = '\x04'])
 test_parse([int], [ 17; comment], [17 ; = 0x11 = '\x11'])
 
 test_parse([int], [0x234], [564 ; = 0x0234])
-test_parse([int], [033], [27 ; = 0x1b = '\e'])
+test_parse([int], [0o33], [27 ; = 0x1b = '\e'])
 
 test_parse([int], [ +192], [192 ; = 0xc0 = '\xc0'])
 test_parse([int], [ -192], [-192 ; = -0xc0])
 
-test_parserr([int], [17 : badness], [3],
-       [syntax error: expected end-of-line but found `:'])
-test_parserr([int], [17: badness], [3],
-       [syntax error: expected end-of-line but found `:'])
-
-test_parserr([int], [xyzzy], [3],
-       [syntax error: expected signed integer but found `x'])
-test_parserr([int], [-splat], [3],
-       [syntax error: expected signed integer but found `s'])
-
-test_parserr([int], [0xq], [3],
-       [syntax error: expected end-of-line but found `x'])
-test_parserr([int], [0x], [3],
-       [syntax error: expected end-of-line but found `x'])
-
-test_parserr([int], [], [3],
-       [syntax error: expected signed integer but found @%:@<eol>])
-
-test_parserr([int], [123456], [3],
-       [integer 123456 out of range (must be in @<:@-32768 .. 32767@:>@)])
+test_parserr([int], [17 : badness],
+       [3], [syntax error: expected end-of-line but found `:'])
+test_parserr([int], [17: badness],
+       [3], [syntax error: expected end-of-line but found `:'])
+
+test_parserr([int], [-_1],
+       [3], [invalid signed integer `-_1'])
+test_parserr([int], [+1234_],
+       [3], [syntax error: expected end-of-line but found `_'])
+test_parse([int], [-1234_5], [-12345 ; = -0x3039])
+test_parserr([int], [+0x_abc],
+       [3], [syntax error: expected end-of-line but found `x'])
+test_parse([int], [-0xa_bc], [-2748 ; = -0x0abc])
+test_parserr([int], [-0xab__c],
+       [3], [syntax error: expected end-of-line but found `_'])
+test_parserr([int], [+010r1234],
+       [3], [syntax error: expected end-of-line but found `r'])
+test_parserr([int], [-1_0r1234],
+       [3], [syntax error: expected end-of-line but found `r'])
+
+test_parserr([int], [xyzzy],
+       [3], [invalid signed integer `xyzzy'])
+test_parserr([int], [-splat],
+       [3], [invalid signed integer `-splat'])
+test_parserr([int], [- 1],
+       [3], [invalid signed integer `-'])
+
+test_parserr([int], [0xq],
+       [3], [syntax error: expected end-of-line but found `x'])
+test_parserr([int], [0x],
+       [3], [syntax error: expected end-of-line but found `x'])
+
+test_parserr([int], [],
+       [3], [syntax error: expected signed integer but found @%:@<eol>])
+
+test_parserr([int], [123456],
+       [3], [integer 123456 out of range (must be in @<:@-32768 .. 32767@:>@)])
 
 AT_CLEANUP
 
@@ -119,38 +136,59 @@ AT_SETUP(tvec type-uint)
 test_parse([uint], [4], [4 ; = 0x04 = '\x04'])
 test_parse([uint], [ 17; comment], [17 ; = 0x11 = '\x11'])
 
+test_parse([uint], [012345], [12345 ; = 0x3039])
 test_parse([uint], [0x234], [564 ; = 0x0234])
-test_parse([uint], [033], [27 ; = 0x1b = '\e'])
-
-test_parserr([uint], [17 : badness], [3],
-       [syntax error: expected end-of-line but found `:'])
-test_parserr([uint], [17: badness], [3],
-       [syntax error: expected end-of-line but found `:'])
-
-test_parserr([uint], [ +192], [3],
-       [syntax error: expected unsigned integer but found `+'])
-test_parserr([uint], [ -192], [3],
-       [syntax error: expected unsigned integer but found `-'])
-
-test_parserr([uint], [xyzzy], [3],
-       [syntax error: expected unsigned integer but found `x'])
-
-test_parserr([uint], [0xq], [3],
-       [syntax error: expected end-of-line but found `x'])
-test_parserr([uint], [0x], [3],
-       [syntax error: expected end-of-line but found `x'])
-
-test_parserr([uint], [], [3],
-       [syntax error: expected unsigned integer but found @%:@<eol>])
-
-test_parserr([uint], [123456], [3],
-       [integer 123456 out of range (must be in @<:@0 .. 65535@:>@)])
+test_parse([uint], [0o33], [27 ; = 0x1b = '\e'])
+test_parse([uint], [0b1011_1101], [189 ; = 0xbd = '\xbd'])
+test_parse([uint], [12r123], [171 ; = 0xab = '\xab'])
+
+test_parserr([uint], [17 : badness],
+       [3], [syntax error: expected end-of-line but found `:'])
+test_parserr([uint], [17: badness],
+       [3], [syntax error: expected end-of-line but found `:'])
+
+test_parserr([uint], [_1],
+       [3], [invalid unsigned integer `_1'])
+test_parserr([uint], [1234_],
+       [3], [syntax error: expected end-of-line but found `_'])
+test_parse([uint], [1234_5], [12345 ; = 0x3039])
+test_parserr([uint], [0x_abcd],
+       [3], [syntax error: expected end-of-line but found `x'])
+test_parse([uint], [0xa_bcd], [43981 ; = 0xabcd])
+test_parserr([uint], [0xab__cd],
+       [3], [syntax error: expected end-of-line but found `_'])
+test_parserr([uint], [010r1234],
+       [3], [syntax error: expected end-of-line but found `r'])
+test_parserr([uint], [1_0r1234],
+       [3], [syntax error: expected end-of-line but found `r'])
+
+test_parserr([uint], [ +192],
+       [3], [invalid unsigned integer `+192'])
+test_parserr([uint], [ -192],
+       [3], [invalid unsigned integer `-192'])
+
+test_parserr([uint], [xyzzy],
+       [3], [invalid unsigned integer `xyzzy'])
+
+test_parserr([uint], [0xq],
+       [3], [syntax error: expected end-of-line but found `x'])
+test_parserr([uint], [0x],
+       [3], [syntax error: expected end-of-line but found `x'])
+
+test_parserr([uint], [],
+       [3], [syntax error: expected unsigned integer but found @%:@<eol>])
+
+test_parserr([uint], [123456],
+       [3], [integer 123456 out of range (must be in @<:@0 .. 65535@:>@)])
 
 AT_CLEANUP
 
 ###--------------------------------------------------------------------------
 AT_SETUP([tvec type-float])
 
+test_parse([float], [0.0], [0])
+test_parse([float], [-0.0], [-0])
+
 test_parse([float], [1.234], [1.234])
 
 AT_CLEANUP
@@ -180,7 +218,7 @@ AT_DATA([tv],
 
 int = -2
 uint = 7
-float = 6.28
+float = @%:@nan
 fltish = 0.1
 char = x
 ienum = greater
@@ -198,6 +236,7 @@ bytes =
        ad4f14f2 444066d0 6bc430b7 323ba122
        f622919d e18b1fda b0ca9902 b9729d49
        2c807ec5 99d5e980 b2eac9cc 53bf67d6
+@show = t
 ])
 AT_CHECK([BUILDDIR/t/tvec.t -fh tv], [0], [ignore])
 
index 6aa49fa06ca618ad136d167d3b71cf0e17d14ab9..03873dc61e167505a8fbd3446e9e1ee61f01dcbd 100644 (file)
@@ -392,7 +392,7 @@ static void begin_test_group(struct tvec_state *tv, struct groupstate *g)
   }
 }
 
-void tvec_reportgroup(struct tvec_state *tv)
+static void report_group(struct tvec_state *tv)
 {
   unsigned i, out, nrun;
 
@@ -404,7 +404,7 @@ void tvec_reportgroup(struct tvec_state *tv)
   else {
     if (tv->curr[TVOUT_LOSE]) out = TVOUT_LOSE;
     else out = TVOUT_WIN;
-    tv->grps[out]++; tv->output->ops->egroup(tv->output, out);
+    tv->grps[out]++; tv->output->ops->egroup(tv->output);
   }
 }
 
@@ -415,7 +415,7 @@ static void end_test_group(struct tvec_state *tv, struct groupstate *g)
 
   if (!t) return;
   if (tv->f&TVSF_OPEN) check(tv, g);
-  if (!(tv->f&TVSF_SKIP)) tvec_reportgroup(tv);
+  if (!(tv->f&TVSF_SKIP)) report_group(tv);
   env = t->env; if (env && env->teardown) env->teardown(tv, g->ctx);
   release_registers(tv); tv->test = 0; xfree(g->ctx); g->ctx = 0;
 }
@@ -548,9 +548,7 @@ void tvec_begin(struct tvec_state *tv_out,
 
   tv_out->tests = config->tests; tv_out->test = 0;
   tv_out->infile = 0; tv_out->lno = 0; tv_out->fp = 0;
-  o->tv = tv_out; tv_out->output = o;
-
-  tv_out->output->ops->bsession(tv_out->output);
+  tv_out->output = o; tv_out->output->ops->bsession(tv_out->output, tv_out);
 }
 
 int tvec_end(struct tvec_state *tv)
@@ -636,7 +634,7 @@ void tvec_begingroup(struct tvec_state *tv, const char *name,
 
 void tvec_endgroup(struct tvec_state *tv)
 {
-  if (!(tv->f&TVSF_SKIP)) tvec_reportgroup(tv);
+  if (!(tv->f&TVSF_SKIP)) report_group(tv);
   tv->test = 0;
 }
 
@@ -680,19 +678,27 @@ static void adhoc_claim_teardown(struct tvec_state *tv,
   if (ck->f&ACF_FRESH) tvec_endtest(tv);
 }
 
-int tvec_claim(struct tvec_state *tv, int ok,
-              const char *file, unsigned lno, const char *expr, ...)
+int tvec_claim_v(struct tvec_state *tv, int ok,
+                const char *file, unsigned lno,
+                const char *msg, va_list *ap)
 {
   struct adhoc_claim ck;
-  va_list ap;
 
   adhoc_claim_setup(tv, &ck, 0, file, lno);
-  if (!ok)
-    { va_start(ap, expr); tvec_fail_v(tv, expr, &ap); va_end(ap); }
+  if (!ok) tvec_fail_v(tv, msg, ap);
   adhoc_claim_teardown(tv, &ck);
   return (ok);
 }
 
+int tvec_claim(struct tvec_state *tv, int ok,
+              const char *file, unsigned lno, const char *msg, ...)
+{
+  va_list ap;
+
+  va_start(ap, msg); tvec_claim_v(tv, ok, file, lno, msg, &ap); va_end(ap);
+  return (ok);
+}
+
 int tvec_claimeq(struct tvec_state *tv,
                 const struct tvec_regty *ty, const union tvec_misc *arg,
                 const char *file, unsigned lno, const char *expr)
index fbdb347582e360dba8dfe8279e4b778718ea826b..c4809bb389ce337a2ab76edd6116abe5fd797756 100644 (file)
@@ -93,14 +93,12 @@ static int register_maxnamelen(const struct tvec_state *tv)
 
 /*----- Skeleton ----------------------------------------------------------*/
 /*
-static void ..._error(struct tvec_output *o, const char *msg, va_list *ap)
-static void ..._notice(struct tvec_output *o, const char *msg, va_list *ap)
-static void ..._bsession(struct tvec_output *o)
+static void ..._bsession(struct tvec_output *o, struct tvec_state *tv)
 static int ..._esession(struct tvec_output *o)
 static void ..._bgroup(struct tvec_output *o)
-static void ..._egroup(struct tvec_output *o, unsigned outcome)
 static void ..._skipgroup(struct tvec_output *o,
                          const char *excuse, va_list *ap)
+static void ..._egroup(struct tvec_output *o)
 static void ..._btest(struct tvec_output *o)
 static void ..._skip(struct tvec_output *o, const char *excuse, va_list *ap)
 static void ..._fail(struct tvec_output *o, const char *detail, va_list *ap)
@@ -112,14 +110,16 @@ static void ..._bbench(struct tvec_output *o,
 static void ..._ebench(struct tvec_output *o,
                       const char *ident, unsigned unit,
                       const struct tvec_timing *t)
+static void ..._error(struct tvec_output *o, const char *msg, va_list *ap)
+static void ..._notice(struct tvec_output *o, const char *msg, va_list *ap)
 static void ..._destroy(struct tvec_output *o)
 
 static const struct tvec_outops ..._ops = {
-  ..._error, ..._notice,
   ..._bsession, ..._esession,
   ..._bgroup, ..._egroup, ..._skip,
   ..._btest, ..._skip, ..._fail, ..._dumpreg, ..._etest,
   ..._bbench, ..._ebench,
+  ..._error, ..._notice,
   ..._destroy
 };
 */
@@ -151,6 +151,7 @@ static const struct tvec_outops ..._ops = {
 
 struct human_output {
   struct tvec_output _o;
+  struct tvec_state *tv;
   FILE *fp;
   dstr scoreboard;
   unsigned attr;
@@ -207,7 +208,7 @@ static void clear_progress(struct human_output *h)
   size_t i, n;
 
   if (h->f&HOF_PROGRESS) {
-    n = strlen(h->_o.tv->test->name) + 2 + h->scoreboard.len;
+    n = strlen(h->tv->test->name) + 2 + h->scoreboard.len;
     for (i = 0; i < n; i++) fputs("\b \b", h->fp);
     h->f &= ~HOF_PROGRESS;
   }
@@ -225,11 +226,11 @@ static void write_scoreboard_char(struct human_output *h, int ch)
 
 static void show_progress(struct human_output *h)
 {
-  struct tvec_state *tv = h->_o.tv;
+  struct tvec_state *tv = h->tv;
   const char *p, *l;
 
   if (tv->test && (h->f&HOF_TTY) && !(h->f&HOF_PROGRESS)) {
-    fprintf(h->fp, "%s: ", h->_o.tv->test->name);
+    fprintf(h->fp, "%s: ", tv->test->name);
     if (!(h->f&HOF_COLOUR))
       dstr_write(&h->scoreboard, h->fp);
     else for (p = h->scoreboard.buf, l = p + h->scoreboard.len; p < l; p++)
@@ -260,27 +261,8 @@ static void report_location(struct human_output *h, FILE *fp,
 #undef FLUSH
 }
 
-static void human_report(struct tvec_output *o, const char *msg, va_list *ap)
-{
-  struct human_output *h = (struct human_output *)o;
-  struct tvec_state *tv = h->_o.tv;
-  dstr d = DSTR_INIT;
-
-  dstr_vputf(&d, msg, ap); dstr_putc(&d, '\n');
-
-  clear_progress(h); fflush(h->fp);
-  fprintf(stderr, "%s: ", QUIS);
-  report_location(h, stderr, tv->infile, tv->lno);
-  fwrite(d.buf, 1, d.len, stderr);
-
-  if (h->f&HOF_DUPERR) {
-    report_location(h, h->fp, tv->infile, tv->lno);
-    fwrite(d.buf, 1, d.len, h->fp);
-  }
-  show_progress(h);
-}
-
-static void human_bsession(struct tvec_output *o) { ; }
+static void human_bsession(struct tvec_output *o, struct tvec_state *tv)
+  { struct human_output *h = (struct human_output *)o; h->tv = tv; }
 
 static void report_skipped(struct human_output *h, unsigned n)
 {
@@ -294,7 +276,7 @@ static void report_skipped(struct human_output *h, unsigned n)
 static int human_esession(struct tvec_output *o)
 {
   struct human_output *h = (struct human_output *)o;
-  struct tvec_state *tv = h->_o.tv;
+  struct tvec_state *tv = h->tv;
   unsigned
     all_win = tv->all[TVOUT_WIN], grps_win = tv->grps[TVOUT_WIN],
     all_lose = tv->all[TVOUT_LOSE], grps_lose = tv->grps[TVOUT_LOSE],
@@ -326,58 +308,51 @@ static int human_esession(struct tvec_output *o)
     fputs(" found in input; tests may not have run correctly\n", h->fp);
   }
 
-  return (tv->f&TVSF_ERROR ? 2 : tv->all[TVOUT_LOSE] ? 1 : 0);
+  h->tv = 0; return (tv->f&TVSF_ERROR ? 2 : tv->all[TVOUT_LOSE] ? 1 : 0);
 }
 
 static void human_bgroup(struct tvec_output *o)
 {
   struct human_output *h = (struct human_output *)o;
 
-  h->maxlen = register_maxnamelen(h->_o.tv);
+  h->maxlen = register_maxnamelen(h->tv);
   dstr_reset(&h->scoreboard); show_progress(h);
 }
 
-static void human_grpsumm(struct human_output *h, unsigned outcome)
+static void human_skipgroup(struct tvec_output *o,
+                           const char *excuse, va_list *ap)
 {
-  struct tvec_state *tv = h->_o.tv;
-  unsigned win = tv->curr[TVOUT_WIN], lose = tv->curr[TVOUT_LOSE],
-    skip = tv->curr[TVOUT_SKIP], run = win + lose;
+  struct human_output *h = (struct human_output *)o;
 
-  if (lose) {
-    assert(outcome == TVOUT_LOSE);
-    fprintf(h->fp, " %u/%u ", lose, run);
-    setattr(h, HA_LOSE); fputs("FAILED", h->fp); setattr(h, 0);
-    report_skipped(h, skip);
+  if (!(~h->f&(HOF_TTY | HOF_PROGRESS))) {
+    h->f &= ~HOF_PROGRESS;
+    setattr(h, HA_SKIP); fputs("skipped", h->fp); setattr(h, 0);
   } else {
-    assert(outcome == TVOUT_WIN);
-    fputc(' ', h->fp); setattr(h, HA_WIN); fputs("ok", h->fp); setattr(h, 0);
-    report_skipped(h, skip);
+    fprintf(h->fp, "%s: ", h->tv->test->name);
+    setattr(h, HA_SKIP); fputs("skipped", h->fp); setattr(h, 0);
   }
+  if (excuse) { fputs(": ", h->fp); vfprintf(h->fp, excuse, *ap); }
   fputc('\n', h->fp);
 }
 
-static void human_egroup(struct tvec_output *o, unsigned outcome)
+static void human_egroup(struct tvec_output *o)
 {
   struct human_output *h = (struct human_output *)o;
+  struct tvec_state *tv = h->tv;
+  unsigned win = tv->curr[TVOUT_WIN], lose = tv->curr[TVOUT_LOSE],
+    skip = tv->curr[TVOUT_SKIP], run = win + lose;
 
   if (h->f&HOF_TTY) h->f &= ~HOF_PROGRESS;
-  else fprintf(h->fp, "%s:", h->_o.tv->test->name);
-  human_grpsumm(h, outcome);
-}
-
-static void human_skipgroup(struct tvec_output *o,
-                           const char *excuse, va_list *ap)
-{
-  struct human_output *h = (struct human_output *)o;
+  else fprintf(h->fp, "%s:", h->tv->test->name);
 
-  if (!(~h->f&(HOF_TTY | HOF_PROGRESS))) {
-    h->f &= ~HOF_PROGRESS;
-    setattr(h, HA_SKIP); fputs("skipped", h->fp); setattr(h, 0);
+  if (lose) {
+    fprintf(h->fp, " %u/%u ", lose, run);
+    setattr(h, HA_LOSE); fputs("FAILED", h->fp); setattr(h, 0);
+    report_skipped(h, skip);
   } else {
-    fprintf(h->fp, "%s: ", h->_o.tv->test->name);
-    setattr(h, HA_SKIP); fputs("skipped", h->fp); setattr(h, 0);
+    fputc(' ', h->fp); setattr(h, HA_WIN); fputs("ok", h->fp); setattr(h, 0);
+    report_skipped(h, skip);
   }
-  if (excuse) { fputs(": ", h->fp); vfprintf(h->fp, excuse, *ap); }
   fputc('\n', h->fp);
 }
 
@@ -388,7 +363,7 @@ static void human_skip(struct tvec_output *o,
                       const char *excuse, va_list *ap)
 {
   struct human_output *h = (struct human_output *)o;
-  struct tvec_state *tv = h->_o.tv;
+  struct tvec_state *tv = h->tv;
 
   clear_progress(h);
   report_location(h, h->fp, tv->infile, tv->test_lno);
@@ -402,7 +377,7 @@ static void human_fail(struct tvec_output *o,
                       const char *detail, va_list *ap)
 {
   struct human_output *h = (struct human_output *)o;
-  struct tvec_state *tv = h->_o.tv;
+  struct tvec_state *tv = h->tv;
 
   clear_progress(h);
   report_location(h, h->fp, tv->infile, tv->test_lno);
@@ -452,7 +427,7 @@ static void human_bbench(struct tvec_output *o,
                         const char *ident, unsigned unit)
 {
   struct human_output *h = (struct human_output *)o;
-  struct tvec_state *tv = h->_o.tv;
+  struct tvec_state *tv = h->tv;
 
   clear_progress(h);
   fprintf(h->fp, "%s: %s: ", tv->test->name, ident); fflush(h->fp);
@@ -466,6 +441,26 @@ static void human_ebench(struct tvec_output *o,
   tvec_benchreport(&file_printops, h->fp, unit, tm); fputc('\n', h->fp);
 }
 
+static void human_report(struct tvec_output *o, const char *msg, va_list *ap)
+{
+  struct human_output *h = (struct human_output *)o;
+  struct tvec_state *tv = h->tv;
+  dstr d = DSTR_INIT;
+
+  dstr_vputf(&d, msg, ap); dstr_putc(&d, '\n');
+
+  clear_progress(h); fflush(h->fp);
+  fprintf(stderr, "%s: ", QUIS);
+  report_location(h, stderr, tv->infile, tv->lno);
+  fwrite(d.buf, 1, d.len, stderr);
+
+  if (h->f&HOF_DUPERR) {
+    report_location(h, h->fp, tv->infile, tv->lno);
+    fwrite(d.buf, 1, d.len, h->fp);
+  }
+  show_progress(h);
+}
+
 static void human_destroy(struct tvec_output *o)
 {
   struct human_output *h = (struct human_output *)o;
@@ -476,11 +471,11 @@ static void human_destroy(struct tvec_output *o)
 }
 
 static const struct tvec_outops human_ops = {
-  human_report, human_report,
   human_bsession, human_esession,
-  human_bgroup, human_egroup, human_skipgroup,
+  human_bgroup, human_skipgroup, human_egroup,
   human_btest, human_skip, human_fail, human_dumpreg, human_etest,
   human_bbench, human_ebench,
+  human_report, human_report,
   human_destroy
 };
 
@@ -521,6 +516,7 @@ struct tvec_output *tvec_humanoutput(FILE *fp)
 
 struct tap_output {
   struct tvec_output _o;
+  struct tvec_state *tv;
   FILE *fp;
   dstr d;
   int maxlen;
@@ -528,26 +524,6 @@ struct tap_output {
 #define TOF_FRESHLINE 1u
 };
 
-static void tap_report(struct tap_output *t, const char *msg, va_list *ap)
-{
-  struct tvec_state *tv = t->_o.tv;
-
-  if (tv->infile) fprintf(t->fp, "%s:%u: ", tv->infile, tv->lno);
-  vfprintf(t->fp, msg, *ap); fputc('\n', t->fp);
-}
-
-static void tap_error(struct tvec_output *o, const char *msg, va_list *ap)
-{
-  struct tap_output *t = (struct tap_output *)o;
-  fputs("Bail out!  ", t->fp); tap_report(t, msg, ap);
-}
-
-static void tap_notice(struct tvec_output *o, const char *msg, va_list *ap)
-{
-  struct tap_output *t = (struct tap_output *)o;
-  fputs("## ", t->fp); tap_report(t, msg, ap);
-}
-
 static int tap_writech(void *go, int ch)
 {
   struct tap_output *t = go;
@@ -601,11 +577,17 @@ static int tap_nwritef(void *go, size_t maxsz, const char *p, ...)
 static const struct gprintf_ops tap_printops =
   { tap_writech, tap_writem, tap_nwritef };
 
-static void tap_bsession(struct tvec_output *o) { ; }
+static void tap_bsession(struct tvec_output *o, struct tvec_state *tv)
+{
+  struct tap_output *t = (struct tap_output *)o;
+
+  t->tv = tv;
+  fputs("TAP version 13\n", t->fp);
+}
 
 static unsigned tap_grpix(struct tap_output *t)
 {
-  struct tvec_state *tv = t->_o.tv;
+  struct tvec_state *tv = t->tv;
 
   return (tv->grps[TVOUT_WIN] +
          tv->grps[TVOUT_LOSE] +
@@ -615,7 +597,7 @@ static unsigned tap_grpix(struct tap_output *t)
 static int tap_esession(struct tvec_output *o)
 {
   struct tap_output *t = (struct tap_output *)o;
-  struct tvec_state *tv = t->_o.tv;
+  struct tvec_state *tv = t->tv;
 
   if (tv->f&TVSF_ERROR) {
     fputs("Bail out!  "
@@ -625,19 +607,29 @@ static int tap_esession(struct tvec_output *o)
   }
 
   fprintf(t->fp, "1..%u\n", tap_grpix(t));
-  return (tv->all[TVOUT_LOSE] ? 1 : 0);
+  t->tv = 0; return (tv->all[TVOUT_LOSE] ? 1 : 0);
 }
 
 static void tap_bgroup(struct tvec_output *o)
 {
   struct tap_output *t = (struct tap_output *)o;
-  t->maxlen = register_maxnamelen(t->_o.tv);
+  t->maxlen = register_maxnamelen(t->tv);
+}
+
+static void tap_skipgroup(struct tvec_output *o,
+                         const char *excuse, va_list *ap)
+{
+  struct tap_output *t = (struct tap_output *)o;
+
+  fprintf(t->fp, "ok %u %s # SKIP", tap_grpix(t), t->tv->test->name);
+  if (excuse) { fputc(' ', t->fp); vfprintf(t->fp, excuse, *ap); }
+  fputc('\n', t->fp);
 }
 
-static void tap_egroup(struct tvec_output *o, unsigned outcome)
+static void tap_egroup(struct tvec_output *o)
 {
   struct tap_output *t = (struct tap_output *)o;
-  struct tvec_state *tv = t->_o.tv;
+  struct tvec_state *tv = t->tv;
   unsigned
     grpix = tap_grpix(t),
     win = tv->curr[TVOUT_WIN],
@@ -645,35 +637,22 @@ static void tap_egroup(struct tvec_output *o, unsigned outcome)
     skip = tv->curr[TVOUT_SKIP];
 
   if (lose) {
-    assert(outcome == TVOUT_LOSE);
-    fprintf(t->fp, "not ok %u %s: FAILED %u/%u",
+    fprintf(t->fp, "not ok %u - %s: FAILED %u/%u",
            grpix, tv->test->name, lose, win + lose);
     if (skip) fprintf(t->fp, " (skipped %u)", skip);
   } else {
-    assert(outcome == TVOUT_WIN);
-    fprintf(t->fp, "ok %u %s: passed %u", grpix, tv->test->name, win);
+    fprintf(t->fp, "ok %u - %s: passed %u", grpix, tv->test->name, win);
     if (skip) fprintf(t->fp, " (skipped %u)", skip);
   }
   fputc('\n', t->fp);
 }
 
-static void tap_skipgroup(struct tvec_output *o,
-                         const char *excuse, va_list *ap)
-{
-  struct tap_output *t = (struct tap_output *)o;
-
-  fprintf(t->fp, "ok %u %s # SKIP", tap_grpix(t), t->_o.tv->test->name);
-  if (excuse)
-    { fputc(' ', t->fp); vfprintf(t->fp, excuse, *ap); }
-  fputc('\n', t->fp);
-}
-
 static void tap_btest(struct tvec_output *o) { ; }
 
 static void tap_skip(struct tvec_output *o, const char *excuse, va_list *ap)
 {
   struct tap_output *t = (struct tap_output *)o;
-  struct tvec_state *tv = t->_o.tv;
+  struct tvec_state *tv = t->tv;
 
   fprintf(t->fp, "## %s:%u: `%s' skipped",
          tv->infile, tv->test_lno, tv->test->name);
@@ -684,7 +663,7 @@ static void tap_skip(struct tvec_output *o, const char *excuse, va_list *ap)
 static void tap_fail(struct tvec_output *o, const char *detail, va_list *ap)
 {
   struct tap_output *t = (struct tap_output *)o;
-  struct tvec_state *tv = t->_o.tv;
+  struct tvec_state *tv = t->tv;
 
   fprintf(t->fp, "## %s:%u: `%s' FAILED",
          tv->infile, tv->test_lno, tv->test->name);
@@ -720,13 +699,33 @@ static void tap_ebench(struct tvec_output *o,
                       const struct bench_timing *tm)
 {
   struct tap_output *t = (struct tap_output *)o;
-  struct tvec_state *tv = t->_o.tv;
+  struct tvec_state *tv = t->tv;
 
   fprintf(t->fp, "## %s: %s: ", tv->test->name, ident);
   t->f &= ~TOF_FRESHLINE; tvec_benchreport(&tap_printops, t, unit, tm);
   fputc('\n', t->fp);
 }
 
+static void tap_report(struct tap_output *t, const char *msg, va_list *ap)
+{
+  struct tvec_state *tv = t->tv;
+
+  if (tv->infile) fprintf(t->fp, "%s:%u: ", tv->infile, tv->lno);
+  vfprintf(t->fp, msg, *ap); fputc('\n', t->fp);
+}
+
+static void tap_error(struct tvec_output *o, const char *msg, va_list *ap)
+{
+  struct tap_output *t = (struct tap_output *)o;
+  fputs("Bail out!  ", t->fp); tap_report(t, msg, ap);
+}
+
+static void tap_notice(struct tvec_output *o, const char *msg, va_list *ap)
+{
+  struct tap_output *t = (struct tap_output *)o;
+  fputs("## ", t->fp); tap_report(t, msg, ap);
+}
+
 static void tap_destroy(struct tvec_output *o)
 {
   struct tap_output *t = (struct tap_output *)o;
@@ -737,11 +736,11 @@ static void tap_destroy(struct tvec_output *o)
 }
 
 static const struct tvec_outops tap_ops = {
-  tap_error, tap_notice,
   tap_bsession, tap_esession,
-  tap_bgroup, tap_egroup, tap_skipgroup,
+  tap_bgroup, tap_skipgroup, tap_egroup,
   tap_btest, tap_skip, tap_fail, tap_dumpreg, tap_etest,
   tap_bbench, tap_ebench,
+  tap_error, tap_notice,
   tap_destroy
 };
 
index 8be8ae2004bcc13482e154194c99013322ebd360..1984a1e3744f21802f3ebbadb0d15e856a0ab9ac 100644 (file)
 #  define INFP(x) ((x) > DBL_MAX || (x) < DBL_MIN)
 #endif
 
+#ifdef signbit
+#  define NEGP(x) signbit(x)
+#else
+#  define NEGP(x) ((x) < 0.0)
+#endif
+
+static void trivial_release(union tvec_regval *rv,
+                           const struct tvec_regdef *rd)
+  { ; }
+
 static int signed_to_buf(buf *b, long i)
 {
   kludge64 k;
@@ -130,46 +140,129 @@ static int check_unsigned_range(unsigned long u,
   return (0);
 }
 
+static int chtodig(int ch)
+{
+  if ('0' <= ch && ch <= '9') return (ch - '0');
+  else if ('a' <= ch && ch <= 'z') return (ch - 'a' + 10);
+  else if ('A' <= ch && ch <= 'Z') return (ch - 'A' + 10);
+  else return (-1);
+}
+
+static int parse_unsigned_integer(unsigned long *u_out, const char **q_out,
+                                 const char *p)
+{
+  unsigned long u;
+  int ch, d, r;
+  const char *q;
+  unsigned f = 0;
+#define f_implicit 1u
+#define f_digit 2u
+#define f_uscore 4u
+
+  if (p[0] != '0' || !p[1]) {
+    d = chtodig(*p); if (0 > d || d >= 10) return (-1);
+    r = 10; u = d; p++; f |= f_implicit | f_digit;
+  } else {
+    u = 0; d = chtodig(p[2]);
+    if (d < 0) { r = 10; f |= f_implicit | f_digit; p++; }
+    else if ((p[1] == 'x' || p[1] == 'X') && d < 16) { r = 16; p += 2; }
+    else if ((p[1] == 'o' || p[1] == 'O') && d < 8) { r = 8; p += 2; }
+    else if ((p[1] == 'b' || p[1] == 'B') && d < 2) { r = 2; p += 2; }
+    else { r = 10; f |= f_digit; p++; }
+  }
+
+  q = p;
+  for (;;) {
+    ch = *p;
+    if (ch == '_') {
+      if (f&f_uscore) break;
+      p++; f = (f&~f_implicit) | f_uscore;
+    }
+    else if (ch == 'r' || ch == 'R') {
+      if (!(f&f_implicit) || !u || u >= 36) break;
+      d = chtodig(p[1]); if (0 > d || d >= u) break;
+      r = u; u = d; f = (f&~f_implicit) | f_digit; p += 2; q = p;
+    } else {
+      d = chtodig(ch);
+      if (d < 0 || d >= r) break;
+      if (u > ULONG_MAX/r) return (-1);
+      u *= r; if (u > ULONG_MAX - d) return (-1);
+      u += d; f = (f&~f_uscore) | f_digit; p++; q = p;
+    }
+  }
+
+  if (!(f&f_digit)) return (-1);
+  *u_out = u; *q_out = q; return (0);
+
+#undef f_implicit
+#undef f_digit
+#undef f_uscore
+}
+
+static int parse_signed_integer(long *i_out, const char **q_out,
+                               const char *p)
+{
+  unsigned long u;
+  unsigned f = 0;
+#define f_neg 1u
+
+  if (*p == '+') p++;
+  else if (*p == '-') { f |= f_neg; p++; }
+
+  if (parse_unsigned_integer(&u, q_out, p)) return (-1);
+
+  if (!(f&f_neg)) {
+    if (u > LONG_MAX) return (-1);
+    *i_out = u;
+  } else {
+    if (u && u - 1 > -(LONG_MIN + 1)) return (-1);
+    *i_out = u ? -(long)(u - 1) - 1 : 0;
+  }
+
+  return (0);
+
+#undef f_neg
+}
+
 static int parse_signed(long *i_out, const char *p,
                        const struct tvec_irange *ir,
                        struct tvec_state *tv)
 {
-  char *q; const char *pp;
-  int olderr;
   long i;
+  const char *q;
 
-  olderr = errno; errno = 0;
-  pp = p; if (*pp == '-' || *pp == '+') pp++;
-  if (!ISDIGIT(*pp))
-    return (tvec_syntax(tv, *pp ? *pp : fgetc(tv->fp), "signed integer"));
-  i = strtol(p, &q, 0);
-  if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line"));
-  if (errno)
-    return (tvec_error(tv, "invalid integer `%s': %s", p, strerror(errno)));
+  if (parse_signed_integer(&i, &q, p))
+    return (tvec_error(tv, "invalid signed integer `%s'", p));
+  if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
   if (check_signed_range(i, ir, tv)) return (-1);
-  errno = olderr; *i_out = i;
-  return (0);
+  *i_out = i; return (0);
 }
 
 static int parse_unsigned(unsigned long *u_out, const char *p,
                          const struct tvec_urange *ur,
                          struct tvec_state *tv)
 {
-  char *q;
-  int olderr;
   unsigned long u;
+  const char *q;
 
-  olderr = errno; errno = 0;
-  if (!ISDIGIT(*p)) return (tvec_syntax(tv, *p, "unsigned integer"));
-  u = strtoul(p, &q, 0);
-  if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line"));
-  if (errno)
-    return (tvec_error(tv, "invalid integer `%s': %s", p, strerror(errno)));
+  if (parse_unsigned_integer(&u, &q, p))
+    return (tvec_error(tv, "invalid unsigned integer `%s'", p));
+  if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
   if (check_unsigned_range(u, ur, tv)) return (-1);
-  errno = olderr; *u_out = u;
-  return (0);
+  *u_out = u; return (0);
+}
+
+static void format_signed_hex(const struct gprintf_ops *gops, void *go,
+                             long i)
+{
+  unsigned long u = i >= 0 ? i : -(unsigned long)i;
+  gprintf(gops, go, "%s0x%0*lx", i < 0 ? "-" : "", hex_width(u), u);
 }
 
+static void format_unsigned_hex(const struct gprintf_ops *gops, void *go,
+                               unsigned long u)
+  { gprintf(gops, go, "0x%0*lx", hex_width(u), u); }
+
 static void format_floating(const struct gprintf_ops *gops, void *go,
                            double x)
 {
@@ -272,13 +365,11 @@ static int eqish_floating_p(double x, double y,
 
   switch (fi ? fi->f&TVFF_EQMASK : TVFF_EXACT) {
     case TVFF_EXACT:
-      return (x == y);
+      return (x == y && NEGP(x) == NEGP(y));
     case TVFF_ABSDELTA:
       t = x - y; if (t < 0) t = -t; return (t < fi->delta);
     case TVFF_RELDELTA:
-      xx = x >= 0 ? x : -x; yy = y >= 0 ? y : -y;
-      if (xx < yy) { t = x; x = y; y = t; }
-      return (1.0 - y/x < fi->delta);
+      t = 1.0 - y/x; if (t < 0) t = -t; return (t < fi->delta);
     default:
       abort();
   }
@@ -303,7 +394,7 @@ static int parse_floating(double *x_out, const char *p,
   } else if (STRCMP(p, ==, "#inf") ||
             STRCMP(p, ==, "#+inf") ||
             STRCMP(p, ==, "+#inf")) {
-#ifdef NAN
+#ifdef INFINITY
     x = INFINITY; rc = 0;
 #else
     tvec_error(tv, "infinity not supported on this system");
@@ -311,7 +402,7 @@ static int parse_floating(double *x_out, const char *p,
 #endif
   } else if (STRCMP(p, ==, "#-inf") ||
             STRCMP(p, ==, "-#inf")) {
-#ifdef NAN
+#ifdef INFINITY
     x = -INFINITY; rc = 0;
 #else
     tvec_error(tv, "infinity not supported on this system");
@@ -322,7 +413,7 @@ static int parse_floating(double *x_out, const char *p,
     if (*pp == '+' || *pp == '-') pp++;
     if (*pp == '.') pp++;
     if (!ISDIGIT(*pp)) {
-      tvec_syntax(tv, *pp ? *pp : fgetc(tv->fp), "floating-point number");
+      tvec_syntax(tv, *p ? *p : fgetc(tv->fp), "floating-point number");
       rc = -1; goto end;
     }
     olderr = errno; errno = 0;
@@ -464,8 +555,8 @@ end:
 }
 
 #define FCF_BRACE 1u
-static void format_charesc(int ch, unsigned f,
-                          const struct gprintf_ops *gops, void *go)
+static void format_charesc(const struct gprintf_ops *gops, void *go,
+                          int ch, unsigned f)
 {
   switch (ch) {
     case '\a': gprintf(gops, go, "\\a"); break;
@@ -487,7 +578,7 @@ static void format_charesc(int ch, unsigned f,
   }
 }
 
-static void format_char(int ch, const struct gprintf_ops *gops, void *go)
+static void format_char(const struct gprintf_ops *gops, void *go, int ch)
 {
   if (ch == EOF)
     gprintf(gops, go, "#eof");
@@ -495,11 +586,25 @@ static void format_char(int ch, const struct gprintf_ops *gops, void *go)
     gprintf(gops, go, "'%c'", ch);
   else {
     gprintf(gops, go, "'");
-    format_charesc(ch, 0, gops, go);
+    format_charesc(gops, go, ch, 0);
     gprintf(gops, go, "'");
   }
 }
 
+static void maybe_format_signed_char
+  (const struct gprintf_ops *gops, void *go, long i)
+{
+  if (i == EOF || (0 <= i && i < UCHAR_MAX))
+    { gprintf(gops, go, " = "); format_char(gops, go, i); }
+}
+
+static void maybe_format_unsigned_char
+  (const struct gprintf_ops *gops, void *go, unsigned long u)
+{
+  if (u < UCHAR_MAX)
+    { gprintf(gops, go, " = "); format_char(gops, go, u); }
+}
+
 enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 };
 
 static int collect_bare(dstr *d, struct tvec_state *tv)
@@ -656,9 +761,6 @@ static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd)
 static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd)
   { rv->u = 0; }
 
-static void release_int(union tvec_regval *rv, const struct tvec_regdef *rd)
-  { ; }
-
 static int eq_int(const union tvec_regval *rv0, const union tvec_regval *rv1,
                  const struct tvec_regdef *rd)
   { return (rv0->i == rv1->i); }
@@ -725,16 +827,12 @@ static void dump_int(const union tvec_regval *rv,
                     unsigned style,
                     const struct gprintf_ops *gops, void *go)
 {
-  unsigned long u;
 
   gprintf(gops, go, "%ld", rv->i);
   if (!(style&TVSF_COMPACT)) {
-    if (rv->i >= 0) u = rv->i;
-    else u = -(unsigned long)rv->i;
-    gprintf(gops, go, " ; = %s0x%0*lx",
-           rv->i < 0 ? "-" : "", hex_width(u), u);
-    if (rv->i == EOF || (0 <= rv->i && rv->i < UCHAR_MAX))
-      { gprintf(gops, go, " = "); format_char(rv->i, gops, go); }
+    gprintf(gops, go, " ; = ");
+    format_signed_hex(gops, go, rv->i);
+    maybe_format_signed_char(gops, go, rv->i);
   }
 }
 
@@ -745,14 +843,14 @@ static void dump_uint(const union tvec_regval *rv,
 {
   gprintf(gops, go, "%lu", rv->u);
   if (!(style&TVSF_COMPACT)) {
-    gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
-    if (rv->u < UCHAR_MAX)
-      { gprintf(gops, go, " = "); format_char(rv->u, gops, go); }
+    gprintf(gops, go, " ; = ");
+    format_unsigned_hex(gops, go, rv->u);
+    maybe_format_unsigned_char(gops, go, rv->u);
   }
 }
 
 const struct tvec_regty tvty_int = {
-  init_int, release_int, eq_int,
+  init_int, trivial_release, eq_int,
   tobuf_int, frombuf_int,
   parse_int, dump_int
 };
@@ -767,7 +865,7 @@ const struct tvec_irange
   tvrange_i32 = { -2147483648, 2147483647 };
 
 const struct tvec_regty tvty_uint = {
-  init_uint, release_int, eq_uint,
+  init_uint, trivial_release, eq_uint,
   tobuf_uint, frombuf_uint,
   parse_uint, dump_uint
 };
@@ -785,7 +883,7 @@ const struct tvec_urange
 int tvec_claimeq_int(struct tvec_state *tv, long i0, long i1,
                     const char *file, unsigned lno, const char *expr)
 {
-  tv->in[0].v.i = i0; tv->out[0].v.i = i1;
+  tv->out[0].v.i = i0; tv->in[0].v.i = i1;
   return (tvec_claimeq(tv, &tvty_int, 0, file, lno, expr));
 }
 
@@ -793,17 +891,14 @@ int tvec_claimeq_uint(struct tvec_state *tv,
                      unsigned long u0, unsigned long u1,
                      const char *file, unsigned lno, const char *expr)
 {
-  tv->in[0].v.u = u0; tv->out[0].v.u = u1;
+  tv->out[0].v.u = u0; tv->in[0].v.u = u1;
   return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr));
 }
 
-/*----- Main code ---------------------------------------------------------*/
+/*----- Floating-point type -----------------------------------------------*/
 
 static void init_float(union tvec_regval *rv, const struct tvec_regdef *rd)
   { rv->f = 0.0; }
-static void release_float(union tvec_regval *rv,
-                         const struct tvec_regdef *rd)
-  { ; }
 
 static int eq_float(const union tvec_regval *rv0,
                    const union tvec_regval *rv1,
@@ -842,11 +937,19 @@ static void dump_float(const union tvec_regval *rv,
   { format_floating(gops, go, rv->f); }
 
 const struct tvec_regty tvty_float = {
-  init_float, release_float, eq_float,
+  init_float, trivial_release, eq_float,
   tobuf_float, frombuf_float,
   parse_float, dump_float
 };
 
+int tvec_claimeq_float(struct tvec_state *tv,
+                      double f0, double f1,
+                      const char *file, unsigned lno,
+                      const char *expr)
+{
+  return (tvec_claimeqish_float(tv, f0, f1, TVFF_EXACT, 0.0,
+                               file, lno, expr));
+}
 int tvec_claimeqish_float(struct tvec_state *tv,
                          double f0, double f1, unsigned f, double delta,
                          const char *file, unsigned lno,
@@ -856,277 +959,197 @@ int tvec_claimeqish_float(struct tvec_state *tv,
   union tvec_misc arg;
 
   fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi;
-  tv->in[0].v.f = f0; tv->in[1].v.f = f1;
+  tv->out[0].v.f = f0; tv->in[0].v.f = f1;
   return (tvec_claimeq(tv, &tvty_float, &arg, file, lno, expr));
 }
-int tvec_claimeq_float(struct tvec_state *tv,
-                      double f0, double f1,
-                      const char *file, unsigned lno,
-                      const char *expr)
-{
-  return (tvec_claimeqish_float(tv, f0, f1, TVFF_EXACT, 0.0,
-                               file, lno, expr));
-}
 
 /*----- Enumerations ------------------------------------------------------*/
 
-static void init_enum(union tvec_regval *rv, const struct tvec_regdef *rd)
-{
-  const struct tvec_enuminfo *ei = rd->arg.p;
-
-  switch (ei->mv) {
-#define CASE(tag, ty, slot)                                            \
-       case TVMISC_##tag: rv->slot = 0; break;
-    TVEC_MISCSLOTS(CASE)
-#undef CASE
-    default: abort();
-  }
-}
+#define init_ienum init_int
+#define init_uenum init_uint
+#define init_fenum init_float
+static void init_penum(union tvec_regval *rv, const struct tvec_regdef *rd)
+  { rv->p = 0; }
 
-static int eq_enum(const union tvec_regval *rv0,
-                  const union tvec_regval *rv1,
-                  const struct tvec_regdef *rd)
+#define eq_ienum eq_int
+#define eq_uenum eq_uint
+static int eq_fenum(const union tvec_regval *rv0,
+                   const union tvec_regval *rv1,
+                   const struct tvec_regdef *rd)
 {
-  const struct tvec_enuminfo *ei = rd->arg.p;
-  const struct tvec_fenuminfo *fei;
-
-  switch (ei->mv) {
-#define CASE(tag, ty, slot)                                            \
-       case TVMISC_##tag: PREP_##tag return (HANDLE_##tag);
-#define PREP_INT
-#define HANDLE_INT     rv0->i == rv1->i
-#define PREP_UINT
-#define HANDLE_UINT    rv0->u == rv1->u
-#define PREP_FLT       fei = (const struct tvec_fenuminfo *)ei;
-#define HANDLE_FLT     eqish_floating_p(rv0->f, rv1->f, fei->fi)
-#define PREP_PTR
-#define HANDLE_PTR     rv0->p == rv1->p
-    TVEC_MISCSLOTS(CASE)
-#undef CASE
-#undef PREP_INT
-#undef HANDLE_INT
-#undef PREP_UINT
-#undef HANDLE_UINT
-#undef PREP_FLT
-#undef HANDLE_FLT
-#undef PREP_PTR
-#undef HANDLE_PTR
-    default: abort();
-  }
+  const struct tvec_fenuminfo *ei = rd->arg.p;
+  return (eqish_floating_p(rv0->f, rv1->f, ei->fi));
 }
+static int eq_penum(const union tvec_regval *rv0,
+                   const union tvec_regval *rv1,
+                   const struct tvec_regdef *rd)
+  { return (rv0->p == rv1->p); }
 
-static int tobuf_enum(buf *b, const union tvec_regval *rv,
-                     const struct tvec_regdef *rd)
+#define tobuf_ienum tobuf_int
+#define tobuf_uenum tobuf_uint
+#define tobuf_fenum tobuf_float
+static int tobuf_penum(buf *b, const union tvec_regval *rv,
+                      const struct tvec_regdef *rd)
 {
-  const struct tvec_enuminfo *ei = rd->arg.p;
-  const struct tvec_penuminfo *pei;
+  const struct tvec_penuminfo *pei = rd->arg.p;
   const struct tvec_passoc *pa;
   long i;
 
-  switch (ei->mv) {
-#define CASE(tag, ty, slot)                                            \
-       case TVMISC_##tag: HANDLE_##tag
-#define HANDLE_INT     return (signed_to_buf(b, rv->i));
-#define HANDLE_UINT    return (unsigned_to_buf(b, rv->u));
-#define HANDLE_FLT     return (buf_putf64l(b, rv->f));
-#define HANDLE_PTR                                                     \
-       pei = (const struct tvec_penuminfo *)ei;                        \
-       for (pa = pei->av, i = 0; pa->tag; pa++, i++)                   \
-         if (pa->p == rv->p) goto found;                               \
-       if (!rv->p) i = -1;                                             \
-       else return (-1);                                               \
-      found:                                                           \
-       return (signed_to_buf(b, i));
-    TVEC_MISCSLOTS(CASE)
-#undef CASE
-#undef HANDLE_INT
-#undef HANDLE_UINT
-#undef HANDLE_FLT
-#undef HANDLE_PTR
-    default: abort();
-  }
-  return (0);
+  for (pa = pei->av, i = 0; pa->tag; pa++, i++)
+    if (pa->p == rv->p) goto found;
+  if (!rv->p) i = -1;
+  else return (-1);
+found:
+  return (signed_to_buf(b, i));
 }
 
-static int frombuf_enum(buf *b, union tvec_regval *rv,
+#define frombuf_ienum frombuf_int
+#define frombuf_uenum frombuf_uint
+#define frombuf_fenum frombuf_float
+static int frombuf_penum(buf *b, union tvec_regval *rv,
                        const struct tvec_regdef *rd)
 {
-  const struct tvec_enuminfo *ei = rd->arg.p;
-  const struct tvec_penuminfo *pei;
+  const struct tvec_penuminfo *pei = rd->arg.p;
   const struct tvec_passoc *pa;
   long i, n;
 
-  switch (ei->mv) {
-#define CASE(tag, ty, slot)                                            \
-       case TVMISC_##tag: HANDLE_##tag
-#define HANDLE_INT     return (signed_from_buf(b, &rv->i));
-#define HANDLE_UINT    return (unsigned_from_buf(b, &rv->u));
-#define HANDLE_FLT     return (buf_getf64l(b, &rv->f));
-#define HANDLE_PTR                                                     \
-       pei = (const struct tvec_penuminfo *)ei;                        \
-       for (pa = pei->av, n = 0; pa->tag; pa++, n++);                  \
-       if (signed_from_buf(b, &i)) return (-1);                        \
-       if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p;  \
-       else if (i == -1) rv->p = 0;                                    \
-       else return (-1);                                               \
-       return (0);
-    TVEC_MISCSLOTS(CASE)
-#undef CASE
-#undef HANDLE_INT
-#undef HANDLE_UINT
-#undef HANDLE_FLT
-#undef HANDLE_PTR
-    default: abort();
-  }
+  for (pa = pei->av, n = 0; pa->tag; pa++, n++);
+  if (signed_from_buf(b, &i)) return (-1);
+  if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p;
+  else if (i == -1) rv->p = 0;
+  else return (-1);
+  return (0);
 }
 
-static int parse_enum(union tvec_regval *rv, const struct tvec_regdef *rd,
-                     struct tvec_state *tv)
-{
-  const struct tvec_enuminfo *ei = rd->arg.p;
-#define DECLS(tag, ty, slot)                                           \
-       const struct tvec_##slot##enuminfo *slot##ei;                   \
-       const struct tvec_##slot##assoc *slot##a;
-  TVEC_MISCSLOTS(DECLS)
-#undef DECLS
-  dstr d = DSTR_INIT;
-  int rc;
+#define DEFPARSE_ENUM(tag_, ty, slot)                                  \
+  static int parse_##slot##enum(union tvec_regval *rv,                 \
+                               const struct tvec_regdef *rd,           \
+                               struct tvec_state *tv)                  \
+  {                                                                    \
+    const struct tvec_##slot##enuminfo *ei = rd->arg.p;                        \
+    const struct tvec_##slot##assoc *a;                                        \
+    dstr d = DSTR_INIT;                                                        \
+    int rc;                                                            \
+                                                                       \
+    if (tvec_readword(tv, &d, ";", "enumeration tag or " LITSTR_##tag_)) \
+      { rc = -1; goto end; }                                           \
+    for (a = ei->av; a->tag; a++)                                      \
+      if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; }       \
+    MISSING_##tag_                                                     \
+    done:                                                              \
+    if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }                 \
+    rc = 0;                                                            \
+  end:                                                                 \
+    dstr_destroy(&d);                                                  \
+    return (rc);                                                       \
+  }
 
-  if (tvec_readword(tv, &d, ";", "enumeration tag or literal integer"))
-    { rc = -1; goto end; }
-  switch (ei->mv) {
-
-#define CASE(tag_, ty, slot)                                           \
-       case TVMISC_##tag_:                                             \
-         slot##ei = (const struct tvec_##slot##enuminfo *)ei;          \
-         for (slot##a = slot##ei->av; slot##a->tag; slot##a++)         \
-           if (STRCMP(d.buf, ==, slot##a->tag))                        \
-             { rv->slot = FETCH_##tag_; goto done; }                   \
-         HANDLE_##tag_ goto done;
-
-#define FETCH_INT (ia->i)
-#define HANDLE_INT                                                     \
-       if (parse_signed(&rv->i, d.buf, iei->ir, tv))                   \
-         { rc = -1; goto end; }
-
-#define FETCH_UINT (ua->u)
-#define HANDLE_UINT                                                    \
-       if (parse_unsigned(&rv->u, d.buf, uei->ur, tv))                 \
-         { rc = -1; goto end; }
-
-#define FETCH_FLT (fa->f)
-#define HANDLE_FLT                                                     \
-       if (parse_floating(&rv->f, d.buf, fei->fi, tv))                 \
-         { rc = -1; goto end; }
-
-#define FETCH_PTR ((/*unconst*/ void *)(pa->p))
-#define HANDLE_PTR                                                     \
-       if (STRCMP(d.buf, ==, "#nil")) rv->p = 0;                       \
-       else goto tagonly;
-
-    TVEC_MISCSLOTS(CASE)
-
-#undef CASE
-#undef FETCH_INT
-#undef HANDLE_INT
-#undef FETCH_UINT
-#undef HANDLE_UINT
-#undef FETCH_FLT
-#undef HANDLE_FLT
-#undef FETCH_PTR
-#undef HANDLE_PTR
-
-    tagonly:
-      tvec_error(tv, "unknown `%s' value `%s'", ei->name, d.buf);
-      rc = -1; goto end;
+#define LITSTR_INT     "literal signed integer"
+#define FOUND_INT      rv->i = a->i;
+#define MISSING_INT    if (parse_signed(&rv->i, d.buf, ei->ir, tv))    \
+                         { rc = -1; goto end; }
+
+#define LITSTR_UINT    "literal unsigned integer"
+#define FOUND_UINT     rv->u = a->u;
+#define MISSING_UINT   if (parse_unsigned(&rv->u, d.buf, ei->ur, tv))  \
+                         { rc = -1; goto end; }
+
+#define LITSTR_FLT     "literal floating-point number, "               \
+                         "`#-inf', `#+inf', or `#nan'"
+#define FOUND_FLT      rv->f = a->f;
+#define MISSING_FLT    if (parse_floating(&rv->f, d.buf, ei->fi, tv))  \
+                         { rc = -1; goto end; }
+
+#define LITSTR_PTR     "`#nil'"
+#define FOUND_PTR      rv->p = (/*unconst*/ void *)a->p;
+#define MISSING_PTR    if (STRCMP(d.buf, ==, "#nil"))                  \
+                         rv->p = 0;                                    \
+                       else {                                          \
+                         tvec_error(tv, "unknown `%s' value `%s'",     \
+                                    ei->name, d.buf);                  \
+                         rc = -1; goto end;                            \
+                       }
+
+TVEC_MISCSLOTS(DEFPARSE_ENUM)
+
+#undef LITSTR_INT
+#undef FOUND_INT
+#undef MISSING_INT
+
+#undef LITSTR_UINT
+#undef FOUND_UINT
+#undef MISSING_UINT
+
+#undef LITSTR_FLT
+#undef FOUND_FLT
+#undef MISSING_FLT
+
+#undef LITSTR_PTR
+#undef FOUND_PTR
+#undef MISSING_PTR
+
+#undef DEFPARSE_ENUM
+
+#define DEFDUMP_ENUM(tag_, ty, slot)                                   \
+  static void dump_##slot##enum(const union tvec_regval *rv,           \
+                               const struct tvec_regdef *rd,           \
+                               unsigned style,                         \
+                               const struct gprintf_ops *gops, void *go) \
+  {                                                                    \
+    const struct tvec_##slot##enuminfo *ei = rd->arg.p;                        \
+    const struct tvec_##slot##assoc *a;                                        \
+                                                                       \
+    for (a = ei->av; a->tag; a++)                                      \
+      if (rv->slot == a->slot) {                                       \
+       gprintf(gops, go, "%s", a->tag);                                \
+       if (style&TVSF_COMPACT) return;                                 \
+       gprintf(gops, go, " ; = "); break;                              \
+      }                                                                        \
+                                                                       \
+    PRINTRAW_##tag_                                                    \
   }
 
-done:
-  if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
-  rc = 0;
-end:
-  dstr_destroy(&d);
-  return (rc);
-}
+#define MAYBE_PRINT_EXTRA                                              \
+       if (style&TVSF_COMPACT) ;                                       \
+       else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; }  \
+       else if (1) { gprintf(gops, go, " = "); goto _extra; }          \
+       else _extra:
 
-static void dump_enum(const union tvec_regval *rv,
-                     const struct tvec_regdef *rd,
-                     unsigned style,
-                     const struct gprintf_ops *gops, void *go)
-{
-  const struct tvec_enuminfo *ei = rd->arg.p;
-#define DECLS(tag, ty, slot)                                           \
-       const struct tvec_##slot##enuminfo *slot##ei;                   \
-       const struct tvec_##slot##assoc *slot##a;
-  TVEC_MISCSLOTS(DECLS)
-#undef DECLS
-  const char *tag;
-  unsigned long u;
-  unsigned f = 0;
-#define f_known 1u
-
-  switch (ei->mv) {
-#define CASE(tag_, ty, slot)                                           \
-       case TVMISC_##tag_:                                             \
-         slot##ei = (const struct tvec_##slot##enuminfo *)ei;          \
-         for (slot##a = slot##ei->av; slot##a->tag; slot##a++)         \
-           if (rv->slot == slot##a->slot)                              \
-             { tag = slot##a->tag; goto found; }                       \
-         break;
-    TVEC_MISCSLOTS(CASE)
-#undef CASE
-    default: abort();
-  }
-  goto print_raw;
+#define PRINTRAW_INT   gprintf(gops, go, "%ld", rv->i);                \
+                       MAYBE_PRINT_EXTRA {                             \
+                         format_signed_hex(gops, go, rv->i);           \
+                         maybe_format_signed_char(gops, go, rv->i);    \
+                       }
 
-found:
-  f |= f_known;
-  gprintf(gops, go, "%s", tag);
-  if (style&TVSF_COMPACT) return;
-  gprintf(gops, go, " ; = ");
-
-print_raw:
-  switch (ei->mv) {
-#define CASE(tag, ty, slot)                                            \
-       case TVMISC_##tag: HANDLE_##tag break;
-#define HANDLE_INT     gprintf(gops, go, "%ld", rv->i);
-#define HANDLE_UINT    gprintf(gops, go, "%lu", rv->u);
-#define HANDLE_FLT     format_floating(gops, go, rv->f);
-#define HANDLE_PTR     if (!rv->p) gprintf(gops, go, "#nil");          \
+#define PRINTRAW_UINT  gprintf(gops, go, "%lu", rv->u);                \
+                       MAYBE_PRINT_EXTRA {                             \
+                         format_unsigned_hex(gops, go, rv->u);         \
+                         maybe_format_unsigned_char(gops, go, rv->u);  \
+                       }
+
+#define PRINTRAW_FLT   format_floating(gops, go, rv->f);
+
+#define PRINTRAW_PTR   if (!rv->p) gprintf(gops, go, "#nil");          \
                        else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
-    TVEC_MISCSLOTS(CASE)
-#undef CASE
-#undef HANDLE_INT
-#undef HANDLE_UINT
-#undef HANDLE_FLT
-#undef HANDLE_PTR
-  }
 
-  if (style&TVSF_COMPACT) return;
-  switch (ei->mv) {
-    case TVMISC_INT:
-      if (!(f&f_known)) gprintf(gops, go, " ;");
-      if (rv->i >= 0) u = rv->i;
-      else u = -(unsigned long)rv->i;
-      gprintf(gops, go, " = %s0x%0*lx",
-             rv->i < 0 ? "-" : "", hex_width(u), u);
-      if (rv->i == EOF || (0 <= rv->i && rv->i < UCHAR_MAX))
-       { gprintf(gops, go, " = "); format_char(rv->i, gops, go); }
-      break;
-    case TVMISC_UINT:
-      if (!(f&f_known)) gprintf(gops, go, " ;");
-      gprintf(gops, go, " = 0x%0*lx", hex_width(rv->u), rv->u);
-      if (rv->u < UCHAR_MAX)
-       { gprintf(gops, go, " = "); format_char(rv->u, gops, go); }
-      break;
-  }
-}
+TVEC_MISCSLOTS(DEFDUMP_ENUM)
 
-const struct tvec_regty tvty_enum = {
-  init_enum, release_int, eq_enum,
-  tobuf_enum, frombuf_enum,
-  parse_enum, dump_enum
-};
+#undef PRINTRAW_INT
+#undef PRINTRAW_UINT
+#undef PRINTRAW_FLT
+#undef PRINTRAW_PTR
+
+#undef MAYBE_PRINT_EXTRA
+#undef DEFDUMP_ENUM
+
+#define DEFTY_ENUM(tag, ty, slot)                                      \
+  const struct tvec_regty tvty_##slot##enum = {                                \
+    init_##slot##enum, trivial_release, eq_##slot##enum,               \
+    tobuf_##slot##enum, frombuf_##slot##enum,                          \
+    parse_##slot##enum, dump_##slot##enum                              \
+  };
+TVEC_MISCSLOTS(DEFTY_ENUM)
+#undef DEFTY_ENUM
 
 static const struct tvec_iassoc bool_assoc[] = {
   { "nil",             0 },
@@ -1146,7 +1169,7 @@ static const struct tvec_iassoc bool_assoc[] = {
 };
 
 const struct tvec_ienuminfo tvenum_bool =
-  { { "bool", TVMISC_INT }, bool_assoc, &tvrange_int };
+  { "bool", bool_assoc, &tvrange_int };
 
 #define DEFCLAIM(tag, ty, slot)                                                \
        int tvec_claimeq_##slot##enum                                   \
@@ -1156,11 +1179,11 @@ const struct tvec_ienuminfo tvenum_bool =
        {                                                               \
          union tvec_misc arg;                                          \
                                                                        \
-         assert(ei->_ei.mv == TVMISC_##tag);                           \
          arg.p = ei;                                                   \
-         tv->in[0].v.slot = GET_##tag(e0);                             \
-         tv->out[0].v.slot = GET_##tag(e1);                            \
-         return (tvec_claimeq(tv, &tvty_enum, &arg, file, lno, expr)); \
+         tv->out[0].v.slot = GET_##tag(e0);                            \
+         tv->in[0].v.slot = GET_##tag(e1);                             \
+         return (tvec_claimeq(tv, &tvty_##slot##enum, &arg,            \
+                              file, lno, expr));                       \
        }
 #define GET_INT(e) (e)
 #define GET_UINT(e) (e)
@@ -1237,7 +1260,7 @@ static void dump_flags(const union tvec_regval *rv,
 }
 
 const struct tvec_regty tvty_flags = {
-  init_uint, release_int, eq_uint,
+  init_uint, trivial_release, eq_uint,
   tobuf_uint, frombuf_uint,
   parse_flags, dump_flags
 };
@@ -1249,7 +1272,7 @@ int tvec_claimeq_flags(struct tvec_state *tv,
 {
   union tvec_misc arg;
 
-  arg.p = fi; tv->in[0].v.u = f0; tv->out[0].v.u = f1;
+  arg.p = fi; tv->out[0].v.u = f0; tv->in[0].v.u = f1;
   return (tvec_claimeq(tv, &tvty_flags, &arg, file, lno, expr));
 }
 
@@ -1327,22 +1350,19 @@ static void dump_char(const union tvec_regval *rv,
                      unsigned style,
                      const struct gprintf_ops *gops, void *go)
 {
-  unsigned u;
-
   if ((style&TVSF_COMPACT) && isprint(rv->i) && rv->i != '\'')
     gprintf(gops, go, "%c", (int)rv->i);
   else
-    format_char(rv->i, gops, go);
+    format_char(gops, go, rv->i);
 
   if (!(style&TVSF_COMPACT)) {
-    u = rv->i < 0 ? -rv->i : rv->i;
-    gprintf(gops, go, " ; = %d = %s0x%0*x",
-           (int)rv->i, rv->i < 0 ? "-" : "", hex_width(u), u);
+    gprintf(gops, go, " ; = %ld = ", rv->i);
+    format_signed_hex(gops, go, rv->i);
   }
 }
 
 const struct tvec_regty tvty_char = {
-  init_int, release_int, eq_int,
+  init_int, trivial_release, eq_int,
   tobuf_char, frombuf_char,
   parse_char, dump_char
 };
@@ -1350,7 +1370,7 @@ const struct tvec_regty tvty_char = {
 int tvec_claimeq_char(struct tvec_state *tv, int c0, int c1,
                      const char *file, unsigned lno, const char *expr)
 {
-  tv->in[0].v.i = c0; tv->out[0].v.i = c1;
+  tv->out[0].v.i = c0; tv->in[0].v.i = c1;
   return (tvec_claimeq(tv, &tvty_char, 0, file, lno, expr));
 }
 
@@ -1488,8 +1508,10 @@ quote:
   for (q = p; q < l; q++)
     if (!isprint(*q) || *q == '"') {
       if (p < q) gops->putm(go, (const char *)p, q - p);
-      if (*q == '\n' && !(style&TVSF_COMPACT))gprintf(gops, go, "\\n\"\t\"");
-      else format_charesc(*q, FCF_BRACE, gops, go);
+      if (*q == '\n' && !(style&TVSF_COMPACT))
+       gprintf(gops, go, "\\n\"\t\"");
+      else
+       format_charesc(gops, go, *q, FCF_BRACE);
     }
   if (p < q) gops->putm(go, (const char *)p, q - p);
   gprintf(gops, go, "\"");
@@ -1556,8 +1578,8 @@ int tvec_claimeq_string(struct tvec_state *tv,
                        const char *p1, size_t sz1,
                        const char *file, unsigned lno, const char *expr)
 {
-  tv->in[0].v.str.p = (/*unconst*/ char *)p0; tv->in[0].v.str.sz = sz0;
-  tv->out[0].v.str.p =(/*unconst*/ char *) p1; tv->out[0].v.str.sz = sz1;
+  tv->out[0].v.str.p = (/*unconst*/ char *)p0; tv->out[0].v.str.sz = sz0;
+  tv->in[0].v.str.p =(/*unconst*/ char *) p1; tv->in[0].v.str.sz = sz1;
   return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr));
 }
 
@@ -1565,10 +1587,10 @@ int tvec_claimeq_strz(struct tvec_state *tv,
                      const char *p0, const char *p1,
                      const char *file, unsigned lno, const char *expr)
 {
-  tv->in[0].v.str.p = (/*unconst*/ char *)p0;
-    tv->in[0].v.str.sz = strlen(p0);
-  tv->out[0].v.str.p = (/*unconst*/ char *)p1;
-    tv->out[0].v.str.sz = strlen(p1);
+  tv->out[0].v.str.p = (/*unconst*/ char *)p0;
+    tv->out[0].v.str.sz = strlen(p0);
+  tv->in[0].v.str.p = (/*unconst*/ char *)p1;
+    tv->in[0].v.str.sz = strlen(p1);
   return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr));
 }
 
@@ -1577,10 +1599,10 @@ int tvec_claimeq_bytes(struct tvec_state *tv,
                       const void *p1, size_t sz1,
                       const char *file, unsigned lno, const char *expr)
 {
-  tv->in[0].v.bytes.p = (/*unconst*/ void *)p0;
-    tv->in[0].v.bytes.sz = sz0;
-  tv->out[0].v.bytes.p = (/*unconst*/ void *)p1;
-    tv->out[0].v.bytes.sz = sz1;
+  tv->out[0].v.bytes.p = (/*unconst*/ void *)p0;
+    tv->out[0].v.bytes.sz = sz0;
+  tv->in[0].v.bytes.p = (/*unconst*/ void *)p1;
+    tv->in[0].v.bytes.sz = sz1;
   return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr));
 }
 
@@ -1613,8 +1635,7 @@ static int parse_buffer(union tvec_regval *rv,
                        struct tvec_state *tv)
 {
   dstr d = DSTR_INIT;
-  char *q; const char *unit;
-  int olderr;
+  const char *q, *unit;
   size_t pos;
   unsigned long u, t;
   int rc;
@@ -1622,10 +1643,7 @@ static int parse_buffer(union tvec_regval *rv,
 #define f_range 1u
 
   if (tvec_readword(tv, &d, ";", "buffer length")) { rc = -1; goto end; }
-  olderr = errno; errno = 0;
-  u = strtoul(d.buf, &q, 0);
-  if (errno) goto bad;
-  errno = olderr;
+  if (parse_unsigned_integer(&u, &q, d.buf)) goto bad;
   if (!*q) {
     tvec_skipspc(tv); pos = d.len;
     if (!tvec_readword(tv, &d, ";", 0)) pos++;
index 740b6beb5797d1c70b7ef6fd81e92d55914d9178..03c3ba462c3786f1926a3f0f0fe4ab13dee605ca 100644 (file)
@@ -260,8 +260,14 @@ extern int tvec_deserialize(struct tvec_reg */*rv*/, buf */*b*/,
 
 /*----- Test state --------------------------------------------------------*/
 
-/* Possible test outcomes. */
-enum { TVOUT_LOSE, TVOUT_SKIP, TVOUT_WIN, TVOUT_LIMIT };
+enum {
+  /* Possible test outcomes. */
+
+  TVOUT_LOSE,                          /* test failed */
+  TVOUT_SKIP,                          /* test skipped */
+  TVOUT_WIN,                           /* test passed */
+  TVOUT_LIMIT                          /* (number of possible outcomes) */
+};
 
 struct tvec_state {
   /* The primary state structure for the test vector machinery. */
@@ -271,8 +277,8 @@ struct tvec_state {
 #define TVSF_OPEN 2u                   /*   test is open */
 #define TVSF_ACTIVE 4u                 /*   test is active */
 #define TVSF_ERROR 8u                  /*   an error occurred */
-#define TVSF_OUTMASK 0xf0              /*   test outcome */
-#define TVSF_OUTSHIFT 4
+#define TVSF_OUTMASK 0xf0              /*   test outcome (@TVOUT_...@) */
+#define TVSF_OUTSHIFT 4                        /*   shift applied to outcome */
 
   /* Registers.  Available to execution environments. */
   unsigned nrout, nreg;                       /* number of output/total registers */
@@ -398,7 +404,7 @@ enum {
 /* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @const char *excuse@, @va_list ap@ = reason why group skipped
+ *             @const char *excuse@, @va_list *ap@ = reason why skipped
  *
  * Returns:    ---
  *
@@ -415,7 +421,7 @@ extern void tvec_skipgroup_v(struct tvec_state */*tv*/,
 /* --- @tvec_skip@, @tvec_skip_v@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @const char *excuse@, @va_list ap@ = reason why test skipped
+ *             @const char *excuse@, @va_list *ap@ = reason why test skipped
  *
  * Returns:    ---
  *
@@ -464,7 +470,7 @@ extern int tvec_checkregs(struct tvec_state */*tv*/);
 /* --- @tvec_fail@, @tvec_fail_v@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @const char *detail@, @va_list ap@ = description of test
+ *             @const char *detail@, @va_list *ap@ = description of test
  *
  * Returns:    ---
  *
@@ -526,7 +532,7 @@ extern void tvec_mismatch(struct tvec_state */*tv*/, unsigned /*f*/);
 /* --- @tvec_check@, @tvec_check_v@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @const char *detail@, @va_list ap@ = description of test
+ *             @const char *detail@, @va_list *ap@ = description of test
  *
  * Returns:    ---
  *
@@ -727,194 +733,778 @@ extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/,
                           const char */*delims*/, const char */*expect*/,
                           va_list */*ap*/);
 
+/*----- Ad-hoc testing ----------------------------------------------------*/
+
+/* --- @tvec_adhoc@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @struct tvec_test *t@ = space for a test definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Begin ad-hoc testing, i.e., without reading a file of
+ *             test-vector data.
+ *
+ *             The structure at @t@ will be used to record information about
+ *             the tests underway, which would normally come from a static
+ *             test definition.  The other functions in this section assume
+ *             that @tvec_adhoc@ has been called.
+ */
+
+extern void tvec_adhoc(struct tvec_state */*tv*/, struct tvec_test */*t*/);
+
+/* --- @tvec_begingroup@, @TVEC_BEGINGROUP@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @const char *name@ = name for this test group
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *
+ * Returns:    ---
+ *
+ * Use:                Begin an ad-hoc test group with the given name.  The @file@
+ *             and @lno@ can be anything, but it's usually best if they
+ *             refer to the source code performing the test: the macro
+ *             @TVEC_BEGINGROUP@ does this automatically.
+ */
+
+extern void tvec_begingroup(struct tvec_state */*tv*/, const char */*name*/,
+                           const char */*file*/, unsigned /*lno*/);
+#define TVEC_BEGINGROUP(tv, name)                                      \
+       do tvec_begingroup(tv, name, __FILE__, __LINE__); while (0)
+
+/* --- @tvec_endgroup@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *
+ * Returns:    ---
+ *
+ * Use:                End an ad-hoc test group.  The statistics are updated and the
+ *             outcome is reported to the output formatter.
+ */
+
+extern void tvec_endgroup(struct tvec_state */*tv*/);
+
+/* --- @TVEC_TESTGROUP@, @TVEC_TESTGROUP_TAG@ --- *
+ *
+ * Arguments:  @tag@ = label-disambiguation tag
+ *             @const struct tvec_state *tv = test-vector state
+ *             @const char *name@ = test-group name
+ *
+ * Returns:    ---
+ *
+ * Use:                Control-structure macro: @TVEC_TESTGROUP(tv, name) stmt@
+ *             establishes a test group with the given @name@ (attributing
+ *             it to the source file and lie number), executes @stmt@, and
+ *             ends the test group.  If @stmt@ invokes @break@ then the test
+ *             group is skipped.  @TVEC_TESTGROUP_TAG@ is the same, with an
+ *             additional @tag@ argument for use in higher-level macros.
+ */
+
+#define TVEC_TESTGROUP_TAG(tag, tv, name)                              \
+       MC_WRAP(tag##__around,                                          \
+         { TVEC_BEGINGROUP(tv, name); },                               \
+         { tvec_endgroup(tv); },                                       \
+         { if (!((tv)->f&TVSF_SKIP)) tvec_skipgroup(tv, 0);            \
+           tvec_endgroup(tv); })
+#define TVEC_TESTGROUP(tv, name) TVEC_TESTGROUP_TAG(grp, tv, name)
+
+/* --- @tvec_begintest@, @TVEC_BEGINTEST@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *
+ * Returns:    ---
+ *
+ * Use:                Begin an ad-hoc test case.  The @file@ and @lno@ can be
+ *             anything, but it's usually best if they refer to the source
+ *             code performing the test: the macro @TVEC_BEGINGROUP@ does
+ *             this automatically.
+ */
+
+extern void tvec_begintest(struct tvec_state */*tv*/,
+                          const char */*file*/, unsigned /*lno*/);
+#define TVEC_BEGINTEST(tv)                                             \
+       do tvec_begintest(tv, __FILE__, __LINE__); while (0)
+
+/* --- *tvec_endtest@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *
+ * Returns:    ---
+ *
+ * Use:                End a ad-hoc test case,  The statistics are updated and the
+ *             outcome is reported to the output formatter.
+ */
+
+extern void tvec_endtest(struct tvec_state */*tv*/);
+
+/* --- @TVEC_TEST@, @TVEC_TEST_TAG@ --- *
+ *
+ * Arguments:  @tag@ = label-disambiguation tag
+ *             @struct tvec_test *t@ = space for a test definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Control-structure macro: @TVEC_TEST(tv) stmt@ begins a test
+ *             case, executes @stmt@, and ends the test case.  If @stmt@
+ *             invokes @break@ then the test case is skipped.
+ *             @TVEC_TEST_TAG@ is the same, with an additional @tag@ argumet
+ *             for use in higher-level macros.
+ */
+
+#define TVEC_TEST_TAG(tag, tv)                                         \
+       MC_WRAP(tag##__around,                                          \
+         { TVEC_BEGINTEST(tv); },                                      \
+         { tvec_endtest(tv); },                                        \
+         { if ((tv)->f&TVSF_ACTIVE) tvec_skip((tv), 0);                \
+           tvec_endtest(tv); })
+#define TVEC_TEST(tv) TVEC_TEST_TAG(test, tv)
+
+/* --- @tvec_claim@, @tvec_claim_v@, @TVEC_CLAIM@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @int ok@ = a flag
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *             @const char *msg@, @va_list *ap@ = message to report on
+ *                     failure
+ *
+ * Returns:    The value @ok@.
+ *
+ * Use:                Check that a claimed condition holds, as (part of) a test.
+ *             If no test case is underway (i.e., if @TVSF_OPEN@ is reset in
+ *             @tv->f@), then a new test case is begun and ended.  The
+ *             @file@ and @lno@ are passed to the output formatter to be
+ *             reported in case of a failure.  If @ok@ is nonzero, then
+ *             nothing else happens; so, in particular, if @tvec_claim@
+ *             established a new test case, then the test case succeeds.  If
+ *             @ok@ is zero, then a failure is reported, quoting @msg@.
+ *
+ *             The @TVEC_CLAIM@ macro is similar, only it (a) identifies the
+ *             file and line number of the call site automatically, and (b)
+ *             implicitly quotes the source text of the @ok@ condition in
+ *             the failure message.
+ */
+
+extern int PRINTF_LIKE(5, 6)
+  tvec_claim(struct tvec_state */*tv*/, int /*ok*/,
+            const char */*file*/, unsigned /*lno*/,
+            const char */*msg*/, ...);
+extern int tvec_claim_v(struct tvec_state */*tv*/, int /*ok*/,
+                       const char */*file*/, unsigned /*lno*/,
+                       const char */*msg*/, va_list */*ap*/);
+#define TVEC_CLAIM(tv, cond)                                           \
+       (tvec_claim(tv, !!(cond), __FILE__, __LINE__, "%s untrue", #cond))
+
+/* --- @tvec_claimeq@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @const struct tvec_regty *ty@ = register type
+ *             @const union tvec_misc *arg@ = register type argument
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *             @const char *expr@ = the expression to quote on failure
+ *
+ * Returns:    Nonzero if the input and output values of register 0 are
+ *             equal, zero if they differ.
+ *
+ * Use:                Check that the input and output values of register 0 are
+ *             equal (according to the register type @ty@).  As for
+ *             @tvec_claim@ above, a test case is automatically begun and
+ *             ended if none is already underway.  If the values are
+ *             unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ *             mismatched values are dumped.
+ *
+ *             This function is not expected to be called directly, but
+ *             through type-specific wrapper functions or macros such as
+ *             @TVEC_CLAIMEQ_INT@.
+ */
+
+extern int tvec_claimeq(struct tvec_state */*tv*/,
+                       const struct tvec_regty */*ty*/,
+                       const union tvec_misc */*arg*/,
+                       const char */*file*/, unsigned /*lno*/,
+                       const char */*expr*/);
+
 /*----- Output formatting -------------------------------------------------*/
 
 struct tvec_output {
-  const struct tvec_outops *ops;
-  struct tvec_state *tv;
+  /* An output formatter. */
+  const struct tvec_outops *ops;       /* pointer to operations */
 };
 
-enum { TVBU_OP, TVBU_BYTE };
-
-struct bench_timing;
+/* Benchmarking details. */
+enum {
+  TVBU_OP,                            /* counting operations of some kind */
+  TVBU_BYTE                            /* counting bytes (@RBUF >= 0@) */
+};
+struct bench_timing;                   /* forward declaration */
 
 struct tvec_outops {
-  void (*error)(struct tvec_output */*o*/,
-               const char */*msg*/, va_list */*ap*/);
-  void (*notice)(struct tvec_output */*o*/,
-                const char */*msg*/, va_list */*ap*/);
+  /* Output operations. */
+
+  void (*bsession)(struct tvec_output */*o*/, struct tvec_state */*tv*/);
+       /* Begin a test session.  The output driver will probably want to
+        * save @tv@, because this isn't provided to any other methods.
+        */
 
-  void (*bsession)(struct tvec_output */*o*/);
   int (*esession)(struct tvec_output */*o*/);
+       /* End a session, and return the suggested exit code. */
 
   void (*bgroup)(struct tvec_output */*o*/);
-  void (*egroup)(struct tvec_output */*o*/, unsigned /*outcome*/);
+       /* Begin a test group.  The test group description is @tv->test@. */
+
   void (*skipgroup)(struct tvec_output */*o*/,
                    const char */*excuse*/, va_list */*ap*/);
+       /* The group is being skipped; @excuse@ may be null or a format
+        * string explaining why.  The @egroup@ method will not be called
+        * separately.
+        */
+
+  void (*egroup)(struct tvec_output */*o*/);
+       /* End a test group.  At least one test was attempted or @skipgroup@
+        * would have been called instead.  If @tv->curr[TVOUT_LOSE]@ is
+        * nonzero then the test group as a whole failed; otherwise it
+        * passed.
+        */
 
   void (*btest)(struct tvec_output */*o*/);
+       /* Begin a test case. */
+
   void (*skip)(struct tvec_output */*o*/,
               const char */*excuse*/, va_list */*ap*/);
+       /* The test case is being skipped; @excuse@ may be null or a format
+        * string explaining why.  The @etest@ function will still be called
+        * (so this works differently from @skipgroup@ and @egroup@).  A test
+        * case can be skipped at most once, and, if skipped, it cannot fail.
+        */
+
   void (*fail)(struct tvec_output */*o*/,
               const char */*detail*/, va_list */*ap*/);
+       /* The test case failed.
+        *
+        * The output driver should preferably report the filename (@infile@)
+        * and line number (@test_lno@, not @lno@) for the failing test.
+        *
+        * The @detail@ may be null or a format string describing detail
+        * about how the failing test was run which can't be determined from
+        * the registers; a @detail@ is usually provided when (and only when)
+        * the test environment potentially invokes the test function more
+        * than once.
+        *
+        * A single test case can fail multiple times!
+        */
+
   void (*dumpreg)(struct tvec_output */*o*/,
                  unsigned /*disp*/, const union tvec_regval */*rv*/,
                  const struct tvec_regdef */*rd*/);
+       /* Dump a register.  The `disposition' @disp@ explains what condition
+        * the register is in; see @tvec_dumpreg@ and the @TVRD_...@ codes.
+        * The register value is at @rv@, and its definition, including its
+        * type, at @rd@.  Note that this function may be called on virtual
+        * registers which aren't in either of the register vectors or
+        * mentioned by the test description.
+        */
+
   void (*etest)(struct tvec_output */*o*/, unsigned /*outcome*/);
+       /* The test case concluded with the given @outcome@ (one of the
+        * @TVOUT_...@ codes.
+        */
 
   void (*bbench)(struct tvec_output */*o*/,
                 const char */*ident*/, unsigned /*unit*/);
+       /* Begin a benchmark.  The @ident@ is a formatted string identifying
+        * the benchmark based on the values of the input registered marked
+        * @TVRF_ID@; the output driver is free to use this or come up with
+        * its own way to identify the test, e.g., by inspecting the register
+        * values for itself.  The @unit@ is one of the @TVBU_...@ constants
+        * explaining what sort of thing is being measured.
+        */
+
   void (*ebench)(struct tvec_output */*o*/,
                 const char */*ident*/, unsigned /*unit*/,
                 const struct bench_timing */*tm*/);
+       /* End a benchmark.  The @ident@ and @unit@ arguments are as for
+        * @bbench@.  If @tm@ is zero then the measurement failed; otherwise
+        * @tm->n@ counts total number of things (operations or bytes, as
+        * indicated by @unit@) processed in the indicated time.
+        */
+
+  void (*error)(struct tvec_output */*o*/,
+               const char */*msg*/, va_list */*ap*/);
+       /* Report an error.  The driver should ideally report the filename
+        * (@infile@) and line number (@lno@) prompting the error.
+        */
+
+  void (*notice)(struct tvec_output */*o*/,
+                const char */*msg*/, va_list */*ap*/);
+       /* Report a miscellaneous message.  The driver should ideally report
+        * the filename (@infile@) and line number (@lno@) prompting the
+        * error.
+        */
 
   void (*destroy)(struct tvec_output */*o*/);
+       /* Release any resources acquired by the driver. */
 };
 
+/* --- @tvec_error@, @tvec_error_v@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @const char *msg@, @va_list ap@ = error message
+ *
+ * Returns:    @-1@.
+ *
+ * Use:                Report an error.  Errors are distinct from test failures,
+ *             and indicate that a problem was encountered which compromised
+ *             the activity of testing.
+ */
+
 extern int PRINTF_LIKE(2, 3)
   tvec_error(struct tvec_state */*tv*/, const char */*msg*/, ...);
 extern int tvec_error_v(struct tvec_state */*tv*/,
                        const char */*msg*/, va_list */*ap*/);
 
+/* --- @tvec_notice@, @tvec_notice_v@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @const char *msg@, @va_list ap@ = message
+ *
+ * Returns:    ---
+ *
+ * Use:                Output a notice: essentially, some important information
+ *             which doesn't fit into any of the existing categories.
+ */
+
 extern void PRINTF_LIKE(2, 3)
   tvec_notice(struct tvec_state */*tv*/, const char */*msg*/, ...);
 extern void tvec_notice_v(struct tvec_state */*tv*/,
                          const char */*msg*/, va_list */*ap*/);
 
+/* --- @tvec_humanoutput@ --- *
+ *
+ * Arguments:  @FILE *fp@ = output file to write on
+ *
+ * Returns:    An output formatter.
+ *
+ * Use:                Return an output formatter which writes on @fp@ with the
+ *             expectation that a human will be watching and interpreting
+ *             the output.  If @fp@ denotes a terminal, the display shows a
+ *             `scoreboard' indicating the outcome of each test case
+ *             attempted, and may in addition use colour and other
+ *             highlighting.
+ */
+
 extern struct tvec_output *tvec_humanoutput(FILE */*fp*/);
+
+/* --- @tvec_tapoutput@ --- *
+ *
+ * Arguments:  @FILE *fp@ = output file to write on
+ *
+ * Returns:    An output formatter.
+ *
+ * Use:                Return an output formatter which writes on @fp@ in `TAP'
+ *             (`Test Anything Protocol') format.
+ *
+ *             TAP comes from the Perl community, but has spread rather
+ *             further.  This driver produces TAP version 13.  The driver
+ *             produces a TAP `test point' -- i.e., a result reported as
+ *             `ok' or `not ok' -- for each input test group.  Failure
+ *             reports and register dumps are produced as diagnostic
+ *             messages before the final group result.  (TAP permits
+ *             structuerd YAML data after the test-point result, which could
+ *             be used to report details, but (a) postponing the details
+ *             until after the report is inconvenient, and (b) there is no
+ *             standardization for the YAML anyway, so in practice it's no
+ *             more useful than the unstructured diagnostics.
+ */
+
 extern struct tvec_output *tvec_tapoutput(FILE */*fp*/);
+
+/* --- @tvec_dfltoutput@ --- *
+ *
+ * Arguments:  @FILE *fp@ = output file to write on
+ *
+ * Returns:    An output formatter.
+ *
+ * Use:                Selects and instantiates an output formatter suitable for
+ *             writing on @fp@.  The policy is subject to change, but
+ *             currently the `human' output format is selected if @fp@ is
+ *             interactive (i.e., if @isatty(fileno(fp))@ is true), and
+ *             otherwise the `tap' format is used.
+ */
+
 extern struct tvec_output *tvec_dfltout(FILE */*fp*/);
 
 /*----- Register types ----------------------------------------------------*/
 
 struct tvec_regty {
+  /* A register type. */
+
   void (*init)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/);
+       /* Initialize the value in @*rv@.  This will be called before any
+        * other function acting on the value, including @release@.
+        */
+
   void (*release)(union tvec_regval */*rv*/,
                  const struct tvec_regdef */*rd*/);
+       /* Release any resources associated with the value in @*rv@. */
+
   int (*eq)(const union tvec_regval */*rv0*/,
            const union tvec_regval */*rv1*/,
            const struct tvec_regdef */*rd*/);
+       /* Return nonzero if @*rv0@ and @*rv1@ are equal values.
+        * Asymmetric criteria are permitted: @tvec_checkregs@ calls @eq@
+        * with the input register as @rv0@ and the output as @rv1@.
+        */
+
   int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/,
               const struct tvec_regdef */*rd*/);
+       /* Serialize the value @*rv@, writing the result to @b@.  Return
+        * zero on success, or @-1@ on error.
+        */
+
   int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/,
                 const struct tvec_regdef */*rd*/);
+       /* Deserialize a value from @b@, storing it in @*rv@.  Return zero on
+        * success, or @-1@ on error.
+        */
+
   int (*parse)(union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/,
               struct tvec_state */*tv*/);
+       /* Parse a value from @tv->fp@, storing it in @*rv@.  Return zero on
+        * success, or @-1@ on error, having reported one or more errors via
+        * @tvec_error@ or @tvec_syntax@.  A successful return should leave
+        * the input position at the start of the next line; the caller will
+        * flush the remainder of the line itself.
+        */
+
   void (*dump)(const union tvec_regval */*rv*/,
               const struct tvec_regdef */*rd*/,
               unsigned /*style*/,
               const struct gprintf_ops */*gops*/, void */*go*/);
 #define TVSF_COMPACT 1u
+       /* Write a human-readable representation of the value @*rv@ using
+        * @gprintf@ on @gops@ and @go@.  The @style@ is a collection of
+        * flags: if @TVSF_COMPACT@ is set, then output should be minimal,
+        * and must fit on a single line; otherwise, output may consist of
+        * multiple lines and may contain redundant information if that is
+        * likely to be useful to a human reader.
+        */
 };
 
+/*----- Integer types: signed and unsigned ------------------------------- */
+
+/* Integers may be input in decimal, hex, binary, or octal, following
+ * approximately usual conventions.
+ *
+ *   * Signed integers may be preceded with a `+' or `-' sign.
+ *
+ *   * Decimal integers are just a sequence of decimal digits `0' ... `9'.
+ *
+ *   * Octal integers are a sequence of digits `0' ... `7', preceded by `0o'
+ *     or `0O'.
+ *
+ *   * Hexadecimal integers are a sequence of digits `0' ... `9', `a'
+ *     ... `f', or `A' ... `F', preceded by `0x' or `0X'.
+ *
+ *   * Radix-B integers are a sequence of digits `0' ... `9', `a' ... `f', or
+ *     `A' ... `F', each with value less than B, preceded by `Br' or `BR',
+ *     where 0 < B < 36 is expressed in decimal without any leading `0' or
+ *     internal underscores `_'.
+ *
+ *   * A digit sequence may contain internal underscore `_' separators, but
+ *     not before or after all of the digits; and two consecutive `_'
+ *     characters are not permitted.
+ */
+
 extern const struct tvec_regty tvty_int, tvty_uint;
+
+/* The @arg.p@ slot may be null or a pointer to @struct tvec_irange@ or
+ * @struct tvec_urange@ as appropriate.  The bounds are inclusive; use, e.g.,
+ * @LONG_MAX@ explicitly if one or the other bound is logically inapplicable.
+ */
 struct tvec_irange { long min, max; };
 struct tvec_urange { unsigned long min, max; };
 
+/* Bounds corresponding to common integer types. */
 extern const struct tvec_irange
   tvrange_schar, tvrange_short, tvrange_int, tvrange_long,
   tvrange_sbyte, tvrange_i16, tvrange_i32;
 extern const struct tvec_urange
   tvrange_uchar, tvrange_ushort, tvrange_uint, tvrange_ulong, tvrange_size,
   tvrange_byte, tvrange_u16, tvrange_u32;
-extern const struct tvec_frange
-  tvrange_float, tvrange_double;
+
+/* --- tvec_claimeq_int@, @TVEC_CLAIMEQ_INT@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @long i0, i1@ = two signed integers
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *             @const char *expr@ = the expression to quote on failure
+ *
+ * Returns:    Nonzero if @i0@ and @i1@ are equal, otherwise zero.
+ *
+ * Use:                Check that values of @i0@ and @i1@ are equal.  As for
+ *             @tvec_claim@ above, a test case is automatically begun and
+ *             ended if none is already underway.  If the values are
+ *             unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ *             mismatched values are dumped: @i0@ is printed as the output
+ *             value and @i1@ is printed as the input reference.
+ *
+ *             The @TVEC_CLAIM_INT@ macro is similar, only it (a) identifies
+ *             the file and line number of the call site automatically, and
+ *             (b) implicitly quotes the source text of the @i0@ and @i1@
+ *             arguments in the failure message.
+ */
 
 extern int tvec_claimeq_int(struct tvec_state */*tv*/,
                            long /*i0*/, long /*i1*/,
                            const char */*file*/, unsigned /*lno*/,
                            const char */*expr*/);
+#define TVEC_CLAIMEQ_INT(tv, i0, i1)                                   \
+       (tvec_claimeq_int(tv, i0, i1, __FILE__, __LINE__, #i0 " /= " #i1))
+
+/* --- @tvec_claimeq_uint@, @TVEC_CLAIMEQ_UINT@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @unsigned long u0, u1@ = two unsigned integers
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *             @const char *expr@ = the expression to quote on failure
+ *
+ * Returns:    Nonzero if @u0@ and @u1@ are equal, otherwise zero.
+ *
+ * Use:                Check that values of @u0@ and @u1@ are equal.  As for
+ *             @tvec_claim@ above, a test case is automatically begun and
+ *             ended if none is already underway.  If the values are
+ *             unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ *             mismatched values are dumped: @u0@ is printed as the output
+ *             value and @u1@ is printed as the input reference.
+ *
+ *             The @TVEC_CLAIM_UINT@ macro is similar, only it (a)
+ *             identifies the file and line number of the call site
+ *             automatically, and (b) implicitly quotes the source text of
+ *             the @u0@ and @u1@ arguments in the failure message.
+ */
+
 extern int tvec_claimeq_uint(struct tvec_state */*tv*/,
                            unsigned long /*u0*/, unsigned long /*u1*/,
                            const char */*file*/, unsigned /*lno*/,
                            const char */*expr*/);
-#define TVEC_CLAIMEQ_INT(tv, i0, i1)                                   \
-       (tvec_claimeq_int(tv, i0, i1, __FILE__, __LINE__, #i0 " /= " #i1))
 #define TVEC_CLAIMEQ_UINT(tv, u0, u1)                                  \
        (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1))
 
+/*----- Floating-point type -----------------------------------------------*/
+
+/* Floating-point are either NaN (`#nan', if supported by the platform);
+ * positive or negative infinity (`#inf', `+#inf', or, preferred, `#+inf',
+ * and `-#inf' or, preferred, `#-inf', if supported by the platform), or a
+ * number in strtod(3) syntax.
+ *
+ * The comparison rules for floating-point numbers are complex: see
+ * @tvec_claimeqish_float@ for details.
+ */
+
 extern const struct tvec_regty tvty_float;
+
 struct tvec_floatinfo {
-  unsigned f;
-#define TVFF_NOMIN 1u
-#define TVFF_NOMAX 2u
-#define TVFF_NANOK 4u
-#define TVFF_EXACT 0u
-#define TVFF_ABSDELTA 0x10
-#define TVFF_RELDELTA 0x20
-#define TVFF_EQMASK 0xf0
-  double min, max;
-  double delta;
+  /* Details about acceptable floating-point values. */
+
+  unsigned f;                          /* flags (@TVFF_...@ bits) */
+#define TVFF_NOMIN 1u                  /*   ignore @min@ (allow -inf) */
+#define TVFF_NOMAX 2u                  /*   ignore @max@ (allow +inf) */
+#define TVFF_NANOK 4u                  /*   permit NaN */
+#define TVFF_EQMASK 0xf0               /*   how to compare */
+#define TVFF_EXACT 0x00                        /*     must equal exactly */
+#define TVFF_ABSDELTA 0x10             /*     must be within @delta@ */
+#define TVFF_RELDELTA 0x20             /*     diff < @delta@ fraction */
+  double min, max;                     /* smallest/largest value allowed */
+  double delta;                                /* maximum tolerable difference */
 };
 
+/* --- @tvec_claimeqish_float@, @TVEC_CLAIMEQISH_FLOAT@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @double f0, f1@ = two floating-point numbers
+ *             @unsigned f@ = flags (@TVFF_...@)
+ *             @double delta@ = maximum tolerable difference
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *             @const char *expr@ = the expression to quote on failure
+ *
+ * Returns:    Nonzero if @f0@ and @u1@ are sufficiently close, otherwise
+ *             zero.
+ *
+ * Use:                Check that values of @f0@ and @f1@ are sufficiently close.
+ *             As for @tvec_claim@ above, a test case is automatically begun
+ *             and ended if none is already underway.  If the values are
+ *             too far apart, then @tvec_fail@ is called, quoting @expr@,
+ *             and the mismatched values are dumped: @f0@ is printed as the
+ *             output value and @f1@ is printed as the input reference.
+ *
+ *             The details for the comparison are as follows.
+ *
+ *               * A NaN value matches any other NaN, and nothing else.
+ *
+ *               * An infinity matches another infinity of the same sign,
+ *                 and nothing else.
+ *
+ *               * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
+ *                 representable number matches only itself: in particular,
+ *                 positive and negative zero are considered distinct.
+ *                 (This allows tests to check that they land on the correct
+ *                 side of branch cuts, for example.)
+ *
+ *               * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
+ *                 %$y$% when %$|x - y| < \delta$%.
+ *
+ *               * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
+ *                 %$y$% when %$|1 - y/x| < \delta$%.  (Note that this
+ *                 criterion is asymmetric
+ *
+ *             The @TVEC_CLAIM_FLOAT@ macro is similar, only it (a)
+ *             identifies the file and line number of the call site
+ *             automatically, and (b) implicitly quotes the source text of
+ *             the @f0@ and @f1@ arguments (and @delta@) in the failure
+ *             message.
+ */
+
 extern int tvec_claimeqish_float(struct tvec_state */*tv*/,
                                 double /*f0*/, double /*f1*/,
                                 unsigned /*f*/, double /*delta*/,
                                 const char */*file*/, unsigned /*lno*/,
                                 const char */*expr*/);
+#define TVEC_CLAIMEQISH_FLOAT(tv, f0, f1, f, delta)                    \
+       (tvec_claimeqish_float(tv, f0, f1, f, delta, , __FILE__, __LINE__, \
+                              #f0 " /= " #f1 " (+/- " #delta ")"))
+
+/* --- @tvec_claimeq_float@, @TVEC_CLAIMEQ_FLOAT@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @double f0, f1@ = two floating-point numbers
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *             @const char *expr@ = the expression to quote on failure
+ *
+ * Returns:    Nonzero if @f0@ and @u1@ are identical, otherwise zero.
+ *
+ * Use:                Check that values of @f0@ and @f1@ are identical.  The
+ *             function is exactly equivalent to @tvec_claimeqish_float@
+ *             with @f == TVFF_EXACT@; the macro is similarly like
+ *             @TVEC_CLAIMEQISH_FLOAT@ with @f == TVFF_EXACT@, except that
+ *             it doesn't bother to quote a delta.
+ */
+
 extern int tvec_claimeq_float(struct tvec_state */*tv*/,
                              double /*f0*/, double /*f1*/,
                              const char */*file*/, unsigned /*lno*/,
                              const char */*expr*/);
-#define TVEC_CLAIMEQISH_FLOAT(tv, f0, f1, f, delta)                    \
-       (tvec_claimeqish_float(tv, f0, f1, f, delta, , __FILE__, __LINE__, \
-                              #f0 " /= " #f1 " (+/- " #delta ")"))
 #define TVEC_CLAIMEQ_FLOAT(tv, f0, f1)                                 \
        (tvec_claimeq_float(tv, f0, f1, __FILE__, __LINE__, #f0 " /= " #f1))
 
-extern const struct tvec_regty tvty_enum;
+/*----- Enumerated types --------------------------------------------------*/
+
+/* There is a distinct enumerated type for each of the branches of
+ * @tvec_misc@.  In the following, we write @t@ for the type code, which is
+ * @i@ for signed integer, @u@ for unsigned integer, @f@ for floating-point,
+ * and @p@ for pointer.
+ */
 
+#define DEFENUMTY(tag, ty, slot)                                       \
+       extern const struct tvec_regty tvty_##slot##enum;
+TVEC_MISCSLOTS(DEFENUMTY)
+#undef DEFENUMTY
+
+/* A @struct tvec_tassoc@ associates a string tag with a value. */
 #define DEFASSOC(tag_, ty, slot)                                       \
        struct tvec_##slot##assoc { const char *tag; ty slot; };
 TVEC_MISCSLOTS(DEFASSOC)
 #undef DEFASSOC
 
-struct tvec_enuminfo { const char *name; unsigned mv; /* ... */ };
-struct tvec_ienuminfo {
-  struct tvec_enuminfo _ei;
-  const struct tvec_iassoc *av;
-  const struct tvec_irange *ir;
-};
-struct tvec_uenuminfo {
-  struct tvec_enuminfo _ei;
-  const struct tvec_uassoc *av;
-  const struct tvec_urange *ur;
-};
-struct tvec_fenuminfo {
-  struct tvec_enuminfo _ei;
-  const struct tvec_fassoc *av;
-  const struct tvec_floatinfo *fi;
-};
-struct tvec_penuminfo {
-  struct tvec_enuminfo _ei;
-  const struct tvec_passoc *av;
-};
+/* Information about an enumerated type. */
+#define DEFINFO(tag, ty, slot)                                         \
+       struct tvec_##slot##enuminfo {                                  \
+         const char *name;             /* type name for diagnostics */ \
+         const struct tvec_##slot##assoc *av; /* name/value mappings */ \
+         EXTRA_##tag##_INFOSLOTS       /* type-specific extra info */  \
+       };
+
+#define EXTRA_INT_INFOSLOTS                                            \
+       const struct tvec_irange *ir;   /* allowed range of raw values */
+
+#define EXTRA_UINT_INFOSLOTS                                           \
+       const struct tvec_urange *ur;   /* allowed range of raw values */
+
+#define EXTRA_FLT_INFOSLOTS                                            \
+       const struct tvec_floatinfo *fi; /* range and matching policy */
+
+#define EXTRA_PTR_INFOSLOTS            /* (nothing) */
 
+TVEC_MISCSLOTS(DEFINFO)
+
+#undef EXTRA_INT_INFOSLOTS
+#undef EXTRA_UINT_INFOSLOTS
+#undef EXTRA_FLT_INFOSLOTS
+#undef EXTRA_PTR_INFOSLOTS
+
+#undef DEFINFO
+
+/* Standard enumerations. */
 const struct tvec_ienuminfo tvenum_bool;
 
+/* --- @tvec_claimeq_tenum@, @TVEC_CLAIMEQ_TENUM@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @ty t0, t1@ = two values
+ *             @const char *file@, @unsigned @lno@ = calling file and line
+ *             @const char *expr@ = the expression to quote on failure
+ *
+ * Returns:    Nonzero if @t0@ and @t1@ are equal, otherwise zero.
+ *
+ * Use:                Check that values of @t0@ and @t1@ are equal.  As for
+ *             @tvec_claim@ above, a test case is automatically begun and
+ *             ended if none is already underway.  If the values are
+ *             unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ *             mismatched values are dumped: @u0@ is printed as the output
+ *             value and @u1@ is printed as the input reference.
+ *
+ *             The @TVEC_CLAIM_TENUM@ macro is similar, only it (a)
+ *             identifies the file and line number of the call site
+ *             automatically, and (b) implicitly quotes the source text of
+ *             the @u0@ and @u1@ arguments in the failure message.
+ */
+
 #define DECLCLAIM(tag, ty, slot)                                       \
        extern int tvec_claimeq_##slot##enum                            \
          (struct tvec_state */*tv*/,                                   \
           const struct tvec_##slot##enuminfo */*ei*/,                  \
-          ty /*e0*/, ty /*e1*/,                                        \
+          ty /*t0*/, ty /*t1*/,                                        \
           const char */*file*/, unsigned /*lno*/, const char */*expr*/);
 TVEC_MISCSLOTS(DECLCLAIM)
 #undef DECLCLAIM
-#define TVEC_CLAIMEQ_IENUM(tv, ei, e0, e1)                             \
-       (tvec_claimeq_ienum(tv, ei, e0, e1,                             \
-                           __FILE__, __LINE__, #e0 " /= " #e1))
-#define TVEC_CLAIMEQ_UENUM(tv, ei, e0, e1)                             \
-       (tvec_claimeq_uenum(tv, ei, e0, e1,                             \
-                           __FILE__, __LINE__, #e0 " /= " #e1))
-#define TVEC_CLAIMEQ_FENUM(tv, ei, e0, e1)                             \
-       (tvec_claimeq_fenum(tv, ei, e0, e1,                             \
-                           __FILE__, __LINE__, #e0 " /= " #e1))
-#define TVEC_CLAIMEQ_PENUM(tv, ei, e0, e1)                             \
-       (tvec_claimeq_penum(tv, ei, e0, e1,                             \
-                           __FILE__, __LINE__, #e0 " /= " #e1))
+#define TVEC_CLAIMEQ_IENUM(tv, ei, i0, i1)                             \
+       (tvec_claimeq_ienum(tv, ei, i0, i1,                             \
+                           __FILE__, __LINE__, #i0 " /= " #i1))
+#define TVEC_CLAIMEQ_UENUM(tv, ei, u0, u1)                             \
+       (tvec_claimeq_uenum(tv, ei, u0, u1,                             \
+                           __FILE__, __LINE__, #u0 " /= " #u1))
+#define TVEC_CLAIMEQ_FENUM(tv, ei, f0, f1)                             \
+       (tvec_claimeq_fenum(tv, ei, f0, f1,                             \
+                           __FILE__, __LINE__, #f0 " /= " #f1))
+#define TVEC_CLAIMEQ_PENUM(tv, ei, p0, p1)                             \
+       (tvec_claimeq_penum(tv, ei, p0, p1,                             \
+                           __FILE__, __LINE__, #p0 " /= " #p1))
+
+/*----- Flags type -------------------------------------------------------*/
 
 extern const struct tvec_regty tvty_flags;
-struct tvec_flag { const char *tag; unsigned long m, v; };
+
+struct tvec_flag {
+  /* Definition of a single flag or bitfield value.
+   *
+   * Each named setting comes with a value @v@ and a mask @m@; the mask
+   * should cover all of the value bits, i.e., @(v&~m) == 0@.  On input,
+   * exactly 
+   */
+
+  const char *tag;                     /* name */
+  unsigned long m, v;                  /* mask and value */
+};
+
 struct tvec_flaginfo {
   const char *name;
   const struct tvec_flag *fv;
@@ -968,53 +1558,6 @@ extern const struct tvec_regty tvty_buffer;
 extern void tvec_allocstring(union tvec_regval */*rv*/, size_t /*sz*/);
 extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/);
 
-/*----- Ad-hoc testing ----------------------------------------------------*/
-
-extern void tvec_adhoc(struct tvec_state */*tv*/, struct tvec_test */*t*/);
-
-extern void tvec_begingroup(struct tvec_state */*tv*/, const char */*name*/,
-                           const char */*file*/, unsigned /*lno*/);
-extern void tvec_reportgroup(struct tvec_state */*tv*/);
-extern void tvec_endgroup(struct tvec_state */*tv*/);
-
-#define TVEC_BEGINGROUP(tv, name)                                      \
-       do tvec_begingroup(tv, name, __FILE__, __LINE__); while (0)
-
-#define TVEC_TESTGROUP(tag, tv, name)                                  \
-       MC_WRAP(tag##__around,                                          \
-         { TVEC_BEGINGROUP(tv, name); },                               \
-         { tvec_endgroup(tv); },                                       \
-         { if (!((tv)->f&TVSF_SKIP)) tvec_skipgroup(tv, 0);            \
-           tvec_endgroup(tv); })
-
-extern void tvec_begintest(struct tvec_state */*tv*/,
-                          const char */*file*/, unsigned /*lno*/);
-extern void tvec_endtest(struct tvec_state */*tv*/);
-
-#define TVEC_BEGINTEST(tv)                                             \
-       do tvec_begintest(tv, __FILE__, __LINE__); while (0)
-
-#define TVEC_TEST(tag, tv)                                             \
-       MC_WRAP(tag##__around,                                          \
-         { TVEC_BEGINTEST(tv); },                                      \
-         { tvec_endtest(tv); },                                        \
-         { if ((tv)->f&TVSF_ACTIVE) tvec_skipgroup((tv), 0);           \
-           tvec_endtest(tv); })
-
-extern int PRINTF_LIKE(5, 6)
-  tvec_claim(struct tvec_state */*tv*/, int /*ok*/,
-            const char */*file*/, unsigned /*lno*/,
-            const char */*expr*/, ...);
-
-#define TVEC_CLAIM(tv, cond)                                           \
-       (tvec_claim(tv, !!(cond), __FILE__, __LINE__, #cond " untrue"))
-
-extern int tvec_claimeq(struct tvec_state */*tv*/,
-                       const struct tvec_regty */*ty*/,
-                       const union tvec_misc */*arg*/,
-                       const char */*file*/, unsigned /*lno*/,
-                       const char */*expr*/);
-
 /*----- Benchmarking ------------------------------------------------------*/
 
 struct tvec_bench {
index cc88bfeaa73a11f780d47f6ba98b99c86e3063b1..b2fd2c70e2524866e8d29dc66eba7f613cb6f584 100644 (file)
@@ -41,9 +41,9 @@ static struct tvec_state tvstate;
 static int step;
 
 #define TESTGROUP(name)                                                        \
-       TVEC_TESTGROUP(tg, &tvstate, name)                              \
+       TVEC_TESTGROUP_TAG(grp, &tvstate, name)                         \
        MC_BEFORE(init, { step = 0; })
-#define TEST TVEC_TEST(test, &tvstate)
+#define TEST TVEC_TEST_TAG(test, &tvstate)
 #define STEP(s) do {                                                   \
          tvec_claim(&tvstate, s == step, __FILE__, __LINE__,           \
                     "found %d /= expected %d", s, step);               \
index d7cd6c3d68fc3494d4d00ceb4218d96944162b33..8d1b0f0d646e5b387e8f7eaf66cd7c631576d80b 100644 (file)
 AT_SETUP([utilities: bits])
 AT_KEYWORDS([utils bits])
 $PYTHON SRCDIR/t/bits-testgen.py 0xaca98e08 0x0b6e95fb - >bits.tests
-AT_CHECK([BUILDDIR/t/bits.t bits.tests], [0], [ignore], [ignore])
+AT_CHECK([BUILDDIR/t/bits.t -fh bits.tests], [0], [ignore])
 AT_CLEANUP
 
 ## control
 AT_SETUP([utilities: control])
 AT_KEYWORDS([utils control])
-AT_CHECK([BUILDDIR/t/control.t], [0], [ignore], [ignore])
+AT_CHECK([BUILDDIR/t/control.t -fh], [0], [ignore])
 AT_CLEANUP
 
 ## exc
@@ -59,8 +59,8 @@ AT_CLEANUP
 ## versioncmp
 AT_SETUP([utilities: versioncmp])
 AT_KEYWORDS([utils versioncmp])
-AT_CHECK([BUILDDIR/t/versioncmp.t SRCDIR/t/versioncmp.tests],
-        [0], [ignore], [ignore])
+AT_CHECK([BUILDDIR/t/versioncmp.t -fh SRCDIR/t/versioncmp.tests],
+        [0], [ignore])
 AT_CLEANUP
 
 ###----- That's all, folks --------------------------------------------------