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