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