@@@ so much mess
[mLib] / test / tvec.h
index 2309edf..2f1cc56 100644 (file)
 #  include "dstr.h"
 #endif
 
+#ifndef MLIB_GPRINTF_H
+#  include "gprintf.h"
+#endif
+
 #ifndef MLIB_MACROS_H
 #  include "macros.h"
 #endif
@@ -64,7 +68,8 @@
 #define TVEC_MISCSLOTS(_)                                              \
        _(PTR, const void *, p)         /* arbitrary pointer */         \
        _(INT, long, i)                 /* signed integer */            \
-       _(UINT, unsigned long, u)       /* signed integer */
+       _(UINT, unsigned long, u)       /* signed integer */            \
+       _(FLT, double, f)               /* floating point */
 
 union tvec_misc {
 #define TVEC_DEFSLOT(tag, ty, slot) ty slot;
@@ -114,6 +119,7 @@ union tvec_regval {
   long i;                              /* signed integer */
   unsigned long u;                     /* unsigned integer */
   void *p;                             /* pointer */
+  double f;                            /* floating point */
   struct { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */
   struct { char *p; size_t sz; } str;  /* text string */
 #ifdef TVEC_REGSLOTS
@@ -164,21 +170,102 @@ struct tvec_regdef {
   union tvec_misc arg;                 /* extra detail for the type */
 };
 
-extern int tvec_serialize(const struct tvec_reg */*rv*/,
+/* @TVEC_GREG(vec, i, regsz)@
+ *
+ * If @vec@ is a data pointer which happens to contain the address of a
+ * vector of @struct tvec_reg@ objects, @i@ is an integer, and @regsz@ is the
+ * size of a @struct tvec_reg@, then this evaluates to the address of the
+ * @i@th element of the vector.
+ *
+ * This is the general tool you need for accessing register vectors when you
+ * don't have absolute knowledge of the size of a @union tvec_regval@.
+ * Usually you want to access one of the register vectors in a @struct
+ * tvec_state@, and @TVEC_REG@ will be more convenient.
+ */
+#define TVEC_GREG(vec, i, regsz)                                       \
+       ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz)))
+
+/*------ Serialization utilities ------------------------------------------*/
+
+/* --- @tvec_serialize@ --- *
+ *
+ * Arguments:  @const struct tvec_reg *rv@ = vector of registers
+ *             @buf *b@ = buffer to write on
+ *             @const struct tvec_regdef *regs@ = vector of register
+ *                     descriptions, terminated by an entry with a null
+ *                     @name@ slot
+ *             @unsigned nr@ = number of entries in the @rv@ vector
+ *             @size_t regsz@ = true size of each element of @rv@
+ *
+ * Returns:    Zero on success, @-1@ on failure.
+ *
+ * Use:                Serialize a collection of register values.
+ *
+ *             The `candidate register definitions' are those entries @r@ in
+ *             the @regs@ vector whose index @r.i@ is strictly less than
+ *             @nr@.  The `selected register definitions' are those
+ *             candidate register definitions @r@ for which the indicated
+ *             register @rv[r.i]@ has the @TVRF_LIVE@ flag set.  The
+ *             serialized output begins with a header bitmap: if there are
+ *             %$n$% candidate register definitions then the header bitmap
+ *             consists of %$\lceil n/8 \rceil$% bytes.  Bits are ordered
+ *             starting from the least significant bit of the first byte,
+ *             end ending at the most significant bit of the final byte.
+ *             The bit corresponding to a candidate register definition is
+ *             set if and only if that register defintion is selected.  The
+ *             header bitmap is then followed by the serializations of the
+ *             selected registers -- i.e., for each selected register
+ *             definition @r@, the serialized value of register @rv[r.i]@ --
+ *             simply concatenated together, with no padding or alignment.
+ *
+ *             The serialized output is written to the buffer @b@.  Failure
+ *             can be caused by running out of buffer space, or a failing
+ *             type handler.
+ */
+
+extern int tvec_serialize(const struct tvec_reg */*rv*/, buf */*b*/,
                          const struct tvec_regdef */*regs*/,
-                         unsigned /*nr*/, size_t /*regsz*/,
-                         void **/*p_out*/, size_t */*sz_out*/);
+                         unsigned /*nr*/, size_t /*regsz*/);
 
