+ 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 (tok == EQN_TOK_BRACE_CLOSE &&
+ (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.
+ */
+ return ROFF_EQN;
+ default:
+ assert(tok == EQN_TOK__MAX);
+ assert(NULL != p);
+ /*
+ * If we already have something in the stack and we're
+ * in an expression, then rewind til we're not any more.
+ */
+ while (parent->args == parent->expectargs)
+ parent = parent->parent;
+ cur = eqn_box_alloc(ep, parent);
+ cur->type = EQN_TEXT;
+ for (i = 0; i < EQNSYM__MAX; i++)
+ if (0 == strcmp(eqnsyms[i].str, p)) {
+ (void)snprintf(sym, sizeof(sym),
+ "\\[%s]", eqnsyms[i].sym);
+ cur->text = mandoc_strdup(sym);
+ free(p);
+ break;
+ }
+
+ if (i == EQNSYM__MAX)
+ cur->text = p;
+ /*
+ * Post-process list status.
+ */
+ while (parent->type == EQN_LISTONE &&
+ parent->args == parent->expectargs)
+ parent = parent->parent;
+ break;