=================================================================== RCS file: /cvs/mandoc/eqn_html.c,v retrieving revision 1.4 retrieving revision 1.5 diff -u -p -r1.4 -r1.5 --- mandoc/eqn_html.c 2014/08/10 23:54:41 1.4 +++ mandoc/eqn_html.c 2014/09/28 13:34:15 1.5 @@ -1,4 +1,4 @@ -/* $Id: eqn_html.c,v 1.4 2014/08/10 23:54:41 schwarze Exp $ */ +/* $Id: eqn_html.c,v 1.5 2014/09/28 13:34:15 kristaps Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons * @@ -35,7 +35,8 @@ static const enum htmltag fontmap[EQNFONT__MAX] = { TAG_I /* EQNFONT_ITALIC */ }; -static void eqn_box(struct html *, const struct eqn_box *); +static const struct eqn_box * + eqn_box(struct html *, const struct eqn_box *, int); void @@ -45,37 +46,164 @@ print_eqn(struct html *p, const struct eqn *ep) struct tag *t; PAIR_CLASS_INIT(&tag, "eqn"); - t = print_otag(p, TAG_SPAN, 1, &tag); + t = print_otag(p, TAG_MATH, 1, &tag); p->flags |= HTML_NONOSPACE; - eqn_box(p, ep->root); + eqn_box(p, ep->root, 1); p->flags &= ~HTML_NONOSPACE; print_tagq(p, t); } -static void -eqn_box(struct html *p, const struct eqn_box *bp) +/* + * This function is fairly brittle. + * This is because the eqn syntax doesn't play so nicely with recusive + * formats, e.g., + * foo sub bar sub baz + * ...needs to resolve into + * foo bar, baz + * In other words, we need to embed some recursive work. + * FIXME: this does NOT handle right-left associativity or precedence! + */ +static const struct eqn_box * +eqn_box(struct html *p, const struct eqn_box *bp, int next) { - struct tag *t; + struct tag *post, *pilet, *tmp; + struct htmlpair tag[2]; + int skiptwo; - t = EQNFONT_NONE == bp->font ? NULL : - print_otag(p, fontmap[(int)bp->font], 0, NULL); + if (NULL == bp) + return(NULL); - if (bp->left) - print_text(p, bp->left); + post = pilet = NULL; + skiptwo = 0; - if (bp->text) - print_text(p, bp->text); + /* + * If we're a "row" under a pile, then open up the piling + * context here. + * We do this first because the pile surrounds the content of + * the contained expression. + */ + if (NULL != bp->parent && bp->parent->pile != EQNPILE_NONE) { + pilet = print_otag(p, TAG_MTR, 0, NULL); + print_otag(p, TAG_MTD, 0, NULL); + } - if (bp->first) - eqn_box(p, bp->first); + /* + * If we're establishing a pile, start the table mode now. + * If we've already in a pile row, then don't override "pilet", + * because we'll be closed out anyway. + */ + if (bp->pile != EQNPILE_NONE) { + tmp = print_otag(p, TAG_MTABLE, 0, NULL); + pilet = (NULL == pilet) ? tmp : pilet; + } - if (NULL != t) - print_tagq(p, t); - if (bp->right) - print_text(p, bp->right); + /* + * Positioning. + * This is the most complicated part, and actually doesn't quite + * work (FIXME) because it doesn't account for associativity. + * Setting "post" will mean that we're only going to process a + * single or double following expression. + */ + switch (bp->pos) { + case (EQNPOS_SUP): + post = print_otag(p, TAG_MSUP, 0, NULL); + break; + case (EQNPOS_FROM): + /* FALLTHROUGH */ + case (EQNPOS_SUB): + post = print_otag(p, TAG_MSUB, 0, NULL); + break; + case (EQNPOS_OVER): + post = print_otag(p, TAG_MFRAC, 0, NULL); + break; + case (EQNPOS_SUBSUP): + /* This requires two elements. */ + post = print_otag(p, TAG_MSUBSUP, 0, NULL); + skiptwo = 1; + break; + default: + break; + } - if (bp->next) - eqn_box(p, bp->next); + /*t = EQNFONT_NONE == bp->font ? NULL : + print_otag(p, fontmap[(int)bp->font], 0, NULL);*/ + + if (NULL != bp->text) { + assert(NULL == bp->first); + /* + * We have text. + * This can be a number, a function, a variable, or + * pretty much anything else. + * First, check for some known functions. + * If we're going to create a structural node (e.g., + * sqrt), then set the "post" variable only if it's not + * already set. + */ + if (0 == strcmp(bp->text, "sqrt")) { + tmp = print_otag(p, TAG_MSQRT, 0, NULL); + post = (NULL == post) ? tmp : post; + } else if (0 == strcmp(bp->text, "+") || + 0 == strcmp(bp->text, "-") || + 0 == strcmp(bp->text, "=") || + 0 == strcmp(bp->text, "(") || + 0 == strcmp(bp->text, ")") || + 0 == strcmp(bp->text, "/")) { + tmp = print_otag(p, TAG_MO, 0, NULL); + print_text(p, bp->text); + print_tagq(p, tmp); + } else { + tmp = print_otag(p, TAG_MI, 0, NULL); + print_text(p, bp->text); + print_tagq(p, tmp); + } + } else if (NULL != bp->first) { + assert(NULL == bp->text); + /* + * If we're a "fenced" component (i.e., having + * brackets), then process those brackets now. + * Otherwise, introduce a dummy row (if we're not + * already in a table context). + */ + tmp = NULL; + if (NULL != bp->left || NULL != bp->right) { + PAIR_INIT(&tag[0], ATTR_OPEN, + NULL != bp->left ? bp->left : ""); + PAIR_INIT(&tag[1], ATTR_CLOSE, + NULL != bp->right ? bp->right : ""); + tmp = print_otag(p, TAG_MFENCED, 2, tag); + print_otag(p, TAG_MROW, 0, NULL); + } else if (NULL == pilet) + tmp = print_otag(p, TAG_MROW, 0, NULL); + eqn_box(p, bp->first, 1); + if (NULL != tmp) + print_tagq(p, tmp); + } + + /* + * If a positional context, invoke the "next" context. + * This is recursive and will return the end of the recursive + * chain of "next" contexts. + */ + if (NULL != post) { + bp = eqn_box(p, bp->next, 0); + if (skiptwo) + bp = eqn_box(p, bp->next, 0); + print_tagq(p, post); + } + + /* + * If we're being piled (either directly, in the table, or + * indirectly in a table row), then close that out. + */ + if (NULL != pilet) + print_tagq(p, pilet); + + /* + * If we're normally processing, then grab the next node. + * If we're in a recursive context, then don't seek to the next + * node; further recursion has already been handled. + */ + return(next ? eqn_box(p, bp->next, 1) : bp); }