-extern int tvec_deserialize(struct tvec_reg */*rv*/,
+/* --- @tvec_deserialize@ --- *
+ *
+ * Arguments:  @struct tvec_reg *rv@ = vector of registers
+ *             @buf *b@ = buffer to write on
+ *             @const struct tvec_regdef *regs@ = vector of register
+ *                     descriptions, terminated by an entry with a null
+ *                     @name@ slot
+ *             @unsigned nr@ = number of entries in the @rv@ vector
+ *             @size_t regsz@ = true size of each element of @rv@
+ *
+ * Returns:    Zero on success, @-1@ on failure.
+ *
+ * Use:                Deserialize a collection of register values.
+ *
+ *             The size of the register vector @nr@ and the register
+ *             definitions @regs@ must match those used when producing the
+ *             serialization.  For each serialized register value,
+ *             deserialize and store the value into the appropriate register
+ *             slot, and set the @TVRF_LIVE@ flag on the register.  See
+ *             @tvec_serialize@ for a description of the format.
+ *
+ *             On successful completion, store the address of the first byte
+ *             after the serialized data in @*end_out@ if @end_out@ is not
+ *             null.  Failure results only from a failing register type
+ *             handler.
+ */
+
+extern int tvec_deserialize(struct tvec_reg */*rv*/, buf */*b*/,
                            const struct tvec_regdef */*regs*/,
-                           unsigned /*nr*/, size_t /*regsz*/,
-                           const void */*p*/, size_t /*sz*/);
+                           unsigned /*nr*/, size_t /*regsz*/);
 
 /*----- Test state --------------------------------------------------------*/
 
+/* Possible test outcomes. */
 enum { TVOUT_LOSE, TVOUT_SKIP, TVOUT_WIN, TVOUT_LIMIT };
 
 struct tvec_state {
+  /* The primary state structure for the test vector machinery. */
+
   unsigned f;                          /* flags */
 #define TVSF_SKIP 1u                   /*   skip this test group */
 #define TVSF_OPEN 2u                   /*   test is open */
@@ -186,59 +273,273 @@ struct tvec_state {
 #define TVSF_ERROR 8u                  /*   an error occurred */
 #define TVSF_OUTMASK 0xf0              /*   test outcome */
 #define TVSF_OUTSHIFT 4
+
+  /* Registers.  Available to execution environments. */
   unsigned nrout, nreg;                       /* number of output/total registers */
   size_t regsz;                                /* size of register entry */
   struct tvec_reg *in, *out;           /* register vectors */
-  char expst, st;                      /* progress status codes */
+
+  /* Test groups state.  Available to output formatters. */
   const struct tvec_test *tests, *test;        /* all tests and current test */
+
+  /* Test scoreboard.  Available to output formatters. */
   unsigned curr[TVOUT_LIMIT], all[TVOUT_LIMIT], grps[TVOUT_LIMIT];
+
+  /* Output machinery. */
   struct tvec_output *output;          /* output formatter */
+
+  /* Input machinery.  Available to type parsers. */
   const char *infile; unsigned lno, test_lno; /* input file name, line */
   FILE *fp;                            /* input file stream */
 };
 
-#define TVEC_GREG(vec, i, regsz)                                       \
-       ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz)))
+/* @TVEC_REG(tv, vec, i)@
+ *
+ * If @tv@ is a pointer to a @struct tvec_state@, @vec@ is either @in@ or
+ * @out@, and @i@ is an integer, then this evaluates to the address of the
+ * @i@th register in the selected vector.
+ */
 #define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->regsz)
 
 /*----- Test descriptions -------------------------------------------------*/
 
-typedef int tvec_hookfn(struct tvec_state */*tv*/);
 typedef void tvec_testfn(const struct tvec_reg */*in*/,
                         struct tvec_reg */*out*/,
                         void */*ctx*/);
