xref: /illumos-gate/usr/src/cmd/mandoc/eqn.c (revision c66b8046543352459a11a51501b628d1c98a8c44)
1*c66b8046SYuri Pankov /*	$Id: eqn.c,v 1.78 2017/07/15 16:26:17 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3260e9a87SYuri Pankov  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*c66b8046SYuri Pankov  * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #include "config.h"
19260e9a87SYuri Pankov 
20260e9a87SYuri Pankov #include <sys/types.h>
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
23*c66b8046SYuri Pankov #include <ctype.h>
2495c635efSGarrett D'Amore #include <limits.h>
2595c635efSGarrett D'Amore #include <stdio.h>
2695c635efSGarrett D'Amore #include <stdlib.h>
2795c635efSGarrett D'Amore #include <string.h>
2895c635efSGarrett D'Amore #include <time.h>
2995c635efSGarrett D'Amore 
30260e9a87SYuri Pankov #include "mandoc_aux.h"
31*c66b8046SYuri Pankov #include "mandoc.h"
32*c66b8046SYuri Pankov #include "roff.h"
3395c635efSGarrett D'Amore #include "libmandoc.h"
3495c635efSGarrett D'Amore #include "libroff.h"
3595c635efSGarrett D'Amore 
3695c635efSGarrett D'Amore #define	EQN_NEST_MAX	 128 /* maximum nesting of defines */
37260e9a87SYuri Pankov #define	STRNEQ(p1, sz1, p2, sz2) \
38260e9a87SYuri Pankov 	((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
3995c635efSGarrett D'Amore 
40260e9a87SYuri Pankov enum	eqn_tok {
41260e9a87SYuri Pankov 	EQN_TOK_DYAD = 0,
42260e9a87SYuri Pankov 	EQN_TOK_VEC,
43260e9a87SYuri Pankov 	EQN_TOK_UNDER,
44260e9a87SYuri Pankov 	EQN_TOK_BAR,
45260e9a87SYuri Pankov 	EQN_TOK_TILDE,
46260e9a87SYuri Pankov 	EQN_TOK_HAT,
47260e9a87SYuri Pankov 	EQN_TOK_DOT,
48260e9a87SYuri Pankov 	EQN_TOK_DOTDOT,
49260e9a87SYuri Pankov 	EQN_TOK_FWD,
50260e9a87SYuri Pankov 	EQN_TOK_BACK,
51260e9a87SYuri Pankov 	EQN_TOK_DOWN,
52260e9a87SYuri Pankov 	EQN_TOK_UP,
53260e9a87SYuri Pankov 	EQN_TOK_FAT,
54260e9a87SYuri Pankov 	EQN_TOK_ROMAN,
55260e9a87SYuri Pankov 	EQN_TOK_ITALIC,
56260e9a87SYuri Pankov 	EQN_TOK_BOLD,
57260e9a87SYuri Pankov 	EQN_TOK_SIZE,
58260e9a87SYuri Pankov 	EQN_TOK_SUB,
59260e9a87SYuri Pankov 	EQN_TOK_SUP,
60260e9a87SYuri Pankov 	EQN_TOK_SQRT,
61260e9a87SYuri Pankov 	EQN_TOK_OVER,
62260e9a87SYuri Pankov 	EQN_TOK_FROM,
63260e9a87SYuri Pankov 	EQN_TOK_TO,
64260e9a87SYuri Pankov 	EQN_TOK_BRACE_OPEN,
65260e9a87SYuri Pankov 	EQN_TOK_BRACE_CLOSE,
66260e9a87SYuri Pankov 	EQN_TOK_GSIZE,
67260e9a87SYuri Pankov 	EQN_TOK_GFONT,
68260e9a87SYuri Pankov 	EQN_TOK_MARK,
69260e9a87SYuri Pankov 	EQN_TOK_LINEUP,
70260e9a87SYuri Pankov 	EQN_TOK_LEFT,
71260e9a87SYuri Pankov 	EQN_TOK_RIGHT,
72260e9a87SYuri Pankov 	EQN_TOK_PILE,
73260e9a87SYuri Pankov 	EQN_TOK_LPILE,
74260e9a87SYuri Pankov 	EQN_TOK_RPILE,
75260e9a87SYuri Pankov 	EQN_TOK_CPILE,
76260e9a87SYuri Pankov 	EQN_TOK_MATRIX,
77260e9a87SYuri Pankov 	EQN_TOK_CCOL,
78260e9a87SYuri Pankov 	EQN_TOK_LCOL,
79260e9a87SYuri Pankov 	EQN_TOK_RCOL,
80260e9a87SYuri Pankov 	EQN_TOK_DELIM,
81260e9a87SYuri Pankov 	EQN_TOK_DEFINE,
82260e9a87SYuri Pankov 	EQN_TOK_TDEFINE,
83260e9a87SYuri Pankov 	EQN_TOK_NDEFINE,
84260e9a87SYuri Pankov 	EQN_TOK_UNDEF,
85260e9a87SYuri Pankov 	EQN_TOK_ABOVE,
86*c66b8046SYuri Pankov 	EQN_TOK__MAX,
87*c66b8046SYuri Pankov 	EQN_TOK_FUNC,
88*c66b8046SYuri Pankov 	EQN_TOK_QUOTED,
89*c66b8046SYuri Pankov 	EQN_TOK_SYM,
90*c66b8046SYuri Pankov 	EQN_TOK_EOF
91260e9a87SYuri Pankov };
92260e9a87SYuri Pankov 
93260e9a87SYuri Pankov static	const char *eqn_toks[EQN_TOK__MAX] = {
94260e9a87SYuri Pankov 	"dyad", /* EQN_TOK_DYAD */
95260e9a87SYuri Pankov 	"vec", /* EQN_TOK_VEC */
96260e9a87SYuri Pankov 	"under", /* EQN_TOK_UNDER */
97260e9a87SYuri Pankov 	"bar", /* EQN_TOK_BAR */
98260e9a87SYuri Pankov 	"tilde", /* EQN_TOK_TILDE */
99260e9a87SYuri Pankov 	"hat", /* EQN_TOK_HAT */
100260e9a87SYuri Pankov 	"dot", /* EQN_TOK_DOT */
101260e9a87SYuri Pankov 	"dotdot", /* EQN_TOK_DOTDOT */
102260e9a87SYuri Pankov 	"fwd", /* EQN_TOK_FWD * */
103260e9a87SYuri Pankov 	"back", /* EQN_TOK_BACK */
104260e9a87SYuri Pankov 	"down", /* EQN_TOK_DOWN */
105260e9a87SYuri Pankov 	"up", /* EQN_TOK_UP */
106260e9a87SYuri Pankov 	"fat", /* EQN_TOK_FAT */
107260e9a87SYuri Pankov 	"roman", /* EQN_TOK_ROMAN */
108260e9a87SYuri Pankov 	"italic", /* EQN_TOK_ITALIC */
109260e9a87SYuri Pankov 	"bold", /* EQN_TOK_BOLD */
110260e9a87SYuri Pankov 	"size", /* EQN_TOK_SIZE */
111260e9a87SYuri Pankov 	"sub", /* EQN_TOK_SUB */
112260e9a87SYuri Pankov 	"sup", /* EQN_TOK_SUP */
113260e9a87SYuri Pankov 	"sqrt", /* EQN_TOK_SQRT */
114260e9a87SYuri Pankov 	"over", /* EQN_TOK_OVER */
115260e9a87SYuri Pankov 	"from", /* EQN_TOK_FROM */
116260e9a87SYuri Pankov 	"to", /* EQN_TOK_TO */
117260e9a87SYuri Pankov 	"{", /* EQN_TOK_BRACE_OPEN */
118260e9a87SYuri Pankov 	"}", /* EQN_TOK_BRACE_CLOSE */
119260e9a87SYuri Pankov 	"gsize", /* EQN_TOK_GSIZE */
120260e9a87SYuri Pankov 	"gfont", /* EQN_TOK_GFONT */
121260e9a87SYuri Pankov 	"mark", /* EQN_TOK_MARK */
122260e9a87SYuri Pankov 	"lineup", /* EQN_TOK_LINEUP */
123260e9a87SYuri Pankov 	"left", /* EQN_TOK_LEFT */
124260e9a87SYuri Pankov 	"right", /* EQN_TOK_RIGHT */
125260e9a87SYuri Pankov 	"pile", /* EQN_TOK_PILE */
126260e9a87SYuri Pankov 	"lpile", /* EQN_TOK_LPILE */
127260e9a87SYuri Pankov 	"rpile", /* EQN_TOK_RPILE */
128260e9a87SYuri Pankov 	"cpile", /* EQN_TOK_CPILE */
129260e9a87SYuri Pankov 	"matrix", /* EQN_TOK_MATRIX */
130260e9a87SYuri Pankov 	"ccol", /* EQN_TOK_CCOL */
131260e9a87SYuri Pankov 	"lcol", /* EQN_TOK_LCOL */
132260e9a87SYuri Pankov 	"rcol", /* EQN_TOK_RCOL */
133260e9a87SYuri Pankov 	"delim", /* EQN_TOK_DELIM */
134260e9a87SYuri Pankov 	"define", /* EQN_TOK_DEFINE */
135260e9a87SYuri Pankov 	"tdefine", /* EQN_TOK_TDEFINE */
136260e9a87SYuri Pankov 	"ndefine", /* EQN_TOK_NDEFINE */
137260e9a87SYuri Pankov 	"undef", /* EQN_TOK_UNDEF */
138260e9a87SYuri Pankov 	"above", /* EQN_TOK_ABOVE */
13995c635efSGarrett D'Amore };
14095c635efSGarrett D'Amore 
141*c66b8046SYuri Pankov static	const char *const eqn_func[] = {
142*c66b8046SYuri Pankov 	"acos",	"acsc",	"and",	"arc",	"asec",	"asin", "atan",
143*c66b8046SYuri Pankov 	"cos",	"cosh", "coth",	"csc",	"det",	"exp",	"for",
144*c66b8046SYuri Pankov 	"if",	"lim",	"ln",	"log",	"max",	"min",
145*c66b8046SYuri Pankov 	"sec",	"sin",	"sinh",	"tan",	"tanh",	"Im",	"Re",
146*c66b8046SYuri Pankov };
147*c66b8046SYuri Pankov 
14895c635efSGarrett D'Amore enum	eqn_symt {
149*c66b8046SYuri Pankov 	EQNSYM_alpha = 0,
15095c635efSGarrett D'Amore 	EQNSYM_beta,
15195c635efSGarrett D'Amore 	EQNSYM_chi,
15295c635efSGarrett D'Amore 	EQNSYM_delta,
15395c635efSGarrett D'Amore 	EQNSYM_epsilon,
15495c635efSGarrett D'Amore 	EQNSYM_eta,
15595c635efSGarrett D'Amore 	EQNSYM_gamma,
15695c635efSGarrett D'Amore 	EQNSYM_iota,
15795c635efSGarrett D'Amore 	EQNSYM_kappa,
15895c635efSGarrett D'Amore 	EQNSYM_lambda,
15995c635efSGarrett D'Amore 	EQNSYM_mu,
16095c635efSGarrett D'Amore 	EQNSYM_nu,
16195c635efSGarrett D'Amore 	EQNSYM_omega,
16295c635efSGarrett D'Amore 	EQNSYM_omicron,
16395c635efSGarrett D'Amore 	EQNSYM_phi,
16495c635efSGarrett D'Amore 	EQNSYM_pi,
16595c635efSGarrett D'Amore 	EQNSYM_ps,
16695c635efSGarrett D'Amore 	EQNSYM_rho,
16795c635efSGarrett D'Amore 	EQNSYM_sigma,
16895c635efSGarrett D'Amore 	EQNSYM_tau,
16995c635efSGarrett D'Amore 	EQNSYM_theta,
17095c635efSGarrett D'Amore 	EQNSYM_upsilon,
17195c635efSGarrett D'Amore 	EQNSYM_xi,
17295c635efSGarrett D'Amore 	EQNSYM_zeta,
17395c635efSGarrett D'Amore 	EQNSYM_DELTA,
17495c635efSGarrett D'Amore 	EQNSYM_GAMMA,
17595c635efSGarrett D'Amore 	EQNSYM_LAMBDA,
17695c635efSGarrett D'Amore 	EQNSYM_OMEGA,
17795c635efSGarrett D'Amore 	EQNSYM_PHI,
17895c635efSGarrett D'Amore 	EQNSYM_PI,
17995c635efSGarrett D'Amore 	EQNSYM_PSI,
18095c635efSGarrett D'Amore 	EQNSYM_SIGMA,
18195c635efSGarrett D'Amore 	EQNSYM_THETA,
18295c635efSGarrett D'Amore 	EQNSYM_UPSILON,
18395c635efSGarrett D'Amore 	EQNSYM_XI,
18495c635efSGarrett D'Amore 	EQNSYM_inter,
18595c635efSGarrett D'Amore 	EQNSYM_union,
18695c635efSGarrett D'Amore 	EQNSYM_prod,
18795c635efSGarrett D'Amore 	EQNSYM_int,
18895c635efSGarrett D'Amore 	EQNSYM_sum,
18995c635efSGarrett D'Amore 	EQNSYM_grad,
19095c635efSGarrett D'Amore 	EQNSYM_del,
19195c635efSGarrett D'Amore 	EQNSYM_times,
19295c635efSGarrett D'Amore 	EQNSYM_cdot,
19395c635efSGarrett D'Amore 	EQNSYM_nothing,
19495c635efSGarrett D'Amore 	EQNSYM_approx,
19595c635efSGarrett D'Amore 	EQNSYM_prime,
19695c635efSGarrett D'Amore 	EQNSYM_half,
19795c635efSGarrett D'Amore 	EQNSYM_partial,
19895c635efSGarrett D'Amore 	EQNSYM_inf,
19995c635efSGarrett D'Amore 	EQNSYM_muchgreat,
20095c635efSGarrett D'Amore 	EQNSYM_muchless,
20195c635efSGarrett D'Amore 	EQNSYM_larrow,
20295c635efSGarrett D'Amore 	EQNSYM_rarrow,
20395c635efSGarrett D'Amore 	EQNSYM_pm,
20495c635efSGarrett D'Amore 	EQNSYM_nequal,
20595c635efSGarrett D'Amore 	EQNSYM_equiv,
20695c635efSGarrett D'Amore 	EQNSYM_lessequal,
20795c635efSGarrett D'Amore 	EQNSYM_moreequal,
208260e9a87SYuri Pankov 	EQNSYM_minus,
20995c635efSGarrett D'Amore 	EQNSYM__MAX
21095c635efSGarrett D'Amore };
21195c635efSGarrett D'Amore 
21295c635efSGarrett D'Amore struct	eqnsym {
213260e9a87SYuri Pankov 	const char	*str;
21495c635efSGarrett D'Amore 	const char	*sym;
21595c635efSGarrett D'Amore };
21695c635efSGarrett D'Amore 
21795c635efSGarrett D'Amore static	const struct eqnsym eqnsyms[EQNSYM__MAX] = {
218260e9a87SYuri Pankov 	{ "alpha", "*a" }, /* EQNSYM_alpha */
219260e9a87SYuri Pankov 	{ "beta", "*b" }, /* EQNSYM_beta */
220260e9a87SYuri Pankov 	{ "chi", "*x" }, /* EQNSYM_chi */
221260e9a87SYuri Pankov 	{ "delta", "*d" }, /* EQNSYM_delta */
222260e9a87SYuri Pankov 	{ "epsilon", "*e" }, /* EQNSYM_epsilon */
223260e9a87SYuri Pankov 	{ "eta", "*y" }, /* EQNSYM_eta */
224260e9a87SYuri Pankov 	{ "gamma", "*g" }, /* EQNSYM_gamma */
225260e9a87SYuri Pankov 	{ "iota", "*i" }, /* EQNSYM_iota */
226260e9a87SYuri Pankov 	{ "kappa", "*k" }, /* EQNSYM_kappa */
227260e9a87SYuri Pankov 	{ "lambda", "*l" }, /* EQNSYM_lambda */
228260e9a87SYuri Pankov 	{ "mu", "*m" }, /* EQNSYM_mu */
229260e9a87SYuri Pankov 	{ "nu", "*n" }, /* EQNSYM_nu */
230260e9a87SYuri Pankov 	{ "omega", "*w" }, /* EQNSYM_omega */
231260e9a87SYuri Pankov 	{ "omicron", "*o" }, /* EQNSYM_omicron */
232260e9a87SYuri Pankov 	{ "phi", "*f" }, /* EQNSYM_phi */
233260e9a87SYuri Pankov 	{ "pi", "*p" }, /* EQNSYM_pi */
234260e9a87SYuri Pankov 	{ "psi", "*q" }, /* EQNSYM_psi */
235260e9a87SYuri Pankov 	{ "rho", "*r" }, /* EQNSYM_rho */
236260e9a87SYuri Pankov 	{ "sigma", "*s" }, /* EQNSYM_sigma */
237260e9a87SYuri Pankov 	{ "tau", "*t" }, /* EQNSYM_tau */
238260e9a87SYuri Pankov 	{ "theta", "*h" }, /* EQNSYM_theta */
239260e9a87SYuri Pankov 	{ "upsilon", "*u" }, /* EQNSYM_upsilon */
240260e9a87SYuri Pankov 	{ "xi", "*c" }, /* EQNSYM_xi */
241260e9a87SYuri Pankov 	{ "zeta", "*z" }, /* EQNSYM_zeta */
242260e9a87SYuri Pankov 	{ "DELTA", "*D" }, /* EQNSYM_DELTA */
243260e9a87SYuri Pankov 	{ "GAMMA", "*G" }, /* EQNSYM_GAMMA */
244260e9a87SYuri Pankov 	{ "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
245260e9a87SYuri Pankov 	{ "OMEGA", "*W" }, /* EQNSYM_OMEGA */
246260e9a87SYuri Pankov 	{ "PHI", "*F" }, /* EQNSYM_PHI */
247260e9a87SYuri Pankov 	{ "PI", "*P" }, /* EQNSYM_PI */
248260e9a87SYuri Pankov 	{ "PSI", "*Q" }, /* EQNSYM_PSI */
249260e9a87SYuri Pankov 	{ "SIGMA", "*S" }, /* EQNSYM_SIGMA */
250260e9a87SYuri Pankov 	{ "THETA", "*H" }, /* EQNSYM_THETA */
251260e9a87SYuri Pankov 	{ "UPSILON", "*U" }, /* EQNSYM_UPSILON */
252260e9a87SYuri Pankov 	{ "XI", "*C" }, /* EQNSYM_XI */
253260e9a87SYuri Pankov 	{ "inter", "ca" }, /* EQNSYM_inter */
254260e9a87SYuri Pankov 	{ "union", "cu" }, /* EQNSYM_union */
255260e9a87SYuri Pankov 	{ "prod", "product" }, /* EQNSYM_prod */
256260e9a87SYuri Pankov 	{ "int", "integral" }, /* EQNSYM_int */
257260e9a87SYuri Pankov 	{ "sum", "sum" }, /* EQNSYM_sum */
258260e9a87SYuri Pankov 	{ "grad", "gr" }, /* EQNSYM_grad */
259260e9a87SYuri Pankov 	{ "del", "gr" }, /* EQNSYM_del */
260260e9a87SYuri Pankov 	{ "times", "mu" }, /* EQNSYM_times */
261260e9a87SYuri Pankov 	{ "cdot", "pc" }, /* EQNSYM_cdot */
262260e9a87SYuri Pankov 	{ "nothing", "&" }, /* EQNSYM_nothing */
263260e9a87SYuri Pankov 	{ "approx", "~~" }, /* EQNSYM_approx */
264260e9a87SYuri Pankov 	{ "prime", "fm" }, /* EQNSYM_prime */
265260e9a87SYuri Pankov 	{ "half", "12" }, /* EQNSYM_half */
266260e9a87SYuri Pankov 	{ "partial", "pd" }, /* EQNSYM_partial */
267260e9a87SYuri Pankov 	{ "inf", "if" }, /* EQNSYM_inf */
268260e9a87SYuri Pankov 	{ ">>", ">>" }, /* EQNSYM_muchgreat */
269260e9a87SYuri Pankov 	{ "<<", "<<" }, /* EQNSYM_muchless */
270260e9a87SYuri Pankov 	{ "<-", "<-" }, /* EQNSYM_larrow */
271260e9a87SYuri Pankov 	{ "->", "->" }, /* EQNSYM_rarrow */
272260e9a87SYuri Pankov 	{ "+-", "+-" }, /* EQNSYM_pm */
273260e9a87SYuri Pankov 	{ "!=", "!=" }, /* EQNSYM_nequal */
274260e9a87SYuri Pankov 	{ "==", "==" }, /* EQNSYM_equiv */
275260e9a87SYuri Pankov 	{ "<=", "<=" }, /* EQNSYM_lessequal */
276260e9a87SYuri Pankov 	{ ">=", ">=" }, /* EQNSYM_moreequal */
277260e9a87SYuri Pankov 	{ "-", "mi" }, /* EQNSYM_minus */
27895c635efSGarrett D'Amore };
27995c635efSGarrett D'Amore 
280*c66b8046SYuri Pankov enum	parse_mode {
281*c66b8046SYuri Pankov 	MODE_QUOTED,
282*c66b8046SYuri Pankov 	MODE_NOSUB,
283*c66b8046SYuri Pankov 	MODE_SUB,
284*c66b8046SYuri Pankov 	MODE_TOK
285*c66b8046SYuri Pankov };
286*c66b8046SYuri Pankov 
287260e9a87SYuri Pankov static	struct eqn_box	*eqn_box_alloc(struct eqn_node *, struct eqn_box *);
288260e9a87SYuri Pankov static	struct eqn_box	*eqn_box_makebinary(struct eqn_node *,
289*c66b8046SYuri Pankov 				struct eqn_box *);
290260e9a87SYuri Pankov static	void		 eqn_def(struct eqn_node *);
291*c66b8046SYuri Pankov static	struct eqn_def	*eqn_def_find(struct eqn_node *);
292260e9a87SYuri Pankov static	void		 eqn_delim(struct eqn_node *);
293*c66b8046SYuri Pankov static	enum eqn_tok	 eqn_next(struct eqn_node *, enum parse_mode);
294260e9a87SYuri Pankov static	void		 eqn_undef(struct eqn_node *);
295260e9a87SYuri Pankov 
296260e9a87SYuri Pankov 
29795c635efSGarrett D'Amore struct eqn_node *
298*c66b8046SYuri Pankov eqn_alloc(struct mparse *parse)
29995c635efSGarrett D'Amore {
300*c66b8046SYuri Pankov 	struct eqn_node *ep;
30195c635efSGarrett D'Amore 
302*c66b8046SYuri Pankov 	ep = mandoc_calloc(1, sizeof(*ep));
303*c66b8046SYuri Pankov 	ep->parse = parse;
304*c66b8046SYuri Pankov 	ep->gsize = EQN_DEFSIZE;
305*c66b8046SYuri Pankov 	return ep;
306*c66b8046SYuri Pankov }
30795c635efSGarrett D'Amore 
308*c66b8046SYuri Pankov void
309*c66b8046SYuri Pankov eqn_reset(struct eqn_node *ep)
310*c66b8046SYuri Pankov {
311*c66b8046SYuri Pankov 	free(ep->data);
312*c66b8046SYuri Pankov 	ep->data = ep->start = ep->end = NULL;
313*c66b8046SYuri Pankov 	ep->sz = ep->toksz = 0;
314*c66b8046SYuri Pankov }
31595c635efSGarrett D'Amore 
316*c66b8046SYuri Pankov void
317*c66b8046SYuri Pankov eqn_read(struct eqn_node *ep, const char *p)
318*c66b8046SYuri Pankov {
319*c66b8046SYuri Pankov 	char		*cp;
320*c66b8046SYuri Pankov 
321*c66b8046SYuri Pankov 	if (ep->data == NULL) {
322*c66b8046SYuri Pankov 		ep->sz = strlen(p);
323*c66b8046SYuri Pankov 		ep->data = mandoc_strdup(p);
324*c66b8046SYuri Pankov 	} else {
325*c66b8046SYuri Pankov 		ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
326*c66b8046SYuri Pankov 		free(ep->data);
327*c66b8046SYuri Pankov 		ep->data = cp;
328*c66b8046SYuri Pankov 	}
329*c66b8046SYuri Pankov 	ep->sz += 1;
33095c635efSGarrett D'Amore }
33195c635efSGarrett D'Amore 
332260e9a87SYuri Pankov /*
333260e9a87SYuri Pankov  * Find the key "key" of the give size within our eqn-defined values.
334260e9a87SYuri Pankov  */
335260e9a87SYuri Pankov static struct eqn_def *
336*c66b8046SYuri Pankov eqn_def_find(struct eqn_node *ep)
33795c635efSGarrett D'Amore {
33895c635efSGarrett D'Amore 	int		 i;
33995c635efSGarrett D'Amore 
340260e9a87SYuri Pankov 	for (i = 0; i < (int)ep->defsz; i++)
341260e9a87SYuri Pankov 		if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
342*c66b8046SYuri Pankov 		    ep->defs[i].keysz, ep->start, ep->toksz))
343371584c2SYuri Pankov 			return &ep->defs[i];
34495c635efSGarrett D'Amore 
345371584c2SYuri Pankov 	return NULL;
34695c635efSGarrett D'Amore }
34795c635efSGarrett D'Amore 
348260e9a87SYuri Pankov /*
349*c66b8046SYuri Pankov  * Parse a token from the input text.  The modes are:
350*c66b8046SYuri Pankov  * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
351*c66b8046SYuri Pankov  *   before its next occurence.  Do not interpret the token in any
352*c66b8046SYuri Pankov  *   way and return EQN_TOK_QUOTED.  All other modes behave like
353*c66b8046SYuri Pankov  *   MODE_QUOTED when *ep->start is '"'.
354*c66b8046SYuri Pankov  * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
355*c66b8046SYuri Pankov  *   otherwise, it ends before the next whitespace or brace.
356*c66b8046SYuri Pankov  *   Do not interpret the token and return EQN_TOK__MAX.
357*c66b8046SYuri Pankov  * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
358*c66b8046SYuri Pankov  *   alias created with define.  If it is an alias, replace it with
359*c66b8046SYuri Pankov  *   its string value and reparse.
360*c66b8046SYuri Pankov  * MODE_TOK: Like MODE_SUB, but also check the token against the list
361*c66b8046SYuri Pankov  *   of tokens, and if there is a match, return that token.  Otherwise,
362*c66b8046SYuri Pankov  *   if the token matches a symbol, return EQN_TOK_SYM; if it matches
363*c66b8046SYuri Pankov  *   a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX.  Except for
364*c66b8046SYuri Pankov  *   a token match, *ep->start is set to an allocated string that the
365*c66b8046SYuri Pankov  *   caller is expected to free.
366*c66b8046SYuri Pankov  * All modes skip whitespace following the end of the token.
367260e9a87SYuri Pankov  */
368260e9a87SYuri Pankov static enum eqn_tok
369*c66b8046SYuri Pankov eqn_next(struct eqn_node *ep, enum parse_mode mode)
370260e9a87SYuri Pankov {
371*c66b8046SYuri Pankov 	static int	 last_len, lim;
372260e9a87SYuri Pankov 
373*c66b8046SYuri Pankov 	struct eqn_def	*def;
374*c66b8046SYuri Pankov 	size_t		 start;
375*c66b8046SYuri Pankov 	int		 diff, i, quoted;
376*c66b8046SYuri Pankov 	enum eqn_tok	 tok;
377260e9a87SYuri Pankov 
378*c66b8046SYuri Pankov 	/*
379*c66b8046SYuri Pankov 	 * Reset the recursion counter after advancing
380*c66b8046SYuri Pankov 	 * beyond the end of the previous substitution.
381*c66b8046SYuri Pankov 	 */
382*c66b8046SYuri Pankov 	if (ep->end - ep->data >= last_len)
383*c66b8046SYuri Pankov 		lim = 0;
384260e9a87SYuri Pankov 
385*c66b8046SYuri Pankov 	ep->start = ep->end;
386*c66b8046SYuri Pankov 	quoted = mode == MODE_QUOTED;
387*c66b8046SYuri Pankov 	for (;;) {
388*c66b8046SYuri Pankov 		switch (*ep->start) {
389*c66b8046SYuri Pankov 		case '\0':
390*c66b8046SYuri Pankov 			ep->toksz = 0;
391371584c2SYuri Pankov 			return EQN_TOK_EOF;
392*c66b8046SYuri Pankov 		case '"':
393*c66b8046SYuri Pankov 			quoted = 1;
394*c66b8046SYuri Pankov 			break;
395*c66b8046SYuri Pankov 		default:
396*c66b8046SYuri Pankov 			break;
397*c66b8046SYuri Pankov 		}
398260e9a87SYuri Pankov 		if (quoted) {
399*c66b8046SYuri Pankov 			ep->end = strchr(ep->start + 1, *ep->start);
400*c66b8046SYuri Pankov 			ep->start++;  /* Skip opening quote. */
401*c66b8046SYuri Pankov 			if (ep->end == NULL) {
402*c66b8046SYuri Pankov 				mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,
403*c66b8046SYuri Pankov 				    ep->node->line, ep->node->pos, NULL);
404*c66b8046SYuri Pankov 				ep->end = strchr(ep->start, '\0');
405*c66b8046SYuri Pankov 			}
406*c66b8046SYuri Pankov 		} else {
407*c66b8046SYuri Pankov 			ep->end = ep->start + 1;
408*c66b8046SYuri Pankov 			if (*ep->start != '{' && *ep->start != '}')
409*c66b8046SYuri Pankov 				ep->end += strcspn(ep->end, " ^~\"{}\t");
410*c66b8046SYuri Pankov 		}
411*c66b8046SYuri Pankov 		ep->toksz = ep->end - ep->start;
412*c66b8046SYuri Pankov 		if (quoted && *ep->end != '\0')
413*c66b8046SYuri Pankov 			ep->end++;  /* Skip closing quote. */
414*c66b8046SYuri Pankov 		while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
415*c66b8046SYuri Pankov 			ep->end++;
416*c66b8046SYuri Pankov 		if (quoted)  /* Cannot return, may have to strndup. */
417*c66b8046SYuri Pankov 			break;
418*c66b8046SYuri Pankov 		if (mode == MODE_NOSUB)
419*c66b8046SYuri Pankov 			return EQN_TOK__MAX;
420*c66b8046SYuri Pankov 		if ((def = eqn_def_find(ep)) == NULL)
421*c66b8046SYuri Pankov 			break;
422*c66b8046SYuri Pankov 		if (++lim > EQN_NEST_MAX) {
423*c66b8046SYuri Pankov 			mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,
424*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, NULL);
425*c66b8046SYuri Pankov 			return EQN_TOK_EOF;
426*c66b8046SYuri Pankov 		}
427*c66b8046SYuri Pankov 
428*c66b8046SYuri Pankov 		/* Replace a defined name with its string value. */
429*c66b8046SYuri Pankov 		if ((diff = def->valsz - ep->toksz) > 0) {
430*c66b8046SYuri Pankov 			start = ep->start - ep->data;
431*c66b8046SYuri Pankov 			ep->sz += diff;
432*c66b8046SYuri Pankov 			ep->data = mandoc_realloc(ep->data, ep->sz + 1);
433*c66b8046SYuri Pankov 			ep->start = ep->data + start;
434*c66b8046SYuri Pankov 		}
435*c66b8046SYuri Pankov 		if (diff)
436*c66b8046SYuri Pankov 			memmove(ep->start + def->valsz, ep->start + ep->toksz,
437*c66b8046SYuri Pankov 			    strlen(ep->start + ep->toksz) + 1);
438*c66b8046SYuri Pankov 		memcpy(ep->start, def->val, def->valsz);
439*c66b8046SYuri Pankov 		last_len = ep->start - ep->data + def->valsz;
440*c66b8046SYuri Pankov 	}
441*c66b8046SYuri Pankov 	if (mode != MODE_TOK)
442*c66b8046SYuri Pankov 		return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
443*c66b8046SYuri Pankov 	if (quoted) {
444*c66b8046SYuri Pankov 		ep->start = mandoc_strndup(ep->start, ep->toksz);
445*c66b8046SYuri Pankov 		return EQN_TOK_QUOTED;
446*c66b8046SYuri Pankov 	}
447*c66b8046SYuri Pankov 	for (tok = 0; tok < EQN_TOK__MAX; tok++)
448*c66b8046SYuri Pankov 		if (STRNEQ(ep->start, ep->toksz,
449*c66b8046SYuri Pankov 		    eqn_toks[tok], strlen(eqn_toks[tok])))
450*c66b8046SYuri Pankov 			return tok;
451*c66b8046SYuri Pankov 
452*c66b8046SYuri Pankov 	for (i = 0; i < EQNSYM__MAX; i++) {
453*c66b8046SYuri Pankov 		if (STRNEQ(ep->start, ep->toksz,
454*c66b8046SYuri Pankov 		    eqnsyms[i].str, strlen(eqnsyms[i].str))) {
455*c66b8046SYuri Pankov 			mandoc_asprintf(&ep->start,
456*c66b8046SYuri Pankov 			    "\\[%s]", eqnsyms[i].sym);
457*c66b8046SYuri Pankov 			return EQN_TOK_SYM;
458*c66b8046SYuri Pankov 		}
459*c66b8046SYuri Pankov 	}
460*c66b8046SYuri Pankov 	ep->start = mandoc_strndup(ep->start, ep->toksz);
461*c66b8046SYuri Pankov 	for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
462*c66b8046SYuri Pankov 		if (STRNEQ(ep->start, ep->toksz,
463*c66b8046SYuri Pankov 		    eqn_func[i], strlen(eqn_func[i])))
464*c66b8046SYuri Pankov 			return EQN_TOK_FUNC;
465371584c2SYuri Pankov 	return EQN_TOK__MAX;
466260e9a87SYuri Pankov }
467260e9a87SYuri Pankov 
468*c66b8046SYuri Pankov void
469260e9a87SYuri Pankov eqn_box_free(struct eqn_box *bp)
47095c635efSGarrett D'Amore {
47195c635efSGarrett D'Amore 
472260e9a87SYuri Pankov 	if (bp->first)
473260e9a87SYuri Pankov 		eqn_box_free(bp->first);
474260e9a87SYuri Pankov 	if (bp->next)
475260e9a87SYuri Pankov 		eqn_box_free(bp->next);
47695c635efSGarrett D'Amore 
477260e9a87SYuri Pankov 	free(bp->text);
478260e9a87SYuri Pankov 	free(bp->left);
479260e9a87SYuri Pankov 	free(bp->right);
480260e9a87SYuri Pankov 	free(bp->top);
481260e9a87SYuri Pankov 	free(bp->bottom);
482260e9a87SYuri Pankov 	free(bp);
48395c635efSGarrett D'Amore }
48495c635efSGarrett D'Amore 
485260e9a87SYuri Pankov /*
486260e9a87SYuri Pankov  * Allocate a box as the last child of the parent node.
487260e9a87SYuri Pankov  */
488260e9a87SYuri Pankov static struct eqn_box *
489260e9a87SYuri Pankov eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
490260e9a87SYuri Pankov {
491260e9a87SYuri Pankov 	struct eqn_box	*bp;
492260e9a87SYuri Pankov 
493260e9a87SYuri Pankov 	bp = mandoc_calloc(1, sizeof(struct eqn_box));
494260e9a87SYuri Pankov 	bp->parent = parent;
495260e9a87SYuri Pankov 	bp->parent->args++;
496260e9a87SYuri Pankov 	bp->expectargs = UINT_MAX;
497*c66b8046SYuri Pankov 	bp->font = bp->parent->font;
498260e9a87SYuri Pankov 	bp->size = ep->gsize;
499260e9a87SYuri Pankov 
500260e9a87SYuri Pankov 	if (NULL != parent->first) {
501260e9a87SYuri Pankov 		parent->last->next = bp;
502260e9a87SYuri Pankov 		bp->prev = parent->last;
503260e9a87SYuri Pankov 	} else
504260e9a87SYuri Pankov 		parent->first = bp;
505260e9a87SYuri Pankov 
506260e9a87SYuri Pankov 	parent->last = bp;
507371584c2SYuri Pankov 	return bp;
508260e9a87SYuri Pankov }
509260e9a87SYuri Pankov 
510260e9a87SYuri Pankov /*
511260e9a87SYuri Pankov  * Reparent the current last node (of the current parent) under a new
512260e9a87SYuri Pankov  * EQN_SUBEXPR as the first element.
513260e9a87SYuri Pankov  * Then return the new parent.
514260e9a87SYuri Pankov  * The new EQN_SUBEXPR will have a two-child limit.
515260e9a87SYuri Pankov  */
516260e9a87SYuri Pankov static struct eqn_box *
517*c66b8046SYuri Pankov eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent)
518260e9a87SYuri Pankov {
519260e9a87SYuri Pankov 	struct eqn_box	*b, *newb;
520260e9a87SYuri Pankov 
521260e9a87SYuri Pankov 	assert(NULL != parent->last);
522260e9a87SYuri Pankov 	b = parent->last;
523260e9a87SYuri Pankov 	if (parent->last == parent->first)
524260e9a87SYuri Pankov 		parent->first = NULL;
525260e9a87SYuri Pankov 	parent->args--;
526260e9a87SYuri Pankov 	parent->last = b->prev;
527260e9a87SYuri Pankov 	b->prev = NULL;
528260e9a87SYuri Pankov 	newb = eqn_box_alloc(ep, parent);
529260e9a87SYuri Pankov 	newb->type = EQN_SUBEXPR;
530260e9a87SYuri Pankov 	newb->expectargs = 2;
531260e9a87SYuri Pankov 	newb->args = 1;
532260e9a87SYuri Pankov 	newb->first = newb->last = b;
533260e9a87SYuri Pankov 	newb->first->next = NULL;
534260e9a87SYuri Pankov 	b->parent = newb;
535371584c2SYuri Pankov 	return newb;
536260e9a87SYuri Pankov }
537260e9a87SYuri Pankov 
538260e9a87SYuri Pankov /*
539260e9a87SYuri Pankov  * Parse the "delim" control statement.
540260e9a87SYuri Pankov  */
541260e9a87SYuri Pankov static void
542260e9a87SYuri Pankov eqn_delim(struct eqn_node *ep)
543260e9a87SYuri Pankov {
544*c66b8046SYuri Pankov 	if (ep->end[0] == '\0' || ep->end[1] == '\0') {
545260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
546*c66b8046SYuri Pankov 		    ep->node->line, ep->node->pos, "delim");
547*c66b8046SYuri Pankov 		if (ep->end[0] != '\0')
548*c66b8046SYuri Pankov 			ep->end++;
549*c66b8046SYuri Pankov 	} else if (strncmp(ep->end, "off", 3) == 0) {
550260e9a87SYuri Pankov 		ep->delim = 0;
551*c66b8046SYuri Pankov 		ep->end += 3;
552*c66b8046SYuri Pankov 	} else if (strncmp(ep->end, "on", 2) == 0) {
553260e9a87SYuri Pankov 		if (ep->odelim && ep->cdelim)
554260e9a87SYuri Pankov 			ep->delim = 1;
555*c66b8046SYuri Pankov 		ep->end += 2;
556*c66b8046SYuri Pankov 	} else {
557*c66b8046SYuri Pankov 		ep->odelim = *ep->end++;
558*c66b8046SYuri Pankov 		ep->cdelim = *ep->end++;
559260e9a87SYuri Pankov 		ep->delim = 1;
560260e9a87SYuri Pankov 	}
561260e9a87SYuri Pankov }
562260e9a87SYuri Pankov 
563260e9a87SYuri Pankov /*
564260e9a87SYuri Pankov  * Undefine a previously-defined string.
565260e9a87SYuri Pankov  */
566260e9a87SYuri Pankov static void
567260e9a87SYuri Pankov eqn_undef(struct eqn_node *ep)
568260e9a87SYuri Pankov {
569260e9a87SYuri Pankov 	struct eqn_def	*def;
570260e9a87SYuri Pankov 
571*c66b8046SYuri Pankov 	if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
572260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
573*c66b8046SYuri Pankov 		    ep->node->line, ep->node->pos, "undef");
574260e9a87SYuri Pankov 		return;
575260e9a87SYuri Pankov 	}
576*c66b8046SYuri Pankov 	if ((def = eqn_def_find(ep)) == NULL)
577260e9a87SYuri Pankov 		return;
578260e9a87SYuri Pankov 	free(def->key);
579260e9a87SYuri Pankov 	free(def->val);
580260e9a87SYuri Pankov 	def->key = def->val = NULL;
581260e9a87SYuri Pankov 	def->keysz = def->valsz = 0;
582260e9a87SYuri Pankov }
583260e9a87SYuri Pankov 
584260e9a87SYuri Pankov static void
585260e9a87SYuri Pankov eqn_def(struct eqn_node *ep)
58695c635efSGarrett D'Amore {
58795c635efSGarrett D'Amore 	struct eqn_def	*def;
58895c635efSGarrett D'Amore 	int		 i;
58995c635efSGarrett D'Amore 
590*c66b8046SYuri Pankov 	if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
591260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
592*c66b8046SYuri Pankov 		    ep->node->line, ep->node->pos, "define");
593260e9a87SYuri Pankov 		return;
59495c635efSGarrett D'Amore 	}
59595c635efSGarrett D'Amore 
59695c635efSGarrett D'Amore 	/*
59795c635efSGarrett D'Amore 	 * Search for a key that already exists.
59895c635efSGarrett D'Amore 	 * Create a new key if none is found.
59995c635efSGarrett D'Amore 	 */
600*c66b8046SYuri Pankov 	if ((def = eqn_def_find(ep)) == NULL) {
60195c635efSGarrett D'Amore 		/* Find holes in string array. */
60295c635efSGarrett D'Amore 		for (i = 0; i < (int)ep->defsz; i++)
60395c635efSGarrett D'Amore 			if (0 == ep->defs[i].keysz)
60495c635efSGarrett D'Amore 				break;
60595c635efSGarrett D'Amore 
60695c635efSGarrett D'Amore 		if (i == (int)ep->defsz) {
60795c635efSGarrett D'Amore 			ep->defsz++;
608260e9a87SYuri Pankov 			ep->defs = mandoc_reallocarray(ep->defs,
609260e9a87SYuri Pankov 			    ep->defsz, sizeof(struct eqn_def));
61095c635efSGarrett D'Amore 			ep->defs[i].key = ep->defs[i].val = NULL;
61195c635efSGarrett D'Amore 		}
61295c635efSGarrett D'Amore 
613260e9a87SYuri Pankov 		def = ep->defs + i;
614260e9a87SYuri Pankov 		free(def->key);
615*c66b8046SYuri Pankov 		def->key = mandoc_strndup(ep->start, ep->toksz);
616*c66b8046SYuri Pankov 		def->keysz = ep->toksz;
61795c635efSGarrett D'Amore 	}
61895c635efSGarrett D'Amore 
619*c66b8046SYuri Pankov 	if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
620260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,
621*c66b8046SYuri Pankov 		    ep->node->line, ep->node->pos, "define %s", def->key);
622260e9a87SYuri Pankov 		free(def->key);
623260e9a87SYuri Pankov 		free(def->val);
624260e9a87SYuri Pankov 		def->key = def->val = NULL;
625260e9a87SYuri Pankov 		def->keysz = def->valsz = 0;
626260e9a87SYuri Pankov 		return;
62795c635efSGarrett D'Amore 	}
628260e9a87SYuri Pankov 	free(def->val);
629*c66b8046SYuri Pankov 	def->val = mandoc_strndup(ep->start, ep->toksz);
630*c66b8046SYuri Pankov 	def->valsz = ep->toksz;
63195c635efSGarrett D'Amore }
63295c635efSGarrett D'Amore 
633*c66b8046SYuri Pankov void
634*c66b8046SYuri Pankov eqn_parse(struct eqn_node *ep)
63595c635efSGarrett D'Amore {
636*c66b8046SYuri Pankov 	struct eqn_box	*cur, *nbox, *parent, *split;
637*c66b8046SYuri Pankov 	const char	*cp, *cpn;
638260e9a87SYuri Pankov 	char		*p;
639*c66b8046SYuri Pankov 	enum eqn_tok	 tok;
640*c66b8046SYuri Pankov 	enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
641260e9a87SYuri Pankov 	int		 size;
64295c635efSGarrett D'Amore 
643*c66b8046SYuri Pankov 	parent = ep->node->eqn;
644260e9a87SYuri Pankov 	assert(parent != NULL);
645260e9a87SYuri Pankov 
646260e9a87SYuri Pankov 	/*
647260e9a87SYuri Pankov 	 * Empty equation.
648260e9a87SYuri Pankov 	 * Do not add it to the high-level syntax tree.
649260e9a87SYuri Pankov 	 */
650260e9a87SYuri Pankov 
651260e9a87SYuri Pankov 	if (ep->data == NULL)
652*c66b8046SYuri Pankov 		return;
653*c66b8046SYuri Pankov 
654*c66b8046SYuri Pankov 	ep->start = ep->end = ep->data + strspn(ep->data, " ^~");
655260e9a87SYuri Pankov 
656260e9a87SYuri Pankov next_tok:
657*c66b8046SYuri Pankov 	tok = eqn_next(ep, MODE_TOK);
658260e9a87SYuri Pankov 	switch (tok) {
659*c66b8046SYuri Pankov 	case EQN_TOK_UNDEF:
660260e9a87SYuri Pankov 		eqn_undef(ep);
661260e9a87SYuri Pankov 		break;
662*c66b8046SYuri Pankov 	case EQN_TOK_NDEFINE:
663*c66b8046SYuri Pankov 	case EQN_TOK_DEFINE:
664260e9a87SYuri Pankov 		eqn_def(ep);
665260e9a87SYuri Pankov 		break;
666*c66b8046SYuri Pankov 	case EQN_TOK_TDEFINE:
667*c66b8046SYuri Pankov 		if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
668*c66b8046SYuri Pankov 		    eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
669260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
670*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, "tdefine");
671260e9a87SYuri Pankov 		break;
672*c66b8046SYuri Pankov 	case EQN_TOK_DELIM:
673260e9a87SYuri Pankov 		eqn_delim(ep);
674260e9a87SYuri Pankov 		break;
675*c66b8046SYuri Pankov 	case EQN_TOK_GFONT:
676*c66b8046SYuri Pankov 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
677260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
678*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
679260e9a87SYuri Pankov 		break;
680*c66b8046SYuri Pankov 	case EQN_TOK_MARK:
681*c66b8046SYuri Pankov 	case EQN_TOK_LINEUP:
682260e9a87SYuri Pankov 		/* Ignore these. */
683260e9a87SYuri Pankov 		break;
684*c66b8046SYuri Pankov 	case EQN_TOK_DYAD:
685*c66b8046SYuri Pankov 	case EQN_TOK_VEC:
686*c66b8046SYuri Pankov 	case EQN_TOK_UNDER:
687*c66b8046SYuri Pankov 	case EQN_TOK_BAR:
688*c66b8046SYuri Pankov 	case EQN_TOK_TILDE:
689*c66b8046SYuri Pankov 	case EQN_TOK_HAT:
690*c66b8046SYuri Pankov 	case EQN_TOK_DOT:
691*c66b8046SYuri Pankov 	case EQN_TOK_DOTDOT:
692260e9a87SYuri Pankov 		if (parent->last == NULL) {
693260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
694*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
695260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
696260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
697260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
69895c635efSGarrett D'Amore 		}
699*c66b8046SYuri Pankov 		parent = eqn_box_makebinary(ep, parent);
700*c66b8046SYuri Pankov 		parent->type = EQN_LIST;
701260e9a87SYuri Pankov 		parent->expectargs = 1;
702*c66b8046SYuri Pankov 		parent->font = EQNFONT_ROMAN;
703260e9a87SYuri Pankov 		switch (tok) {
704*c66b8046SYuri Pankov 		case EQN_TOK_DOTDOT:
705*c66b8046SYuri Pankov 			parent->top = mandoc_strdup("\\[ad]");
706260e9a87SYuri Pankov 			break;
707*c66b8046SYuri Pankov 		case EQN_TOK_VEC:
708*c66b8046SYuri Pankov 			parent->top = mandoc_strdup("\\[->]");
709260e9a87SYuri Pankov 			break;
710*c66b8046SYuri Pankov 		case EQN_TOK_DYAD:
711*c66b8046SYuri Pankov 			parent->top = mandoc_strdup("\\[<>]");
712260e9a87SYuri Pankov 			break;
713*c66b8046SYuri Pankov 		case EQN_TOK_TILDE:
714*c66b8046SYuri Pankov 			parent->top = mandoc_strdup("\\[a~]");
715260e9a87SYuri Pankov 			break;
716*c66b8046SYuri Pankov 		case EQN_TOK_UNDER:
717*c66b8046SYuri Pankov 			parent->bottom = mandoc_strdup("\\[ul]");
718260e9a87SYuri Pankov 			break;
719*c66b8046SYuri Pankov 		case EQN_TOK_BAR:
720*c66b8046SYuri Pankov 			parent->top = mandoc_strdup("\\[rn]");
721260e9a87SYuri Pankov 			break;
722*c66b8046SYuri Pankov 		case EQN_TOK_DOT:
723*c66b8046SYuri Pankov 			parent->top = mandoc_strdup("\\[a.]");
724260e9a87SYuri Pankov 			break;
725*c66b8046SYuri Pankov 		case EQN_TOK_HAT:
726*c66b8046SYuri Pankov 			parent->top = mandoc_strdup("\\[ha]");
727260e9a87SYuri Pankov 			break;
728260e9a87SYuri Pankov 		default:
729260e9a87SYuri Pankov 			abort();
730260e9a87SYuri Pankov 		}
731260e9a87SYuri Pankov 		parent = parent->parent;
732260e9a87SYuri Pankov 		break;
733*c66b8046SYuri Pankov 	case EQN_TOK_FWD:
734*c66b8046SYuri Pankov 	case EQN_TOK_BACK:
735*c66b8046SYuri Pankov 	case EQN_TOK_DOWN:
736*c66b8046SYuri Pankov 	case EQN_TOK_UP:
737*c66b8046SYuri Pankov 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
738260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
739*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
740260e9a87SYuri Pankov 		break;
741*c66b8046SYuri Pankov 	case EQN_TOK_FAT:
742*c66b8046SYuri Pankov 	case EQN_TOK_ROMAN:
743*c66b8046SYuri Pankov 	case EQN_TOK_ITALIC:
744*c66b8046SYuri Pankov 	case EQN_TOK_BOLD:
745260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
746260e9a87SYuri Pankov 			parent = parent->parent;
747260e9a87SYuri Pankov 		/*
748260e9a87SYuri Pankov 		 * These values apply to the next word or sequence of
749260e9a87SYuri Pankov 		 * words; thus, we mark that we'll have a child with
750260e9a87SYuri Pankov 		 * exactly one of those.
751260e9a87SYuri Pankov 		 */
752260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
753*c66b8046SYuri Pankov 		parent->type = EQN_LIST;
754260e9a87SYuri Pankov 		parent->expectargs = 1;
755260e9a87SYuri Pankov 		switch (tok) {
756*c66b8046SYuri Pankov 		case EQN_TOK_FAT:
757260e9a87SYuri Pankov 			parent->font = EQNFONT_FAT;
758260e9a87SYuri Pankov 			break;
759*c66b8046SYuri Pankov 		case EQN_TOK_ROMAN:
760260e9a87SYuri Pankov 			parent->font = EQNFONT_ROMAN;
761260e9a87SYuri Pankov 			break;
762*c66b8046SYuri Pankov 		case EQN_TOK_ITALIC:
763260e9a87SYuri Pankov 			parent->font = EQNFONT_ITALIC;
764260e9a87SYuri Pankov 			break;
765*c66b8046SYuri Pankov 		case EQN_TOK_BOLD:
766260e9a87SYuri Pankov 			parent->font = EQNFONT_BOLD;
767260e9a87SYuri Pankov 			break;
768260e9a87SYuri Pankov 		default:
769260e9a87SYuri Pankov 			abort();
770260e9a87SYuri Pankov 		}
771260e9a87SYuri Pankov 		break;
772*c66b8046SYuri Pankov 	case EQN_TOK_SIZE:
773*c66b8046SYuri Pankov 	case EQN_TOK_GSIZE:
774260e9a87SYuri Pankov 		/* Accept two values: integral size and a single. */
775*c66b8046SYuri Pankov 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
776260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
777*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
778260e9a87SYuri Pankov 			break;
779260e9a87SYuri Pankov 		}
780*c66b8046SYuri Pankov 		size = mandoc_strntoi(ep->start, ep->toksz, 10);
781260e9a87SYuri Pankov 		if (-1 == size) {
782260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
783*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
784260e9a87SYuri Pankov 			break;
785260e9a87SYuri Pankov 		}
786260e9a87SYuri Pankov 		if (EQN_TOK_GSIZE == tok) {
787260e9a87SYuri Pankov 			ep->gsize = size;
788260e9a87SYuri Pankov 			break;
789260e9a87SYuri Pankov 		}
790*c66b8046SYuri Pankov 		while (parent->args == parent->expectargs)
791*c66b8046SYuri Pankov 			parent = parent->parent;
792260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
793*c66b8046SYuri Pankov 		parent->type = EQN_LIST;
794260e9a87SYuri Pankov 		parent->expectargs = 1;
795260e9a87SYuri Pankov 		parent->size = size;
796260e9a87SYuri Pankov 		break;
797*c66b8046SYuri Pankov 	case EQN_TOK_FROM:
798*c66b8046SYuri Pankov 	case EQN_TOK_TO:
799*c66b8046SYuri Pankov 	case EQN_TOK_SUB:
800*c66b8046SYuri Pankov 	case EQN_TOK_SUP:
801260e9a87SYuri Pankov 		/*
802260e9a87SYuri Pankov 		 * We have a left-right-associative expression.
803260e9a87SYuri Pankov 		 * Repivot under a positional node, open a child scope
804260e9a87SYuri Pankov 		 * and keep on reading.
805260e9a87SYuri Pankov 		 */
806260e9a87SYuri Pankov 		if (parent->last == NULL) {
807260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
808*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
809260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
810260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
811260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
812260e9a87SYuri Pankov 		}
813*c66b8046SYuri Pankov 		while (parent->expectargs == 1 && parent->args == 1)
814*c66b8046SYuri Pankov 			parent = parent->parent;
815*c66b8046SYuri Pankov 		if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO)  {
816*c66b8046SYuri Pankov 			for (cur = parent; cur != NULL; cur = cur->parent)
817*c66b8046SYuri Pankov 				if (cur->pos == EQNPOS_SUB ||
818*c66b8046SYuri Pankov 				    cur->pos == EQNPOS_SUP ||
819*c66b8046SYuri Pankov 				    cur->pos == EQNPOS_SUBSUP ||
820*c66b8046SYuri Pankov 				    cur->pos == EQNPOS_SQRT ||
821*c66b8046SYuri Pankov 				    cur->pos == EQNPOS_OVER)
822*c66b8046SYuri Pankov 					break;
823*c66b8046SYuri Pankov 			if (cur != NULL)
824*c66b8046SYuri Pankov 				parent = cur->parent;
825*c66b8046SYuri Pankov 		}
826*c66b8046SYuri Pankov 		if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
827260e9a87SYuri Pankov 			parent->expectargs = 3;
828260e9a87SYuri Pankov 			parent->pos = EQNPOS_SUBSUP;
829260e9a87SYuri Pankov 			break;
830260e9a87SYuri Pankov 		}
831*c66b8046SYuri Pankov 		if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
832260e9a87SYuri Pankov 			parent->expectargs = 3;
833260e9a87SYuri Pankov 			parent->pos = EQNPOS_FROMTO;
834260e9a87SYuri Pankov 			break;
835260e9a87SYuri Pankov 		}
836*c66b8046SYuri Pankov 		parent = eqn_box_makebinary(ep, parent);
837260e9a87SYuri Pankov 		switch (tok) {
838*c66b8046SYuri Pankov 		case EQN_TOK_FROM:
839*c66b8046SYuri Pankov 			parent->pos = EQNPOS_FROM;
840260e9a87SYuri Pankov 			break;
841*c66b8046SYuri Pankov 		case EQN_TOK_TO:
842*c66b8046SYuri Pankov 			parent->pos = EQNPOS_TO;
843260e9a87SYuri Pankov 			break;
844*c66b8046SYuri Pankov 		case EQN_TOK_SUP:
845*c66b8046SYuri Pankov 			parent->pos = EQNPOS_SUP;
846260e9a87SYuri Pankov 			break;
847*c66b8046SYuri Pankov 		case EQN_TOK_SUB:
848*c66b8046SYuri Pankov 			parent->pos = EQNPOS_SUB;
849260e9a87SYuri Pankov 			break;
850260e9a87SYuri Pankov 		default:
851260e9a87SYuri Pankov 			abort();
852260e9a87SYuri Pankov 		}
853260e9a87SYuri Pankov 		break;
854*c66b8046SYuri Pankov 	case EQN_TOK_SQRT:
855260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
856260e9a87SYuri Pankov 			parent = parent->parent;
857260e9a87SYuri Pankov 		/*
858260e9a87SYuri Pankov 		 * Accept a left-right-associative set of arguments just
859260e9a87SYuri Pankov 		 * like sub and sup and friends but without rebalancing
860260e9a87SYuri Pankov 		 * under a pivot.
861260e9a87SYuri Pankov 		 */
862260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
863260e9a87SYuri Pankov 		parent->type = EQN_SUBEXPR;
864260e9a87SYuri Pankov 		parent->pos = EQNPOS_SQRT;
865260e9a87SYuri Pankov 		parent->expectargs = 1;
866260e9a87SYuri Pankov 		break;
867*c66b8046SYuri Pankov 	case EQN_TOK_OVER:
868260e9a87SYuri Pankov 		/*
869260e9a87SYuri Pankov 		 * We have a right-left-associative fraction.
870260e9a87SYuri Pankov 		 * Close out anything that's currently open, then
871260e9a87SYuri Pankov 		 * rebalance and continue reading.
872260e9a87SYuri Pankov 		 */
873260e9a87SYuri Pankov 		if (parent->last == NULL) {
874260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
875*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
876260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
877260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
878260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
879260e9a87SYuri Pankov 		}
880*c66b8046SYuri Pankov 		while (parent->args == parent->expectargs)
881*c66b8046SYuri Pankov 			parent = parent->parent;
882260e9a87SYuri Pankov 		while (EQN_SUBEXPR == parent->type)
883260e9a87SYuri Pankov 			parent = parent->parent;
884*c66b8046SYuri Pankov 		parent = eqn_box_makebinary(ep, parent);
885*c66b8046SYuri Pankov 		parent->pos = EQNPOS_OVER;
886260e9a87SYuri Pankov 		break;
887*c66b8046SYuri Pankov 	case EQN_TOK_RIGHT:
888*c66b8046SYuri Pankov 	case EQN_TOK_BRACE_CLOSE:
889260e9a87SYuri Pankov 		/*
890260e9a87SYuri Pankov 		 * Close out the existing brace.
891260e9a87SYuri Pankov 		 * FIXME: this is a shitty sentinel: we should really
892260e9a87SYuri Pankov 		 * have a native EQN_BRACE type or whatnot.
893260e9a87SYuri Pankov 		 */
894260e9a87SYuri Pankov 		for (cur = parent; cur != NULL; cur = cur->parent)
895260e9a87SYuri Pankov 			if (cur->type == EQN_LIST &&
896*c66b8046SYuri Pankov 			    cur->expectargs > 1 &&
897260e9a87SYuri Pankov 			    (tok == EQN_TOK_BRACE_CLOSE ||
898260e9a87SYuri Pankov 			     cur->left != NULL))
899260e9a87SYuri Pankov 				break;
900260e9a87SYuri Pankov 		if (cur == NULL) {
901260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
902*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
903260e9a87SYuri Pankov 			break;
904260e9a87SYuri Pankov 		}
905260e9a87SYuri Pankov 		parent = cur;
906260e9a87SYuri Pankov 		if (EQN_TOK_RIGHT == tok) {
907*c66b8046SYuri Pankov 			if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
908260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_REQ_EMPTY,
909*c66b8046SYuri Pankov 				    ep->parse, ep->node->line,
910*c66b8046SYuri Pankov 				    ep->node->pos, eqn_toks[tok]);
911260e9a87SYuri Pankov 				break;
912260e9a87SYuri Pankov 			}
913260e9a87SYuri Pankov 			/* Handling depends on right/left. */
914*c66b8046SYuri Pankov 			if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
915*c66b8046SYuri Pankov 				parent->right = mandoc_strdup("\\[rc]");
916*c66b8046SYuri Pankov 			else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
917*c66b8046SYuri Pankov 				parent->right = mandoc_strdup("\\[rf]");
918*c66b8046SYuri Pankov 			else
919*c66b8046SYuri Pankov 				parent->right =
920*c66b8046SYuri Pankov 				    mandoc_strndup(ep->start, ep->toksz);
921260e9a87SYuri Pankov 		}
922260e9a87SYuri Pankov 		parent = parent->parent;
923371584c2SYuri Pankov 		if (tok == EQN_TOK_BRACE_CLOSE &&
924260e9a87SYuri Pankov 		    (parent->type == EQN_PILE ||
925260e9a87SYuri Pankov 		     parent->type == EQN_MATRIX))
926260e9a87SYuri Pankov 			parent = parent->parent;
927260e9a87SYuri Pankov 		/* Close out any "singleton" lists. */
928*c66b8046SYuri Pankov 		while (parent->type == EQN_LIST &&
929*c66b8046SYuri Pankov 		    parent->expectargs == 1 &&
930*c66b8046SYuri Pankov 		    parent->args == 1)
931260e9a87SYuri Pankov 			parent = parent->parent;
932260e9a87SYuri Pankov 		break;
933*c66b8046SYuri Pankov 	case EQN_TOK_BRACE_OPEN:
934*c66b8046SYuri Pankov 	case EQN_TOK_LEFT:
935260e9a87SYuri Pankov 		/*
936260e9a87SYuri Pankov 		 * If we already have something in the stack and we're
937260e9a87SYuri Pankov 		 * in an expression, then rewind til we're not any more
938260e9a87SYuri Pankov 		 * (just like with the text node).
939260e9a87SYuri Pankov 		 */
940260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
941260e9a87SYuri Pankov 			parent = parent->parent;
942260e9a87SYuri Pankov 		if (EQN_TOK_LEFT == tok &&
943*c66b8046SYuri Pankov 		    eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
944260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
945*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
946260e9a87SYuri Pankov 			break;
947260e9a87SYuri Pankov 		}
948260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
949260e9a87SYuri Pankov 		parent->type = EQN_LIST;
950260e9a87SYuri Pankov 		if (EQN_TOK_LEFT == tok) {
951*c66b8046SYuri Pankov 			if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
952*c66b8046SYuri Pankov 				parent->left = mandoc_strdup("\\[lc]");
953*c66b8046SYuri Pankov 			else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
954*c66b8046SYuri Pankov 				parent->left = mandoc_strdup("\\[lf]");
955*c66b8046SYuri Pankov 			else
956*c66b8046SYuri Pankov 				parent->left =
957*c66b8046SYuri Pankov 				    mandoc_strndup(ep->start, ep->toksz);
958260e9a87SYuri Pankov 		}
959260e9a87SYuri Pankov 		break;
960*c66b8046SYuri Pankov 	case EQN_TOK_PILE:
961*c66b8046SYuri Pankov 	case EQN_TOK_LPILE:
962*c66b8046SYuri Pankov 	case EQN_TOK_RPILE:
963*c66b8046SYuri Pankov 	case EQN_TOK_CPILE:
964*c66b8046SYuri Pankov 	case EQN_TOK_CCOL:
965*c66b8046SYuri Pankov 	case EQN_TOK_LCOL:
966*c66b8046SYuri Pankov 	case EQN_TOK_RCOL:
967260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
968260e9a87SYuri Pankov 			parent = parent->parent;
969260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
970260e9a87SYuri Pankov 		parent->type = EQN_PILE;
971260e9a87SYuri Pankov 		parent->expectargs = 1;
972260e9a87SYuri Pankov 		break;
973*c66b8046SYuri Pankov 	case EQN_TOK_ABOVE:
974260e9a87SYuri Pankov 		for (cur = parent; cur != NULL; cur = cur->parent)
975260e9a87SYuri Pankov 			if (cur->type == EQN_PILE)
976260e9a87SYuri Pankov 				break;
977260e9a87SYuri Pankov 		if (cur == NULL) {
978260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
979*c66b8046SYuri Pankov 			    ep->node->line, ep->node->pos, eqn_toks[tok]);
980260e9a87SYuri Pankov 			break;
981260e9a87SYuri Pankov 		}
982260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, cur);
983260e9a87SYuri Pankov 		parent->type = EQN_LIST;
984260e9a87SYuri Pankov 		break;
985*c66b8046SYuri Pankov 	case EQN_TOK_MATRIX:
986260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
987260e9a87SYuri Pankov 			parent = parent->parent;
988260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
989260e9a87SYuri Pankov 		parent->type = EQN_MATRIX;
990260e9a87SYuri Pankov 		parent->expectargs = 1;
991260e9a87SYuri Pankov 		break;
992*c66b8046SYuri Pankov 	case EQN_TOK_EOF:
993*c66b8046SYuri Pankov 		return;
994*c66b8046SYuri Pankov 	case EQN_TOK__MAX:
995*c66b8046SYuri Pankov 	case EQN_TOK_FUNC:
996*c66b8046SYuri Pankov 	case EQN_TOK_QUOTED:
997*c66b8046SYuri Pankov 	case EQN_TOK_SYM:
998*c66b8046SYuri Pankov 		p = ep->start;
999*c66b8046SYuri Pankov 		assert(p != NULL);
1000260e9a87SYuri Pankov 		/*
1001260e9a87SYuri Pankov 		 * If we already have something in the stack and we're
1002260e9a87SYuri Pankov 		 * in an expression, then rewind til we're not any more.
1003260e9a87SYuri Pankov 		 */
1004260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1005260e9a87SYuri Pankov 			parent = parent->parent;
1006260e9a87SYuri Pankov 		cur = eqn_box_alloc(ep, parent);
1007260e9a87SYuri Pankov 		cur->type = EQN_TEXT;
1008*c66b8046SYuri Pankov 		cur->text = p;
1009*c66b8046SYuri Pankov 		switch (tok) {
1010*c66b8046SYuri Pankov 		case EQN_TOK_FUNC:
1011*c66b8046SYuri Pankov 			cur->font = EQNFONT_ROMAN;
1012*c66b8046SYuri Pankov 			break;
1013*c66b8046SYuri Pankov 		case EQN_TOK_QUOTED:
1014*c66b8046SYuri Pankov 			if (cur->font == EQNFONT_NONE)
1015*c66b8046SYuri Pankov 				cur->font = EQNFONT_ITALIC;
1016*c66b8046SYuri Pankov 			break;
1017*c66b8046SYuri Pankov 		case EQN_TOK_SYM:
1018*c66b8046SYuri Pankov 			break;
1019*c66b8046SYuri Pankov 		default:
1020*c66b8046SYuri Pankov 			if (cur->font != EQNFONT_NONE || *p == '\0')
1021*c66b8046SYuri Pankov 				break;
1022*c66b8046SYuri Pankov 			cpn = p - 1;
1023*c66b8046SYuri Pankov 			ccln = CCL_LET;
1024*c66b8046SYuri Pankov 			split = NULL;
1025*c66b8046SYuri Pankov 			for (;;) {
1026*c66b8046SYuri Pankov 				/* Advance to next character. */
1027*c66b8046SYuri Pankov 				cp = cpn++;
1028*c66b8046SYuri Pankov 				ccl = ccln;
1029*c66b8046SYuri Pankov 				ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
1030*c66b8046SYuri Pankov 				    isdigit((unsigned char)*cpn) ||
1031*c66b8046SYuri Pankov 				    (*cpn == '.' && (ccl == CCL_DIG ||
1032*c66b8046SYuri Pankov 				     isdigit((unsigned char)cpn[1]))) ?
1033*c66b8046SYuri Pankov 				    CCL_DIG : CCL_PUN;
1034*c66b8046SYuri Pankov 				/* No boundary before first character. */
1035*c66b8046SYuri Pankov 				if (cp < p)
1036*c66b8046SYuri Pankov 					continue;
1037*c66b8046SYuri Pankov 				cur->font = ccl == CCL_LET ?
1038*c66b8046SYuri Pankov 				    EQNFONT_ITALIC : EQNFONT_ROMAN;
1039*c66b8046SYuri Pankov 				if (*cp == '\\')
1040*c66b8046SYuri Pankov 					mandoc_escape(&cpn, NULL, NULL);
1041*c66b8046SYuri Pankov 				/* No boundary after last character. */
1042*c66b8046SYuri Pankov 				if (*cpn == '\0')
1043*c66b8046SYuri Pankov 					break;
1044*c66b8046SYuri Pankov 				if (ccln == ccl && *cp != ',' && *cpn != ',')
1045*c66b8046SYuri Pankov 					continue;
1046*c66b8046SYuri Pankov 				/* Boundary found, split the text. */
1047*c66b8046SYuri Pankov 				if (parent->args == parent->expectargs) {
1048*c66b8046SYuri Pankov 					/* Remove the text from the tree. */
1049*c66b8046SYuri Pankov 					if (cur->prev == NULL)
1050*c66b8046SYuri Pankov 						parent->first = cur->next;
1051*c66b8046SYuri Pankov 					else
1052*c66b8046SYuri Pankov 						cur->prev->next = NULL;
1053*c66b8046SYuri Pankov 					parent->last = cur->prev;
1054*c66b8046SYuri Pankov 					parent->args--;
1055*c66b8046SYuri Pankov 					/* Set up a list instead. */
1056*c66b8046SYuri Pankov 					split = eqn_box_alloc(ep, parent);
1057*c66b8046SYuri Pankov 					split->type = EQN_LIST;
1058*c66b8046SYuri Pankov 					/* Insert the word into the list. */
1059*c66b8046SYuri Pankov 					split->first = split->last = cur;
1060*c66b8046SYuri Pankov 					cur->parent = split;
1061*c66b8046SYuri Pankov 					cur->prev = NULL;
1062*c66b8046SYuri Pankov 					parent = split;
1063*c66b8046SYuri Pankov 				}
1064*c66b8046SYuri Pankov 				/* Append a new text box. */
1065*c66b8046SYuri Pankov 				nbox = eqn_box_alloc(ep, parent);
1066*c66b8046SYuri Pankov 				nbox->type = EQN_TEXT;
1067*c66b8046SYuri Pankov 				nbox->text = mandoc_strdup(cpn);
1068*c66b8046SYuri Pankov 				/* Truncate the old box. */
1069*c66b8046SYuri Pankov 				p = mandoc_strndup(cur->text,
1070*c66b8046SYuri Pankov 				    cpn - cur->text);
1071*c66b8046SYuri Pankov 				free(cur->text);
1072*c66b8046SYuri Pankov 				cur->text = p;
1073*c66b8046SYuri Pankov 				/* Setup to process the new box. */
1074*c66b8046SYuri Pankov 				cur = nbox;
1075*c66b8046SYuri Pankov 				p = nbox->text;
1076*c66b8046SYuri Pankov 				cpn = p - 1;
1077*c66b8046SYuri Pankov 				ccln = CCL_LET;
1078*c66b8046SYuri Pankov 			}
1079*c66b8046SYuri Pankov 			if (split != NULL)
1080*c66b8046SYuri Pankov 				parent = split->parent;
1081260e9a87SYuri Pankov 			break;
1082260e9a87SYuri Pankov 		}
1083260e9a87SYuri Pankov 		break;
1084*c66b8046SYuri Pankov 	default:
1085*c66b8046SYuri Pankov 		abort();
1086260e9a87SYuri Pankov 	}
1087260e9a87SYuri Pankov 	goto next_tok;
1088260e9a87SYuri Pankov }
1089260e9a87SYuri Pankov 
1090260e9a87SYuri Pankov void
1091260e9a87SYuri Pankov eqn_free(struct eqn_node *p)
109295c635efSGarrett D'Amore {
109395c635efSGarrett D'Amore 	int		 i;
109495c635efSGarrett D'Amore 
1095260e9a87SYuri Pankov 	for (i = 0; i < (int)p->defsz; i++) {
1096260e9a87SYuri Pankov 		free(p->defs[i].key);
1097260e9a87SYuri Pankov 		free(p->defs[i].val);
1098260e9a87SYuri Pankov 	}
1099260e9a87SYuri Pankov 
1100260e9a87SYuri Pankov 	free(p->data);
1101260e9a87SYuri Pankov 	free(p->defs);
1102260e9a87SYuri Pankov 	free(p);
110395c635efSGarrett D'Amore }
1104