+this_tok:
+ switch (tok) {
+ case (EQN_TOK_UNDEF):
+ if ((rc = eqn_undef(ep)) <= 0)
+ return(rc);
+ break;
+ case (EQN_TOK_NDEFINE):
+ case (EQN_TOK_DEFINE):
+ if ((rc = eqn_def(ep)) <= 0)
+ return(rc);
+ break;
+ case (EQN_TOK_TDEFINE):
+ if (NULL == eqn_nextrawtok(ep, NULL))
+ EQN_MSG(MANDOCERR_EQNEOF, ep);
+ else if (NULL == eqn_next(ep,
+ ep->data[(int)ep->cur], NULL, 0))
+ EQN_MSG(MANDOCERR_EQNEOF, ep);
+ break;
+ case (EQN_TOK_DELIM):
+ eqn_delim(ep);
+ break;
+ case (EQN_TOK_GFONT):
+ if (eqn_nextrawtok(ep, NULL) == NULL)
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ case (EQN_TOK_MARK):
+ case (EQN_TOK_LINEUP):
+ /* Ignore these. */
+ break;
+ case (EQN_TOK_DYAD):
+ case (EQN_TOK_VEC):
+ case (EQN_TOK_UNDER):
+ case (EQN_TOK_BAR):
+ case (EQN_TOK_TILDE):
+ case (EQN_TOK_HAT):
+ case (EQN_TOK_DOT):
+ case (EQN_TOK_DOTDOT):
+ if (parent->last == NULL) {
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ cur = eqn_box_alloc(ep, parent);
+ cur->type = EQN_TEXT;
+ cur->text = mandoc_strdup("");
+ }
+ parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent);
+ parent->type = EQN_LISTONE;
+ parent->expectargs = 1;
+ switch (tok) {
+ case (EQN_TOK_DOTDOT):
+ strlcpy(sym, "\\[ad]", sizeof(sym));
+ break;
+ case (EQN_TOK_VEC):
+ strlcpy(sym, "\\[->]", sizeof(sym));
+ break;
+ case (EQN_TOK_DYAD):
+ strlcpy(sym, "\\[<>]", sizeof(sym));
+ break;
+ case (EQN_TOK_TILDE):
+ strlcpy(sym, "\\[a~]", sizeof(sym));
+ break;
+ case (EQN_TOK_UNDER):
+ strlcpy(sym, "\\[ul]", sizeof(sym));
+ break;
+ case (EQN_TOK_BAR):
+ strlcpy(sym, "\\[rl]", sizeof(sym));
+ break;
+ case (EQN_TOK_DOT):
+ strlcpy(sym, "\\[a.]", sizeof(sym));
+ break;
+ case (EQN_TOK_HAT):
+ strlcpy(sym, "\\[ha]", sizeof(sym));
+ break;
+ default:
+ abort();
+ }
+
+ switch (tok) {
+ case (EQN_TOK_DOTDOT):
+ case (EQN_TOK_VEC):
+ case (EQN_TOK_DYAD):
+ case (EQN_TOK_TILDE):
+ case (EQN_TOK_BAR):
+ case (EQN_TOK_DOT):
+ case (EQN_TOK_HAT):
+ parent->top = mandoc_strdup(sym);
+ break;
+ case (EQN_TOK_UNDER):
+ parent->bottom = mandoc_strdup(sym);
+ break;
+ default:
+ abort();
+ }
+ parent = parent->parent;
+ break;
+ case (EQN_TOK_FWD):
+ case (EQN_TOK_BACK):
+ case (EQN_TOK_DOWN):
+ case (EQN_TOK_UP):
+ subtok = eqn_tok_parse(ep, NULL);
+ if (subtok != EQN_TOK__MAX) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ tok = subtok;
+ goto this_tok;
+ }
+ break;
+ case (EQN_TOK_FAT):
+ case (EQN_TOK_ROMAN):
+ case (EQN_TOK_ITALIC):
+ case (EQN_TOK_BOLD):
+ while (parent->args == parent->expectargs)
+ parent = parent->parent;
+ /*
+ * These values apply to the next word or sequence of
+ * words; thus, we mark that we'll have a child with
+ * exactly one of those.
+ */
+ parent = eqn_box_alloc(ep, parent);
+ parent->type = EQN_LISTONE;
+ parent->expectargs = 1;
+ switch (tok) {
+ case (EQN_TOK_FAT):
+ parent->font = EQNFONT_FAT;
+ break;
+ case (EQN_TOK_ROMAN):
+ parent->font = EQNFONT_ROMAN;
+ break;
+ case (EQN_TOK_ITALIC):
+ parent->font = EQNFONT_ITALIC;
+ break;
+ case (EQN_TOK_BOLD):
+ parent->font = EQNFONT_BOLD;
+ break;
+ default:
+ abort();
+ }
+ break;
+ case (EQN_TOK_SIZE):
+ case (EQN_TOK_GSIZE):
+ /* Accept two values: integral size and a single. */
+ if (NULL == (start = eqn_nexttok(ep, &sz))) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
+ size = mandoc_strntoi(start, sz, 10);
+ if (-1 == size) {
+ mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
+ if (EQN_TOK_GSIZE == tok) {
+ ep->gsize = size;
+ break;
+ }
+ parent = eqn_box_alloc(ep, parent);
+ parent->type = EQN_LISTONE;
+ parent->expectargs = 1;
+ parent->size = size;
+ break;
+ case (EQN_TOK_FROM):
+ case (EQN_TOK_TO):
+ case (EQN_TOK_SUB):
+ case (EQN_TOK_SUP):
+ /*
+ * We have a left-right-associative expression.
+ * Repivot under a positional node, open a child scope
+ * and keep on reading.
+ */
+ if (parent->last == NULL) {
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ cur = eqn_box_alloc(ep, parent);
+ cur->type = EQN_TEXT;
+ cur->text = mandoc_strdup("");
+ }
+ /* Handle the "subsup" and "fromto" positions. */
+ if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) {
+ parent->expectargs = 3;
+ parent->pos = EQNPOS_SUBSUP;
+ break;
+ }
+ if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) {
+ parent->expectargs = 3;
+ parent->pos = EQNPOS_FROMTO;
+ break;
+ }
+ switch (tok) {
+ case (EQN_TOK_FROM):
+ pos = EQNPOS_FROM;
+ break;
+ case (EQN_TOK_TO):
+ pos = EQNPOS_TO;
+ break;
+ case (EQN_TOK_SUP):
+ pos = EQNPOS_SUP;
+ break;
+ case (EQN_TOK_SUB):
+ pos = EQNPOS_SUB;
+ break;
+ default:
+ abort();
+ }
+ parent = eqn_box_makebinary(ep, pos, parent);
+ break;
+ case (EQN_TOK_SQRT):
+ while (parent->args == parent->expectargs)
+ parent = parent->parent;
+ /*
+ * Accept a left-right-associative set of arguments just
+ * like sub and sup and friends but without rebalancing
+ * under a pivot.
+ */
+ parent = eqn_box_alloc(ep, parent);
+ parent->type = EQN_SUBEXPR;
+ parent->pos = EQNPOS_SQRT;
+ parent->expectargs = 1;
+ break;
+ case (EQN_TOK_OVER):
+ /*
+ * We have a right-left-associative fraction.
+ * Close out anything that's currently open, then
+ * rebalance and continue reading.
+ */
+ if (parent->last == NULL) {
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ cur = eqn_box_alloc(ep, parent);
+ cur->type = EQN_TEXT;
+ cur->text = mandoc_strdup("");
+ }
+ while (EQN_SUBEXPR == parent->type)
+ parent = parent->parent;
+ parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent);
+ break;
+ case (EQN_TOK_RIGHT):
+ case (EQN_TOK_BRACE_CLOSE):
+ /*
+ * Close out the existing brace.
+ * FIXME: this is a shitty sentinel: we should really
+ * have a native EQN_BRACE type or whatnot.
+ */
+ for (cur = parent; cur != NULL; cur = cur->parent)
+ if (cur->type == EQN_LIST &&
+ (tok == EQN_TOK_BRACE_CLOSE ||
+ cur->left != NULL))
+ break;
+ if (cur == NULL) {
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
+ parent = cur;
+ if (EQN_TOK_RIGHT == tok) {
+ if (NULL == (start = eqn_nexttok(ep, &sz))) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY,
+ ep->parse, ep->eqn.ln,
+ ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
+ /* Handling depends on right/left. */
+ if (STRNEQ(start, sz, "ceiling", 7)) {
+ strlcpy(sym, "\\[rc]", sizeof(sym));
+ parent->right = mandoc_strdup(sym);
+ } else if (STRNEQ(start, sz, "floor", 5)) {
+ strlcpy(sym, "\\[rf]", sizeof(sym));
+ parent->right = mandoc_strdup(sym);
+ } else
+ parent->right = mandoc_strndup(start, sz);
+ }
+ parent = parent->parent;
+ if (EQN_TOK_BRACE_CLOSE == tok && parent &&
+ (parent->type == EQN_PILE ||
+ parent->type == EQN_MATRIX))
+ parent = parent->parent;
+ /* Close out any "singleton" lists. */
+ while (parent->type == EQN_LISTONE &&
+ parent->args == parent->expectargs)
+ parent = parent->parent;
+ break;
+ case (EQN_TOK_BRACE_OPEN):
+ case (EQN_TOK_LEFT):
+ /*
+ * If we already have something in the stack and we're
+ * in an expression, then rewind til we're not any more
+ * (just like with the text node).
+ */
+ while (parent->args == parent->expectargs)
+ parent = parent->parent;
+ if (EQN_TOK_LEFT == tok &&
+ (start = eqn_nexttok(ep, &sz)) == NULL) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
+ parent = eqn_box_alloc(ep, parent);
+ parent->type = EQN_LIST;
+ if (EQN_TOK_LEFT == tok) {
+ if (STRNEQ(start, sz, "ceiling", 7)) {
+ strlcpy(sym, "\\[lc]", sizeof(sym));
+ parent->left = mandoc_strdup(sym);
+ } else if (STRNEQ(start, sz, "floor", 5)) {
+ strlcpy(sym, "\\[lf]", sizeof(sym));
+ parent->left = mandoc_strdup(sym);
+ } else
+ parent->left = mandoc_strndup(start, sz);
+ }
+ break;
+ case (EQN_TOK_PILE):
+ case (EQN_TOK_LPILE):
+ case (EQN_TOK_RPILE):
+ case (EQN_TOK_CPILE):
+ case (EQN_TOK_CCOL):
+ case (EQN_TOK_LCOL):
+ case (EQN_TOK_RCOL):
+ while (parent->args == parent->expectargs)
+ parent = parent->parent;
+ parent = eqn_box_alloc(ep, parent);
+ parent->type = EQN_PILE;
+ parent->expectargs = 1;
+ break;
+ case (EQN_TOK_ABOVE):
+ for (cur = parent; cur != NULL; cur = cur->parent)
+ if (cur->type == EQN_PILE)
+ break;
+ if (cur == NULL) {
+ mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
+ parent = eqn_box_alloc(ep, cur);
+ parent->type = EQN_LIST;
+ break;
+ case (EQN_TOK_MATRIX):
+ while (parent->args == parent->expectargs)
+ parent = parent->parent;
+ parent = eqn_box_alloc(ep, parent);
+ parent->type = EQN_MATRIX;
+ parent->expectargs = 1;
+ break;
+ case (EQN_TOK_EOF):
+ /*
+ * End of file!
+ * TODO: make sure we're not in an open subexpression.
+ */