xref: /dflybsd-src/contrib/mdocml/eqn.c (revision 1e4d43f9c96723e4e55543d240f182e1aac9a4c2)
1*99db7d0eSSascha Wildner /*	$Id: eqn.c,v 1.84 2020/01/08 12:16:24 schwarze Exp $ */
260e1e752SSascha Wildner /*
354ba9607SSascha Wildner  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*99db7d0eSSascha Wildner  * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org>
560e1e752SSascha Wildner  *
660e1e752SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
760e1e752SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
860e1e752SSascha Wildner  * copyright notice and this permission notice appear in all copies.
960e1e752SSascha Wildner  *
1060e1e752SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1160e1e752SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1260e1e752SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1360e1e752SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1460e1e752SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1560e1e752SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1660e1e752SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1760e1e752SSascha Wildner  */
1860e1e752SSascha Wildner #include "config.h"
1954ba9607SSascha Wildner 
2054ba9607SSascha Wildner #include <sys/types.h>
2160e1e752SSascha Wildner 
2260e1e752SSascha Wildner #include <assert.h>
2354ba9607SSascha Wildner #include <ctype.h>
2436342e81SSascha Wildner #include <limits.h>
2560e1e752SSascha Wildner #include <stdio.h>
2660e1e752SSascha Wildner #include <stdlib.h>
2760e1e752SSascha Wildner #include <string.h>
2860e1e752SSascha Wildner #include <time.h>
2960e1e752SSascha Wildner 
30070c62a6SFranco Fichtner #include "mandoc_aux.h"
3154ba9607SSascha Wildner #include "mandoc.h"
3254ba9607SSascha Wildner #include "roff.h"
3354ba9607SSascha Wildner #include "eqn.h"
3460e1e752SSascha Wildner #include "libmandoc.h"
3554ba9607SSascha Wildner #include "eqn_parse.h"
3660e1e752SSascha Wildner 
3736342e81SSascha Wildner #define	EQN_NEST_MAX	 128 /* maximum nesting of defines */
3854ba9607SSascha Wildner #define	STRNEQ(p1, sz1, p2, sz2) \
3954ba9607SSascha Wildner 	((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
4036342e81SSascha Wildner 
4154ba9607SSascha Wildner enum	eqn_tok {
4254ba9607SSascha Wildner 	EQN_TOK_DYAD = 0,
4354ba9607SSascha Wildner 	EQN_TOK_VEC,
4454ba9607SSascha Wildner 	EQN_TOK_UNDER,
4554ba9607SSascha Wildner 	EQN_TOK_BAR,
4654ba9607SSascha Wildner 	EQN_TOK_TILDE,
4754ba9607SSascha Wildner 	EQN_TOK_HAT,
4854ba9607SSascha Wildner 	EQN_TOK_DOT,
4954ba9607SSascha Wildner 	EQN_TOK_DOTDOT,
5054ba9607SSascha Wildner 	EQN_TOK_FWD,
5154ba9607SSascha Wildner 	EQN_TOK_BACK,
5254ba9607SSascha Wildner 	EQN_TOK_DOWN,
5354ba9607SSascha Wildner 	EQN_TOK_UP,
5454ba9607SSascha Wildner 	EQN_TOK_FAT,
5554ba9607SSascha Wildner 	EQN_TOK_ROMAN,
5654ba9607SSascha Wildner 	EQN_TOK_ITALIC,
5754ba9607SSascha Wildner 	EQN_TOK_BOLD,
5854ba9607SSascha Wildner 	EQN_TOK_SIZE,
5954ba9607SSascha Wildner 	EQN_TOK_SUB,
6054ba9607SSascha Wildner 	EQN_TOK_SUP,
6154ba9607SSascha Wildner 	EQN_TOK_SQRT,
6254ba9607SSascha Wildner 	EQN_TOK_OVER,
6354ba9607SSascha Wildner 	EQN_TOK_FROM,
6454ba9607SSascha Wildner 	EQN_TOK_TO,
6554ba9607SSascha Wildner 	EQN_TOK_BRACE_OPEN,
6654ba9607SSascha Wildner 	EQN_TOK_BRACE_CLOSE,
6754ba9607SSascha Wildner 	EQN_TOK_GSIZE,
6854ba9607SSascha Wildner 	EQN_TOK_GFONT,
6954ba9607SSascha Wildner 	EQN_TOK_MARK,
7054ba9607SSascha Wildner 	EQN_TOK_LINEUP,
7154ba9607SSascha Wildner 	EQN_TOK_LEFT,
7254ba9607SSascha Wildner 	EQN_TOK_RIGHT,
7354ba9607SSascha Wildner 	EQN_TOK_PILE,
7454ba9607SSascha Wildner 	EQN_TOK_LPILE,
7554ba9607SSascha Wildner 	EQN_TOK_RPILE,
7654ba9607SSascha Wildner 	EQN_TOK_CPILE,
7754ba9607SSascha Wildner 	EQN_TOK_MATRIX,
7854ba9607SSascha Wildner 	EQN_TOK_CCOL,
7954ba9607SSascha Wildner 	EQN_TOK_LCOL,
8054ba9607SSascha Wildner 	EQN_TOK_RCOL,
8154ba9607SSascha Wildner 	EQN_TOK_DELIM,
8254ba9607SSascha Wildner 	EQN_TOK_DEFINE,
8354ba9607SSascha Wildner 	EQN_TOK_TDEFINE,
8454ba9607SSascha Wildner 	EQN_TOK_NDEFINE,
8554ba9607SSascha Wildner 	EQN_TOK_UNDEF,
8654ba9607SSascha Wildner 	EQN_TOK_ABOVE,
8754ba9607SSascha Wildner 	EQN_TOK__MAX,
8854ba9607SSascha Wildner 	EQN_TOK_FUNC,
8954ba9607SSascha Wildner 	EQN_TOK_QUOTED,
9054ba9607SSascha Wildner 	EQN_TOK_SYM,
9154ba9607SSascha Wildner 	EQN_TOK_EOF
9254ba9607SSascha Wildner };
9354ba9607SSascha Wildner 
9454ba9607SSascha Wildner static	const char *eqn_toks[EQN_TOK__MAX] = {
9554ba9607SSascha Wildner 	"dyad", /* EQN_TOK_DYAD */
9654ba9607SSascha Wildner 	"vec", /* EQN_TOK_VEC */
9754ba9607SSascha Wildner 	"under", /* EQN_TOK_UNDER */
9854ba9607SSascha Wildner 	"bar", /* EQN_TOK_BAR */
9954ba9607SSascha Wildner 	"tilde", /* EQN_TOK_TILDE */
10054ba9607SSascha Wildner 	"hat", /* EQN_TOK_HAT */
10154ba9607SSascha Wildner 	"dot", /* EQN_TOK_DOT */
10254ba9607SSascha Wildner 	"dotdot", /* EQN_TOK_DOTDOT */
10354ba9607SSascha Wildner 	"fwd", /* EQN_TOK_FWD * */
10454ba9607SSascha Wildner 	"back", /* EQN_TOK_BACK */
10554ba9607SSascha Wildner 	"down", /* EQN_TOK_DOWN */
10654ba9607SSascha Wildner 	"up", /* EQN_TOK_UP */
10754ba9607SSascha Wildner 	"fat", /* EQN_TOK_FAT */
10854ba9607SSascha Wildner 	"roman", /* EQN_TOK_ROMAN */
10954ba9607SSascha Wildner 	"italic", /* EQN_TOK_ITALIC */
11054ba9607SSascha Wildner 	"bold", /* EQN_TOK_BOLD */
11154ba9607SSascha Wildner 	"size", /* EQN_TOK_SIZE */
11254ba9607SSascha Wildner 	"sub", /* EQN_TOK_SUB */
11354ba9607SSascha Wildner 	"sup", /* EQN_TOK_SUP */
11454ba9607SSascha Wildner 	"sqrt", /* EQN_TOK_SQRT */
11554ba9607SSascha Wildner 	"over", /* EQN_TOK_OVER */
11654ba9607SSascha Wildner 	"from", /* EQN_TOK_FROM */
11754ba9607SSascha Wildner 	"to", /* EQN_TOK_TO */
11854ba9607SSascha Wildner 	"{", /* EQN_TOK_BRACE_OPEN */
11954ba9607SSascha Wildner 	"}", /* EQN_TOK_BRACE_CLOSE */
12054ba9607SSascha Wildner 	"gsize", /* EQN_TOK_GSIZE */
12154ba9607SSascha Wildner 	"gfont", /* EQN_TOK_GFONT */
12254ba9607SSascha Wildner 	"mark", /* EQN_TOK_MARK */
12354ba9607SSascha Wildner 	"lineup", /* EQN_TOK_LINEUP */
12454ba9607SSascha Wildner 	"left", /* EQN_TOK_LEFT */
12554ba9607SSascha Wildner 	"right", /* EQN_TOK_RIGHT */
12654ba9607SSascha Wildner 	"pile", /* EQN_TOK_PILE */
12754ba9607SSascha Wildner 	"lpile", /* EQN_TOK_LPILE */
12854ba9607SSascha Wildner 	"rpile", /* EQN_TOK_RPILE */
12954ba9607SSascha Wildner 	"cpile", /* EQN_TOK_CPILE */
13054ba9607SSascha Wildner 	"matrix", /* EQN_TOK_MATRIX */
13154ba9607SSascha Wildner 	"ccol", /* EQN_TOK_CCOL */
13254ba9607SSascha Wildner 	"lcol", /* EQN_TOK_LCOL */
13354ba9607SSascha Wildner 	"rcol", /* EQN_TOK_RCOL */
13454ba9607SSascha Wildner 	"delim", /* EQN_TOK_DELIM */
13554ba9607SSascha Wildner 	"define", /* EQN_TOK_DEFINE */
13654ba9607SSascha Wildner 	"tdefine", /* EQN_TOK_TDEFINE */
13754ba9607SSascha Wildner 	"ndefine", /* EQN_TOK_NDEFINE */
13854ba9607SSascha Wildner 	"undef", /* EQN_TOK_UNDEF */
13954ba9607SSascha Wildner 	"above", /* EQN_TOK_ABOVE */
14054ba9607SSascha Wildner };
14154ba9607SSascha Wildner 
14254ba9607SSascha Wildner static	const char *const eqn_func[] = {
14354ba9607SSascha Wildner 	"acos",	"acsc",	"and",	"arc",	"asec",	"asin", "atan",
14454ba9607SSascha Wildner 	"cos",	"cosh", "coth",	"csc",	"det",	"exp",	"for",
14554ba9607SSascha Wildner 	"if",	"lim",	"ln",	"log",	"max",	"min",
14654ba9607SSascha Wildner 	"sec",	"sin",	"sinh",	"tan",	"tanh",	"Im",	"Re",
14736342e81SSascha Wildner };
14836342e81SSascha Wildner 
14936342e81SSascha Wildner enum	eqn_symt {
15054ba9607SSascha Wildner 	EQNSYM_alpha = 0,
15136342e81SSascha Wildner 	EQNSYM_beta,
15236342e81SSascha Wildner 	EQNSYM_chi,
15336342e81SSascha Wildner 	EQNSYM_delta,
15436342e81SSascha Wildner 	EQNSYM_epsilon,
15536342e81SSascha Wildner 	EQNSYM_eta,
15636342e81SSascha Wildner 	EQNSYM_gamma,
15736342e81SSascha Wildner 	EQNSYM_iota,
15836342e81SSascha Wildner 	EQNSYM_kappa,
15936342e81SSascha Wildner 	EQNSYM_lambda,
16036342e81SSascha Wildner 	EQNSYM_mu,
16136342e81SSascha Wildner 	EQNSYM_nu,
16236342e81SSascha Wildner 	EQNSYM_omega,
16336342e81SSascha Wildner 	EQNSYM_omicron,
16436342e81SSascha Wildner 	EQNSYM_phi,
16536342e81SSascha Wildner 	EQNSYM_pi,
16636342e81SSascha Wildner 	EQNSYM_ps,
16736342e81SSascha Wildner 	EQNSYM_rho,
16836342e81SSascha Wildner 	EQNSYM_sigma,
16936342e81SSascha Wildner 	EQNSYM_tau,
17036342e81SSascha Wildner 	EQNSYM_theta,
17136342e81SSascha Wildner 	EQNSYM_upsilon,
17236342e81SSascha Wildner 	EQNSYM_xi,
17336342e81SSascha Wildner 	EQNSYM_zeta,
17436342e81SSascha Wildner 	EQNSYM_DELTA,
17536342e81SSascha Wildner 	EQNSYM_GAMMA,
17636342e81SSascha Wildner 	EQNSYM_LAMBDA,
17736342e81SSascha Wildner 	EQNSYM_OMEGA,
17836342e81SSascha Wildner 	EQNSYM_PHI,
17936342e81SSascha Wildner 	EQNSYM_PI,
18036342e81SSascha Wildner 	EQNSYM_PSI,
18136342e81SSascha Wildner 	EQNSYM_SIGMA,
18236342e81SSascha Wildner 	EQNSYM_THETA,
18336342e81SSascha Wildner 	EQNSYM_UPSILON,
18436342e81SSascha Wildner 	EQNSYM_XI,
18536342e81SSascha Wildner 	EQNSYM_inter,
18636342e81SSascha Wildner 	EQNSYM_union,
18736342e81SSascha Wildner 	EQNSYM_prod,
18836342e81SSascha Wildner 	EQNSYM_int,
18936342e81SSascha Wildner 	EQNSYM_sum,
19036342e81SSascha Wildner 	EQNSYM_grad,
19136342e81SSascha Wildner 	EQNSYM_del,
19236342e81SSascha Wildner 	EQNSYM_times,
19336342e81SSascha Wildner 	EQNSYM_cdot,
19436342e81SSascha Wildner 	EQNSYM_nothing,
19536342e81SSascha Wildner 	EQNSYM_approx,
19636342e81SSascha Wildner 	EQNSYM_prime,
19736342e81SSascha Wildner 	EQNSYM_half,
19836342e81SSascha Wildner 	EQNSYM_partial,
19936342e81SSascha Wildner 	EQNSYM_inf,
20036342e81SSascha Wildner 	EQNSYM_muchgreat,
20136342e81SSascha Wildner 	EQNSYM_muchless,
20236342e81SSascha Wildner 	EQNSYM_larrow,
20336342e81SSascha Wildner 	EQNSYM_rarrow,
20436342e81SSascha Wildner 	EQNSYM_pm,
20536342e81SSascha Wildner 	EQNSYM_nequal,
20636342e81SSascha Wildner 	EQNSYM_equiv,
20736342e81SSascha Wildner 	EQNSYM_lessequal,
20836342e81SSascha Wildner 	EQNSYM_moreequal,
20954ba9607SSascha Wildner 	EQNSYM_minus,
21036342e81SSascha Wildner 	EQNSYM__MAX
21136342e81SSascha Wildner };
21236342e81SSascha Wildner 
21336342e81SSascha Wildner struct	eqnsym {
21454ba9607SSascha Wildner 	const char	*str;
21536342e81SSascha Wildner 	const char	*sym;
21636342e81SSascha Wildner };
21736342e81SSascha Wildner 
21836342e81SSascha Wildner static	const struct eqnsym eqnsyms[EQNSYM__MAX] = {
21954ba9607SSascha Wildner 	{ "alpha", "*a" }, /* EQNSYM_alpha */
22054ba9607SSascha Wildner 	{ "beta", "*b" }, /* EQNSYM_beta */
22154ba9607SSascha Wildner 	{ "chi", "*x" }, /* EQNSYM_chi */
22254ba9607SSascha Wildner 	{ "delta", "*d" }, /* EQNSYM_delta */
22354ba9607SSascha Wildner 	{ "epsilon", "*e" }, /* EQNSYM_epsilon */
22454ba9607SSascha Wildner 	{ "eta", "*y" }, /* EQNSYM_eta */
22554ba9607SSascha Wildner 	{ "gamma", "*g" }, /* EQNSYM_gamma */
22654ba9607SSascha Wildner 	{ "iota", "*i" }, /* EQNSYM_iota */
22754ba9607SSascha Wildner 	{ "kappa", "*k" }, /* EQNSYM_kappa */
22854ba9607SSascha Wildner 	{ "lambda", "*l" }, /* EQNSYM_lambda */
22954ba9607SSascha Wildner 	{ "mu", "*m" }, /* EQNSYM_mu */
23054ba9607SSascha Wildner 	{ "nu", "*n" }, /* EQNSYM_nu */
23154ba9607SSascha Wildner 	{ "omega", "*w" }, /* EQNSYM_omega */
23254ba9607SSascha Wildner 	{ "omicron", "*o" }, /* EQNSYM_omicron */
23354ba9607SSascha Wildner 	{ "phi", "*f" }, /* EQNSYM_phi */
23454ba9607SSascha Wildner 	{ "pi", "*p" }, /* EQNSYM_pi */
23554ba9607SSascha Wildner 	{ "psi", "*q" }, /* EQNSYM_psi */
23654ba9607SSascha Wildner 	{ "rho", "*r" }, /* EQNSYM_rho */
23754ba9607SSascha Wildner 	{ "sigma", "*s" }, /* EQNSYM_sigma */
23854ba9607SSascha Wildner 	{ "tau", "*t" }, /* EQNSYM_tau */
23954ba9607SSascha Wildner 	{ "theta", "*h" }, /* EQNSYM_theta */
24054ba9607SSascha Wildner 	{ "upsilon", "*u" }, /* EQNSYM_upsilon */
24154ba9607SSascha Wildner 	{ "xi", "*c" }, /* EQNSYM_xi */
24254ba9607SSascha Wildner 	{ "zeta", "*z" }, /* EQNSYM_zeta */
24354ba9607SSascha Wildner 	{ "DELTA", "*D" }, /* EQNSYM_DELTA */
24454ba9607SSascha Wildner 	{ "GAMMA", "*G" }, /* EQNSYM_GAMMA */
24554ba9607SSascha Wildner 	{ "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
24654ba9607SSascha Wildner 	{ "OMEGA", "*W" }, /* EQNSYM_OMEGA */
24754ba9607SSascha Wildner 	{ "PHI", "*F" }, /* EQNSYM_PHI */
24854ba9607SSascha Wildner 	{ "PI", "*P" }, /* EQNSYM_PI */
24954ba9607SSascha Wildner 	{ "PSI", "*Q" }, /* EQNSYM_PSI */
25054ba9607SSascha Wildner 	{ "SIGMA", "*S" }, /* EQNSYM_SIGMA */
25154ba9607SSascha Wildner 	{ "THETA", "*H" }, /* EQNSYM_THETA */
25254ba9607SSascha Wildner 	{ "UPSILON", "*U" }, /* EQNSYM_UPSILON */
25354ba9607SSascha Wildner 	{ "XI", "*C" }, /* EQNSYM_XI */
25454ba9607SSascha Wildner 	{ "inter", "ca" }, /* EQNSYM_inter */
25554ba9607SSascha Wildner 	{ "union", "cu" }, /* EQNSYM_union */
25654ba9607SSascha Wildner 	{ "prod", "product" }, /* EQNSYM_prod */
25754ba9607SSascha Wildner 	{ "int", "integral" }, /* EQNSYM_int */
25854ba9607SSascha Wildner 	{ "sum", "sum" }, /* EQNSYM_sum */
25954ba9607SSascha Wildner 	{ "grad", "gr" }, /* EQNSYM_grad */
26054ba9607SSascha Wildner 	{ "del", "gr" }, /* EQNSYM_del */
26154ba9607SSascha Wildner 	{ "times", "mu" }, /* EQNSYM_times */
26254ba9607SSascha Wildner 	{ "cdot", "pc" }, /* EQNSYM_cdot */
26354ba9607SSascha Wildner 	{ "nothing", "&" }, /* EQNSYM_nothing */
26454ba9607SSascha Wildner 	{ "approx", "~~" }, /* EQNSYM_approx */
26554ba9607SSascha Wildner 	{ "prime", "fm" }, /* EQNSYM_prime */
26654ba9607SSascha Wildner 	{ "half", "12" }, /* EQNSYM_half */
26754ba9607SSascha Wildner 	{ "partial", "pd" }, /* EQNSYM_partial */
26854ba9607SSascha Wildner 	{ "inf", "if" }, /* EQNSYM_inf */
26954ba9607SSascha Wildner 	{ ">>", ">>" }, /* EQNSYM_muchgreat */
27054ba9607SSascha Wildner 	{ "<<", "<<" }, /* EQNSYM_muchless */
27154ba9607SSascha Wildner 	{ "<-", "<-" }, /* EQNSYM_larrow */
27254ba9607SSascha Wildner 	{ "->", "->" }, /* EQNSYM_rarrow */
27354ba9607SSascha Wildner 	{ "+-", "+-" }, /* EQNSYM_pm */
27454ba9607SSascha Wildner 	{ "!=", "!=" }, /* EQNSYM_nequal */
27554ba9607SSascha Wildner 	{ "==", "==" }, /* EQNSYM_equiv */
27654ba9607SSascha Wildner 	{ "<=", "<=" }, /* EQNSYM_lessequal */
27754ba9607SSascha Wildner 	{ ">=", ">=" }, /* EQNSYM_moreequal */
27854ba9607SSascha Wildner 	{ "-", "mi" }, /* EQNSYM_minus */
27936342e81SSascha Wildner };
28036342e81SSascha Wildner 
28154ba9607SSascha Wildner enum	parse_mode {
28254ba9607SSascha Wildner 	MODE_QUOTED,
28354ba9607SSascha Wildner 	MODE_NOSUB,
28454ba9607SSascha Wildner 	MODE_SUB,
28554ba9607SSascha Wildner 	MODE_TOK
28654ba9607SSascha Wildner };
287070c62a6SFranco Fichtner 
28854ba9607SSascha Wildner struct	eqn_def {
28954ba9607SSascha Wildner 	char		 *key;
29054ba9607SSascha Wildner 	size_t		  keysz;
29154ba9607SSascha Wildner 	char		 *val;
29254ba9607SSascha Wildner 	size_t		  valsz;
29354ba9607SSascha Wildner };
29460e1e752SSascha Wildner 
29554ba9607SSascha Wildner static	struct eqn_box	*eqn_box_alloc(struct eqn_node *, struct eqn_box *);
29654ba9607SSascha Wildner static	struct eqn_box	*eqn_box_makebinary(struct eqn_node *,
29754ba9607SSascha Wildner 				struct eqn_box *);
29854ba9607SSascha Wildner static	void		 eqn_def(struct eqn_node *);
29954ba9607SSascha Wildner static	struct eqn_def	*eqn_def_find(struct eqn_node *);
30054ba9607SSascha Wildner static	void		 eqn_delim(struct eqn_node *);
30154ba9607SSascha Wildner static	enum eqn_tok	 eqn_next(struct eqn_node *, enum parse_mode);
30254ba9607SSascha Wildner static	void		 eqn_undef(struct eqn_node *);
30360e1e752SSascha Wildner 
30460e1e752SSascha Wildner 
30560e1e752SSascha Wildner struct eqn_node *
eqn_alloc(void)30654ba9607SSascha Wildner eqn_alloc(void)
30760e1e752SSascha Wildner {
30836342e81SSascha Wildner 	struct eqn_node *ep;
30960e1e752SSascha Wildner 
31054ba9607SSascha Wildner 	ep = mandoc_calloc(1, sizeof(*ep));
31154ba9607SSascha Wildner 	ep->gsize = EQN_DEFSIZE;
31254ba9607SSascha Wildner 	return ep;
31360e1e752SSascha Wildner }
31460e1e752SSascha Wildner 
31560e1e752SSascha Wildner void
eqn_reset(struct eqn_node * ep)31654ba9607SSascha Wildner eqn_reset(struct eqn_node *ep)
31754ba9607SSascha Wildner {
31854ba9607SSascha Wildner 	free(ep->data);
31954ba9607SSascha Wildner 	ep->data = ep->start = ep->end = NULL;
32054ba9607SSascha Wildner 	ep->sz = ep->toksz = 0;
32154ba9607SSascha Wildner }
32254ba9607SSascha Wildner 
32354ba9607SSascha Wildner void
eqn_read(struct eqn_node * ep,const char * p)32454ba9607SSascha Wildner eqn_read(struct eqn_node *ep, const char *p)
32554ba9607SSascha Wildner {
32654ba9607SSascha Wildner 	char		*cp;
32754ba9607SSascha Wildner 
32854ba9607SSascha Wildner 	if (ep->data == NULL) {
32954ba9607SSascha Wildner 		ep->sz = strlen(p);
33054ba9607SSascha Wildner 		ep->data = mandoc_strdup(p);
33154ba9607SSascha Wildner 	} else {
33254ba9607SSascha Wildner 		ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
33354ba9607SSascha Wildner 		free(ep->data);
33454ba9607SSascha Wildner 		ep->data = cp;
33554ba9607SSascha Wildner 	}
33654ba9607SSascha Wildner 	ep->sz += 1;
33754ba9607SSascha Wildner }
33854ba9607SSascha Wildner 
33954ba9607SSascha Wildner /*
34054ba9607SSascha Wildner  * Find the key "key" of the give size within our eqn-defined values.
34154ba9607SSascha Wildner  */
34254ba9607SSascha Wildner static struct eqn_def *
eqn_def_find(struct eqn_node * ep)34354ba9607SSascha Wildner eqn_def_find(struct eqn_node *ep)
34460e1e752SSascha Wildner {
34536342e81SSascha Wildner 	int		 i;
34660e1e752SSascha Wildner 
34754ba9607SSascha Wildner 	for (i = 0; i < (int)ep->defsz; i++)
34854ba9607SSascha Wildner 		if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
34954ba9607SSascha Wildner 		    ep->defs[i].keysz, ep->start, ep->toksz))
35054ba9607SSascha Wildner 			return &ep->defs[i];
35136342e81SSascha Wildner 
35254ba9607SSascha Wildner 	return NULL;
35336342e81SSascha Wildner }
35436342e81SSascha Wildner 
35554ba9607SSascha Wildner /*
35654ba9607SSascha Wildner  * Parse a token from the input text.  The modes are:
35754ba9607SSascha Wildner  * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
35854ba9607SSascha Wildner  *   before its next occurence.  Do not interpret the token in any
35954ba9607SSascha Wildner  *   way and return EQN_TOK_QUOTED.  All other modes behave like
36054ba9607SSascha Wildner  *   MODE_QUOTED when *ep->start is '"'.
36154ba9607SSascha Wildner  * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
36254ba9607SSascha Wildner  *   otherwise, it ends before the next whitespace or brace.
36354ba9607SSascha Wildner  *   Do not interpret the token and return EQN_TOK__MAX.
36454ba9607SSascha Wildner  * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
36554ba9607SSascha Wildner  *   alias created with define.  If it is an alias, replace it with
36654ba9607SSascha Wildner  *   its string value and reparse.
36754ba9607SSascha Wildner  * MODE_TOK: Like MODE_SUB, but also check the token against the list
36854ba9607SSascha Wildner  *   of tokens, and if there is a match, return that token.  Otherwise,
36954ba9607SSascha Wildner  *   if the token matches a symbol, return EQN_TOK_SYM; if it matches
37054ba9607SSascha Wildner  *   a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX.  Except for
37154ba9607SSascha Wildner  *   a token match, *ep->start is set to an allocated string that the
37254ba9607SSascha Wildner  *   caller is expected to free.
37354ba9607SSascha Wildner  * All modes skip whitespace following the end of the token.
37454ba9607SSascha Wildner  */
37554ba9607SSascha Wildner static enum eqn_tok
eqn_next(struct eqn_node * ep,enum parse_mode mode)37654ba9607SSascha Wildner eqn_next(struct eqn_node *ep, enum parse_mode mode)
37736342e81SSascha Wildner {
37854ba9607SSascha Wildner 	static int	 last_len, lim;
37936342e81SSascha Wildner 
38054ba9607SSascha Wildner 	struct eqn_def	*def;
38154ba9607SSascha Wildner 	size_t		 start;
38254ba9607SSascha Wildner 	int		 diff, i, quoted;
38354ba9607SSascha Wildner 	enum eqn_tok	 tok;
38436342e81SSascha Wildner 
38554ba9607SSascha Wildner 	/*
38654ba9607SSascha Wildner 	 * Reset the recursion counter after advancing
38754ba9607SSascha Wildner 	 * beyond the end of the previous substitution.
38854ba9607SSascha Wildner 	 */
38954ba9607SSascha Wildner 	if (ep->end - ep->data >= last_len)
39054ba9607SSascha Wildner 		lim = 0;
39136342e81SSascha Wildner 
39254ba9607SSascha Wildner 	ep->start = ep->end;
39354ba9607SSascha Wildner 	quoted = mode == MODE_QUOTED;
39454ba9607SSascha Wildner 	for (;;) {
39554ba9607SSascha Wildner 		switch (*ep->start) {
39654ba9607SSascha Wildner 		case '\0':
39754ba9607SSascha Wildner 			ep->toksz = 0;
39854ba9607SSascha Wildner 			return EQN_TOK_EOF;
39954ba9607SSascha Wildner 		case '"':
40054ba9607SSascha Wildner 			quoted = 1;
40154ba9607SSascha Wildner 			break;
402*99db7d0eSSascha Wildner 		case ' ':
403*99db7d0eSSascha Wildner 		case '\t':
404*99db7d0eSSascha Wildner 		case '~':
405*99db7d0eSSascha Wildner 		case '^':
406*99db7d0eSSascha Wildner 			if (quoted)
407*99db7d0eSSascha Wildner 				break;
408*99db7d0eSSascha Wildner 			ep->start++;
409*99db7d0eSSascha Wildner 			continue;
41054ba9607SSascha Wildner 		default:
41154ba9607SSascha Wildner 			break;
41254ba9607SSascha Wildner 		}
41354ba9607SSascha Wildner 		if (quoted) {
41454ba9607SSascha Wildner 			ep->end = strchr(ep->start + 1, *ep->start);
41554ba9607SSascha Wildner 			ep->start++;  /* Skip opening quote. */
41654ba9607SSascha Wildner 			if (ep->end == NULL) {
41754ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_ARG_QUOTE,
41854ba9607SSascha Wildner 				    ep->node->line, ep->node->pos, NULL);
41954ba9607SSascha Wildner 				ep->end = strchr(ep->start, '\0');
42054ba9607SSascha Wildner 			}
42154ba9607SSascha Wildner 		} else {
42254ba9607SSascha Wildner 			ep->end = ep->start + 1;
42354ba9607SSascha Wildner 			if (*ep->start != '{' && *ep->start != '}')
42454ba9607SSascha Wildner 				ep->end += strcspn(ep->end, " ^~\"{}\t");
42554ba9607SSascha Wildner 		}
42654ba9607SSascha Wildner 		ep->toksz = ep->end - ep->start;
42754ba9607SSascha Wildner 		if (quoted && *ep->end != '\0')
42854ba9607SSascha Wildner 			ep->end++;  /* Skip closing quote. */
42954ba9607SSascha Wildner 		while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
43054ba9607SSascha Wildner 			ep->end++;
43154ba9607SSascha Wildner 		if (quoted)  /* Cannot return, may have to strndup. */
43254ba9607SSascha Wildner 			break;
43354ba9607SSascha Wildner 		if (mode == MODE_NOSUB)
43454ba9607SSascha Wildner 			return EQN_TOK__MAX;
43554ba9607SSascha Wildner 		if ((def = eqn_def_find(ep)) == NULL)
43654ba9607SSascha Wildner 			break;
43754ba9607SSascha Wildner 		if (++lim > EQN_NEST_MAX) {
43854ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_ROFFLOOP,
43954ba9607SSascha Wildner 			    ep->node->line, ep->node->pos, NULL);
44054ba9607SSascha Wildner 			return EQN_TOK_EOF;
44136342e81SSascha Wildner 		}
44236342e81SSascha Wildner 
44354ba9607SSascha Wildner 		/* Replace a defined name with its string value. */
44454ba9607SSascha Wildner 		if ((diff = def->valsz - ep->toksz) > 0) {
44554ba9607SSascha Wildner 			start = ep->start - ep->data;
44654ba9607SSascha Wildner 			ep->sz += diff;
44754ba9607SSascha Wildner 			ep->data = mandoc_realloc(ep->data, ep->sz + 1);
44854ba9607SSascha Wildner 			ep->start = ep->data + start;
44954ba9607SSascha Wildner 		}
45054ba9607SSascha Wildner 		if (diff)
45154ba9607SSascha Wildner 			memmove(ep->start + def->valsz, ep->start + ep->toksz,
45254ba9607SSascha Wildner 			    strlen(ep->start + ep->toksz) + 1);
45354ba9607SSascha Wildner 		memcpy(ep->start, def->val, def->valsz);
45454ba9607SSascha Wildner 		last_len = ep->start - ep->data + def->valsz;
45554ba9607SSascha Wildner 	}
45654ba9607SSascha Wildner 	if (mode != MODE_TOK)
45754ba9607SSascha Wildner 		return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
45854ba9607SSascha Wildner 	if (quoted) {
45954ba9607SSascha Wildner 		ep->start = mandoc_strndup(ep->start, ep->toksz);
46054ba9607SSascha Wildner 		return EQN_TOK_QUOTED;
46154ba9607SSascha Wildner 	}
46254ba9607SSascha Wildner 	for (tok = 0; tok < EQN_TOK__MAX; tok++)
46354ba9607SSascha Wildner 		if (STRNEQ(ep->start, ep->toksz,
46454ba9607SSascha Wildner 		    eqn_toks[tok], strlen(eqn_toks[tok])))
46554ba9607SSascha Wildner 			return tok;
46654ba9607SSascha Wildner 
46754ba9607SSascha Wildner 	for (i = 0; i < EQNSYM__MAX; i++) {
46854ba9607SSascha Wildner 		if (STRNEQ(ep->start, ep->toksz,
46954ba9607SSascha Wildner 		    eqnsyms[i].str, strlen(eqnsyms[i].str))) {
47054ba9607SSascha Wildner 			mandoc_asprintf(&ep->start,
47154ba9607SSascha Wildner 			    "\\[%s]", eqnsyms[i].sym);
47254ba9607SSascha Wildner 			return EQN_TOK_SYM;
47354ba9607SSascha Wildner 		}
47454ba9607SSascha Wildner 	}
47554ba9607SSascha Wildner 	ep->start = mandoc_strndup(ep->start, ep->toksz);
47654ba9607SSascha Wildner 	for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
47754ba9607SSascha Wildner 		if (STRNEQ(ep->start, ep->toksz,
47854ba9607SSascha Wildner 		    eqn_func[i], strlen(eqn_func[i])))
47954ba9607SSascha Wildner 			return EQN_TOK_FUNC;
48054ba9607SSascha Wildner 	return EQN_TOK__MAX;
48154ba9607SSascha Wildner }
48254ba9607SSascha Wildner 
48354ba9607SSascha Wildner void
eqn_box_free(struct eqn_box * bp)48436342e81SSascha Wildner eqn_box_free(struct eqn_box *bp)
48536342e81SSascha Wildner {
48654ba9607SSascha Wildner 	if (bp == NULL)
48754ba9607SSascha Wildner 		return;
48836342e81SSascha Wildner 
48936342e81SSascha Wildner 	if (bp->first)
49036342e81SSascha Wildner 		eqn_box_free(bp->first);
49136342e81SSascha Wildner 	if (bp->next)
49236342e81SSascha Wildner 		eqn_box_free(bp->next);
49336342e81SSascha Wildner 
49436342e81SSascha Wildner 	free(bp->text);
49536342e81SSascha Wildner 	free(bp->left);
49636342e81SSascha Wildner 	free(bp->right);
49754ba9607SSascha Wildner 	free(bp->top);
49854ba9607SSascha Wildner 	free(bp->bottom);
49936342e81SSascha Wildner 	free(bp);
50036342e81SSascha Wildner }
50136342e81SSascha Wildner 
50254ba9607SSascha Wildner struct eqn_box *
eqn_box_new(void)50354ba9607SSascha Wildner eqn_box_new(void)
50436342e81SSascha Wildner {
50554ba9607SSascha Wildner 	struct eqn_box	*bp;
50636342e81SSascha Wildner 
50754ba9607SSascha Wildner 	bp = mandoc_calloc(1, sizeof(*bp));
50854ba9607SSascha Wildner 	bp->expectargs = UINT_MAX;
50954ba9607SSascha Wildner 	return bp;
51036342e81SSascha Wildner }
51136342e81SSascha Wildner 
51254ba9607SSascha Wildner /*
51354ba9607SSascha Wildner  * Allocate a box as the last child of the parent node.
51454ba9607SSascha Wildner  */
51554ba9607SSascha Wildner static struct eqn_box *
eqn_box_alloc(struct eqn_node * ep,struct eqn_box * parent)51654ba9607SSascha Wildner eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
51736342e81SSascha Wildner {
51854ba9607SSascha Wildner 	struct eqn_box	*bp;
51936342e81SSascha Wildner 
52054ba9607SSascha Wildner 	bp = eqn_box_new();
52154ba9607SSascha Wildner 	bp->parent = parent;
52254ba9607SSascha Wildner 	bp->parent->args++;
52354ba9607SSascha Wildner 	bp->font = bp->parent->font;
52454ba9607SSascha Wildner 	bp->size = ep->gsize;
52554ba9607SSascha Wildner 
52654ba9607SSascha Wildner 	if (NULL != parent->first) {
52754ba9607SSascha Wildner 		parent->last->next = bp;
52854ba9607SSascha Wildner 		bp->prev = parent->last;
52954ba9607SSascha Wildner 	} else
53054ba9607SSascha Wildner 		parent->first = bp;
53154ba9607SSascha Wildner 
53254ba9607SSascha Wildner 	parent->last = bp;
53354ba9607SSascha Wildner 	return bp;
53454ba9607SSascha Wildner }
53554ba9607SSascha Wildner 
53654ba9607SSascha Wildner /*
53754ba9607SSascha Wildner  * Reparent the current last node (of the current parent) under a new
53854ba9607SSascha Wildner  * EQN_SUBEXPR as the first element.
53954ba9607SSascha Wildner  * Then return the new parent.
54054ba9607SSascha Wildner  * The new EQN_SUBEXPR will have a two-child limit.
54154ba9607SSascha Wildner  */
54254ba9607SSascha Wildner static struct eqn_box *
eqn_box_makebinary(struct eqn_node * ep,struct eqn_box * parent)54354ba9607SSascha Wildner eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent)
54454ba9607SSascha Wildner {
54554ba9607SSascha Wildner 	struct eqn_box	*b, *newb;
54654ba9607SSascha Wildner 
54754ba9607SSascha Wildner 	assert(NULL != parent->last);
54854ba9607SSascha Wildner 	b = parent->last;
54954ba9607SSascha Wildner 	if (parent->last == parent->first)
55054ba9607SSascha Wildner 		parent->first = NULL;
55154ba9607SSascha Wildner 	parent->args--;
55254ba9607SSascha Wildner 	parent->last = b->prev;
55354ba9607SSascha Wildner 	b->prev = NULL;
55454ba9607SSascha Wildner 	newb = eqn_box_alloc(ep, parent);
55554ba9607SSascha Wildner 	newb->type = EQN_SUBEXPR;
55654ba9607SSascha Wildner 	newb->expectargs = 2;
55754ba9607SSascha Wildner 	newb->args = 1;
55854ba9607SSascha Wildner 	newb->first = newb->last = b;
55954ba9607SSascha Wildner 	newb->first->next = NULL;
56054ba9607SSascha Wildner 	b->parent = newb;
56154ba9607SSascha Wildner 	return newb;
56254ba9607SSascha Wildner }
56354ba9607SSascha Wildner 
56454ba9607SSascha Wildner /*
56554ba9607SSascha Wildner  * Parse the "delim" control statement.
56654ba9607SSascha Wildner  */
56754ba9607SSascha Wildner static void
eqn_delim(struct eqn_node * ep)56854ba9607SSascha Wildner eqn_delim(struct eqn_node *ep)
56954ba9607SSascha Wildner {
57054ba9607SSascha Wildner 	if (ep->end[0] == '\0' || ep->end[1] == '\0') {
57154ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_REQ_EMPTY,
57254ba9607SSascha Wildner 		    ep->node->line, ep->node->pos, "delim");
57354ba9607SSascha Wildner 		if (ep->end[0] != '\0')
57454ba9607SSascha Wildner 			ep->end++;
57554ba9607SSascha Wildner 	} else if (strncmp(ep->end, "off", 3) == 0) {
57654ba9607SSascha Wildner 		ep->delim = 0;
57754ba9607SSascha Wildner 		ep->end += 3;
57854ba9607SSascha Wildner 	} else if (strncmp(ep->end, "on", 2) == 0) {
57954ba9607SSascha Wildner 		if (ep->odelim && ep->cdelim)
58054ba9607SSascha Wildner 			ep->delim = 1;
58154ba9607SSascha Wildner 		ep->end += 2;
58254ba9607SSascha Wildner 	} else {
58354ba9607SSascha Wildner 		ep->odelim = *ep->end++;
58454ba9607SSascha Wildner 		ep->cdelim = *ep->end++;
58554ba9607SSascha Wildner 		ep->delim = 1;
58654ba9607SSascha Wildner 	}
58754ba9607SSascha Wildner }
58854ba9607SSascha Wildner 
58954ba9607SSascha Wildner /*
59054ba9607SSascha Wildner  * Undefine a previously-defined string.
59154ba9607SSascha Wildner  */
59254ba9607SSascha Wildner static void
eqn_undef(struct eqn_node * ep)59354ba9607SSascha Wildner eqn_undef(struct eqn_node *ep)
59454ba9607SSascha Wildner {
59554ba9607SSascha Wildner 	struct eqn_def	*def;
59654ba9607SSascha Wildner 
59754ba9607SSascha Wildner 	if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
59854ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_REQ_EMPTY,
59954ba9607SSascha Wildner 		    ep->node->line, ep->node->pos, "undef");
60054ba9607SSascha Wildner 		return;
60154ba9607SSascha Wildner 	}
60254ba9607SSascha Wildner 	if ((def = eqn_def_find(ep)) == NULL)
60354ba9607SSascha Wildner 		return;
60454ba9607SSascha Wildner 	free(def->key);
60554ba9607SSascha Wildner 	free(def->val);
60654ba9607SSascha Wildner 	def->key = def->val = NULL;
60754ba9607SSascha Wildner 	def->keysz = def->valsz = 0;
60836342e81SSascha Wildner }
60936342e81SSascha Wildner 
61036342e81SSascha Wildner static void
eqn_def(struct eqn_node * ep)61154ba9607SSascha Wildner eqn_def(struct eqn_node *ep)
61236342e81SSascha Wildner {
61336342e81SSascha Wildner 	struct eqn_def	*def;
61436342e81SSascha Wildner 	int		 i;
61536342e81SSascha Wildner 
61654ba9607SSascha Wildner 	if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
61754ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_REQ_EMPTY,
61854ba9607SSascha Wildner 		    ep->node->line, ep->node->pos, "define");
61954ba9607SSascha Wildner 		return;
62036342e81SSascha Wildner 	}
62136342e81SSascha Wildner 
62236342e81SSascha Wildner 	/*
62336342e81SSascha Wildner 	 * Search for a key that already exists.
62436342e81SSascha Wildner 	 * Create a new key if none is found.
62536342e81SSascha Wildner 	 */
62654ba9607SSascha Wildner 	if ((def = eqn_def_find(ep)) == NULL) {
62736342e81SSascha Wildner 		/* Find holes in string array. */
62836342e81SSascha Wildner 		for (i = 0; i < (int)ep->defsz; i++)
62936342e81SSascha Wildner 			if (0 == ep->defs[i].keysz)
63036342e81SSascha Wildner 				break;
63136342e81SSascha Wildner 
63236342e81SSascha Wildner 		if (i == (int)ep->defsz) {
63336342e81SSascha Wildner 			ep->defsz++;
634070c62a6SFranco Fichtner 			ep->defs = mandoc_reallocarray(ep->defs,
635070c62a6SFranco Fichtner 			    ep->defsz, sizeof(struct eqn_def));
63636342e81SSascha Wildner 			ep->defs[i].key = ep->defs[i].val = NULL;
63736342e81SSascha Wildner 		}
63836342e81SSascha Wildner 
63954ba9607SSascha Wildner 		def = ep->defs + i;
64054ba9607SSascha Wildner 		free(def->key);
64154ba9607SSascha Wildner 		def->key = mandoc_strndup(ep->start, ep->toksz);
64254ba9607SSascha Wildner 		def->keysz = ep->toksz;
64336342e81SSascha Wildner 	}
64436342e81SSascha Wildner 
64554ba9607SSascha Wildner 	if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
64654ba9607SSascha Wildner 		mandoc_msg(MANDOCERR_REQ_EMPTY,
64754ba9607SSascha Wildner 		    ep->node->line, ep->node->pos, "define %s", def->key);
64854ba9607SSascha Wildner 		free(def->key);
64954ba9607SSascha Wildner 		free(def->val);
65054ba9607SSascha Wildner 		def->key = def->val = NULL;
65154ba9607SSascha Wildner 		def->keysz = def->valsz = 0;
65254ba9607SSascha Wildner 		return;
65354ba9607SSascha Wildner 	}
65454ba9607SSascha Wildner 	free(def->val);
65554ba9607SSascha Wildner 	def->val = mandoc_strndup(ep->start, ep->toksz);
65654ba9607SSascha Wildner 	def->valsz = ep->toksz;
65736342e81SSascha Wildner }
65836342e81SSascha Wildner 
65954ba9607SSascha Wildner void
eqn_parse(struct eqn_node * ep)66054ba9607SSascha Wildner eqn_parse(struct eqn_node *ep)
66136342e81SSascha Wildner {
66254ba9607SSascha Wildner 	struct eqn_box	*cur, *nbox, *parent, *split;
66354ba9607SSascha Wildner 	const char	*cp, *cpn;
66454ba9607SSascha Wildner 	char		*p;
66554ba9607SSascha Wildner 	enum eqn_tok	 tok;
66654ba9607SSascha Wildner 	enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
66754ba9607SSascha Wildner 	int		 size;
66836342e81SSascha Wildner 
66954ba9607SSascha Wildner 	parent = ep->node->eqn;
67054ba9607SSascha Wildner 	assert(parent != NULL);
67154ba9607SSascha Wildner 
67254ba9607SSascha Wildner 	/*
67354ba9607SSascha Wildner 	 * Empty equation.
67454ba9607SSascha Wildner 	 * Do not add it to the high-level syntax tree.
67554ba9607SSascha Wildner 	 */
67654ba9607SSascha Wildner 
67754ba9607SSascha Wildner 	if (ep->data == NULL)
67854ba9607SSascha Wildner 		return;
67954ba9607SSascha Wildner 
680*99db7d0eSSascha Wildner 	ep->start = ep->end = ep->data;
68154ba9607SSascha Wildner 
68254ba9607SSascha Wildner next_tok:
68354ba9607SSascha Wildner 	tok = eqn_next(ep, MODE_TOK);
68454ba9607SSascha Wildner 	switch (tok) {
68554ba9607SSascha Wildner 	case EQN_TOK_UNDEF:
68654ba9607SSascha Wildner 		eqn_undef(ep);
68754ba9607SSascha Wildner 		break;
68854ba9607SSascha Wildner 	case EQN_TOK_NDEFINE:
68954ba9607SSascha Wildner 	case EQN_TOK_DEFINE:
69054ba9607SSascha Wildner 		eqn_def(ep);
69154ba9607SSascha Wildner 		break;
69254ba9607SSascha Wildner 	case EQN_TOK_TDEFINE:
69354ba9607SSascha Wildner 		if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
69454ba9607SSascha Wildner 		    eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
69554ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_REQ_EMPTY,
69654ba9607SSascha Wildner 			    ep->node->line, ep->node->pos, "tdefine");
69754ba9607SSascha Wildner 		break;
69854ba9607SSascha Wildner 	case EQN_TOK_DELIM:
69954ba9607SSascha Wildner 		eqn_delim(ep);
70054ba9607SSascha Wildner 		break;
70154ba9607SSascha Wildner 	case EQN_TOK_GFONT:
70254ba9607SSascha Wildner 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
70354ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
70454ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
70554ba9607SSascha Wildner 		break;
70654ba9607SSascha Wildner 	case EQN_TOK_MARK:
70754ba9607SSascha Wildner 	case EQN_TOK_LINEUP:
70854ba9607SSascha Wildner 		/* Ignore these. */
70954ba9607SSascha Wildner 		break;
71054ba9607SSascha Wildner 	case EQN_TOK_DYAD:
71154ba9607SSascha Wildner 	case EQN_TOK_VEC:
71254ba9607SSascha Wildner 	case EQN_TOK_UNDER:
71354ba9607SSascha Wildner 	case EQN_TOK_BAR:
71454ba9607SSascha Wildner 	case EQN_TOK_TILDE:
71554ba9607SSascha Wildner 	case EQN_TOK_HAT:
71654ba9607SSascha Wildner 	case EQN_TOK_DOT:
71754ba9607SSascha Wildner 	case EQN_TOK_DOTDOT:
71854ba9607SSascha Wildner 		if (parent->last == NULL) {
71954ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
72054ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
72154ba9607SSascha Wildner 			cur = eqn_box_alloc(ep, parent);
72254ba9607SSascha Wildner 			cur->type = EQN_TEXT;
72354ba9607SSascha Wildner 			cur->text = mandoc_strdup("");
72436342e81SSascha Wildner 		}
72554ba9607SSascha Wildner 		parent = eqn_box_makebinary(ep, parent);
72654ba9607SSascha Wildner 		parent->type = EQN_LIST;
72754ba9607SSascha Wildner 		parent->expectargs = 1;
72854ba9607SSascha Wildner 		parent->font = EQNFONT_ROMAN;
72954ba9607SSascha Wildner 		switch (tok) {
73054ba9607SSascha Wildner 		case EQN_TOK_DOTDOT:
73154ba9607SSascha Wildner 			parent->top = mandoc_strdup("\\[ad]");
73254ba9607SSascha Wildner 			break;
73354ba9607SSascha Wildner 		case EQN_TOK_VEC:
73454ba9607SSascha Wildner 			parent->top = mandoc_strdup("\\[->]");
73554ba9607SSascha Wildner 			break;
73654ba9607SSascha Wildner 		case EQN_TOK_DYAD:
73754ba9607SSascha Wildner 			parent->top = mandoc_strdup("\\[<>]");
73854ba9607SSascha Wildner 			break;
73954ba9607SSascha Wildner 		case EQN_TOK_TILDE:
74054ba9607SSascha Wildner 			parent->top = mandoc_strdup("\\[a~]");
74154ba9607SSascha Wildner 			break;
74254ba9607SSascha Wildner 		case EQN_TOK_UNDER:
74354ba9607SSascha Wildner 			parent->bottom = mandoc_strdup("\\[ul]");
74454ba9607SSascha Wildner 			break;
74554ba9607SSascha Wildner 		case EQN_TOK_BAR:
74654ba9607SSascha Wildner 			parent->top = mandoc_strdup("\\[rn]");
74754ba9607SSascha Wildner 			break;
74854ba9607SSascha Wildner 		case EQN_TOK_DOT:
74954ba9607SSascha Wildner 			parent->top = mandoc_strdup("\\[a.]");
75054ba9607SSascha Wildner 			break;
75154ba9607SSascha Wildner 		case EQN_TOK_HAT:
75254ba9607SSascha Wildner 			parent->top = mandoc_strdup("\\[ha]");
75354ba9607SSascha Wildner 			break;
75454ba9607SSascha Wildner 		default:
75554ba9607SSascha Wildner 			abort();
75654ba9607SSascha Wildner 		}
75754ba9607SSascha Wildner 		parent = parent->parent;
75854ba9607SSascha Wildner 		break;
75954ba9607SSascha Wildner 	case EQN_TOK_FWD:
76054ba9607SSascha Wildner 	case EQN_TOK_BACK:
76154ba9607SSascha Wildner 	case EQN_TOK_DOWN:
76254ba9607SSascha Wildner 	case EQN_TOK_UP:
76354ba9607SSascha Wildner 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
76454ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
76554ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
76654ba9607SSascha Wildner 		break;
76754ba9607SSascha Wildner 	case EQN_TOK_FAT:
76854ba9607SSascha Wildner 	case EQN_TOK_ROMAN:
76954ba9607SSascha Wildner 	case EQN_TOK_ITALIC:
77054ba9607SSascha Wildner 	case EQN_TOK_BOLD:
77154ba9607SSascha Wildner 		while (parent->args == parent->expectargs)
77254ba9607SSascha Wildner 			parent = parent->parent;
77354ba9607SSascha Wildner 		/*
77454ba9607SSascha Wildner 		 * These values apply to the next word or sequence of
77554ba9607SSascha Wildner 		 * words; thus, we mark that we'll have a child with
77654ba9607SSascha Wildner 		 * exactly one of those.
77754ba9607SSascha Wildner 		 */
77854ba9607SSascha Wildner 		parent = eqn_box_alloc(ep, parent);
77954ba9607SSascha Wildner 		parent->type = EQN_LIST;
78054ba9607SSascha Wildner 		parent->expectargs = 1;
78154ba9607SSascha Wildner 		switch (tok) {
78254ba9607SSascha Wildner 		case EQN_TOK_FAT:
78354ba9607SSascha Wildner 			parent->font = EQNFONT_FAT;
78454ba9607SSascha Wildner 			break;
78554ba9607SSascha Wildner 		case EQN_TOK_ROMAN:
78654ba9607SSascha Wildner 			parent->font = EQNFONT_ROMAN;
78754ba9607SSascha Wildner 			break;
78854ba9607SSascha Wildner 		case EQN_TOK_ITALIC:
78954ba9607SSascha Wildner 			parent->font = EQNFONT_ITALIC;
79054ba9607SSascha Wildner 			break;
79154ba9607SSascha Wildner 		case EQN_TOK_BOLD:
79254ba9607SSascha Wildner 			parent->font = EQNFONT_BOLD;
79354ba9607SSascha Wildner 			break;
79454ba9607SSascha Wildner 		default:
79554ba9607SSascha Wildner 			abort();
79654ba9607SSascha Wildner 		}
79754ba9607SSascha Wildner 		break;
79854ba9607SSascha Wildner 	case EQN_TOK_SIZE:
79954ba9607SSascha Wildner 	case EQN_TOK_GSIZE:
80054ba9607SSascha Wildner 		/* Accept two values: integral size and a single. */
80154ba9607SSascha Wildner 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
80254ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
80354ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
80454ba9607SSascha Wildner 			break;
80554ba9607SSascha Wildner 		}
80654ba9607SSascha Wildner 		size = mandoc_strntoi(ep->start, ep->toksz, 10);
80754ba9607SSascha Wildner 		if (-1 == size) {
80854ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line,
80954ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
81054ba9607SSascha Wildner 			break;
81154ba9607SSascha Wildner 		}
81254ba9607SSascha Wildner 		if (EQN_TOK_GSIZE == tok) {
81354ba9607SSascha Wildner 			ep->gsize = size;
81454ba9607SSascha Wildner 			break;
81554ba9607SSascha Wildner 		}
81654ba9607SSascha Wildner 		while (parent->args == parent->expectargs)
81754ba9607SSascha Wildner 			parent = parent->parent;
81854ba9607SSascha Wildner 		parent = eqn_box_alloc(ep, parent);
81954ba9607SSascha Wildner 		parent->type = EQN_LIST;
82054ba9607SSascha Wildner 		parent->expectargs = 1;
82154ba9607SSascha Wildner 		parent->size = size;
82254ba9607SSascha Wildner 		break;
82354ba9607SSascha Wildner 	case EQN_TOK_FROM:
82454ba9607SSascha Wildner 	case EQN_TOK_TO:
82554ba9607SSascha Wildner 	case EQN_TOK_SUB:
82654ba9607SSascha Wildner 	case EQN_TOK_SUP:
82754ba9607SSascha Wildner 		/*
82854ba9607SSascha Wildner 		 * We have a left-right-associative expression.
82954ba9607SSascha Wildner 		 * Repivot under a positional node, open a child scope
83054ba9607SSascha Wildner 		 * and keep on reading.
83154ba9607SSascha Wildner 		 */
83254ba9607SSascha Wildner 		if (parent->last == NULL) {
83354ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
83454ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
83554ba9607SSascha Wildner 			cur = eqn_box_alloc(ep, parent);
83654ba9607SSascha Wildner 			cur->type = EQN_TEXT;
83754ba9607SSascha Wildner 			cur->text = mandoc_strdup("");
83854ba9607SSascha Wildner 		}
83954ba9607SSascha Wildner 		while (parent->expectargs == 1 && parent->args == 1)
84054ba9607SSascha Wildner 			parent = parent->parent;
84154ba9607SSascha Wildner 		if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO)  {
84254ba9607SSascha Wildner 			for (cur = parent; cur != NULL; cur = cur->parent)
84354ba9607SSascha Wildner 				if (cur->pos == EQNPOS_SUB ||
84454ba9607SSascha Wildner 				    cur->pos == EQNPOS_SUP ||
84554ba9607SSascha Wildner 				    cur->pos == EQNPOS_SUBSUP ||
84654ba9607SSascha Wildner 				    cur->pos == EQNPOS_SQRT ||
84754ba9607SSascha Wildner 				    cur->pos == EQNPOS_OVER)
84854ba9607SSascha Wildner 					break;
84954ba9607SSascha Wildner 			if (cur != NULL)
85054ba9607SSascha Wildner 				parent = cur->parent;
85154ba9607SSascha Wildner 		}
85254ba9607SSascha Wildner 		if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
85354ba9607SSascha Wildner 			parent->expectargs = 3;
85454ba9607SSascha Wildner 			parent->pos = EQNPOS_SUBSUP;
85554ba9607SSascha Wildner 			break;
85654ba9607SSascha Wildner 		}
85754ba9607SSascha Wildner 		if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
85854ba9607SSascha Wildner 			parent->expectargs = 3;
85954ba9607SSascha Wildner 			parent->pos = EQNPOS_FROMTO;
86054ba9607SSascha Wildner 			break;
86154ba9607SSascha Wildner 		}
86254ba9607SSascha Wildner 		parent = eqn_box_makebinary(ep, parent);
86354ba9607SSascha Wildner 		switch (tok) {
86454ba9607SSascha Wildner 		case EQN_TOK_FROM:
86554ba9607SSascha Wildner 			parent->pos = EQNPOS_FROM;
86654ba9607SSascha Wildner 			break;
86754ba9607SSascha Wildner 		case EQN_TOK_TO:
86854ba9607SSascha Wildner 			parent->pos = EQNPOS_TO;
86954ba9607SSascha Wildner 			break;
87054ba9607SSascha Wildner 		case EQN_TOK_SUP:
87154ba9607SSascha Wildner 			parent->pos = EQNPOS_SUP;
87254ba9607SSascha Wildner 			break;
87354ba9607SSascha Wildner 		case EQN_TOK_SUB:
87454ba9607SSascha Wildner 			parent->pos = EQNPOS_SUB;
87554ba9607SSascha Wildner 			break;
87654ba9607SSascha Wildner 		default:
87754ba9607SSascha Wildner 			abort();
87854ba9607SSascha Wildner 		}
87954ba9607SSascha Wildner 		break;
88054ba9607SSascha Wildner 	case EQN_TOK_SQRT:
88154ba9607SSascha Wildner 		while (parent->args == parent->expectargs)
88254ba9607SSascha Wildner 			parent = parent->parent;
88354ba9607SSascha Wildner 		/*
88454ba9607SSascha Wildner 		 * Accept a left-right-associative set of arguments just
88554ba9607SSascha Wildner 		 * like sub and sup and friends but without rebalancing
88654ba9607SSascha Wildner 		 * under a pivot.
88754ba9607SSascha Wildner 		 */
88854ba9607SSascha Wildner 		parent = eqn_box_alloc(ep, parent);
88954ba9607SSascha Wildner 		parent->type = EQN_SUBEXPR;
89054ba9607SSascha Wildner 		parent->pos = EQNPOS_SQRT;
89154ba9607SSascha Wildner 		parent->expectargs = 1;
89254ba9607SSascha Wildner 		break;
89354ba9607SSascha Wildner 	case EQN_TOK_OVER:
89454ba9607SSascha Wildner 		/*
89554ba9607SSascha Wildner 		 * We have a right-left-associative fraction.
89654ba9607SSascha Wildner 		 * Close out anything that's currently open, then
89754ba9607SSascha Wildner 		 * rebalance and continue reading.
89854ba9607SSascha Wildner 		 */
89954ba9607SSascha Wildner 		if (parent->last == NULL) {
90054ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
90154ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
90254ba9607SSascha Wildner 			cur = eqn_box_alloc(ep, parent);
90354ba9607SSascha Wildner 			cur->type = EQN_TEXT;
90454ba9607SSascha Wildner 			cur->text = mandoc_strdup("");
90554ba9607SSascha Wildner 		}
90654ba9607SSascha Wildner 		while (parent->args == parent->expectargs)
90754ba9607SSascha Wildner 			parent = parent->parent;
90854ba9607SSascha Wildner 		while (EQN_SUBEXPR == parent->type)
90954ba9607SSascha Wildner 			parent = parent->parent;
91054ba9607SSascha Wildner 		parent = eqn_box_makebinary(ep, parent);
91154ba9607SSascha Wildner 		parent->pos = EQNPOS_OVER;
91254ba9607SSascha Wildner 		break;
91354ba9607SSascha Wildner 	case EQN_TOK_RIGHT:
91454ba9607SSascha Wildner 	case EQN_TOK_BRACE_CLOSE:
91554ba9607SSascha Wildner 		/*
91654ba9607SSascha Wildner 		 * Close out the existing brace.
91754ba9607SSascha Wildner 		 * FIXME: this is a shitty sentinel: we should really
91854ba9607SSascha Wildner 		 * have a native EQN_BRACE type or whatnot.
91954ba9607SSascha Wildner 		 */
92054ba9607SSascha Wildner 		for (cur = parent; cur != NULL; cur = cur->parent)
92154ba9607SSascha Wildner 			if (cur->type == EQN_LIST &&
92254ba9607SSascha Wildner 			    cur->expectargs > 1 &&
92354ba9607SSascha Wildner 			    (tok == EQN_TOK_BRACE_CLOSE ||
92454ba9607SSascha Wildner 			     cur->left != NULL))
92554ba9607SSascha Wildner 				break;
92654ba9607SSascha Wildner 		if (cur == NULL) {
92754ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line,
92854ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
92954ba9607SSascha Wildner 			break;
93054ba9607SSascha Wildner 		}
93154ba9607SSascha Wildner 		parent = cur;
93254ba9607SSascha Wildner 		if (EQN_TOK_RIGHT == tok) {
93354ba9607SSascha Wildner 			if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
93454ba9607SSascha Wildner 				mandoc_msg(MANDOCERR_REQ_EMPTY,
93554ba9607SSascha Wildner 				    ep->node->line, ep->node->pos,
93654ba9607SSascha Wildner 				    "%s", eqn_toks[tok]);
93754ba9607SSascha Wildner 				break;
93854ba9607SSascha Wildner 			}
93954ba9607SSascha Wildner 			/* Handling depends on right/left. */
94054ba9607SSascha Wildner 			if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
94154ba9607SSascha Wildner 				parent->right = mandoc_strdup("\\[rc]");
94254ba9607SSascha Wildner 			else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
94354ba9607SSascha Wildner 				parent->right = mandoc_strdup("\\[rf]");
94454ba9607SSascha Wildner 			else
94554ba9607SSascha Wildner 				parent->right =
94654ba9607SSascha Wildner 				    mandoc_strndup(ep->start, ep->toksz);
94754ba9607SSascha Wildner 		}
94854ba9607SSascha Wildner 		parent = parent->parent;
94954ba9607SSascha Wildner 		if (tok == EQN_TOK_BRACE_CLOSE &&
95054ba9607SSascha Wildner 		    (parent->type == EQN_PILE ||
95154ba9607SSascha Wildner 		     parent->type == EQN_MATRIX))
95254ba9607SSascha Wildner 			parent = parent->parent;
95354ba9607SSascha Wildner 		/* Close out any "singleton" lists. */
95454ba9607SSascha Wildner 		while (parent->type == EQN_LIST &&
95554ba9607SSascha Wildner 		    parent->expectargs == 1 &&
95654ba9607SSascha Wildner 		    parent->args == 1)
95754ba9607SSascha Wildner 			parent = parent->parent;
95854ba9607SSascha Wildner 		break;
95954ba9607SSascha Wildner 	case EQN_TOK_BRACE_OPEN:
96054ba9607SSascha Wildner 	case EQN_TOK_LEFT:
96154ba9607SSascha Wildner 		/*
96254ba9607SSascha Wildner 		 * If we already have something in the stack and we're
96354ba9607SSascha Wildner 		 * in an expression, then rewind til we're not any more
96454ba9607SSascha Wildner 		 * (just like with the text node).
96554ba9607SSascha Wildner 		 */
96654ba9607SSascha Wildner 		while (parent->args == parent->expectargs)
96754ba9607SSascha Wildner 			parent = parent->parent;
96854ba9607SSascha Wildner 		if (EQN_TOK_LEFT == tok &&
96954ba9607SSascha Wildner 		    eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
97054ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
97154ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
97254ba9607SSascha Wildner 			break;
97354ba9607SSascha Wildner 		}
97454ba9607SSascha Wildner 		parent = eqn_box_alloc(ep, parent);
97554ba9607SSascha Wildner 		parent->type = EQN_LIST;
97654ba9607SSascha Wildner 		if (EQN_TOK_LEFT == tok) {
97754ba9607SSascha Wildner 			if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
97854ba9607SSascha Wildner 				parent->left = mandoc_strdup("\\[lc]");
97954ba9607SSascha Wildner 			else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
98054ba9607SSascha Wildner 				parent->left = mandoc_strdup("\\[lf]");
98154ba9607SSascha Wildner 			else
98254ba9607SSascha Wildner 				parent->left =
98354ba9607SSascha Wildner 				    mandoc_strndup(ep->start, ep->toksz);
98454ba9607SSascha Wildner 		}
98554ba9607SSascha Wildner 		break;
98654ba9607SSascha Wildner 	case EQN_TOK_PILE:
98754ba9607SSascha Wildner 	case EQN_TOK_LPILE:
98854ba9607SSascha Wildner 	case EQN_TOK_RPILE:
98954ba9607SSascha Wildner 	case EQN_TOK_CPILE:
99054ba9607SSascha Wildner 	case EQN_TOK_CCOL:
99154ba9607SSascha Wildner 	case EQN_TOK_LCOL:
99254ba9607SSascha Wildner 	case EQN_TOK_RCOL:
99354ba9607SSascha Wildner 		while (parent->args == parent->expectargs)
99454ba9607SSascha Wildner 			parent = parent->parent;
99554ba9607SSascha Wildner 		parent = eqn_box_alloc(ep, parent);
99654ba9607SSascha Wildner 		parent->type = EQN_PILE;
99754ba9607SSascha Wildner 		parent->expectargs = 1;
99854ba9607SSascha Wildner 		break;
99954ba9607SSascha Wildner 	case EQN_TOK_ABOVE:
100054ba9607SSascha Wildner 		for (cur = parent; cur != NULL; cur = cur->parent)
100154ba9607SSascha Wildner 			if (cur->type == EQN_PILE)
100254ba9607SSascha Wildner 				break;
100354ba9607SSascha Wildner 		if (cur == NULL) {
100454ba9607SSascha Wildner 			mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line,
100554ba9607SSascha Wildner 			    ep->node->pos, "%s", eqn_toks[tok]);
100654ba9607SSascha Wildner 			break;
100754ba9607SSascha Wildner 		}
100854ba9607SSascha Wildner 		parent = eqn_box_alloc(ep, cur);
100954ba9607SSascha Wildner 		parent->type = EQN_LIST;
101054ba9607SSascha Wildner 		break;
101154ba9607SSascha Wildner 	case EQN_TOK_MATRIX:
101254ba9607SSascha Wildner 		while (parent->args == parent->expectargs)
101354ba9607SSascha Wildner 			parent = parent->parent;
101454ba9607SSascha Wildner 		parent = eqn_box_alloc(ep, parent);
101554ba9607SSascha Wildner 		parent->type = EQN_MATRIX;
101654ba9607SSascha Wildner 		parent->expectargs = 1;
101754ba9607SSascha Wildner 		break;
101854ba9607SSascha Wildner 	case EQN_TOK_EOF:
101954ba9607SSascha Wildner 		return;
102054ba9607SSascha Wildner 	case EQN_TOK__MAX:
102154ba9607SSascha Wildner 	case EQN_TOK_FUNC:
102254ba9607SSascha Wildner 	case EQN_TOK_QUOTED:
102354ba9607SSascha Wildner 	case EQN_TOK_SYM:
102454ba9607SSascha Wildner 		p = ep->start;
102554ba9607SSascha Wildner 		assert(p != NULL);
102654ba9607SSascha Wildner 		/*
102754ba9607SSascha Wildner 		 * If we already have something in the stack and we're
102854ba9607SSascha Wildner 		 * in an expression, then rewind til we're not any more.
102954ba9607SSascha Wildner 		 */
103054ba9607SSascha Wildner 		while (parent->args == parent->expectargs)
103154ba9607SSascha Wildner 			parent = parent->parent;
103254ba9607SSascha Wildner 		cur = eqn_box_alloc(ep, parent);
103354ba9607SSascha Wildner 		cur->type = EQN_TEXT;
103454ba9607SSascha Wildner 		cur->text = p;
103554ba9607SSascha Wildner 		switch (tok) {
103654ba9607SSascha Wildner 		case EQN_TOK_FUNC:
103754ba9607SSascha Wildner 			cur->font = EQNFONT_ROMAN;
103854ba9607SSascha Wildner 			break;
103954ba9607SSascha Wildner 		case EQN_TOK_QUOTED:
104054ba9607SSascha Wildner 			if (cur->font == EQNFONT_NONE)
104154ba9607SSascha Wildner 				cur->font = EQNFONT_ITALIC;
104254ba9607SSascha Wildner 			break;
104354ba9607SSascha Wildner 		case EQN_TOK_SYM:
104454ba9607SSascha Wildner 			break;
104554ba9607SSascha Wildner 		default:
104654ba9607SSascha Wildner 			if (cur->font != EQNFONT_NONE || *p == '\0')
104754ba9607SSascha Wildner 				break;
104854ba9607SSascha Wildner 			cpn = p - 1;
104954ba9607SSascha Wildner 			ccln = CCL_LET;
105054ba9607SSascha Wildner 			split = NULL;
105154ba9607SSascha Wildner 			for (;;) {
105254ba9607SSascha Wildner 				/* Advance to next character. */
105354ba9607SSascha Wildner 				cp = cpn++;
105454ba9607SSascha Wildner 				ccl = ccln;
105554ba9607SSascha Wildner 				ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
105654ba9607SSascha Wildner 				    isdigit((unsigned char)*cpn) ||
105754ba9607SSascha Wildner 				    (*cpn == '.' && (ccl == CCL_DIG ||
105854ba9607SSascha Wildner 				     isdigit((unsigned char)cpn[1]))) ?
105954ba9607SSascha Wildner 				    CCL_DIG : CCL_PUN;
106054ba9607SSascha Wildner 				/* No boundary before first character. */
106154ba9607SSascha Wildner 				if (cp < p)
106254ba9607SSascha Wildner 					continue;
106354ba9607SSascha Wildner 				cur->font = ccl == CCL_LET ?
106454ba9607SSascha Wildner 				    EQNFONT_ITALIC : EQNFONT_ROMAN;
106554ba9607SSascha Wildner 				if (*cp == '\\')
106654ba9607SSascha Wildner 					mandoc_escape(&cpn, NULL, NULL);
106754ba9607SSascha Wildner 				/* No boundary after last character. */
106854ba9607SSascha Wildner 				if (*cpn == '\0')
106954ba9607SSascha Wildner 					break;
107054ba9607SSascha Wildner 				if (ccln == ccl && *cp != ',' && *cpn != ',')
107154ba9607SSascha Wildner 					continue;
107254ba9607SSascha Wildner 				/* Boundary found, split the text. */
107354ba9607SSascha Wildner 				if (parent->args == parent->expectargs) {
107454ba9607SSascha Wildner 					/* Remove the text from the tree. */
107554ba9607SSascha Wildner 					if (cur->prev == NULL)
107654ba9607SSascha Wildner 						parent->first = cur->next;
107754ba9607SSascha Wildner 					else
107854ba9607SSascha Wildner 						cur->prev->next = NULL;
107954ba9607SSascha Wildner 					parent->last = cur->prev;
108054ba9607SSascha Wildner 					parent->args--;
108154ba9607SSascha Wildner 					/* Set up a list instead. */
108254ba9607SSascha Wildner 					split = eqn_box_alloc(ep, parent);
108354ba9607SSascha Wildner 					split->type = EQN_LIST;
108454ba9607SSascha Wildner 					/* Insert the word into the list. */
108554ba9607SSascha Wildner 					split->first = split->last = cur;
108654ba9607SSascha Wildner 					cur->parent = split;
108754ba9607SSascha Wildner 					cur->prev = NULL;
108854ba9607SSascha Wildner 					parent = split;
108954ba9607SSascha Wildner 				}
109054ba9607SSascha Wildner 				/* Append a new text box. */
109154ba9607SSascha Wildner 				nbox = eqn_box_alloc(ep, parent);
109254ba9607SSascha Wildner 				nbox->type = EQN_TEXT;
109354ba9607SSascha Wildner 				nbox->text = mandoc_strdup(cpn);
109454ba9607SSascha Wildner 				/* Truncate the old box. */
109554ba9607SSascha Wildner 				p = mandoc_strndup(cur->text,
109654ba9607SSascha Wildner 				    cpn - cur->text);
109754ba9607SSascha Wildner 				free(cur->text);
109854ba9607SSascha Wildner 				cur->text = p;
109954ba9607SSascha Wildner 				/* Setup to process the new box. */
110054ba9607SSascha Wildner 				cur = nbox;
110154ba9607SSascha Wildner 				p = nbox->text;
110254ba9607SSascha Wildner 				cpn = p - 1;
110354ba9607SSascha Wildner 				ccln = CCL_LET;
110454ba9607SSascha Wildner 			}
110554ba9607SSascha Wildner 			if (split != NULL)
110654ba9607SSascha Wildner 				parent = split->parent;
110754ba9607SSascha Wildner 			break;
110854ba9607SSascha Wildner 		}
110954ba9607SSascha Wildner 		break;
111054ba9607SSascha Wildner 	default:
111154ba9607SSascha Wildner 		abort();
111254ba9607SSascha Wildner 	}
111354ba9607SSascha Wildner 	goto next_tok;
111436342e81SSascha Wildner }
111536342e81SSascha Wildner 
111654ba9607SSascha Wildner void
eqn_free(struct eqn_node * p)111754ba9607SSascha Wildner eqn_free(struct eqn_node *p)
111836342e81SSascha Wildner {
111936342e81SSascha Wildner 	int		 i;
112036342e81SSascha Wildner 
112154ba9607SSascha Wildner 	if (p == NULL)
112254ba9607SSascha Wildner 		return;
112336342e81SSascha Wildner 
112454ba9607SSascha Wildner 	for (i = 0; i < (int)p->defsz; i++) {
112554ba9607SSascha Wildner 		free(p->defs[i].key);
112654ba9607SSascha Wildner 		free(p->defs[i].val);
112754ba9607SSascha Wildner 	}
112854ba9607SSascha Wildner 
112954ba9607SSascha Wildner 	free(p->data);
113054ba9607SSascha Wildner 	free(p->defs);
113154ba9607SSascha Wildner 	free(p);
113236342e81SSascha Wildner }
1133