+  /* A test function.  It should read inputs from @in@ and write outputs to
+   * @out@.  The @TVRF_LIVE@ is set on inputs which are actually present, and
+   * on outputs which are wanted to test.  A test function can set additional
+   * `gratuitous outputs' by setting @TVRF_LIVE@ on them; clearing
+   * @TVRF_LIVE@ on a wanted output causes a mismatch.
+   *
+   * A test function may be called zero or more times by the environment.  In
+   * particular, it may be called multiple times, though usually by prior
+   * arrangement with the environment.
+   *
+   * The @ctx@ is supplied by the environment's @run@ function (see below).
+   * The default environment calls the test function once, with a null
+   * @ctx@.  There is no expectation that the environment's context has
+   * anything to do with the test function's context.
+   */
+
+struct tvec_env {
+  /* A test environment sets things up for and arranges to run the test.
+   *
+   * The caller is responsible for allocating storage for the environment's
+   * context, based on the @ctxsz@ slot, and freeing it later; this space is
+   * passed in as the @ctx@ parameter to the remaining functions; if @ctxsz@
+   * is zero then @ctx@ is null.
+   */
+
+  size_t ctxsz;                              /* environment context size */
+
+  int (*setup)(struct tvec_state */*tv*/, const struct tvec_env */*env*/,
+              void */*pctx*/, void */*ctx*/);
+    /* Initialize the context; called at the start of a test group.  Return
+     * zero on success, or @-1@ on failure.  If setup fails, the context is
+     * freed, and the test group is skipped.
+     */
+
+  int (*set)(struct tvec_state */*tv*/, const char */*var*/,
+            const struct tvec_env */*env*/, void */*ctx*/);
+    /* Called when the parser finds a %|@var|%' setting to parse and store
+     * the value.  If @setup@ failed, this is still called (so as to skip the
+     * value), but @ctx@ is null.
+     */
+
+  int (*before)(struct tvec_state */*tv*/, void */*ctx*/);
+    /* Called prior to running a test.  This is the right place to act on any
+     * `%|@var|%' settings.  Return zero on success or @-1@ on failure (which
+     * causes the test to be skipped).  This function is never called if the
+     * test group is skipped.
+     */
+
+  void (*run)(struct tvec_state */*tv*/, tvec_testfn */*fn*/, void */*ctx*/);
+    /* Run the test.  It should either call @tvec_skip@, or run @fn@ one or
+     * more times.  In the latter case, it is responsible for checking the
+     * outputs, and calling @tvec_fail@ as necessary; @tvec_checkregs@ will
+     * check the register values against the supplied test vector, while
+     * @tvec_check@ does pretty much everything necessary.  This function is
+     * never called if the test group is skipped.
+     */
+
+  void (*after)(struct tvec_state */*tv*/, void */*ctx*/);
+    /* Called after running or skipping a test.  Typical actions involve
+     * resetting whatever things were established by @set@.  This function is
+     * never called if the test group is skipped.
+     */
+
+  void (*teardown)(struct tvec_state */*tv*/, void */*ctx*/);
+    /* Tear down the environment: called at the end of a test group.  If the
+     * setup failed, then this function is still called, with a null @ctx@.
+     */
+};
 
 struct tvec_test {
+  /* A test description. */
+
   const char *name;                    /* name of the test */
   const struct tvec_regdef *regs;      /* descriptions of the registers */
-  tvec_hookfn *preflight;              /* check before starting */
-  tvec_hookfn *run;                    /* test runner */
+  const struct tvec_env *env;          /* environment to run test in */
   tvec_testfn *fn;                     /* test function */
-  union tvec_misc arg;                 /* additional parameter to `run' */
 };
 
+
+enum {
+  /* Register output dispositions. */
+
+  TVRD_INPUT,                          /* input-only register */
+  TVRD_OUTPUT,                         /* output-only (input is dead) */
+  TVRD_MATCH,                          /* matching (equal) registers */
+  TVRD_FOUND,                          /* mismatching output register */
+  TVRD_EXPECT                          /* mismatching input register */
+};
+
+/* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @const char *excuse@, @va_list ap@ = reason why group skipped
+ *
+ * Returns:    ---
+ *
+ * Use:                Skip the current group.  This should only be called from a
+ *             test environment @setup@ function; a similar effect occurs if
+ *             the @setup@ function fails.
+ */
+
 extern void PRINTF_LIKE(2, 3)
