aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/eqn_html.c
diff options
context:
space:
mode:
authorKristaps Dzonsons <kristaps@bsd.lv>2014-10-10 08:44:24 +0000
committerKristaps Dzonsons <kristaps@bsd.lv>2014-10-10 08:44:24 +0000
commit08be124a58f07e4e41c58ea794f3e9eb04230e2b (patch)
tree7ae377429c8c326e79f2bb6133d4f43f2a9dfecc /eqn_html.c
parent12f47a5652f90a1d86d7f6245d2d7f4dfae9f04e (diff)
downloadmandoc-08be124a58f07e4e41c58ea794f3e9eb04230e2b.tar.gz
mandoc-08be124a58f07e4e41c58ea794f3e9eb04230e2b.tar.zst
mandoc-08be124a58f07e4e41c58ea794f3e9eb04230e2b.zip
Re-write of eqn(7) parser and MathML output.
This adds parser-level support for the grammar described by the eqn second-edition technical paper, "Typesetting Mathematics — User's Guide" (Kernighan, Cherry). The reason for this re-write is the grouping rules, which were not possible given the existing implementation. The re-write has also considerably simplified the HTML (and, if it ever is completed, terminal) front-end.
Diffstat (limited to 'eqn_html.c')
-rw-r--r--eqn_html.c257
1 files changed, 112 insertions, 145 deletions
diff --git a/eqn_html.c b/eqn_html.c
index f2e7f79d..73828684 100644
--- a/eqn_html.c
+++ b/eqn_html.c
@@ -1,4 +1,4 @@
-/* $Id: eqn_html.c,v 1.7 2014/09/28 20:14:20 kristaps Exp $ */
+/* $Id: eqn_html.c,v 1.8 2014/10/10 08:44:24 kristaps Exp $ */
/*
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
@@ -27,90 +27,64 @@
#include "out.h"
#include "html.h"
-static const enum htmltag fontmap[EQNFONT__MAX] = {
- TAG_SPAN, /* EQNFONT_NONE */
- TAG_SPAN, /* EQNFONT_ROMAN */
- TAG_B, /* EQNFONT_BOLD */
- TAG_B, /* EQNFONT_FAT */
- TAG_I /* EQNFONT_ITALIC */
-};
-
-static const struct eqn_box *
- eqn_box(struct html *, const struct eqn_box *, int);
-
-
-void
-print_eqn(struct html *p, const struct eqn *ep)
+static void
+eqn_box(struct html *p, const struct eqn_box *bp)
{
- struct htmlpair tag;
- struct tag *t;
-
- PAIR_CLASS_INIT(&tag, "eqn");
- t = print_otag(p, TAG_MATH, 1, &tag);
-
- p->flags |= HTML_NONOSPACE;
- eqn_box(p, ep->root, 1);
- p->flags &= ~HTML_NONOSPACE;
-
- print_tagq(p, t);
-}
-
-/*
- * 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
- * <msub> foo <msub> bar, baz </msub> </msub>
- * 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 *post, *pilet, *tmp;
+ struct tag *post, *row, *cell, *t;
struct htmlpair tag[2];
- int skiptwo;
+ const struct eqn_box *child, *parent;
+ size_t i, j, rows;
if (NULL == bp)
- return(NULL);
-
- post = pilet = NULL;
- skiptwo = 0;
+ return;
- /*
- * 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 (NULL != bp->parent && bp->parent->type == EQN_MATRIX) {
- pilet = print_otag(p, TAG_MTABLE, 0, NULL);
- print_otag(p, TAG_MTR, 0, NULL);
- print_otag(p, TAG_MTD, 0, NULL);
- }
+ post = NULL;
/*
- * 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.
+ * Special handling for a matrix, which is presented to us in
+ * column order, but must be printed in row-order.
*/
- if (bp->pile != EQNPILE_NONE) {
- tmp = print_otag(p, TAG_MTABLE, 0, NULL);
- pilet = (NULL == pilet) ? tmp : pilet;
+ if (EQN_MATRIX == bp->type) {
+ if (NULL == bp->first)
+ goto out;
+ assert(EQN_LIST == bp->first->type);
+ if (NULL == (parent = bp->first->first))
+ goto out;
+ assert(EQN_PILE == parent->type);
+ /* Estimate the number of rows, first. */
+ if (NULL == (child = parent->first))
+ goto out;
+ for (rows = 0; NULL != child; rows++)
+ child = child->next;
+ /* Print row-by-row. */
+ post = print_otag(p, TAG_MTABLE, 0, NULL);
+ for (i = 0; i < rows; i++) {
+ parent = bp->first->first;
+ row = print_otag(p, TAG_MTR, 0, NULL);
+ while (NULL != parent) {
+ child = parent->first;
+ for (j = 0; j < i; j++) {
+ if (NULL == child)
+ break;
+ child = child->next;
+ }
+ cell = print_otag
+ (p, TAG_MTD, 0, NULL);
+ /*
+ * If we have no data for this
+ * particular cell, then print a
+ * placeholder and continue--don't puke.
+ */
+ if (NULL != child)
+ eqn_box(p, child->first);
+ print_tagq(p, cell);
+ parent = parent->next;
+ }
+ print_tagq(p, row);
+ }
+ goto out;
}
- /*
- * 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_TO):
post = print_otag(p, TAG_MOVER, 0, NULL);
@@ -129,93 +103,86 @@ eqn_box(struct html *p, const struct eqn_box *bp, int next)
break;
case (EQNPOS_FROMTO):
post = print_otag(p, TAG_MUNDEROVER, 0, NULL);
- skiptwo = 1;
break;
case (EQNPOS_SUBSUP):
post = print_otag(p, TAG_MSUBSUP, 0, NULL);
- skiptwo = 1;
+ break;
+ case (EQNPOS_SQRT):
+ post = print_otag(p, TAG_MSQRT, 0, NULL);
break;
default:
break;
}
- /*t = EQNFONT_NONE == bp->font ? NULL :
- print_otag(p, fontmap[(int)bp->font], 0, NULL);*/
+ if (bp->top || bp->bottom) {
+ assert(NULL == post);
+ if (bp->top && NULL == bp->bottom)
+ post = print_otag(p, TAG_MOVER, 0, NULL);
+ else if (bp->top && bp->bottom)
+ post = print_otag(p, TAG_MUNDEROVER, 0, NULL);
+ else if (bp->bottom)
+ post = print_otag(p, TAG_MUNDER, 0, NULL);
+ }
+
+ if (EQN_PILE == bp->type) {
+ assert(NULL == post);
+ post = print_otag(p, TAG_MTABLE, 0, NULL);
+ } else if (bp->parent && EQN_PILE == bp->parent->type) {
+ assert(NULL == post);
+ post = print_otag(p, TAG_MTR, 0, NULL);
+ print_otag(p, TAG_MTD, 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;
+ assert(NULL == post);
+ post = print_otag(p, TAG_MI, 0, NULL);
+ print_text(p, bp->text);
+ } else if (NULL == post) {
if (NULL != bp->left || NULL != bp->right) {
PAIR_INIT(&tag[0], ATTR_OPEN,
- NULL != bp->left ? bp->left : "");
+ NULL == bp->left ? "" : bp->left);
PAIR_INIT(&tag[1], ATTR_CLOSE,
- NULL != bp->right ? bp->right : "");
- tmp = print_otag(p, TAG_MFENCED, 2, tag);
+ NULL == bp->right ? "" : bp->right);
+ post = print_otag(p, TAG_MFENCED, 2, tag);
+ }
+ if (NULL == post)
+ post = print_otag(p, TAG_MROW, 0, NULL);
+ else
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);
+ eqn_box(p, bp->first);
+
+out:
+ if (NULL != bp->bottom) {
+ t = print_otag(p, TAG_MO, 0, NULL);
+ print_text(p, bp->bottom);
+ print_tagq(p, t);
+ }
+ if (NULL != bp->top) {
+ t = print_otag(p, TAG_MO, 0, NULL);
+ print_text(p, bp->top);
+ print_tagq(p, t);
}
- /*
- * 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 (NULL != post)
+ print_tagq(p, post);
- /*
- * 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);
+ eqn_box(p, bp->next);
+}
+
+void
+print_eqn(struct html *p, const struct eqn *ep)
+{
+ struct htmlpair tag;
+ struct tag *t;
+
+ PAIR_CLASS_INIT(&tag, "eqn");
+ t = print_otag(p, TAG_MATH, 1, &tag);
+
+ p->flags |= HTML_NONOSPACE;
+ eqn_box(p, ep->root);
+ p->flags &= ~HTML_NONOSPACE;
+
+ print_tagq(p, t);
}