for (rd = tv->test->regs; rd->name; rd++) {
if (rd->i >= tv->cfg.nrout) continue;
rin = TVEC_REG(tv, in, rd->i); rout = TVEC_REG(tv, out, rd->i);
- if (!rin->f&TVRF_LIVE) continue;
+ if (!(rin->f&TVRF_LIVE)) continue;
if (!(rout->f&TVRF_LIVE) || !rd->ty->eq(&rin->v, &rout->v, rd))
return (-1);
}
* output registers as live if the corresponding inputs are
* live. It calls the environment's @before@, @run@, and
* @after@ functions if provided; if there is no @run@ function,
- * it calls @tvec_check@ to verify the output values.
+ * then it calls the test function directly, passing it the
+ * environment's context pointer, and then calls @tvec_check@ to
+ * verify the output values.
*/
static void check(struct tvec_state *tv, struct groupstate *g)
const struct tvec_test *t = tv->test;
const struct tvec_env *env = t->env;
const struct tvec_regdef *rd;
+ struct tvec_reg *r;
unsigned f = 0;
#define f_err 1u
if (!(tv->f&TVSF_OPEN)) return;
for (rd = t->regs; rd->name; rd++) {
- if (TVEC_REG(tv, in, rd->i)->f&TVRF_LIVE) {
+ r = TVEC_REG(tv, in, rd->i);
+ if (r->f&TVRF_LIVE) {
if (rd->i < tv->cfg.nrout)
TVEC_REG(tv, out, rd->i)->f |= TVRF_LIVE;
- } else if (!(rd->f&TVRF_OPT)) {
+ } else if (!(r->f&TVRF_SEEN) && !(rd->f&TVRF_OPT)) {
tvec_error(tv, "required register `%s' not set in test `%s'",
rd->name, t->name);
f |= f_err;
{
const struct tvec_test *t = tv->test;
const struct tvec_env *env = t->env;
+ const struct tvec_regdef *rd0, *rd1;
unsigned i;
+#ifndef NDEBUG
+ /* Check that the register names and indices are distinct. */
+ for (rd0 = t->regs; rd0->name; rd0++) {
+ assert(rd0->i < tv->cfg.nreg);
+ for (rd1 = t->regs; rd1->name; rd1++)
+ if (rd0 != rd1) {
+ assert(rd0->i != rd1->i);
+ assert(STRCMP(rd0->name, !=, rd1->name));
+ }
+ }
+#endif
+
tv->output->ops->bgroup(tv->output);
tv->f &= ~(TVSF_SKIP | TVSF_MUFFLE);
tvec_initregs(tv);
*/
ungetc(ch, tv->fp);
DRESET(&d);
- if (tvec_readword(tv, &d, 0, "=:;", "register name"))
+ if (tvec_readword(tv, &d, 0, "=:*;", "register name"))
goto flush_line;
- /* Now there should be a separator. */
- tvec_skipspc(tv); ch = getc(tv->fp);
- if (ch != '=' && ch != ':')
- { tvec_syntax(tv, ch, "`=' or `:'"); goto flush_line; }
- tvec_skipspc(tv);
-
- /* If there's no test, then report an error. Set the muffle flag,
- * because there's no point in complaining about every assignment
- * in this block.
- */
- if (!tv->test) {
- if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
- tv->f |= TVSF_MUFFLE; goto flush_line;
- }
-
- /* Open the test. This is syntactically a stanza of settings, so
- * it's fair to report on missing register assignments.
- */
- open_test(tv);
-
+ /* See what sort of thing we have found. */
if (d.buf[0] == '@') {
/* A special register assignment. */
}
tvec_unkreg(tv, d.buf); goto flush_line;
found_var:
-
- /* Set up the register. */
- if (vd->regsz <= sizeof(rbuf))
- r = &rbuf;
- else {
- GROWBUF_REPLACE(&arena_stdlib, r_alloc, rsz, vd->regsz,
- 8*sizeof(void *), 1);
- r = r_alloc;
- }
-
- /* Read and set the value. */
- vd->def.ty->init(&r->v, &vd->def);
- if (vd->def.ty->parse(&r->v, &vd->def, tv)) goto flush_line;
- if (!(tv->f&TVSF_SKIP) && vd->setvar(tv, d.buf, &r->v, varctx))
- goto bad;
-
- /* Clean up. */
- vd->def.ty->release(&r->v, &vd->def); vd = 0;
+ rd = &vd->def;
} else {
/* A standard register. */
tvec_error(tv, "unknown register `%s' for test `%s'",
d.buf, tv->test->name);
goto flush_line;
-
found_reg:
+
/* Complain if the register is already set. */
r = TVEC_REG(tv, in, rd->i);
- if (r->f&TVRF_LIVE)
+ if (r->f&TVRF_SEEN)
{ tvec_dupreg(tv, rd->name); goto flush_line; }
+ }
+
+ /* If there's no test, then report an error. Set the muffle flag,
+ * because there's no point in complaining about every assignment
+ * in this block.
+ */
+ if (!tv->test) {
+ if (!(tv->f&TVSF_MUFFLE)) tvec_error(tv, "no current test");
+ tv->f |= TVSF_MUFFLE; goto flush_line;
+ }
- /* Parse a value and mark the register as live. */
- if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
- r->f |= TVRF_LIVE;
+ /* Open the test. This is syntactically a paragraph of settings,
+ * so it's fair to report on missing register assignments.
+ */
+ open_test(tv);
+
+ /* Now there should be a separator. */
+ tvec_skipspc(tv); ch = getc(tv->fp);
+
+ if (ch == '*') {
+ /* Register explicitly marked unset. */
+
+ if (vd) {
+ tvec_error(tv, "can't unset special variables");
+ goto flush_line;
+ }
+ if (!(rd->f&(TVRF_OPT | TVRF_UNSET))) {
+ tvec_error(tv, "register `%s' must be assigned "
+ "a value in test `%s'", rd->name, tv->test->name);
+ goto flush_line;
+ }
+ r->f |= TVRF_SEEN;
+ if (tvec_flushtoeol(tv, 0)) goto bad;
+ } else {
+ /* Common case of a proper assignment. */
+
+ /* We must have a separator. */
+ if (ch != '=' && ch != ':')
+ { tvec_syntax(tv, ch, "`=', `:', or `*'"); goto flush_line; }
+ tvec_skipspc(tv);
+
+ if (!vd) {
+ /* An ordinary register. Parse a value and mark the register
+ * as live.
+ */
+
+ if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
+ r->f |= TVRF_LIVE | TVRF_SEEN;
+ } else {
+ /* A special register defined by an environment. */
+
+ /* Set up the register. */
+ if (vd->regsz <= sizeof(rbuf))
+ r = &rbuf;
+ else {
+ GROWBUF_REPLACE(&arena_stdlib, r_alloc, rsz, vd->regsz,
+ 8*sizeof(void *), 1);
+ r = r_alloc;
+ }
+
+ /* Read and set the value. */
+ rd->ty->init(&r->v, rd);
+ if (rd->ty->parse(&r->v, rd, tv)) goto flush_line;
+ if (!(tv->f&TVSF_SKIP) && vd->setvar(tv, d.buf, &r->v, varctx))
+ goto bad;
+
+ /* Clean up. */
+ rd->ty->release(&r->v, &vd->def); vd = 0;
+ }
}
}
break;