-  tvec_check(struct tvec_state */*tv*/, const char */*detail*/, ...);
-extern void tvec_check_v(struct tvec_state */*tv*/,
-                        const char */*detail*/, va_list */*ap*/);
+  tvec_skipgroup(struct tvec_state */*tv*/, const char */*excuse*/, ...);
+extern void tvec_skipgroup_v(struct tvec_state */*tv*/,
+                            const char */*excuse*/, va_list */*ap*/);
 
-extern int tvec_runtest(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
+ *
+ * Returns:    ---
+ *
+ * Use:                Skip the current test.  This should only be called from a
+ *             test environment @run@ function; a similar effect occurs if
+ *             the @before@ function fails.
+ */
 
-/*----- Input utilities ---------------------------------------------------*/
+extern void PRINTF_LIKE(2, 3)
+  tvec_skip(struct tvec_state */*tv*/, const char */*excuse*/, ...);
+extern void tvec_skip_v(struct tvec_state */*tv*/,
+                       const char */*excuse*/, va_list */*ap*/);
 
-extern void tvec_skipspc(struct tvec_state */*tv*/);
+/* --- @tvec_resetoutputs@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *
+ * Returns:    ---
+ *
+ * Use:                Reset (releases and reinitializes) the output registers in
+ *             the test state.  This is mostly of use to test environment
+ *             @run@ functions, between invocations of the test function.
+ */
 
-#define TVFF_ALLOWANY 1u
-extern int tvec_flushtoeol(struct tvec_state */*tv*/, unsigned /*f*/);
+extern void tvec_resetoutputs(struct tvec_state */*tv*/);
 
-extern int PRINTF_LIKE(4, 5)
-  tvec_readword(struct tvec_state */*tv*/, dstr */*d*/,
-               const char */*delims*/, const char */*expect*/, ...);
-extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/,
-                          const char */*delims*/, const char */*expect*/,
-                          va_list */*ap*/);
+/* --- @tvec_checkregs@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *
+ * Returns:    Zero on success, @-1@ on mismatch.
+ *
+ * Use:                Compare the active output registers (according to the current
+ *             test group definition) with the corresponding input register
+ *             values.  A mismatch occurs if the two values differ
+ *             (according to the register type's @eq@ method), or if the
+ *             input is live but the output is dead.
+ *
+ *             This function only checks for a mismatch and returns the
+ *             result; it takes no other action.  In particular, it doesn't
+ *             report a failure, or dump register values.
+ */
 
-extern int tvec_nexttoken(struct tvec_state */*tv*/);
+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
+ *
+ * Returns:    ---
+ *
+ * Use:                Report the current test as a failure.  This function can be
+ *             called multiple times for a single test, e.g., if the test
+ *             environment's @run@ function invokes the test function
+ *             repeatedly; but a single test that fails repeatedly still
+ *             only counts as a single failure in the statistics.  The
+ *             @detail@ string and its format parameters can be used to
+ *             distinguish which of several invocations failed; it can
+ *             safely be left null if the test function is run only once.
+ */
+
+extern void PRINTF_LIKE(2, 3)
+  tvec_fail(struct tvec_state */*tv*/, const char */*detail*/, ...);
+extern void tvec_fail_v(struct tvec_state */*tv*/,
+                       const char */*detail*/, va_list */*ap*/);
+
+/* --- @tvec_dumpreg@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @unsigned disp@ = the register disposition (@TVRD_...@)
+ *             @const union tvec_regval *tv@ = register value
+ *             @const struct tvec_regdef *rd@ = register definition
+ *
+ * Returns:    ---
+ *
+ * Use:                Dump a register value to the output.  This is the lowest-
+ *             level function for dumping registers, and calls the output
+ *             formatter directly.
+ *
+ *             Usually @tvec_mismatch@ is much more convenient.  Low-level
+ *             access is required for reporting `virtual' registers
+ *             corresponding to test environment settings.
+ */
+
+extern void tvec_dumpreg(struct tvec_state */*tv*/,
+                        unsigned /*disp*/, const union tvec_regval */*rv*/,
+                        const struct tvec_regdef */*rd*/);
+
+/* --- @tvec_mismatch@ --- *
+ *
+ * Arguments:  @struct tvec_state *tv@ = test-vector state
+ *             @unsigned f@ = flags (@TVMF_...@)
+ *
+ * Returns:    ---
+ *
+ * Use:                Dumps registers suitably to report a mismatch.  The flag bits
+ *             @TVMF_IN@ and @TVF_OUT@ select input-only and output
+ *             registers.  If both are reset then nothing happens.
+ *             Suppressing the output registers may be useful, e.g., if the
+ *             test function crashed rather than returning outputs.
+ */
+
+#define TVMF_IN 1u
+#define TVMF_OUT 2u
+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
+ *
+ * Returns:    ---
+ *
+ * Use:                Check the register values, reporting a failure and dumping
+ *             the registers in the event of a mismatch.  This just wraps up
+ *             @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the
+ *             obvious way.
+ */
+
+extern void PRINTF_LIKE(2, 3)
+  tvec_check(struct tvec_state */*tv*/, const char */*detail*/, ...);
+extern void tvec_check_v(struct tvec_state */*tv*/,
+                        const char */*detail*/, va_list */*ap*/);
 
 /*----- Session lifecycle -------------------------------------------------*/
 
