Home | History | Annotate | Line # | Download | only in dist
      1  1.9       wiz /*	Id: eqn.c,v 1.84 2020/01/08 12:16:24 schwarze Exp  */
      2  1.1     joerg /*
      3  1.2  christos  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps (at) bsd.lv>
      4  1.9       wiz  * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze (at) openbsd.org>
      5  1.1     joerg  *
      6  1.1     joerg  * Permission to use, copy, modify, and distribute this software for any
      7  1.1     joerg  * purpose with or without fee is hereby granted, provided that the above
      8  1.1     joerg  * copyright notice and this permission notice appear in all copies.
      9  1.1     joerg  *
     10  1.1     joerg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  1.1     joerg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  1.1     joerg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  1.1     joerg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  1.1     joerg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  1.1     joerg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  1.1     joerg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  1.1     joerg  */
     18  1.1     joerg #include "config.h"
     19  1.2  christos 
     20  1.2  christos #include <sys/types.h>
     21  1.1     joerg 
     22  1.1     joerg #include <assert.h>
     23  1.7  christos #include <ctype.h>
     24  1.2  christos #include <limits.h>
     25  1.1     joerg #include <stdio.h>
     26  1.1     joerg #include <stdlib.h>
     27  1.1     joerg #include <string.h>
     28  1.1     joerg #include <time.h>
     29  1.1     joerg 
     30  1.7  christos #include "mandoc_aux.h"
     31  1.1     joerg #include "mandoc.h"
     32  1.7  christos #include "roff.h"
     33  1.8  christos #include "eqn.h"
     34  1.1     joerg #include "libmandoc.h"
     35  1.8  christos #include "eqn_parse.h"
     36  1.1     joerg 
     37  1.2  christos #define	EQN_NEST_MAX	 128 /* maximum nesting of defines */
     38  1.2  christos #define	STRNEQ(p1, sz1, p2, sz2) \
     39  1.2  christos 	((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
     40  1.2  christos 
     41  1.2  christos enum	eqn_tok {
     42  1.2  christos 	EQN_TOK_DYAD = 0,
     43  1.2  christos 	EQN_TOK_VEC,
     44  1.2  christos 	EQN_TOK_UNDER,
     45  1.2  christos 	EQN_TOK_BAR,
     46  1.2  christos 	EQN_TOK_TILDE,
     47  1.2  christos 	EQN_TOK_HAT,
     48  1.2  christos 	EQN_TOK_DOT,
     49  1.2  christos 	EQN_TOK_DOTDOT,
     50  1.2  christos 	EQN_TOK_FWD,
     51  1.2  christos 	EQN_TOK_BACK,
     52  1.2  christos 	EQN_TOK_DOWN,
     53  1.2  christos 	EQN_TOK_UP,
     54  1.2  christos 	EQN_TOK_FAT,
     55  1.2  christos 	EQN_TOK_ROMAN,
     56  1.2  christos 	EQN_TOK_ITALIC,
     57  1.2  christos 	EQN_TOK_BOLD,
     58  1.2  christos 	EQN_TOK_SIZE,
     59  1.2  christos 	EQN_TOK_SUB,
     60  1.2  christos 	EQN_TOK_SUP,
     61  1.2  christos 	EQN_TOK_SQRT,
     62  1.2  christos 	EQN_TOK_OVER,
     63  1.2  christos 	EQN_TOK_FROM,
     64  1.2  christos 	EQN_TOK_TO,
     65  1.2  christos 	EQN_TOK_BRACE_OPEN,
     66  1.2  christos 	EQN_TOK_BRACE_CLOSE,
     67  1.2  christos 	EQN_TOK_GSIZE,
     68  1.2  christos 	EQN_TOK_GFONT,
     69  1.2  christos 	EQN_TOK_MARK,
     70  1.2  christos 	EQN_TOK_LINEUP,
     71  1.2  christos 	EQN_TOK_LEFT,
     72  1.2  christos 	EQN_TOK_RIGHT,
     73  1.2  christos 	EQN_TOK_PILE,
     74  1.2  christos 	EQN_TOK_LPILE,
     75  1.2  christos 	EQN_TOK_RPILE,
     76  1.2  christos 	EQN_TOK_CPILE,
     77  1.2  christos 	EQN_TOK_MATRIX,
     78  1.2  christos 	EQN_TOK_CCOL,
     79  1.2  christos 	EQN_TOK_LCOL,
     80  1.2  christos 	EQN_TOK_RCOL,
     81  1.2  christos 	EQN_TOK_DELIM,
     82  1.2  christos 	EQN_TOK_DEFINE,
     83  1.2  christos 	EQN_TOK_TDEFINE,
     84  1.2  christos 	EQN_TOK_NDEFINE,
     85  1.2  christos 	EQN_TOK_UNDEF,
     86  1.2  christos 	EQN_TOK_ABOVE,
     87  1.7  christos 	EQN_TOK__MAX,
     88  1.7  christos 	EQN_TOK_FUNC,
     89  1.7  christos 	EQN_TOK_QUOTED,
     90  1.7  christos 	EQN_TOK_SYM,
     91  1.7  christos 	EQN_TOK_EOF
     92  1.2  christos };
     93  1.2  christos 
     94  1.2  christos static	const char *eqn_toks[EQN_TOK__MAX] = {
     95  1.2  christos 	"dyad", /* EQN_TOK_DYAD */
     96  1.2  christos 	"vec", /* EQN_TOK_VEC */
     97  1.2  christos 	"under", /* EQN_TOK_UNDER */
     98  1.2  christos 	"bar", /* EQN_TOK_BAR */
     99  1.2  christos 	"tilde", /* EQN_TOK_TILDE */
    100  1.2  christos 	"hat", /* EQN_TOK_HAT */
    101  1.2  christos 	"dot", /* EQN_TOK_DOT */
    102  1.2  christos 	"dotdot", /* EQN_TOK_DOTDOT */
    103  1.2  christos 	"fwd", /* EQN_TOK_FWD * */
    104  1.2  christos 	"back", /* EQN_TOK_BACK */
    105  1.2  christos 	"down", /* EQN_TOK_DOWN */
    106  1.2  christos 	"up", /* EQN_TOK_UP */
    107  1.2  christos 	"fat", /* EQN_TOK_FAT */
    108  1.2  christos 	"roman", /* EQN_TOK_ROMAN */
    109  1.2  christos 	"italic", /* EQN_TOK_ITALIC */
    110  1.2  christos 	"bold", /* EQN_TOK_BOLD */
    111  1.2  christos 	"size", /* EQN_TOK_SIZE */
    112  1.2  christos 	"sub", /* EQN_TOK_SUB */
    113  1.2  christos 	"sup", /* EQN_TOK_SUP */
    114  1.2  christos 	"sqrt", /* EQN_TOK_SQRT */
    115  1.2  christos 	"over", /* EQN_TOK_OVER */
    116  1.2  christos 	"from", /* EQN_TOK_FROM */
    117  1.2  christos 	"to", /* EQN_TOK_TO */
    118  1.2  christos 	"{", /* EQN_TOK_BRACE_OPEN */
    119  1.2  christos 	"}", /* EQN_TOK_BRACE_CLOSE */
    120  1.2  christos 	"gsize", /* EQN_TOK_GSIZE */
    121  1.2  christos 	"gfont", /* EQN_TOK_GFONT */
    122  1.2  christos 	"mark", /* EQN_TOK_MARK */
    123  1.2  christos 	"lineup", /* EQN_TOK_LINEUP */
    124  1.2  christos 	"left", /* EQN_TOK_LEFT */
    125  1.2  christos 	"right", /* EQN_TOK_RIGHT */
    126  1.2  christos 	"pile", /* EQN_TOK_PILE */
    127  1.2  christos 	"lpile", /* EQN_TOK_LPILE */
    128  1.2  christos 	"rpile", /* EQN_TOK_RPILE */
    129  1.2  christos 	"cpile", /* EQN_TOK_CPILE */
    130  1.2  christos 	"matrix", /* EQN_TOK_MATRIX */
    131  1.2  christos 	"ccol", /* EQN_TOK_CCOL */
    132  1.2  christos 	"lcol", /* EQN_TOK_LCOL */
    133  1.2  christos 	"rcol", /* EQN_TOK_RCOL */
    134  1.2  christos 	"delim", /* EQN_TOK_DELIM */
    135  1.2  christos 	"define", /* EQN_TOK_DEFINE */
    136  1.2  christos 	"tdefine", /* EQN_TOK_TDEFINE */
    137  1.2  christos 	"ndefine", /* EQN_TOK_NDEFINE */
    138  1.2  christos 	"undef", /* EQN_TOK_UNDEF */
    139  1.2  christos 	"above", /* EQN_TOK_ABOVE */
    140  1.2  christos };
    141  1.2  christos 
    142  1.7  christos static	const char *const eqn_func[] = {
    143  1.7  christos 	"acos",	"acsc",	"and",	"arc",	"asec",	"asin", "atan",
    144  1.7  christos 	"cos",	"cosh", "coth",	"csc",	"det",	"exp",	"for",
    145  1.7  christos 	"if",	"lim",	"ln",	"log",	"max",	"min",
    146  1.7  christos 	"sec",	"sin",	"sinh",	"tan",	"tanh",	"Im",	"Re",
    147  1.7  christos };
    148  1.7  christos 
    149  1.2  christos enum	eqn_symt {
    150  1.7  christos 	EQNSYM_alpha = 0,
    151  1.2  christos 	EQNSYM_beta,
    152  1.2  christos 	EQNSYM_chi,
    153  1.2  christos 	EQNSYM_delta,
    154  1.2  christos 	EQNSYM_epsilon,
    155  1.2  christos 	EQNSYM_eta,
    156  1.2  christos 	EQNSYM_gamma,
    157  1.2  christos 	EQNSYM_iota,
    158  1.2  christos 	EQNSYM_kappa,
    159  1.2  christos 	EQNSYM_lambda,
    160  1.2  christos 	EQNSYM_mu,
    161  1.2  christos 	EQNSYM_nu,
    162  1.2  christos 	EQNSYM_omega,
    163  1.2  christos 	EQNSYM_omicron,
    164  1.2  christos 	EQNSYM_phi,
    165  1.2  christos 	EQNSYM_pi,
    166  1.2  christos 	EQNSYM_ps,
    167  1.2  christos 	EQNSYM_rho,
    168  1.2  christos 	EQNSYM_sigma,
    169  1.2  christos 	EQNSYM_tau,
    170  1.2  christos 	EQNSYM_theta,
    171  1.2  christos 	EQNSYM_upsilon,
    172  1.2  christos 	EQNSYM_xi,
    173  1.2  christos 	EQNSYM_zeta,
    174  1.2  christos 	EQNSYM_DELTA,
    175  1.2  christos 	EQNSYM_GAMMA,
    176  1.2  christos 	EQNSYM_LAMBDA,
    177  1.2  christos 	EQNSYM_OMEGA,
    178  1.2  christos 	EQNSYM_PHI,
    179  1.2  christos 	EQNSYM_PI,
    180  1.2  christos 	EQNSYM_PSI,
    181  1.2  christos 	EQNSYM_SIGMA,
    182  1.2  christos 	EQNSYM_THETA,
    183  1.2  christos 	EQNSYM_UPSILON,
    184  1.2  christos 	EQNSYM_XI,
    185  1.2  christos 	EQNSYM_inter,
    186  1.2  christos 	EQNSYM_union,
    187  1.2  christos 	EQNSYM_prod,
    188  1.2  christos 	EQNSYM_int,
    189  1.2  christos 	EQNSYM_sum,
    190  1.2  christos 	EQNSYM_grad,
    191  1.2  christos 	EQNSYM_del,
    192  1.2  christos 	EQNSYM_times,
    193  1.2  christos 	EQNSYM_cdot,
    194  1.2  christos 	EQNSYM_nothing,
    195  1.2  christos 	EQNSYM_approx,
    196  1.2  christos 	EQNSYM_prime,
    197  1.2  christos 	EQNSYM_half,
    198  1.2  christos 	EQNSYM_partial,
    199  1.2  christos 	EQNSYM_inf,
    200  1.2  christos 	EQNSYM_muchgreat,
    201  1.2  christos 	EQNSYM_muchless,
    202  1.2  christos 	EQNSYM_larrow,
    203  1.2  christos 	EQNSYM_rarrow,
    204  1.2  christos 	EQNSYM_pm,
    205  1.2  christos 	EQNSYM_nequal,
    206  1.2  christos 	EQNSYM_equiv,
    207  1.2  christos 	EQNSYM_lessequal,
    208  1.2  christos 	EQNSYM_moreequal,
    209  1.2  christos 	EQNSYM_minus,
    210  1.2  christos 	EQNSYM__MAX
    211  1.2  christos };
    212  1.2  christos 
    213  1.2  christos struct	eqnsym {
    214  1.2  christos 	const char	*str;
    215  1.2  christos 	const char	*sym;
    216  1.2  christos };
    217  1.2  christos 
    218  1.2  christos static	const struct eqnsym eqnsyms[EQNSYM__MAX] = {
    219  1.2  christos 	{ "alpha", "*a" }, /* EQNSYM_alpha */
    220  1.2  christos 	{ "beta", "*b" }, /* EQNSYM_beta */
    221  1.2  christos 	{ "chi", "*x" }, /* EQNSYM_chi */
    222  1.2  christos 	{ "delta", "*d" }, /* EQNSYM_delta */
    223  1.2  christos 	{ "epsilon", "*e" }, /* EQNSYM_epsilon */
    224  1.2  christos 	{ "eta", "*y" }, /* EQNSYM_eta */
    225  1.2  christos 	{ "gamma", "*g" }, /* EQNSYM_gamma */
    226  1.2  christos 	{ "iota", "*i" }, /* EQNSYM_iota */
    227  1.2  christos 	{ "kappa", "*k" }, /* EQNSYM_kappa */
    228  1.2  christos 	{ "lambda", "*l" }, /* EQNSYM_lambda */
    229  1.2  christos 	{ "mu", "*m" }, /* EQNSYM_mu */
    230  1.2  christos 	{ "nu", "*n" }, /* EQNSYM_nu */
    231  1.2  christos 	{ "omega", "*w" }, /* EQNSYM_omega */
    232  1.2  christos 	{ "omicron", "*o" }, /* EQNSYM_omicron */
    233  1.2  christos 	{ "phi", "*f" }, /* EQNSYM_phi */
    234  1.2  christos 	{ "pi", "*p" }, /* EQNSYM_pi */
    235  1.2  christos 	{ "psi", "*q" }, /* EQNSYM_psi */
    236  1.2  christos 	{ "rho", "*r" }, /* EQNSYM_rho */
    237  1.2  christos 	{ "sigma", "*s" }, /* EQNSYM_sigma */
    238  1.2  christos 	{ "tau", "*t" }, /* EQNSYM_tau */
    239  1.2  christos 	{ "theta", "*h" }, /* EQNSYM_theta */
    240  1.2  christos 	{ "upsilon", "*u" }, /* EQNSYM_upsilon */
    241  1.2  christos 	{ "xi", "*c" }, /* EQNSYM_xi */
    242  1.2  christos 	{ "zeta", "*z" }, /* EQNSYM_zeta */
    243  1.2  christos 	{ "DELTA", "*D" }, /* EQNSYM_DELTA */
    244  1.2  christos 	{ "GAMMA", "*G" }, /* EQNSYM_GAMMA */
    245  1.2  christos 	{ "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
    246  1.2  christos 	{ "OMEGA", "*W" }, /* EQNSYM_OMEGA */
    247  1.2  christos 	{ "PHI", "*F" }, /* EQNSYM_PHI */
    248  1.2  christos 	{ "PI", "*P" }, /* EQNSYM_PI */
    249  1.2  christos 	{ "PSI", "*Q" }, /* EQNSYM_PSI */
    250  1.2  christos 	{ "SIGMA", "*S" }, /* EQNSYM_SIGMA */
    251  1.2  christos 	{ "THETA", "*H" }, /* EQNSYM_THETA */
    252  1.2  christos 	{ "UPSILON", "*U" }, /* EQNSYM_UPSILON */
    253  1.2  christos 	{ "XI", "*C" }, /* EQNSYM_XI */
    254  1.2  christos 	{ "inter", "ca" }, /* EQNSYM_inter */
    255  1.2  christos 	{ "union", "cu" }, /* EQNSYM_union */
    256  1.2  christos 	{ "prod", "product" }, /* EQNSYM_prod */
    257  1.2  christos 	{ "int", "integral" }, /* EQNSYM_int */
    258  1.2  christos 	{ "sum", "sum" }, /* EQNSYM_sum */
    259  1.2  christos 	{ "grad", "gr" }, /* EQNSYM_grad */
    260  1.2  christos 	{ "del", "gr" }, /* EQNSYM_del */
    261  1.2  christos 	{ "times", "mu" }, /* EQNSYM_times */
    262  1.2  christos 	{ "cdot", "pc" }, /* EQNSYM_cdot */
    263  1.2  christos 	{ "nothing", "&" }, /* EQNSYM_nothing */
    264  1.2  christos 	{ "approx", "~~" }, /* EQNSYM_approx */
    265  1.2  christos 	{ "prime", "fm" }, /* EQNSYM_prime */
    266  1.2  christos 	{ "half", "12" }, /* EQNSYM_half */
    267  1.2  christos 	{ "partial", "pd" }, /* EQNSYM_partial */
    268  1.2  christos 	{ "inf", "if" }, /* EQNSYM_inf */
    269  1.2  christos 	{ ">>", ">>" }, /* EQNSYM_muchgreat */
    270  1.2  christos 	{ "<<", "<<" }, /* EQNSYM_muchless */
    271  1.2  christos 	{ "<-", "<-" }, /* EQNSYM_larrow */
    272  1.2  christos 	{ "->", "->" }, /* EQNSYM_rarrow */
    273  1.2  christos 	{ "+-", "+-" }, /* EQNSYM_pm */
    274  1.2  christos 	{ "!=", "!=" }, /* EQNSYM_nequal */
    275  1.2  christos 	{ "==", "==" }, /* EQNSYM_equiv */
    276  1.2  christos 	{ "<=", "<=" }, /* EQNSYM_lessequal */
    277  1.2  christos 	{ ">=", ">=" }, /* EQNSYM_moreequal */
    278  1.2  christos 	{ "-", "mi" }, /* EQNSYM_minus */
    279  1.2  christos };
    280  1.2  christos 
    281  1.7  christos enum	parse_mode {
    282  1.7  christos 	MODE_QUOTED,
    283  1.7  christos 	MODE_NOSUB,
    284  1.7  christos 	MODE_SUB,
    285  1.7  christos 	MODE_TOK
    286  1.7  christos };
    287  1.7  christos 
    288  1.8  christos struct	eqn_def {
    289  1.8  christos 	char		 *key;
    290  1.8  christos 	size_t		  keysz;
    291  1.8  christos 	char		 *val;
    292  1.8  christos 	size_t		  valsz;
    293  1.8  christos };
    294  1.8  christos 
    295  1.2  christos static	struct eqn_box	*eqn_box_alloc(struct eqn_node *, struct eqn_box *);
    296  1.2  christos static	struct eqn_box	*eqn_box_makebinary(struct eqn_node *,
    297  1.7  christos 				struct eqn_box *);
    298  1.2  christos static	void		 eqn_def(struct eqn_node *);
    299  1.7  christos static	struct eqn_def	*eqn_def_find(struct eqn_node *);
    300  1.2  christos static	void		 eqn_delim(struct eqn_node *);
    301  1.7  christos static	enum eqn_tok	 eqn_next(struct eqn_node *, enum parse_mode);
    302  1.2  christos static	void		 eqn_undef(struct eqn_node *);
    303  1.2  christos 
    304  1.2  christos 
    305  1.7  christos struct eqn_node *
    306  1.8  christos eqn_alloc(void)
    307  1.1     joerg {
    308  1.7  christos 	struct eqn_node *ep;
    309  1.1     joerg 
    310  1.7  christos 	ep = mandoc_calloc(1, sizeof(*ep));
    311  1.7  christos 	ep->gsize = EQN_DEFSIZE;
    312  1.7  christos 	return ep;
    313  1.7  christos }
    314  1.2  christos 
    315  1.7  christos void
    316  1.7  christos eqn_reset(struct eqn_node *ep)
    317  1.7  christos {
    318  1.7  christos 	free(ep->data);
    319  1.7  christos 	ep->data = ep->start = ep->end = NULL;
    320  1.7  christos 	ep->sz = ep->toksz = 0;
    321  1.1     joerg }
    322  1.1     joerg 
    323  1.7  christos void
    324  1.7  christos eqn_read(struct eqn_node *ep, const char *p)
    325  1.1     joerg {
    326  1.7  christos 	char		*cp;
    327  1.1     joerg 
    328  1.7  christos 	if (ep->data == NULL) {
    329  1.7  christos 		ep->sz = strlen(p);
    330  1.7  christos 		ep->data = mandoc_strdup(p);
    331  1.7  christos 	} else {
    332  1.7  christos 		ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
    333  1.7  christos 		free(ep->data);
    334  1.7  christos 		ep->data = cp;
    335  1.7  christos 	}
    336  1.7  christos 	ep->sz += 1;
    337  1.1     joerg }
    338  1.1     joerg 
    339  1.2  christos /*
    340  1.2  christos  * Find the key "key" of the give size within our eqn-defined values.
    341  1.2  christos  */
    342  1.2  christos static struct eqn_def *
    343  1.7  christos eqn_def_find(struct eqn_node *ep)
    344  1.2  christos {
    345  1.2  christos 	int		 i;
    346  1.2  christos 
    347  1.2  christos 	for (i = 0; i < (int)ep->defsz; i++)
    348  1.2  christos 		if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
    349  1.7  christos 		    ep->defs[i].keysz, ep->start, ep->toksz))
    350  1.5  christos 			return &ep->defs[i];
    351  1.2  christos 
    352  1.5  christos 	return NULL;
    353  1.2  christos }
    354  1.2  christos 
    355  1.2  christos /*
    356  1.7  christos  * Parse a token from the input text.  The modes are:
    357  1.7  christos  * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
    358  1.7  christos  *   before its next occurence.  Do not interpret the token in any
    359  1.7  christos  *   way and return EQN_TOK_QUOTED.  All other modes behave like
    360  1.7  christos  *   MODE_QUOTED when *ep->start is '"'.
    361  1.7  christos  * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
    362  1.7  christos  *   otherwise, it ends before the next whitespace or brace.
    363  1.7  christos  *   Do not interpret the token and return EQN_TOK__MAX.
    364  1.7  christos  * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
    365  1.7  christos  *   alias created with define.  If it is an alias, replace it with
    366  1.7  christos  *   its string value and reparse.
    367  1.7  christos  * MODE_TOK: Like MODE_SUB, but also check the token against the list
    368  1.7  christos  *   of tokens, and if there is a match, return that token.  Otherwise,
    369  1.7  christos  *   if the token matches a symbol, return EQN_TOK_SYM; if it matches
    370  1.7  christos  *   a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX.  Except for
    371  1.7  christos  *   a token match, *ep->start is set to an allocated string that the
    372  1.7  christos  *   caller is expected to free.
    373  1.7  christos  * All modes skip whitespace following the end of the token.
    374  1.2  christos  */
    375  1.7  christos static enum eqn_tok
    376  1.7  christos eqn_next(struct eqn_node *ep, enum parse_mode mode)
    377  1.2  christos {
    378  1.7  christos 	static int	 last_len, lim;
    379  1.7  christos 
    380  1.2  christos 	struct eqn_def	*def;
    381  1.7  christos 	size_t		 start;
    382  1.7  christos 	int		 diff, i, quoted;
    383  1.7  christos 	enum eqn_tok	 tok;
    384  1.2  christos 
    385  1.7  christos 	/*
    386  1.7  christos 	 * Reset the recursion counter after advancing
    387  1.7  christos 	 * beyond the end of the previous substitution.
    388  1.7  christos 	 */
    389  1.7  christos 	if (ep->end - ep->data >= last_len)
    390  1.7  christos 		lim = 0;
    391  1.2  christos 
    392  1.7  christos 	ep->start = ep->end;
    393  1.7  christos 	quoted = mode == MODE_QUOTED;
    394  1.7  christos 	for (;;) {
    395  1.7  christos 		switch (*ep->start) {
    396  1.7  christos 		case '\0':
    397  1.7  christos 			ep->toksz = 0;
    398  1.7  christos 			return EQN_TOK_EOF;
    399  1.7  christos 		case '"':
    400  1.7  christos 			quoted = 1;
    401  1.7  christos 			break;
    402  1.9       wiz 		case ' ':
    403  1.9       wiz 		case '\t':
    404  1.9       wiz 		case '~':
    405  1.9       wiz 		case '^':
    406  1.9       wiz 			if (quoted)
    407  1.9       wiz 				break;
    408  1.9       wiz 			ep->start++;
    409  1.9       wiz 			continue;
    410  1.7  christos 		default:
    411  1.7  christos 			break;
    412  1.7  christos 		}
    413  1.7  christos 		if (quoted) {
    414  1.7  christos 			ep->end = strchr(ep->start + 1, *ep->start);
    415  1.7  christos 			ep->start++;  /* Skip opening quote. */
    416  1.7  christos 			if (ep->end == NULL) {
    417  1.8  christos 				mandoc_msg(MANDOCERR_ARG_QUOTE,
    418  1.7  christos 				    ep->node->line, ep->node->pos, NULL);
    419  1.7  christos 				ep->end = strchr(ep->start, '\0');
    420  1.7  christos 			}
    421  1.7  christos 		} else {
    422  1.7  christos 			ep->end = ep->start + 1;
    423  1.7  christos 			if (*ep->start != '{' && *ep->start != '}')
    424  1.7  christos 				ep->end += strcspn(ep->end, " ^~\"{}\t");
    425  1.7  christos 		}
    426  1.7  christos 		ep->toksz = ep->end - ep->start;
    427  1.7  christos 		if (quoted && *ep->end != '\0')
    428  1.7  christos 			ep->end++;  /* Skip closing quote. */
    429  1.7  christos 		while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
    430  1.7  christos 			ep->end++;
    431  1.7  christos 		if (quoted)  /* Cannot return, may have to strndup. */
    432  1.7  christos 			break;
    433  1.7  christos 		if (mode == MODE_NOSUB)
    434  1.7  christos 			return EQN_TOK__MAX;
    435  1.7  christos 		if ((def = eqn_def_find(ep)) == NULL)
    436  1.7  christos 			break;
    437  1.7  christos 		if (++lim > EQN_NEST_MAX) {
    438  1.8  christos 			mandoc_msg(MANDOCERR_ROFFLOOP,
    439  1.7  christos 			    ep->node->line, ep->node->pos, NULL);
    440  1.7  christos 			return EQN_TOK_EOF;
    441  1.7  christos 		}
    442  1.2  christos 
    443  1.7  christos 		/* Replace a defined name with its string value. */
    444  1.7  christos 		if ((diff = def->valsz - ep->toksz) > 0) {
    445  1.7  christos 			start = ep->start - ep->data;
    446  1.2  christos 			ep->sz += diff;
    447  1.2  christos 			ep->data = mandoc_realloc(ep->data, ep->sz + 1);
    448  1.7  christos 			ep->start = ep->data + start;
    449  1.2  christos 		}
    450  1.7  christos 		if (diff)
    451  1.7  christos 			memmove(ep->start + def->valsz, ep->start + ep->toksz,
    452  1.7  christos 			    strlen(ep->start + ep->toksz) + 1);
    453  1.7  christos 		memcpy(ep->start, def->val, def->valsz);
    454  1.7  christos 		last_len = ep->start - ep->data + def->valsz;
    455  1.2  christos 	}
    456  1.7  christos 	if (mode != MODE_TOK)
    457  1.7  christos 		return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
    458  1.2  christos 	if (quoted) {
    459  1.7  christos 		ep->start = mandoc_strndup(ep->start, ep->toksz);
    460  1.7  christos 		return EQN_TOK_QUOTED;
    461  1.2  christos 	}
    462  1.7  christos 	for (tok = 0; tok < EQN_TOK__MAX; tok++)
    463  1.7  christos 		if (STRNEQ(ep->start, ep->toksz,
    464  1.7  christos 		    eqn_toks[tok], strlen(eqn_toks[tok])))
    465  1.7  christos 			return tok;
    466  1.7  christos 
    467  1.7  christos 	for (i = 0; i < EQNSYM__MAX; i++) {
    468  1.7  christos 		if (STRNEQ(ep->start, ep->toksz,
    469  1.7  christos 		    eqnsyms[i].str, strlen(eqnsyms[i].str))) {
    470  1.7  christos 			mandoc_asprintf(&ep->start,
    471  1.7  christos 			    "\\[%s]", eqnsyms[i].sym);
    472  1.7  christos 			return EQN_TOK_SYM;
    473  1.7  christos 		}
    474  1.2  christos 	}
    475  1.7  christos 	ep->start = mandoc_strndup(ep->start, ep->toksz);
    476  1.7  christos 	for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
    477  1.7  christos 		if (STRNEQ(ep->start, ep->toksz,
    478  1.7  christos 		    eqn_func[i], strlen(eqn_func[i])))
    479  1.7  christos 			return EQN_TOK_FUNC;
    480  1.7  christos 	return EQN_TOK__MAX;
    481  1.2  christos }
    482  1.2  christos 
    483  1.7  christos void
    484  1.2  christos eqn_box_free(struct eqn_box *bp)
    485  1.2  christos {
    486  1.8  christos 	if (bp == NULL)
    487  1.8  christos 		return;
    488  1.2  christos 
    489  1.2  christos 	if (bp->first)
    490  1.2  christos 		eqn_box_free(bp->first);
    491  1.2  christos 	if (bp->next)
    492  1.2  christos 		eqn_box_free(bp->next);
    493  1.2  christos 
    494  1.2  christos 	free(bp->text);
    495  1.2  christos 	free(bp->left);
    496  1.2  christos 	free(bp->right);
    497  1.2  christos 	free(bp->top);
    498  1.2  christos 	free(bp->bottom);
    499  1.2  christos 	free(bp);
    500  1.2  christos }
    501  1.2  christos 
    502  1.8  christos struct eqn_box *
    503  1.8  christos eqn_box_new(void)
    504  1.8  christos {
    505  1.8  christos 	struct eqn_box	*bp;
    506  1.8  christos 
    507  1.8  christos 	bp = mandoc_calloc(1, sizeof(*bp));
    508  1.8  christos 	bp->expectargs = UINT_MAX;
    509  1.8  christos 	return bp;
    510  1.8  christos }
    511  1.8  christos 
    512  1.2  christos /*
    513  1.2  christos  * Allocate a box as the last child of the parent node.
    514  1.2  christos  */
    515  1.2  christos static struct eqn_box *
    516  1.2  christos eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
    517  1.2  christos {
    518  1.2  christos 	struct eqn_box	*bp;
    519  1.2  christos 
    520  1.8  christos 	bp = eqn_box_new();
    521  1.2  christos 	bp->parent = parent;
    522  1.2  christos 	bp->parent->args++;
    523  1.7  christos 	bp->font = bp->parent->font;
    524  1.2  christos 	bp->size = ep->gsize;
    525  1.2  christos 
    526  1.2  christos 	if (NULL != parent->first) {
    527  1.2  christos 		parent->last->next = bp;
    528  1.2  christos 		bp->prev = parent->last;
    529  1.2  christos 	} else
    530  1.2  christos 		parent->first = bp;
    531  1.2  christos 
    532  1.2  christos 	parent->last = bp;
    533  1.5  christos 	return bp;
    534  1.2  christos }
    535  1.2  christos 
    536  1.2  christos /*
    537  1.2  christos  * Reparent the current last node (of the current parent) under a new
    538  1.2  christos  * EQN_SUBEXPR as the first element.
    539  1.2  christos  * Then return the new parent.
    540  1.2  christos  * The new EQN_SUBEXPR will have a two-child limit.
    541  1.2  christos  */
    542  1.2  christos static struct eqn_box *
    543  1.7  christos eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent)
    544  1.2  christos {
    545  1.2  christos 	struct eqn_box	*b, *newb;
    546  1.2  christos 
    547  1.2  christos 	assert(NULL != parent->last);
    548  1.2  christos 	b = parent->last;
    549  1.2  christos 	if (parent->last == parent->first)
    550  1.2  christos 		parent->first = NULL;
    551  1.2  christos 	parent->args--;
    552  1.2  christos 	parent->last = b->prev;
    553  1.2  christos 	b->prev = NULL;
    554  1.2  christos 	newb = eqn_box_alloc(ep, parent);
    555  1.2  christos 	newb->type = EQN_SUBEXPR;
    556  1.2  christos 	newb->expectargs = 2;
    557  1.2  christos 	newb->args = 1;
    558  1.2  christos 	newb->first = newb->last = b;
    559  1.2  christos 	newb->first->next = NULL;
    560  1.2  christos 	b->parent = newb;
    561  1.5  christos 	return newb;
    562  1.2  christos }
    563  1.2  christos 
    564  1.2  christos /*
    565  1.2  christos  * Parse the "delim" control statement.
    566  1.2  christos  */
    567  1.2  christos static void
    568  1.2  christos eqn_delim(struct eqn_node *ep)
    569  1.2  christos {
    570  1.7  christos 	if (ep->end[0] == '\0' || ep->end[1] == '\0') {
    571  1.8  christos 		mandoc_msg(MANDOCERR_REQ_EMPTY,
    572  1.7  christos 		    ep->node->line, ep->node->pos, "delim");
    573  1.7  christos 		if (ep->end[0] != '\0')
    574  1.7  christos 			ep->end++;
    575  1.7  christos 	} else if (strncmp(ep->end, "off", 3) == 0) {
    576  1.2  christos 		ep->delim = 0;
    577  1.7  christos 		ep->end += 3;
    578  1.7  christos 	} else if (strncmp(ep->end, "on", 2) == 0) {
    579  1.2  christos 		if (ep->odelim && ep->cdelim)
    580  1.2  christos 			ep->delim = 1;
    581  1.7  christos 		ep->end += 2;
    582  1.7  christos 	} else {
    583  1.7  christos 		ep->odelim = *ep->end++;
    584  1.7  christos 		ep->cdelim = *ep->end++;
    585  1.2  christos 		ep->delim = 1;
    586  1.2  christos 	}
    587  1.2  christos }
    588  1.2  christos 
    589  1.2  christos /*
    590  1.2  christos  * Undefine a previously-defined string.
    591  1.2  christos  */
    592  1.2  christos static void
    593  1.2  christos eqn_undef(struct eqn_node *ep)
    594  1.2  christos {
    595  1.2  christos 	struct eqn_def	*def;
    596  1.2  christos 
    597  1.7  christos 	if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
    598  1.8  christos 		mandoc_msg(MANDOCERR_REQ_EMPTY,
    599  1.7  christos 		    ep->node->line, ep->node->pos, "undef");
    600  1.2  christos 		return;
    601  1.2  christos 	}
    602  1.7  christos 	if ((def = eqn_def_find(ep)) == NULL)
    603  1.2  christos 		return;
    604  1.2  christos 	free(def->key);
    605  1.2  christos 	free(def->val);
    606  1.2  christos 	def->key = def->val = NULL;
    607  1.2  christos 	def->keysz = def->valsz = 0;
    608  1.2  christos }
    609  1.2  christos 
    610  1.2  christos static void
    611  1.2  christos eqn_def(struct eqn_node *ep)
    612  1.2  christos {
    613  1.2  christos 	struct eqn_def	*def;
    614  1.2  christos 	int		 i;
    615  1.2  christos 
    616  1.7  christos 	if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
    617  1.8  christos 		mandoc_msg(MANDOCERR_REQ_EMPTY,
    618  1.7  christos 		    ep->node->line, ep->node->pos, "define");
    619  1.2  christos 		return;
    620  1.2  christos 	}
    621  1.2  christos 
    622  1.2  christos 	/*
    623  1.2  christos 	 * Search for a key that already exists.
    624  1.2  christos 	 * Create a new key if none is found.
    625  1.2  christos 	 */
    626  1.7  christos 	if ((def = eqn_def_find(ep)) == NULL) {
    627  1.2  christos 		/* Find holes in string array. */
    628  1.2  christos 		for (i = 0; i < (int)ep->defsz; i++)
    629  1.2  christos 			if (0 == ep->defs[i].keysz)
    630  1.2  christos 				break;
    631  1.2  christos 
    632  1.2  christos 		if (i == (int)ep->defsz) {
    633  1.2  christos 			ep->defsz++;
    634  1.2  christos 			ep->defs = mandoc_reallocarray(ep->defs,
    635  1.2  christos 			    ep->defsz, sizeof(struct eqn_def));
    636  1.2  christos 			ep->defs[i].key = ep->defs[i].val = NULL;
    637  1.2  christos 		}
    638  1.2  christos 
    639  1.2  christos 		def = ep->defs + i;
    640  1.2  christos 		free(def->key);
    641  1.7  christos 		def->key = mandoc_strndup(ep->start, ep->toksz);
    642  1.7  christos 		def->keysz = ep->toksz;
    643  1.2  christos 	}
    644  1.2  christos 
    645  1.7  christos 	if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
    646  1.8  christos 		mandoc_msg(MANDOCERR_REQ_EMPTY,
    647  1.7  christos 		    ep->node->line, ep->node->pos, "define %s", def->key);
    648  1.2  christos 		free(def->key);
    649  1.2  christos 		free(def->val);
    650  1.2  christos 		def->key = def->val = NULL;
    651  1.2  christos 		def->keysz = def->valsz = 0;
    652  1.2  christos 		return;
    653  1.2  christos 	}
    654  1.2  christos 	free(def->val);
    655  1.7  christos 	def->val = mandoc_strndup(ep->start, ep->toksz);
    656  1.7  christos 	def->valsz = ep->toksz;
    657  1.2  christos }
    658  1.2  christos 
    659  1.7  christos void
    660  1.7  christos eqn_parse(struct eqn_node *ep)
    661  1.2  christos {
    662  1.7  christos 	struct eqn_box	*cur, *nbox, *parent, *split;
    663  1.7  christos 	const char	*cp, *cpn;
    664  1.2  christos 	char		*p;
    665  1.7  christos 	enum eqn_tok	 tok;
    666  1.7  christos 	enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
    667  1.2  christos 	int		 size;
    668  1.2  christos 
    669  1.7  christos 	parent = ep->node->eqn;
    670  1.2  christos 	assert(parent != NULL);
    671  1.2  christos 
    672  1.2  christos 	/*
    673  1.2  christos 	 * Empty equation.
    674  1.2  christos 	 * Do not add it to the high-level syntax tree.
    675  1.2  christos 	 */
    676  1.2  christos 
    677  1.2  christos 	if (ep->data == NULL)
    678  1.7  christos 		return;
    679  1.7  christos 
    680  1.9       wiz 	ep->start = ep->end = ep->data;
    681  1.2  christos 
    682  1.2  christos next_tok:
    683  1.7  christos 	tok = eqn_next(ep, MODE_TOK);
    684  1.2  christos 	switch (tok) {
    685  1.7  christos 	case EQN_TOK_UNDEF:
    686  1.2  christos 		eqn_undef(ep);
    687  1.2  christos 		break;
    688  1.7  christos 	case EQN_TOK_NDEFINE:
    689  1.7  christos 	case EQN_TOK_DEFINE:
    690  1.2  christos 		eqn_def(ep);
    691  1.2  christos 		break;
    692  1.7  christos 	case EQN_TOK_TDEFINE:
    693  1.7  christos 		if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
    694  1.7  christos 		    eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
    695  1.8  christos 			mandoc_msg(MANDOCERR_REQ_EMPTY,
    696  1.7  christos 			    ep->node->line, ep->node->pos, "tdefine");
    697  1.2  christos 		break;
    698  1.7  christos 	case EQN_TOK_DELIM:
    699  1.2  christos 		eqn_delim(ep);
    700  1.2  christos 		break;
    701  1.7  christos 	case EQN_TOK_GFONT:
    702  1.7  christos 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
    703  1.8  christos 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
    704  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    705  1.2  christos 		break;
    706  1.7  christos 	case EQN_TOK_MARK:
    707  1.7  christos 	case EQN_TOK_LINEUP:
    708  1.2  christos 		/* Ignore these. */
    709  1.2  christos 		break;
    710  1.7  christos 	case EQN_TOK_DYAD:
    711  1.7  christos 	case EQN_TOK_VEC:
    712  1.7  christos 	case EQN_TOK_UNDER:
    713  1.7  christos 	case EQN_TOK_BAR:
    714  1.7  christos 	case EQN_TOK_TILDE:
    715  1.7  christos 	case EQN_TOK_HAT:
    716  1.7  christos 	case EQN_TOK_DOT:
    717  1.7  christos 	case EQN_TOK_DOTDOT:
    718  1.2  christos 		if (parent->last == NULL) {
    719  1.8  christos 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
    720  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    721  1.2  christos 			cur = eqn_box_alloc(ep, parent);
    722  1.2  christos 			cur->type = EQN_TEXT;
    723  1.2  christos 			cur->text = mandoc_strdup("");
    724  1.2  christos 		}
    725  1.7  christos 		parent = eqn_box_makebinary(ep, parent);
    726  1.7  christos 		parent->type = EQN_LIST;
    727  1.2  christos 		parent->expectargs = 1;
    728  1.7  christos 		parent->font = EQNFONT_ROMAN;
    729  1.2  christos 		switch (tok) {
    730  1.7  christos 		case EQN_TOK_DOTDOT:
    731  1.7  christos 			parent->top = mandoc_strdup("\\[ad]");
    732  1.2  christos 			break;
    733  1.7  christos 		case EQN_TOK_VEC:
    734  1.7  christos 			parent->top = mandoc_strdup("\\[->]");
    735  1.2  christos 			break;
    736  1.7  christos 		case EQN_TOK_DYAD:
    737  1.7  christos 			parent->top = mandoc_strdup("\\[<>]");
    738  1.2  christos 			break;
    739  1.7  christos 		case EQN_TOK_TILDE:
    740  1.7  christos 			parent->top = mandoc_strdup("\\[a~]");
    741  1.2  christos 			break;
    742  1.7  christos 		case EQN_TOK_UNDER:
    743  1.7  christos 			parent->bottom = mandoc_strdup("\\[ul]");
    744  1.2  christos 			break;
    745  1.7  christos 		case EQN_TOK_BAR:
    746  1.7  christos 			parent->top = mandoc_strdup("\\[rn]");
    747  1.2  christos 			break;
    748  1.7  christos 		case EQN_TOK_DOT:
    749  1.7  christos 			parent->top = mandoc_strdup("\\[a.]");
    750  1.2  christos 			break;
    751  1.7  christos 		case EQN_TOK_HAT:
    752  1.7  christos 			parent->top = mandoc_strdup("\\[ha]");
    753  1.2  christos 			break;
    754  1.2  christos 		default:
    755  1.2  christos 			abort();
    756  1.2  christos 		}
    757  1.2  christos 		parent = parent->parent;
    758  1.2  christos 		break;
    759  1.7  christos 	case EQN_TOK_FWD:
    760  1.7  christos 	case EQN_TOK_BACK:
    761  1.7  christos 	case EQN_TOK_DOWN:
    762  1.7  christos 	case EQN_TOK_UP:
    763  1.7  christos 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
    764  1.8  christos 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
    765  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    766  1.2  christos 		break;
    767  1.7  christos 	case EQN_TOK_FAT:
    768  1.7  christos 	case EQN_TOK_ROMAN:
    769  1.7  christos 	case EQN_TOK_ITALIC:
    770  1.7  christos 	case EQN_TOK_BOLD:
    771  1.2  christos 		while (parent->args == parent->expectargs)
    772  1.2  christos 			parent = parent->parent;
    773  1.2  christos 		/*
    774  1.2  christos 		 * These values apply to the next word or sequence of
    775  1.2  christos 		 * words; thus, we mark that we'll have a child with
    776  1.2  christos 		 * exactly one of those.
    777  1.2  christos 		 */
    778  1.2  christos 		parent = eqn_box_alloc(ep, parent);
    779  1.7  christos 		parent->type = EQN_LIST;
    780  1.2  christos 		parent->expectargs = 1;
    781  1.2  christos 		switch (tok) {
    782  1.7  christos 		case EQN_TOK_FAT:
    783  1.2  christos 			parent->font = EQNFONT_FAT;
    784  1.2  christos 			break;
    785  1.7  christos 		case EQN_TOK_ROMAN:
    786  1.2  christos 			parent->font = EQNFONT_ROMAN;
    787  1.2  christos 			break;
    788  1.7  christos 		case EQN_TOK_ITALIC:
    789  1.2  christos 			parent->font = EQNFONT_ITALIC;
    790  1.2  christos 			break;
    791  1.7  christos 		case EQN_TOK_BOLD:
    792  1.2  christos 			parent->font = EQNFONT_BOLD;
    793  1.2  christos 			break;
    794  1.2  christos 		default:
    795  1.2  christos 			abort();
    796  1.2  christos 		}
    797  1.2  christos 		break;
    798  1.7  christos 	case EQN_TOK_SIZE:
    799  1.7  christos 	case EQN_TOK_GSIZE:
    800  1.2  christos 		/* Accept two values: integral size and a single. */
    801  1.7  christos 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
    802  1.8  christos 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
    803  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    804  1.2  christos 			break;
    805  1.2  christos 		}
    806  1.7  christos 		size = mandoc_strntoi(ep->start, ep->toksz, 10);
    807  1.2  christos 		if (-1 == size) {
    808  1.8  christos 			mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line,
    809  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    810  1.2  christos 			break;
    811  1.2  christos 		}
    812  1.2  christos 		if (EQN_TOK_GSIZE == tok) {
    813  1.2  christos 			ep->gsize = size;
    814  1.2  christos 			break;
    815  1.2  christos 		}
    816  1.7  christos 		while (parent->args == parent->expectargs)
    817  1.7  christos 			parent = parent->parent;
    818  1.2  christos 		parent = eqn_box_alloc(ep, parent);
    819  1.7  christos 		parent->type = EQN_LIST;
    820  1.2  christos 		parent->expectargs = 1;
    821  1.2  christos 		parent->size = size;
    822  1.2  christos 		break;
    823  1.7  christos 	case EQN_TOK_FROM:
    824  1.7  christos 	case EQN_TOK_TO:
    825  1.7  christos 	case EQN_TOK_SUB:
    826  1.7  christos 	case EQN_TOK_SUP:
    827  1.2  christos 		/*
    828  1.2  christos 		 * We have a left-right-associative expression.
    829  1.2  christos 		 * Repivot under a positional node, open a child scope
    830  1.2  christos 		 * and keep on reading.
    831  1.2  christos 		 */
    832  1.2  christos 		if (parent->last == NULL) {
    833  1.8  christos 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
    834  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    835  1.2  christos 			cur = eqn_box_alloc(ep, parent);
    836  1.2  christos 			cur->type = EQN_TEXT;
    837  1.2  christos 			cur->text = mandoc_strdup("");
    838  1.2  christos 		}
    839  1.7  christos 		while (parent->expectargs == 1 && parent->args == 1)
    840  1.7  christos 			parent = parent->parent;
    841  1.7  christos 		if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO)  {
    842  1.7  christos 			for (cur = parent; cur != NULL; cur = cur->parent)
    843  1.7  christos 				if (cur->pos == EQNPOS_SUB ||
    844  1.7  christos 				    cur->pos == EQNPOS_SUP ||
    845  1.7  christos 				    cur->pos == EQNPOS_SUBSUP ||
    846  1.7  christos 				    cur->pos == EQNPOS_SQRT ||
    847  1.7  christos 				    cur->pos == EQNPOS_OVER)
    848  1.7  christos 					break;
    849  1.7  christos 			if (cur != NULL)
    850  1.7  christos 				parent = cur->parent;
    851  1.7  christos 		}
    852  1.7  christos 		if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
    853  1.2  christos 			parent->expectargs = 3;
    854  1.2  christos 			parent->pos = EQNPOS_SUBSUP;
    855  1.2  christos 			break;
    856  1.2  christos 		}
    857  1.7  christos 		if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
    858  1.2  christos 			parent->expectargs = 3;
    859  1.2  christos 			parent->pos = EQNPOS_FROMTO;
    860  1.2  christos 			break;
    861  1.2  christos 		}
    862  1.7  christos 		parent = eqn_box_makebinary(ep, parent);
    863  1.2  christos 		switch (tok) {
    864  1.7  christos 		case EQN_TOK_FROM:
    865  1.7  christos 			parent->pos = EQNPOS_FROM;
    866  1.2  christos 			break;
    867  1.7  christos 		case EQN_TOK_TO:
    868  1.7  christos 			parent->pos = EQNPOS_TO;
    869  1.2  christos 			break;
    870  1.7  christos 		case EQN_TOK_SUP:
    871  1.7  christos 			parent->pos = EQNPOS_SUP;
    872  1.2  christos 			break;
    873  1.7  christos 		case EQN_TOK_SUB:
    874  1.7  christos 			parent->pos = EQNPOS_SUB;
    875  1.2  christos 			break;
    876  1.2  christos 		default:
    877  1.2  christos 			abort();
    878  1.2  christos 		}
    879  1.2  christos 		break;
    880  1.7  christos 	case EQN_TOK_SQRT:
    881  1.2  christos 		while (parent->args == parent->expectargs)
    882  1.2  christos 			parent = parent->parent;
    883  1.2  christos 		/*
    884  1.2  christos 		 * Accept a left-right-associative set of arguments just
    885  1.2  christos 		 * like sub and sup and friends but without rebalancing
    886  1.2  christos 		 * under a pivot.
    887  1.2  christos 		 */
    888  1.2  christos 		parent = eqn_box_alloc(ep, parent);
    889  1.2  christos 		parent->type = EQN_SUBEXPR;
    890  1.2  christos 		parent->pos = EQNPOS_SQRT;
    891  1.2  christos 		parent->expectargs = 1;
    892  1.2  christos 		break;
    893  1.7  christos 	case EQN_TOK_OVER:
    894  1.2  christos 		/*
    895  1.2  christos 		 * We have a right-left-associative fraction.
    896  1.2  christos 		 * Close out anything that's currently open, then
    897  1.2  christos 		 * rebalance and continue reading.
    898  1.2  christos 		 */
    899  1.2  christos 		if (parent->last == NULL) {
    900  1.8  christos 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
    901  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    902  1.2  christos 			cur = eqn_box_alloc(ep, parent);
    903  1.2  christos 			cur->type = EQN_TEXT;
    904  1.2  christos 			cur->text = mandoc_strdup("");
    905  1.2  christos 		}
    906  1.7  christos 		while (parent->args == parent->expectargs)
    907  1.7  christos 			parent = parent->parent;
    908  1.2  christos 		while (EQN_SUBEXPR == parent->type)
    909  1.2  christos 			parent = parent->parent;
    910  1.7  christos 		parent = eqn_box_makebinary(ep, parent);
    911  1.7  christos 		parent->pos = EQNPOS_OVER;
    912  1.2  christos 		break;
    913  1.7  christos 	case EQN_TOK_RIGHT:
    914  1.7  christos 	case EQN_TOK_BRACE_CLOSE:
    915  1.2  christos 		/*
    916  1.2  christos 		 * Close out the existing brace.
    917  1.2  christos 		 * FIXME: this is a shitty sentinel: we should really
    918  1.2  christos 		 * have a native EQN_BRACE type or whatnot.
    919  1.2  christos 		 */
    920  1.2  christos 		for (cur = parent; cur != NULL; cur = cur->parent)
    921  1.2  christos 			if (cur->type == EQN_LIST &&
    922  1.7  christos 			    cur->expectargs > 1 &&
    923  1.2  christos 			    (tok == EQN_TOK_BRACE_CLOSE ||
    924  1.2  christos 			     cur->left != NULL))
    925  1.2  christos 				break;
    926  1.2  christos 		if (cur == NULL) {
    927  1.8  christos 			mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line,
    928  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    929  1.2  christos 			break;
    930  1.2  christos 		}
    931  1.2  christos 		parent = cur;
    932  1.2  christos 		if (EQN_TOK_RIGHT == tok) {
    933  1.7  christos 			if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
    934  1.2  christos 				mandoc_msg(MANDOCERR_REQ_EMPTY,
    935  1.8  christos 				    ep->node->line, ep->node->pos,
    936  1.8  christos 				    "%s", eqn_toks[tok]);
    937  1.2  christos 				break;
    938  1.2  christos 			}
    939  1.2  christos 			/* Handling depends on right/left. */
    940  1.7  christos 			if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
    941  1.7  christos 				parent->right = mandoc_strdup("\\[rc]");
    942  1.7  christos 			else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
    943  1.7  christos 				parent->right = mandoc_strdup("\\[rf]");
    944  1.7  christos 			else
    945  1.7  christos 				parent->right =
    946  1.7  christos 				    mandoc_strndup(ep->start, ep->toksz);
    947  1.2  christos 		}
    948  1.2  christos 		parent = parent->parent;
    949  1.5  christos 		if (tok == EQN_TOK_BRACE_CLOSE &&
    950  1.2  christos 		    (parent->type == EQN_PILE ||
    951  1.2  christos 		     parent->type == EQN_MATRIX))
    952  1.2  christos 			parent = parent->parent;
    953  1.2  christos 		/* Close out any "singleton" lists. */
    954  1.7  christos 		while (parent->type == EQN_LIST &&
    955  1.7  christos 		    parent->expectargs == 1 &&
    956  1.7  christos 		    parent->args == 1)
    957  1.2  christos 			parent = parent->parent;
    958  1.2  christos 		break;
    959  1.7  christos 	case EQN_TOK_BRACE_OPEN:
    960  1.7  christos 	case EQN_TOK_LEFT:
    961  1.2  christos 		/*
    962  1.2  christos 		 * If we already have something in the stack and we're
    963  1.2  christos 		 * in an expression, then rewind til we're not any more
    964  1.2  christos 		 * (just like with the text node).
    965  1.2  christos 		 */
    966  1.2  christos 		while (parent->args == parent->expectargs)
    967  1.2  christos 			parent = parent->parent;
    968  1.2  christos 		if (EQN_TOK_LEFT == tok &&
    969  1.7  christos 		    eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
    970  1.8  christos 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
    971  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
    972  1.2  christos 			break;
    973  1.2  christos 		}
    974  1.2  christos 		parent = eqn_box_alloc(ep, parent);
    975  1.2  christos 		parent->type = EQN_LIST;
    976  1.2  christos 		if (EQN_TOK_LEFT == tok) {
    977  1.7  christos 			if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
    978  1.7  christos 				parent->left = mandoc_strdup("\\[lc]");
    979  1.7  christos 			else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
    980  1.7  christos 				parent->left = mandoc_strdup("\\[lf]");
    981  1.7  christos 			else
    982  1.7  christos 				parent->left =
    983  1.7  christos 				    mandoc_strndup(ep->start, ep->toksz);
    984  1.2  christos 		}
    985  1.2  christos 		break;
    986  1.7  christos 	case EQN_TOK_PILE:
    987  1.7  christos 	case EQN_TOK_LPILE:
    988  1.7  christos 	case EQN_TOK_RPILE:
    989  1.7  christos 	case EQN_TOK_CPILE:
    990  1.7  christos 	case EQN_TOK_CCOL:
    991  1.7  christos 	case EQN_TOK_LCOL:
    992  1.7  christos 	case EQN_TOK_RCOL:
    993  1.2  christos 		while (parent->args == parent->expectargs)
    994  1.2  christos 			parent = parent->parent;
    995  1.2  christos 		parent = eqn_box_alloc(ep, parent);
    996  1.2  christos 		parent->type = EQN_PILE;
    997  1.2  christos 		parent->expectargs = 1;
    998  1.2  christos 		break;
    999  1.7  christos 	case EQN_TOK_ABOVE:
   1000  1.2  christos 		for (cur = parent; cur != NULL; cur = cur->parent)
   1001  1.2  christos 			if (cur->type == EQN_PILE)
   1002  1.2  christos 				break;
   1003  1.2  christos 		if (cur == NULL) {
   1004  1.8  christos 			mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line,
   1005  1.8  christos 			    ep->node->pos, "%s", eqn_toks[tok]);
   1006  1.2  christos 			break;
   1007  1.2  christos 		}
   1008  1.2  christos 		parent = eqn_box_alloc(ep, cur);
   1009  1.2  christos 		parent->type = EQN_LIST;
   1010  1.2  christos 		break;
   1011  1.7  christos 	case EQN_TOK_MATRIX:
   1012  1.2  christos 		while (parent->args == parent->expectargs)
   1013  1.2  christos 			parent = parent->parent;
   1014  1.2  christos 		parent = eqn_box_alloc(ep, parent);
   1015  1.2  christos 		parent->type = EQN_MATRIX;
   1016  1.2  christos 		parent->expectargs = 1;
   1017  1.2  christos 		break;
   1018  1.7  christos 	case EQN_TOK_EOF:
   1019  1.7  christos 		return;
   1020  1.7  christos 	case EQN_TOK__MAX:
   1021  1.7  christos 	case EQN_TOK_FUNC:
   1022  1.7  christos 	case EQN_TOK_QUOTED:
   1023  1.7  christos 	case EQN_TOK_SYM:
   1024  1.7  christos 		p = ep->start;
   1025  1.7  christos 		assert(p != NULL);
   1026  1.2  christos 		/*
   1027  1.2  christos 		 * If we already have something in the stack and we're
   1028  1.2  christos 		 * in an expression, then rewind til we're not any more.
   1029  1.2  christos 		 */
   1030  1.2  christos 		while (parent->args == parent->expectargs)
   1031  1.2  christos 			parent = parent->parent;
   1032  1.2  christos 		cur = eqn_box_alloc(ep, parent);
   1033  1.2  christos 		cur->type = EQN_TEXT;
   1034  1.7  christos 		cur->text = p;
   1035  1.7  christos 		switch (tok) {
   1036  1.7  christos 		case EQN_TOK_FUNC:
   1037  1.7  christos 			cur->font = EQNFONT_ROMAN;
   1038  1.7  christos 			break;
   1039  1.7  christos 		case EQN_TOK_QUOTED:
   1040  1.7  christos 			if (cur->font == EQNFONT_NONE)
   1041  1.7  christos 				cur->font = EQNFONT_ITALIC;
   1042  1.7  christos 			break;
   1043  1.7  christos 		case EQN_TOK_SYM:
   1044  1.7  christos 			break;
   1045  1.7  christos 		default:
   1046  1.7  christos 			if (cur->font != EQNFONT_NONE || *p == '\0')
   1047  1.2  christos 				break;
   1048  1.7  christos 			cpn = p - 1;
   1049  1.7  christos 			ccln = CCL_LET;
   1050  1.7  christos 			split = NULL;
   1051  1.7  christos 			for (;;) {
   1052  1.7  christos 				/* Advance to next character. */
   1053  1.7  christos 				cp = cpn++;
   1054  1.7  christos 				ccl = ccln;
   1055  1.7  christos 				ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
   1056  1.7  christos 				    isdigit((unsigned char)*cpn) ||
   1057  1.7  christos 				    (*cpn == '.' && (ccl == CCL_DIG ||
   1058  1.7  christos 				     isdigit((unsigned char)cpn[1]))) ?
   1059  1.7  christos 				    CCL_DIG : CCL_PUN;
   1060  1.7  christos 				/* No boundary before first character. */
   1061  1.7  christos 				if (cp < p)
   1062  1.7  christos 					continue;
   1063  1.7  christos 				cur->font = ccl == CCL_LET ?
   1064  1.7  christos 				    EQNFONT_ITALIC : EQNFONT_ROMAN;
   1065  1.7  christos 				if (*cp == '\\')
   1066  1.7  christos 					mandoc_escape(&cpn, NULL, NULL);
   1067  1.7  christos 				/* No boundary after last character. */
   1068  1.7  christos 				if (*cpn == '\0')
   1069  1.7  christos 					break;
   1070  1.7  christos 				if (ccln == ccl && *cp != ',' && *cpn != ',')
   1071  1.7  christos 					continue;
   1072  1.7  christos 				/* Boundary found, split the text. */
   1073  1.7  christos 				if (parent->args == parent->expectargs) {
   1074  1.7  christos 					/* Remove the text from the tree. */
   1075  1.7  christos 					if (cur->prev == NULL)
   1076  1.7  christos 						parent->first = cur->next;
   1077  1.7  christos 					else
   1078  1.7  christos 						cur->prev->next = NULL;
   1079  1.7  christos 					parent->last = cur->prev;
   1080  1.7  christos 					parent->args--;
   1081  1.7  christos 					/* Set up a list instead. */
   1082  1.7  christos 					split = eqn_box_alloc(ep, parent);
   1083  1.7  christos 					split->type = EQN_LIST;
   1084  1.7  christos 					/* Insert the word into the list. */
   1085  1.7  christos 					split->first = split->last = cur;
   1086  1.7  christos 					cur->parent = split;
   1087  1.7  christos 					cur->prev = NULL;
   1088  1.7  christos 					parent = split;
   1089  1.7  christos 				}
   1090  1.7  christos 				/* Append a new text box. */
   1091  1.7  christos 				nbox = eqn_box_alloc(ep, parent);
   1092  1.7  christos 				nbox->type = EQN_TEXT;
   1093  1.7  christos 				nbox->text = mandoc_strdup(cpn);
   1094  1.7  christos 				/* Truncate the old box. */
   1095  1.7  christos 				p = mandoc_strndup(cur->text,
   1096  1.7  christos 				    cpn - cur->text);
   1097  1.7  christos 				free(cur->text);
   1098  1.7  christos 				cur->text = p;
   1099  1.7  christos 				/* Setup to process the new box. */
   1100  1.7  christos 				cur = nbox;
   1101  1.7  christos 				p = nbox->text;
   1102  1.7  christos 				cpn = p - 1;
   1103  1.7  christos 				ccln = CCL_LET;
   1104  1.2  christos 			}
   1105  1.7  christos 			if (split != NULL)
   1106  1.7  christos 				parent = split->parent;
   1107  1.7  christos 			break;
   1108  1.7  christos 		}
   1109  1.2  christos 		break;
   1110  1.7  christos 	default:
   1111  1.7  christos 		abort();
   1112  1.2  christos 	}
   1113  1.2  christos 	goto next_tok;
   1114  1.2  christos }
   1115  1.2  christos 
   1116  1.1     joerg void
   1117  1.1     joerg eqn_free(struct eqn_node *p)
   1118  1.1     joerg {
   1119  1.2  christos 	int		 i;
   1120  1.2  christos 
   1121  1.8  christos 	if (p == NULL)
   1122  1.8  christos 		return;
   1123  1.8  christos 
   1124  1.2  christos 	for (i = 0; i < (int)p->defsz; i++) {
   1125  1.2  christos 		free(p->defs[i].key);
   1126  1.2  christos 		free(p->defs[i].val);
   1127  1.2  christos 	}
   1128  1.1     joerg 
   1129  1.2  christos 	free(p->data);
   1130  1.2  christos 	free(p->defs);
   1131  1.1     joerg 	free(p);
   1132  1.1     joerg }
   1133