@@ -259,21 +560,46 @@ extern int tvec_read(struct tvec_state */*tv*/,
 /*----- Benchmarking ------------------------------------------------------*/
 
 struct tvec_bench {
+  struct tvec_env _env;                        /* benchmarking is an environment */
+  struct bench_state **bst;            /* benchmark state anchor or null */
   unsigned long niter;                 /* iterations done per unit */
   int riter, rbuf;                     /* iterations and buffer registers */
-  size_t ctxsz;                                /* size of context */
-  int (*setup)(const struct tvec_reg */*in*/, struct tvec_reg */*out*/,
-              const union tvec_misc */*arg*/, void */*ctx*/); /* setup fn */
-  void (*teardown)(void */*ctx*/);     /* teardown function, or null */
-  struct bench_state **b;              /* benchmark state anchor or null */
-  union tvec_misc arg;                 /* argument to setup */
+  const struct tvec_env *env;          /* environment (per test, not grp) */
+};
+#define TVEC_BENCHENV                                                  \
+  { sizeof(struct tvec_benchctx),                                      \
+    tvec_benchsetup,                                                   \
+    tvec_benchset,                                                     \
+    tvec_benchbefore,                                                  \
+    tvec_benchrun,                                                     \
+    tvec_benchafter,                                                   \
+    tvec_benchteardown }
+#define TVEC_BENCHINIT TVEC_BENCHENV, &tvec_benchstate
+
+struct tvec_benchctx {
+  const struct tvec_bench *b;
+  struct bench_state *bst;
+  double dflt_target;
+  void *subctx;
 };
 
+struct bench_timing;
 extern struct bench_state *tvec_benchstate;
 
-extern int tvec_ensurebench(struct tvec_state */*tv*/,
-                           struct bench_state **/*b_out*/);
-extern int tvec_bench(struct tvec_state */*tv*/);
+extern int tvec_benchsetup(struct tvec_state */*tv*/,
+                          const struct tvec_env */*env*/,
+                          void */*pctx*/, void */*ctx*/);
+extern int tvec_benchset(struct tvec_state */*tv*/, const char */*var*/,
+                        const struct tvec_env */*env*/, void */*ctx*/);
+extern int tvec_benchbefore(struct tvec_state */*tv*/, void */*ctx*/);
+extern void tvec_benchrun(struct tvec_state */*tv*/,
+                         tvec_testfn */*fn*/, void */*ctx*/);
+extern void tvec_benchafter(struct tvec_state */*tv*/, void */*ctx*/);
+extern void tvec_benchteardown(struct tvec_state */*tv*/, void */*ctx*/);
+
+extern void tvec_benchreport
+  (const struct gprintf_ops */*gops*/, void */*go*/,
+   unsigned /*unit*/, const struct bench_timing */*tm*/);
 
 /*----- Ad-hoc testing ----------------------------------------------------*/
 
@@ -351,14 +677,13 @@ struct tvec_output {
   struct tvec_state *tv;
 };
 
-struct bench_timing;
+enum { TVBU_OP, TVBU_BYTE };
 
 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*/);
-  void (*write)(struct tvec_output */*o*/, const char */*p*/, size_t /*sz*/);
 
   void (*bsession)(struct tvec_output */*o*/);
   int (*esession)(struct tvec_output */*o*/);
@@ -373,11 +698,15 @@ struct tvec_outops {
               const char */*excuse*/, va_list */*ap*/);
   void (*fail)(struct tvec_output */*o*/,
               const char */*detail*/, va_list */*ap*/);
-  void (*mismatch)(struct tvec_output */*o*/);
+  void (*dumpreg)(struct tvec_output */*o*/,
+                 unsigned /*disp*/, const union tvec_regval */*rv*/,
+                 const struct tvec_regdef */*rd*/);
   void (*etest)(struct tvec_output */*o*/, unsigned /*outcome*/);
 
-  void (*bbench)(struct tvec_output */*o*/);
+  void (*bbench)(struct tvec_output */*o*/,
+                const char */*ident*/, unsigned /*unit*/);
   void (*ebench)(struct tvec_output */*o*/,
+                const char */*ident*/, unsigned /*unit*/,
                 const struct bench_timing */*tm*/);
 
   void (*destroy)(struct tvec_output */*o*/);
@@ -399,31 +728,25 @@ extern int PRINTF_LIKE(3, 4)
 extern int tvec_syntax_v(struct tvec_state */*tv*/, int /*ch*/,
                         const char */*expect*/, va_list */*ap*/);
 
-extern void PRINTF_LIKE(2, 3)
-  tvec_skipgroup(struct tvec_state */*tv*/, const char */*note*/, ...);
-extern void tvec_skipgroup_v(struct tvec_state */*tv*/,
-                            const char */*note*/, va_list */*ap*/);
+extern struct tvec_output *tvec_humanoutput(FILE */*fp*/);
+extern struct tvec_output *tvec_tapoutput(FILE */*fp*/);
+extern struct tvec_output *tvec_dfltout(FILE */*fp*/);
 
-extern void PRINTF_LIKE(2, 3)
-  tvec_skip(struct tvec_state */*tv*/, const char */*excuse*/, ...);
-extern void tvec_skip_v(struct tvec_state */*tv*/,
-                       const char */*excuse*/, va_list */*ap*/);
+/*----- Input utilities ---------------------------------------------------*/
 
-extern void PRINTF_LIKE(2, 3)
-  tvec_fail(struct tvec_state */*tv*/, const char */*detail*/, ...);
-extern void tvec_fail_v(struct tvec_state */*tv*/,
-                       const char */*detail*/, va_list */*ap*/);
+extern void tvec_skipspc(struct tvec_state */*tv*/);
 
-extern void tvec_mismatch(struct tvec_state */*tv*/);
+#define TVFF_ALLOWANY 1u
+extern int tvec_flushtoeol(struct tvec_state */*tv*/, unsigned /*f*/);
 
-extern void PRINTF_LIKE(2, 3)
-  tvec_write(struct tvec_state */*tv*/, const char */*p*/, ...);
-extern void tvec_write_v(struct tvec_state */*tv*/,
-                        const char */*p*/, va_list */*ap*/);
+extern int PRINTF_LIKE(4, 5)
+  tvec_readword(struct tvec_state */*tv*/, dstr */*d*/,
+               const char */*delims*/, const char */*expect*/, ...);
+extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/,
+                          const char */*delims*/, const char */*expect*/,
+                          va_list */*ap*/);
 
-extern struct tvec_output *tvec_humanoutput(FILE */*fp*/);
-extern struct tvec_output *tvec_tapoutput(FILE */*fp*/);
-extern struct tvec_output *tvec_dfltout(FILE */*fp*/);
+extern int tvec_nexttoken(struct tvec_state */*tv*/);
 
 /*----- Register types ----------------------------------------------------*/
 
@@ -434,8 +757,6 @@ struct tvec_regty {
   int (*eq)(const union tvec_regval */*rv0*/,
            const union tvec_regval */*rv1*/,
            const struct tvec_regdef */*rd*/);
-  size_t (*measure)(const union tvec_regval */*rv*/,
-                   const struct tvec_regdef */*rd*/);
   int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/,
               const struct tvec_regdef */*rd*/);
   int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/,
@@ -444,7 +765,8 @@ struct tvec_regty {
               struct tvec_state */*tv*/);
   void (*dump)(const union tvec_regval */*rv*/,
               const struct tvec_regdef */*rd*/,
-              struct tvec_state */*tv*/, unsigned /*style*/);
+              unsigned /*style*/,
+              const struct gprintf_ops */*gops*/, void */*go*/);
 #define TVSF_COMPACT 1u
 };
 
@@ -458,6 +780,8 @@ extern const struct tvec_irange
 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;
 
 extern int tvec_claimeq_int(struct tvec_state */*tv*/,
                            long /*i0*/, long /*i1*/,
@@ -472,6 +796,35 @@ extern int tvec_claimeq_uint(struct tvec_state */*tv*/,
 #define TVEC_CLAIMEQ_UINT(tv, u0, u1)                                  \
        (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1))
 
+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;
+};
+
+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*/);
+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;
 
 #define DEFASSOC(tag_, ty, slot)                                       \
@@ -479,28 +832,34 @@ extern const struct tvec_regty tvty_enum;
 TVEC_MISCSLOTS(DEFASSOC)
 #undef DEFASSOC
 
-struct tvec_enuminfo {
-  const char *name; unsigned mv;
-  union {
-#define DEFENUMINFO(tag, ty, slot) struct {                            \
-         const struct tvec_##slot##assoc *av;                          \
-         RANGESLOT_##tag                                               \
-       } slot;
-#define RANGESLOT_INT const struct tvec_irange *ir;
-#define RANGESLOT_UINT const struct tvec_urange *ur;
-#define RANGESLOT_PTR
-    TVEC_MISCSLOTS(DEFENUMINFO)
-#undef DEFENUMINFO
-#undef RANGESLOT_INT
-#undef RANGESLOT_UINT
-#undef RANGESLOT_PTR
-  } u;
+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;
 };
 
+const struct tvec_ienuminfo tvenum_bool;
+
 #define DECLCLAIM(tag, ty, slot)                                       \
        extern int tvec_claimeq_##slot##enum                            \
          (struct tvec_state */*tv*/,                                   \
-          const struct tvec_enuminfo */*ei*/, ty /*e0*/, ty /*e1*/,    \
+          const struct tvec_##slot##enuminfo */*ei*/,                  \
+          ty /*e0*/, ty /*e1*/,                                        \
           const char */*file*/, unsigned /*lno*/, const char */*expr*/);
 TVEC_MISCSLOTS(DECLCLAIM)
 #undef DECLCLAIM
@@ -510,6 +869,9 @@ TVEC_MISCSLOTS(DECLCLAIM)
 #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))
@@ -531,6 +893,14 @@ extern int tvec_claimeq_flags(struct tvec_state */*tv*/,
        (tvec_claimeq_flags(tv, fi, f0, f1,                             \
                            __FILE__, __LINE__, #f0 " /= " #f1))
 
+extern const struct tvec_regty tvty_char;
+extern int tvec_claimeq_char(struct tvec_state */*tv*/,
+                            int /*ch0*/, int /*ch1*/,
+                            const char */*file*/, unsigned /*lno*/,
+                            const char */*expr*/);
+#define TVEC_CLAIMEQ_CHAR(tv, c0, c1)                                  \
+       (tvec_claimeq_char(tv, c0, c1, __FILE__, __LINE__, #c0 " /= " #c1))
+
 extern const struct tvec_regty tvty_string, tvty_bytes;
 
 extern int tvec_claimeq_string(struct tvec_state */*tv*/,
@@ -547,7 +917,6 @@ extern int tvec_claimeq_bytes(struct tvec_state */*tv*/,
                               const void */*p1*/, size_t /*sz1*/,
                               const char */*file*/, unsigned /*lno*/,
                               const char */*expr*/);
-
 #define TVEC_CLAIMEQ_STRING(tv, p0, sz0, p1, sz1)                      \
        (tvec_claimeq_string(tv, p0, sz0, p1, sz1, __FILE__, __LINE__,  \
                             #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]"))