xref: /freebsd/contrib/mandoc/eqn.c (revision 7295610f5da64ab1818458ce007d9eb924496330)
1*7295610fSBaptiste Daroussin /*	$Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*7295610fSBaptiste Daroussin  * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
561d06d6bSBaptiste Daroussin  *
661d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin  *
1061d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1161d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1361d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1761d06d6bSBaptiste Daroussin  */
1861d06d6bSBaptiste Daroussin #include "config.h"
1961d06d6bSBaptiste Daroussin 
2061d06d6bSBaptiste Daroussin #include <sys/types.h>
2161d06d6bSBaptiste Daroussin 
2261d06d6bSBaptiste Daroussin #include <assert.h>
2361d06d6bSBaptiste Daroussin #include <ctype.h>
2461d06d6bSBaptiste Daroussin #include <limits.h>
2561d06d6bSBaptiste Daroussin #include <stdio.h>
2661d06d6bSBaptiste Daroussin #include <stdlib.h>
2761d06d6bSBaptiste Daroussin #include <string.h>
2861d06d6bSBaptiste Daroussin #include <time.h>
2961d06d6bSBaptiste Daroussin 
3061d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
3161d06d6bSBaptiste Daroussin #include "mandoc.h"
3261d06d6bSBaptiste Daroussin #include "roff.h"
33*7295610fSBaptiste Daroussin #include "eqn.h"
3461d06d6bSBaptiste Daroussin #include "libmandoc.h"
35*7295610fSBaptiste Daroussin #include "eqn_parse.h"
3661d06d6bSBaptiste Daroussin 
3761d06d6bSBaptiste Daroussin #define	EQN_NEST_MAX	 128 /* maximum nesting of defines */
3861d06d6bSBaptiste Daroussin #define	STRNEQ(p1, sz1, p2, sz2) \
3961d06d6bSBaptiste Daroussin 	((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
4061d06d6bSBaptiste Daroussin 
4161d06d6bSBaptiste Daroussin enum	eqn_tok {
4261d06d6bSBaptiste Daroussin 	EQN_TOK_DYAD = 0,
4361d06d6bSBaptiste Daroussin 	EQN_TOK_VEC,
4461d06d6bSBaptiste Daroussin 	EQN_TOK_UNDER,
4561d06d6bSBaptiste Daroussin 	EQN_TOK_BAR,
4661d06d6bSBaptiste Daroussin 	EQN_TOK_TILDE,
4761d06d6bSBaptiste Daroussin 	EQN_TOK_HAT,
4861d06d6bSBaptiste Daroussin 	EQN_TOK_DOT,
4961d06d6bSBaptiste Daroussin 	EQN_TOK_DOTDOT,
5061d06d6bSBaptiste Daroussin 	EQN_TOK_FWD,
5161d06d6bSBaptiste Daroussin 	EQN_TOK_BACK,
5261d06d6bSBaptiste Daroussin 	EQN_TOK_DOWN,
5361d06d6bSBaptiste Daroussin 	EQN_TOK_UP,
5461d06d6bSBaptiste Daroussin 	EQN_TOK_FAT,
5561d06d6bSBaptiste Daroussin 	EQN_TOK_ROMAN,
5661d06d6bSBaptiste Daroussin 	EQN_TOK_ITALIC,
5761d06d6bSBaptiste Daroussin 	EQN_TOK_BOLD,
5861d06d6bSBaptiste Daroussin 	EQN_TOK_SIZE,
5961d06d6bSBaptiste Daroussin 	EQN_TOK_SUB,
6061d06d6bSBaptiste Daroussin 	EQN_TOK_SUP,
6161d06d6bSBaptiste Daroussin 	EQN_TOK_SQRT,
6261d06d6bSBaptiste Daroussin 	EQN_TOK_OVER,
6361d06d6bSBaptiste Daroussin 	EQN_TOK_FROM,
6461d06d6bSBaptiste Daroussin 	EQN_TOK_TO,
6561d06d6bSBaptiste Daroussin 	EQN_TOK_BRACE_OPEN,
6661d06d6bSBaptiste Daroussin 	EQN_TOK_BRACE_CLOSE,
6761d06d6bSBaptiste Daroussin 	EQN_TOK_GSIZE,
6861d06d6bSBaptiste Daroussin 	EQN_TOK_GFONT,
6961d06d6bSBaptiste Daroussin 	EQN_TOK_MARK,
7061d06d6bSBaptiste Daroussin 	EQN_TOK_LINEUP,
7161d06d6bSBaptiste Daroussin 	EQN_TOK_LEFT,
7261d06d6bSBaptiste Daroussin 	EQN_TOK_RIGHT,
7361d06d6bSBaptiste Daroussin 	EQN_TOK_PILE,
7461d06d6bSBaptiste Daroussin 	EQN_TOK_LPILE,
7561d06d6bSBaptiste Daroussin 	EQN_TOK_RPILE,
7661d06d6bSBaptiste Daroussin 	EQN_TOK_CPILE,
7761d06d6bSBaptiste Daroussin 	EQN_TOK_MATRIX,
7861d06d6bSBaptiste Daroussin 	EQN_TOK_CCOL,
7961d06d6bSBaptiste Daroussin 	EQN_TOK_LCOL,
8061d06d6bSBaptiste Daroussin 	EQN_TOK_RCOL,
8161d06d6bSBaptiste Daroussin 	EQN_TOK_DELIM,
8261d06d6bSBaptiste Daroussin 	EQN_TOK_DEFINE,
8361d06d6bSBaptiste Daroussin 	EQN_TOK_TDEFINE,
8461d06d6bSBaptiste Daroussin 	EQN_TOK_NDEFINE,
8561d06d6bSBaptiste Daroussin 	EQN_TOK_UNDEF,
8661d06d6bSBaptiste Daroussin 	EQN_TOK_ABOVE,
8761d06d6bSBaptiste Daroussin 	EQN_TOK__MAX,
8861d06d6bSBaptiste Daroussin 	EQN_TOK_FUNC,
8961d06d6bSBaptiste Daroussin 	EQN_TOK_QUOTED,
9061d06d6bSBaptiste Daroussin 	EQN_TOK_SYM,
9161d06d6bSBaptiste Daroussin 	EQN_TOK_EOF
9261d06d6bSBaptiste Daroussin };
9361d06d6bSBaptiste Daroussin 
9461d06d6bSBaptiste Daroussin static	const char *eqn_toks[EQN_TOK__MAX] = {
9561d06d6bSBaptiste Daroussin 	"dyad", /* EQN_TOK_DYAD */
9661d06d6bSBaptiste Daroussin 	"vec", /* EQN_TOK_VEC */
9761d06d6bSBaptiste Daroussin 	"under", /* EQN_TOK_UNDER */
9861d06d6bSBaptiste Daroussin 	"bar", /* EQN_TOK_BAR */
9961d06d6bSBaptiste Daroussin 	"tilde", /* EQN_TOK_TILDE */
10061d06d6bSBaptiste Daroussin 	"hat", /* EQN_TOK_HAT */
10161d06d6bSBaptiste Daroussin 	"dot", /* EQN_TOK_DOT */
10261d06d6bSBaptiste Daroussin 	"dotdot", /* EQN_TOK_DOTDOT */
10361d06d6bSBaptiste Daroussin 	"fwd", /* EQN_TOK_FWD * */
10461d06d6bSBaptiste Daroussin 	"back", /* EQN_TOK_BACK */
10561d06d6bSBaptiste Daroussin 	"down", /* EQN_TOK_DOWN */
10661d06d6bSBaptiste Daroussin 	"up", /* EQN_TOK_UP */
10761d06d6bSBaptiste Daroussin 	"fat", /* EQN_TOK_FAT */
10861d06d6bSBaptiste Daroussin 	"roman", /* EQN_TOK_ROMAN */
10961d06d6bSBaptiste Daroussin 	"italic", /* EQN_TOK_ITALIC */
11061d06d6bSBaptiste Daroussin 	"bold", /* EQN_TOK_BOLD */
11161d06d6bSBaptiste Daroussin 	"size", /* EQN_TOK_SIZE */
11261d06d6bSBaptiste Daroussin 	"sub", /* EQN_TOK_SUB */
11361d06d6bSBaptiste Daroussin 	"sup", /* EQN_TOK_SUP */
11461d06d6bSBaptiste Daroussin 	"sqrt", /* EQN_TOK_SQRT */
11561d06d6bSBaptiste Daroussin 	"over", /* EQN_TOK_OVER */
11661d06d6bSBaptiste Daroussin 	"from", /* EQN_TOK_FROM */
11761d06d6bSBaptiste Daroussin 	"to", /* EQN_TOK_TO */
11861d06d6bSBaptiste Daroussin 	"{", /* EQN_TOK_BRACE_OPEN */
11961d06d6bSBaptiste Daroussin 	"}", /* EQN_TOK_BRACE_CLOSE */
12061d06d6bSBaptiste Daroussin 	"gsize", /* EQN_TOK_GSIZE */
12161d06d6bSBaptiste Daroussin 	"gfont", /* EQN_TOK_GFONT */
12261d06d6bSBaptiste Daroussin 	"mark", /* EQN_TOK_MARK */
12361d06d6bSBaptiste Daroussin 	"lineup", /* EQN_TOK_LINEUP */
12461d06d6bSBaptiste Daroussin 	"left", /* EQN_TOK_LEFT */
12561d06d6bSBaptiste Daroussin 	"right", /* EQN_TOK_RIGHT */
12661d06d6bSBaptiste Daroussin 	"pile", /* EQN_TOK_PILE */
12761d06d6bSBaptiste Daroussin 	"lpile", /* EQN_TOK_LPILE */
12861d06d6bSBaptiste Daroussin 	"rpile", /* EQN_TOK_RPILE */
12961d06d6bSBaptiste Daroussin 	"cpile", /* EQN_TOK_CPILE */
13061d06d6bSBaptiste Daroussin 	"matrix", /* EQN_TOK_MATRIX */
13161d06d6bSBaptiste Daroussin 	"ccol", /* EQN_TOK_CCOL */
13261d06d6bSBaptiste Daroussin 	"lcol", /* EQN_TOK_LCOL */
13361d06d6bSBaptiste Daroussin 	"rcol", /* EQN_TOK_RCOL */
13461d06d6bSBaptiste Daroussin 	"delim", /* EQN_TOK_DELIM */
13561d06d6bSBaptiste Daroussin 	"define", /* EQN_TOK_DEFINE */
13661d06d6bSBaptiste Daroussin 	"tdefine", /* EQN_TOK_TDEFINE */
13761d06d6bSBaptiste Daroussin 	"ndefine", /* EQN_TOK_NDEFINE */
13861d06d6bSBaptiste Daroussin 	"undef", /* EQN_TOK_UNDEF */
13961d06d6bSBaptiste Daroussin 	"above", /* EQN_TOK_ABOVE */
14061d06d6bSBaptiste Daroussin };
14161d06d6bSBaptiste Daroussin 
14261d06d6bSBaptiste Daroussin static	const char *const eqn_func[] = {
14361d06d6bSBaptiste Daroussin 	"acos",	"acsc",	"and",	"arc",	"asec",	"asin", "atan",
14461d06d6bSBaptiste Daroussin 	"cos",	"cosh", "coth",	"csc",	"det",	"exp",	"for",
14561d06d6bSBaptiste Daroussin 	"if",	"lim",	"ln",	"log",	"max",	"min",
14661d06d6bSBaptiste Daroussin 	"sec",	"sin",	"sinh",	"tan",	"tanh",	"Im",	"Re",
14761d06d6bSBaptiste Daroussin };
14861d06d6bSBaptiste Daroussin 
14961d06d6bSBaptiste Daroussin enum	eqn_symt {
15061d06d6bSBaptiste Daroussin 	EQNSYM_alpha = 0,
15161d06d6bSBaptiste Daroussin 	EQNSYM_beta,
15261d06d6bSBaptiste Daroussin 	EQNSYM_chi,
15361d06d6bSBaptiste Daroussin 	EQNSYM_delta,
15461d06d6bSBaptiste Daroussin 	EQNSYM_epsilon,
15561d06d6bSBaptiste Daroussin 	EQNSYM_eta,
15661d06d6bSBaptiste Daroussin 	EQNSYM_gamma,
15761d06d6bSBaptiste Daroussin 	EQNSYM_iota,
15861d06d6bSBaptiste Daroussin 	EQNSYM_kappa,
15961d06d6bSBaptiste Daroussin 	EQNSYM_lambda,
16061d06d6bSBaptiste Daroussin 	EQNSYM_mu,
16161d06d6bSBaptiste Daroussin 	EQNSYM_nu,
16261d06d6bSBaptiste Daroussin 	EQNSYM_omega,
16361d06d6bSBaptiste Daroussin 	EQNSYM_omicron,
16461d06d6bSBaptiste Daroussin 	EQNSYM_phi,
16561d06d6bSBaptiste Daroussin 	EQNSYM_pi,
16661d06d6bSBaptiste Daroussin 	EQNSYM_ps,
16761d06d6bSBaptiste Daroussin 	EQNSYM_rho,
16861d06d6bSBaptiste Daroussin 	EQNSYM_sigma,
16961d06d6bSBaptiste Daroussin 	EQNSYM_tau,
17061d06d6bSBaptiste Daroussin 	EQNSYM_theta,
17161d06d6bSBaptiste Daroussin 	EQNSYM_upsilon,
17261d06d6bSBaptiste Daroussin 	EQNSYM_xi,
17361d06d6bSBaptiste Daroussin 	EQNSYM_zeta,
17461d06d6bSBaptiste Daroussin 	EQNSYM_DELTA,
17561d06d6bSBaptiste Daroussin 	EQNSYM_GAMMA,
17661d06d6bSBaptiste Daroussin 	EQNSYM_LAMBDA,
17761d06d6bSBaptiste Daroussin 	EQNSYM_OMEGA,
17861d06d6bSBaptiste Daroussin 	EQNSYM_PHI,
17961d06d6bSBaptiste Daroussin 	EQNSYM_PI,
18061d06d6bSBaptiste Daroussin 	EQNSYM_PSI,
18161d06d6bSBaptiste Daroussin 	EQNSYM_SIGMA,
18261d06d6bSBaptiste Daroussin 	EQNSYM_THETA,
18361d06d6bSBaptiste Daroussin 	EQNSYM_UPSILON,
18461d06d6bSBaptiste Daroussin 	EQNSYM_XI,
18561d06d6bSBaptiste Daroussin 	EQNSYM_inter,
18661d06d6bSBaptiste Daroussin 	EQNSYM_union,
18761d06d6bSBaptiste Daroussin 	EQNSYM_prod,
18861d06d6bSBaptiste Daroussin 	EQNSYM_int,
18961d06d6bSBaptiste Daroussin 	EQNSYM_sum,
19061d06d6bSBaptiste Daroussin 	EQNSYM_grad,
19161d06d6bSBaptiste Daroussin 	EQNSYM_del,
19261d06d6bSBaptiste Daroussin 	EQNSYM_times,
19361d06d6bSBaptiste Daroussin 	EQNSYM_cdot,
19461d06d6bSBaptiste Daroussin 	EQNSYM_nothing,
19561d06d6bSBaptiste Daroussin 	EQNSYM_approx,
19661d06d6bSBaptiste Daroussin 	EQNSYM_prime,
19761d06d6bSBaptiste Daroussin 	EQNSYM_half,
19861d06d6bSBaptiste Daroussin 	EQNSYM_partial,
19961d06d6bSBaptiste Daroussin 	EQNSYM_inf,
20061d06d6bSBaptiste Daroussin 	EQNSYM_muchgreat,
20161d06d6bSBaptiste Daroussin 	EQNSYM_muchless,
20261d06d6bSBaptiste Daroussin 	EQNSYM_larrow,
20361d06d6bSBaptiste Daroussin 	EQNSYM_rarrow,
20461d06d6bSBaptiste Daroussin 	EQNSYM_pm,
20561d06d6bSBaptiste Daroussin 	EQNSYM_nequal,
20661d06d6bSBaptiste Daroussin 	EQNSYM_equiv,
20761d06d6bSBaptiste Daroussin 	EQNSYM_lessequal,
20861d06d6bSBaptiste Daroussin 	EQNSYM_moreequal,
20961d06d6bSBaptiste Daroussin 	EQNSYM_minus,
21061d06d6bSBaptiste Daroussin 	EQNSYM__MAX
21161d06d6bSBaptiste Daroussin };
21261d06d6bSBaptiste Daroussin 
21361d06d6bSBaptiste Daroussin struct	eqnsym {
21461d06d6bSBaptiste Daroussin 	const char	*str;
21561d06d6bSBaptiste Daroussin 	const char	*sym;
21661d06d6bSBaptiste Daroussin };
21761d06d6bSBaptiste Daroussin 
21861d06d6bSBaptiste Daroussin static	const struct eqnsym eqnsyms[EQNSYM__MAX] = {
21961d06d6bSBaptiste Daroussin 	{ "alpha", "*a" }, /* EQNSYM_alpha */
22061d06d6bSBaptiste Daroussin 	{ "beta", "*b" }, /* EQNSYM_beta */
22161d06d6bSBaptiste Daroussin 	{ "chi", "*x" }, /* EQNSYM_chi */
22261d06d6bSBaptiste Daroussin 	{ "delta", "*d" }, /* EQNSYM_delta */
22361d06d6bSBaptiste Daroussin 	{ "epsilon", "*e" }, /* EQNSYM_epsilon */
22461d06d6bSBaptiste Daroussin 	{ "eta", "*y" }, /* EQNSYM_eta */
22561d06d6bSBaptiste Daroussin 	{ "gamma", "*g" }, /* EQNSYM_gamma */
22661d06d6bSBaptiste Daroussin 	{ "iota", "*i" }, /* EQNSYM_iota */
22761d06d6bSBaptiste Daroussin 	{ "kappa", "*k" }, /* EQNSYM_kappa */
22861d06d6bSBaptiste Daroussin 	{ "lambda", "*l" }, /* EQNSYM_lambda */
22961d06d6bSBaptiste Daroussin 	{ "mu", "*m" }, /* EQNSYM_mu */
23061d06d6bSBaptiste Daroussin 	{ "nu", "*n" }, /* EQNSYM_nu */
23161d06d6bSBaptiste Daroussin 	{ "omega", "*w" }, /* EQNSYM_omega */
23261d06d6bSBaptiste Daroussin 	{ "omicron", "*o" }, /* EQNSYM_omicron */
23361d06d6bSBaptiste Daroussin 	{ "phi", "*f" }, /* EQNSYM_phi */
23461d06d6bSBaptiste Daroussin 	{ "pi", "*p" }, /* EQNSYM_pi */
23561d06d6bSBaptiste Daroussin 	{ "psi", "*q" }, /* EQNSYM_psi */
23661d06d6bSBaptiste Daroussin 	{ "rho", "*r" }, /* EQNSYM_rho */
23761d06d6bSBaptiste Daroussin 	{ "sigma", "*s" }, /* EQNSYM_sigma */
23861d06d6bSBaptiste Daroussin 	{ "tau", "*t" }, /* EQNSYM_tau */
23961d06d6bSBaptiste Daroussin 	{ "theta", "*h" }, /* EQNSYM_theta */
24061d06d6bSBaptiste Daroussin 	{ "upsilon", "*u" }, /* EQNSYM_upsilon */
24161d06d6bSBaptiste Daroussin 	{ "xi", "*c" }, /* EQNSYM_xi */
24261d06d6bSBaptiste Daroussin 	{ "zeta", "*z" }, /* EQNSYM_zeta */
24361d06d6bSBaptiste Daroussin 	{ "DELTA", "*D" }, /* EQNSYM_DELTA */
24461d06d6bSBaptiste Daroussin 	{ "GAMMA", "*G" }, /* EQNSYM_GAMMA */
24561d06d6bSBaptiste Daroussin 	{ "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
24661d06d6bSBaptiste Daroussin 	{ "OMEGA", "*W" }, /* EQNSYM_OMEGA */
24761d06d6bSBaptiste Daroussin 	{ "PHI", "*F" }, /* EQNSYM_PHI */
24861d06d6bSBaptiste Daroussin 	{ "PI", "*P" }, /* EQNSYM_PI */
24961d06d6bSBaptiste Daroussin 	{ "PSI", "*Q" }, /* EQNSYM_PSI */
25061d06d6bSBaptiste Daroussin 	{ "SIGMA", "*S" }, /* EQNSYM_SIGMA */
25161d06d6bSBaptiste Daroussin 	{ "THETA", "*H" }, /* EQNSYM_THETA */
25261d06d6bSBaptiste Daroussin 	{ "UPSILON", "*U" }, /* EQNSYM_UPSILON */
25361d06d6bSBaptiste Daroussin 	{ "XI", "*C" }, /* EQNSYM_XI */
25461d06d6bSBaptiste Daroussin 	{ "inter", "ca" }, /* EQNSYM_inter */
25561d06d6bSBaptiste Daroussin 	{ "union", "cu" }, /* EQNSYM_union */
25661d06d6bSBaptiste Daroussin 	{ "prod", "product" }, /* EQNSYM_prod */
25761d06d6bSBaptiste Daroussin 	{ "int", "integral" }, /* EQNSYM_int */
25861d06d6bSBaptiste Daroussin 	{ "sum", "sum" }, /* EQNSYM_sum */
25961d06d6bSBaptiste Daroussin 	{ "grad", "gr" }, /* EQNSYM_grad */
26061d06d6bSBaptiste Daroussin 	{ "del", "gr" }, /* EQNSYM_del */
26161d06d6bSBaptiste Daroussin 	{ "times", "mu" }, /* EQNSYM_times */
26261d06d6bSBaptiste Daroussin 	{ "cdot", "pc" }, /* EQNSYM_cdot */
26361d06d6bSBaptiste Daroussin 	{ "nothing", "&" }, /* EQNSYM_nothing */
26461d06d6bSBaptiste Daroussin 	{ "approx", "~~" }, /* EQNSYM_approx */
26561d06d6bSBaptiste Daroussin 	{ "prime", "fm" }, /* EQNSYM_prime */
26661d06d6bSBaptiste Daroussin 	{ "half", "12" }, /* EQNSYM_half */
26761d06d6bSBaptiste Daroussin 	{ "partial", "pd" }, /* EQNSYM_partial */
26861d06d6bSBaptiste Daroussin 	{ "inf", "if" }, /* EQNSYM_inf */
26961d06d6bSBaptiste Daroussin 	{ ">>", ">>" }, /* EQNSYM_muchgreat */
27061d06d6bSBaptiste Daroussin 	{ "<<", "<<" }, /* EQNSYM_muchless */
27161d06d6bSBaptiste Daroussin 	{ "<-", "<-" }, /* EQNSYM_larrow */
27261d06d6bSBaptiste Daroussin 	{ "->", "->" }, /* EQNSYM_rarrow */
27361d06d6bSBaptiste Daroussin 	{ "+-", "+-" }, /* EQNSYM_pm */
27461d06d6bSBaptiste Daroussin 	{ "!=", "!=" }, /* EQNSYM_nequal */
27561d06d6bSBaptiste Daroussin 	{ "==", "==" }, /* EQNSYM_equiv */
27661d06d6bSBaptiste Daroussin 	{ "<=", "<=" }, /* EQNSYM_lessequal */
27761d06d6bSBaptiste Daroussin 	{ ">=", ">=" }, /* EQNSYM_moreequal */
27861d06d6bSBaptiste Daroussin 	{ "-", "mi" }, /* EQNSYM_minus */
27961d06d6bSBaptiste Daroussin };
28061d06d6bSBaptiste Daroussin 
28161d06d6bSBaptiste Daroussin enum	parse_mode {
28261d06d6bSBaptiste Daroussin 	MODE_QUOTED,
28361d06d6bSBaptiste Daroussin 	MODE_NOSUB,
28461d06d6bSBaptiste Daroussin 	MODE_SUB,
28561d06d6bSBaptiste Daroussin 	MODE_TOK
28661d06d6bSBaptiste Daroussin };
28761d06d6bSBaptiste Daroussin 
288*7295610fSBaptiste Daroussin struct	eqn_def {
289*7295610fSBaptiste Daroussin 	char		 *key;
290*7295610fSBaptiste Daroussin 	size_t		  keysz;
291*7295610fSBaptiste Daroussin 	char		 *val;
292*7295610fSBaptiste Daroussin 	size_t		  valsz;
293*7295610fSBaptiste Daroussin };
294*7295610fSBaptiste Daroussin 
29561d06d6bSBaptiste Daroussin static	struct eqn_box	*eqn_box_alloc(struct eqn_node *, struct eqn_box *);
29661d06d6bSBaptiste Daroussin static	struct eqn_box	*eqn_box_makebinary(struct eqn_node *,
29761d06d6bSBaptiste Daroussin 				struct eqn_box *);
29861d06d6bSBaptiste Daroussin static	void		 eqn_def(struct eqn_node *);
29961d06d6bSBaptiste Daroussin static	struct eqn_def	*eqn_def_find(struct eqn_node *);
30061d06d6bSBaptiste Daroussin static	void		 eqn_delim(struct eqn_node *);
30161d06d6bSBaptiste Daroussin static	enum eqn_tok	 eqn_next(struct eqn_node *, enum parse_mode);
30261d06d6bSBaptiste Daroussin static	void		 eqn_undef(struct eqn_node *);
30361d06d6bSBaptiste Daroussin 
30461d06d6bSBaptiste Daroussin 
30561d06d6bSBaptiste Daroussin struct eqn_node *
306*7295610fSBaptiste Daroussin eqn_alloc(void)
30761d06d6bSBaptiste Daroussin {
30861d06d6bSBaptiste Daroussin 	struct eqn_node *ep;
30961d06d6bSBaptiste Daroussin 
31061d06d6bSBaptiste Daroussin 	ep = mandoc_calloc(1, sizeof(*ep));
31161d06d6bSBaptiste Daroussin 	ep->gsize = EQN_DEFSIZE;
31261d06d6bSBaptiste Daroussin 	return ep;
31361d06d6bSBaptiste Daroussin }
31461d06d6bSBaptiste Daroussin 
31561d06d6bSBaptiste Daroussin void
31661d06d6bSBaptiste Daroussin eqn_reset(struct eqn_node *ep)
31761d06d6bSBaptiste Daroussin {
31861d06d6bSBaptiste Daroussin 	free(ep->data);
31961d06d6bSBaptiste Daroussin 	ep->data = ep->start = ep->end = NULL;
32061d06d6bSBaptiste Daroussin 	ep->sz = ep->toksz = 0;
32161d06d6bSBaptiste Daroussin }
32261d06d6bSBaptiste Daroussin 
32361d06d6bSBaptiste Daroussin void
32461d06d6bSBaptiste Daroussin eqn_read(struct eqn_node *ep, const char *p)
32561d06d6bSBaptiste Daroussin {
32661d06d6bSBaptiste Daroussin 	char		*cp;
32761d06d6bSBaptiste Daroussin 
32861d06d6bSBaptiste Daroussin 	if (ep->data == NULL) {
32961d06d6bSBaptiste Daroussin 		ep->sz = strlen(p);
33061d06d6bSBaptiste Daroussin 		ep->data = mandoc_strdup(p);
33161d06d6bSBaptiste Daroussin 	} else {
33261d06d6bSBaptiste Daroussin 		ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
33361d06d6bSBaptiste Daroussin 		free(ep->data);
33461d06d6bSBaptiste Daroussin 		ep->data = cp;
33561d06d6bSBaptiste Daroussin 	}
33661d06d6bSBaptiste Daroussin 	ep->sz += 1;
33761d06d6bSBaptiste Daroussin }
33861d06d6bSBaptiste Daroussin 
33961d06d6bSBaptiste Daroussin /*
34061d06d6bSBaptiste Daroussin  * Find the key "key" of the give size within our eqn-defined values.
34161d06d6bSBaptiste Daroussin  */
34261d06d6bSBaptiste Daroussin static struct eqn_def *
34361d06d6bSBaptiste Daroussin eqn_def_find(struct eqn_node *ep)
34461d06d6bSBaptiste Daroussin {
34561d06d6bSBaptiste Daroussin 	int		 i;
34661d06d6bSBaptiste Daroussin 
34761d06d6bSBaptiste Daroussin 	for (i = 0; i < (int)ep->defsz; i++)
34861d06d6bSBaptiste Daroussin 		if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
34961d06d6bSBaptiste Daroussin 		    ep->defs[i].keysz, ep->start, ep->toksz))
35061d06d6bSBaptiste Daroussin 			return &ep->defs[i];
35161d06d6bSBaptiste Daroussin 
35261d06d6bSBaptiste Daroussin 	return NULL;
35361d06d6bSBaptiste Daroussin }
35461d06d6bSBaptiste Daroussin 
35561d06d6bSBaptiste Daroussin /*
35661d06d6bSBaptiste Daroussin  * Parse a token from the input text.  The modes are:
35761d06d6bSBaptiste Daroussin  * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
35861d06d6bSBaptiste Daroussin  *   before its next occurence.  Do not interpret the token in any
35961d06d6bSBaptiste Daroussin  *   way and return EQN_TOK_QUOTED.  All other modes behave like
36061d06d6bSBaptiste Daroussin  *   MODE_QUOTED when *ep->start is '"'.
36161d06d6bSBaptiste Daroussin  * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
36261d06d6bSBaptiste Daroussin  *   otherwise, it ends before the next whitespace or brace.
36361d06d6bSBaptiste Daroussin  *   Do not interpret the token and return EQN_TOK__MAX.
36461d06d6bSBaptiste Daroussin  * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
36561d06d6bSBaptiste Daroussin  *   alias created with define.  If it is an alias, replace it with
36661d06d6bSBaptiste Daroussin  *   its string value and reparse.
36761d06d6bSBaptiste Daroussin  * MODE_TOK: Like MODE_SUB, but also check the token against the list
36861d06d6bSBaptiste Daroussin  *   of tokens, and if there is a match, return that token.  Otherwise,
36961d06d6bSBaptiste Daroussin  *   if the token matches a symbol, return EQN_TOK_SYM; if it matches
37061d06d6bSBaptiste Daroussin  *   a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX.  Except for
37161d06d6bSBaptiste Daroussin  *   a token match, *ep->start is set to an allocated string that the
37261d06d6bSBaptiste Daroussin  *   caller is expected to free.
37361d06d6bSBaptiste Daroussin  * All modes skip whitespace following the end of the token.
37461d06d6bSBaptiste Daroussin  */
37561d06d6bSBaptiste Daroussin static enum eqn_tok
37661d06d6bSBaptiste Daroussin eqn_next(struct eqn_node *ep, enum parse_mode mode)
37761d06d6bSBaptiste Daroussin {
37861d06d6bSBaptiste Daroussin 	static int	 last_len, lim;
37961d06d6bSBaptiste Daroussin 
38061d06d6bSBaptiste Daroussin 	struct eqn_def	*def;
38161d06d6bSBaptiste Daroussin 	size_t		 start;
38261d06d6bSBaptiste Daroussin 	int		 diff, i, quoted;
38361d06d6bSBaptiste Daroussin 	enum eqn_tok	 tok;
38461d06d6bSBaptiste Daroussin 
38561d06d6bSBaptiste Daroussin 	/*
38661d06d6bSBaptiste Daroussin 	 * Reset the recursion counter after advancing
38761d06d6bSBaptiste Daroussin 	 * beyond the end of the previous substitution.
38861d06d6bSBaptiste Daroussin 	 */
38961d06d6bSBaptiste Daroussin 	if (ep->end - ep->data >= last_len)
39061d06d6bSBaptiste Daroussin 		lim = 0;
39161d06d6bSBaptiste Daroussin 
39261d06d6bSBaptiste Daroussin 	ep->start = ep->end;
39361d06d6bSBaptiste Daroussin 	quoted = mode == MODE_QUOTED;
39461d06d6bSBaptiste Daroussin 	for (;;) {
39561d06d6bSBaptiste Daroussin 		switch (*ep->start) {
39661d06d6bSBaptiste Daroussin 		case '\0':
39761d06d6bSBaptiste Daroussin 			ep->toksz = 0;
39861d06d6bSBaptiste Daroussin 			return EQN_TOK_EOF;
39961d06d6bSBaptiste Daroussin 		case '"':
40061d06d6bSBaptiste Daroussin 			quoted = 1;
40161d06d6bSBaptiste Daroussin 			break;
40261d06d6bSBaptiste Daroussin 		default:
40361d06d6bSBaptiste Daroussin 			break;
40461d06d6bSBaptiste Daroussin 		}
40561d06d6bSBaptiste Daroussin 		if (quoted) {
40661d06d6bSBaptiste Daroussin 			ep->end = strchr(ep->start + 1, *ep->start);
40761d06d6bSBaptiste Daroussin 			ep->start++;  /* Skip opening quote. */
40861d06d6bSBaptiste Daroussin 			if (ep->end == NULL) {
409*7295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ARG_QUOTE,
41061d06d6bSBaptiste Daroussin 				    ep->node->line, ep->node->pos, NULL);
41161d06d6bSBaptiste Daroussin 				ep->end = strchr(ep->start, '\0');
41261d06d6bSBaptiste Daroussin 			}
41361d06d6bSBaptiste Daroussin 		} else {
41461d06d6bSBaptiste Daroussin 			ep->end = ep->start + 1;
41561d06d6bSBaptiste Daroussin 			if (*ep->start != '{' && *ep->start != '}')
41661d06d6bSBaptiste Daroussin 				ep->end += strcspn(ep->end, " ^~\"{}\t");
41761d06d6bSBaptiste Daroussin 		}
41861d06d6bSBaptiste Daroussin 		ep->toksz = ep->end - ep->start;
41961d06d6bSBaptiste Daroussin 		if (quoted && *ep->end != '\0')
42061d06d6bSBaptiste Daroussin 			ep->end++;  /* Skip closing quote. */
42161d06d6bSBaptiste Daroussin 		while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
42261d06d6bSBaptiste Daroussin 			ep->end++;
42361d06d6bSBaptiste Daroussin 		if (quoted)  /* Cannot return, may have to strndup. */
42461d06d6bSBaptiste Daroussin 			break;
42561d06d6bSBaptiste Daroussin 		if (mode == MODE_NOSUB)
42661d06d6bSBaptiste Daroussin 			return EQN_TOK__MAX;
42761d06d6bSBaptiste Daroussin 		if ((def = eqn_def_find(ep)) == NULL)
42861d06d6bSBaptiste Daroussin 			break;
42961d06d6bSBaptiste Daroussin 		if (++lim > EQN_NEST_MAX) {
430*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_ROFFLOOP,
43161d06d6bSBaptiste Daroussin 			    ep->node->line, ep->node->pos, NULL);
43261d06d6bSBaptiste Daroussin 			return EQN_TOK_EOF;
43361d06d6bSBaptiste Daroussin 		}
43461d06d6bSBaptiste Daroussin 
43561d06d6bSBaptiste Daroussin 		/* Replace a defined name with its string value. */
43661d06d6bSBaptiste Daroussin 		if ((diff = def->valsz - ep->toksz) > 0) {
43761d06d6bSBaptiste Daroussin 			start = ep->start - ep->data;
43861d06d6bSBaptiste Daroussin 			ep->sz += diff;
43961d06d6bSBaptiste Daroussin 			ep->data = mandoc_realloc(ep->data, ep->sz + 1);
44061d06d6bSBaptiste Daroussin 			ep->start = ep->data + start;
44161d06d6bSBaptiste Daroussin 		}
44261d06d6bSBaptiste Daroussin 		if (diff)
44361d06d6bSBaptiste Daroussin 			memmove(ep->start + def->valsz, ep->start + ep->toksz,
44461d06d6bSBaptiste Daroussin 			    strlen(ep->start + ep->toksz) + 1);
44561d06d6bSBaptiste Daroussin 		memcpy(ep->start, def->val, def->valsz);
44661d06d6bSBaptiste Daroussin 		last_len = ep->start - ep->data + def->valsz;
44761d06d6bSBaptiste Daroussin 	}
44861d06d6bSBaptiste Daroussin 	if (mode != MODE_TOK)
44961d06d6bSBaptiste Daroussin 		return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
45061d06d6bSBaptiste Daroussin 	if (quoted) {
45161d06d6bSBaptiste Daroussin 		ep->start = mandoc_strndup(ep->start, ep->toksz);
45261d06d6bSBaptiste Daroussin 		return EQN_TOK_QUOTED;
45361d06d6bSBaptiste Daroussin 	}
45461d06d6bSBaptiste Daroussin 	for (tok = 0; tok < EQN_TOK__MAX; tok++)
45561d06d6bSBaptiste Daroussin 		if (STRNEQ(ep->start, ep->toksz,
45661d06d6bSBaptiste Daroussin 		    eqn_toks[tok], strlen(eqn_toks[tok])))
45761d06d6bSBaptiste Daroussin 			return tok;
45861d06d6bSBaptiste Daroussin 
45961d06d6bSBaptiste Daroussin 	for (i = 0; i < EQNSYM__MAX; i++) {
46061d06d6bSBaptiste Daroussin 		if (STRNEQ(ep->start, ep->toksz,
46161d06d6bSBaptiste Daroussin 		    eqnsyms[i].str, strlen(eqnsyms[i].str))) {
46261d06d6bSBaptiste Daroussin 			mandoc_asprintf(&ep->start,
46361d06d6bSBaptiste Daroussin 			    "\\[%s]", eqnsyms[i].sym);
46461d06d6bSBaptiste Daroussin 			return EQN_TOK_SYM;
46561d06d6bSBaptiste Daroussin 		}
46661d06d6bSBaptiste Daroussin 	}
46761d06d6bSBaptiste Daroussin 	ep->start = mandoc_strndup(ep->start, ep->toksz);
46861d06d6bSBaptiste Daroussin 	for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
46961d06d6bSBaptiste Daroussin 		if (STRNEQ(ep->start, ep->toksz,
47061d06d6bSBaptiste Daroussin 		    eqn_func[i], strlen(eqn_func[i])))
47161d06d6bSBaptiste Daroussin 			return EQN_TOK_FUNC;
47261d06d6bSBaptiste Daroussin 	return EQN_TOK__MAX;
47361d06d6bSBaptiste Daroussin }
47461d06d6bSBaptiste Daroussin 
47561d06d6bSBaptiste Daroussin void
47661d06d6bSBaptiste Daroussin eqn_box_free(struct eqn_box *bp)
47761d06d6bSBaptiste Daroussin {
478*7295610fSBaptiste Daroussin 	if (bp == NULL)
479*7295610fSBaptiste Daroussin 		return;
48061d06d6bSBaptiste Daroussin 
48161d06d6bSBaptiste Daroussin 	if (bp->first)
48261d06d6bSBaptiste Daroussin 		eqn_box_free(bp->first);
48361d06d6bSBaptiste Daroussin 	if (bp->next)
48461d06d6bSBaptiste Daroussin 		eqn_box_free(bp->next);
48561d06d6bSBaptiste Daroussin 
48661d06d6bSBaptiste Daroussin 	free(bp->text);
48761d06d6bSBaptiste Daroussin 	free(bp->left);
48861d06d6bSBaptiste Daroussin 	free(bp->right);
48961d06d6bSBaptiste Daroussin 	free(bp->top);
49061d06d6bSBaptiste Daroussin 	free(bp->bottom);
49161d06d6bSBaptiste Daroussin 	free(bp);
49261d06d6bSBaptiste Daroussin }
49361d06d6bSBaptiste Daroussin 
494*7295610fSBaptiste Daroussin struct eqn_box *
495*7295610fSBaptiste Daroussin eqn_box_new(void)
496*7295610fSBaptiste Daroussin {
497*7295610fSBaptiste Daroussin 	struct eqn_box	*bp;
498*7295610fSBaptiste Daroussin 
499*7295610fSBaptiste Daroussin 	bp = mandoc_calloc(1, sizeof(*bp));
500*7295610fSBaptiste Daroussin 	bp->expectargs = UINT_MAX;
501*7295610fSBaptiste Daroussin 	return bp;
502*7295610fSBaptiste Daroussin }
503*7295610fSBaptiste Daroussin 
50461d06d6bSBaptiste Daroussin /*
50561d06d6bSBaptiste Daroussin  * Allocate a box as the last child of the parent node.
50661d06d6bSBaptiste Daroussin  */
50761d06d6bSBaptiste Daroussin static struct eqn_box *
50861d06d6bSBaptiste Daroussin eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
50961d06d6bSBaptiste Daroussin {
51061d06d6bSBaptiste Daroussin 	struct eqn_box	*bp;
51161d06d6bSBaptiste Daroussin 
512*7295610fSBaptiste Daroussin 	bp = eqn_box_new();
51361d06d6bSBaptiste Daroussin 	bp->parent = parent;
51461d06d6bSBaptiste Daroussin 	bp->parent->args++;
51561d06d6bSBaptiste Daroussin 	bp->font = bp->parent->font;
51661d06d6bSBaptiste Daroussin 	bp->size = ep->gsize;
51761d06d6bSBaptiste Daroussin 
51861d06d6bSBaptiste Daroussin 	if (NULL != parent->first) {
51961d06d6bSBaptiste Daroussin 		parent->last->next = bp;
52061d06d6bSBaptiste Daroussin 		bp->prev = parent->last;
52161d06d6bSBaptiste Daroussin 	} else
52261d06d6bSBaptiste Daroussin 		parent->first = bp;
52361d06d6bSBaptiste Daroussin 
52461d06d6bSBaptiste Daroussin 	parent->last = bp;
52561d06d6bSBaptiste Daroussin 	return bp;
52661d06d6bSBaptiste Daroussin }
52761d06d6bSBaptiste Daroussin 
52861d06d6bSBaptiste Daroussin /*
52961d06d6bSBaptiste Daroussin  * Reparent the current last node (of the current parent) under a new
53061d06d6bSBaptiste Daroussin  * EQN_SUBEXPR as the first element.
53161d06d6bSBaptiste Daroussin  * Then return the new parent.
53261d06d6bSBaptiste Daroussin  * The new EQN_SUBEXPR will have a two-child limit.
53361d06d6bSBaptiste Daroussin  */
53461d06d6bSBaptiste Daroussin static struct eqn_box *
53561d06d6bSBaptiste Daroussin eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent)
53661d06d6bSBaptiste Daroussin {
53761d06d6bSBaptiste Daroussin 	struct eqn_box	*b, *newb;
53861d06d6bSBaptiste Daroussin 
53961d06d6bSBaptiste Daroussin 	assert(NULL != parent->last);
54061d06d6bSBaptiste Daroussin 	b = parent->last;
54161d06d6bSBaptiste Daroussin 	if (parent->last == parent->first)
54261d06d6bSBaptiste Daroussin 		parent->first = NULL;
54361d06d6bSBaptiste Daroussin 	parent->args--;
54461d06d6bSBaptiste Daroussin 	parent->last = b->prev;
54561d06d6bSBaptiste Daroussin 	b->prev = NULL;
54661d06d6bSBaptiste Daroussin 	newb = eqn_box_alloc(ep, parent);
54761d06d6bSBaptiste Daroussin 	newb->type = EQN_SUBEXPR;
54861d06d6bSBaptiste Daroussin 	newb->expectargs = 2;
54961d06d6bSBaptiste Daroussin 	newb->args = 1;
55061d06d6bSBaptiste Daroussin 	newb->first = newb->last = b;
55161d06d6bSBaptiste Daroussin 	newb->first->next = NULL;
55261d06d6bSBaptiste Daroussin 	b->parent = newb;
55361d06d6bSBaptiste Daroussin 	return newb;
55461d06d6bSBaptiste Daroussin }
55561d06d6bSBaptiste Daroussin 
55661d06d6bSBaptiste Daroussin /*
55761d06d6bSBaptiste Daroussin  * Parse the "delim" control statement.
55861d06d6bSBaptiste Daroussin  */
55961d06d6bSBaptiste Daroussin static void
56061d06d6bSBaptiste Daroussin eqn_delim(struct eqn_node *ep)
56161d06d6bSBaptiste Daroussin {
56261d06d6bSBaptiste Daroussin 	if (ep->end[0] == '\0' || ep->end[1] == '\0') {
563*7295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_EMPTY,
56461d06d6bSBaptiste Daroussin 		    ep->node->line, ep->node->pos, "delim");
56561d06d6bSBaptiste Daroussin 		if (ep->end[0] != '\0')
56661d06d6bSBaptiste Daroussin 			ep->end++;
56761d06d6bSBaptiste Daroussin 	} else if (strncmp(ep->end, "off", 3) == 0) {
56861d06d6bSBaptiste Daroussin 		ep->delim = 0;
56961d06d6bSBaptiste Daroussin 		ep->end += 3;
57061d06d6bSBaptiste Daroussin 	} else if (strncmp(ep->end, "on", 2) == 0) {
57161d06d6bSBaptiste Daroussin 		if (ep->odelim && ep->cdelim)
57261d06d6bSBaptiste Daroussin 			ep->delim = 1;
57361d06d6bSBaptiste Daroussin 		ep->end += 2;
57461d06d6bSBaptiste Daroussin 	} else {
57561d06d6bSBaptiste Daroussin 		ep->odelim = *ep->end++;
57661d06d6bSBaptiste Daroussin 		ep->cdelim = *ep->end++;
57761d06d6bSBaptiste Daroussin 		ep->delim = 1;
57861d06d6bSBaptiste Daroussin 	}
57961d06d6bSBaptiste Daroussin }
58061d06d6bSBaptiste Daroussin 
58161d06d6bSBaptiste Daroussin /*
58261d06d6bSBaptiste Daroussin  * Undefine a previously-defined string.
58361d06d6bSBaptiste Daroussin  */
58461d06d6bSBaptiste Daroussin static void
58561d06d6bSBaptiste Daroussin eqn_undef(struct eqn_node *ep)
58661d06d6bSBaptiste Daroussin {
58761d06d6bSBaptiste Daroussin 	struct eqn_def	*def;
58861d06d6bSBaptiste Daroussin 
58961d06d6bSBaptiste Daroussin 	if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
590*7295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_EMPTY,
59161d06d6bSBaptiste Daroussin 		    ep->node->line, ep->node->pos, "undef");
59261d06d6bSBaptiste Daroussin 		return;
59361d06d6bSBaptiste Daroussin 	}
59461d06d6bSBaptiste Daroussin 	if ((def = eqn_def_find(ep)) == NULL)
59561d06d6bSBaptiste Daroussin 		return;
59661d06d6bSBaptiste Daroussin 	free(def->key);
59761d06d6bSBaptiste Daroussin 	free(def->val);
59861d06d6bSBaptiste Daroussin 	def->key = def->val = NULL;
59961d06d6bSBaptiste Daroussin 	def->keysz = def->valsz = 0;
60061d06d6bSBaptiste Daroussin }
60161d06d6bSBaptiste Daroussin 
60261d06d6bSBaptiste Daroussin static void
60361d06d6bSBaptiste Daroussin eqn_def(struct eqn_node *ep)
60461d06d6bSBaptiste Daroussin {
60561d06d6bSBaptiste Daroussin 	struct eqn_def	*def;
60661d06d6bSBaptiste Daroussin 	int		 i;
60761d06d6bSBaptiste Daroussin 
60861d06d6bSBaptiste Daroussin 	if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
609*7295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_EMPTY,
61061d06d6bSBaptiste Daroussin 		    ep->node->line, ep->node->pos, "define");
61161d06d6bSBaptiste Daroussin 		return;
61261d06d6bSBaptiste Daroussin 	}
61361d06d6bSBaptiste Daroussin 
61461d06d6bSBaptiste Daroussin 	/*
61561d06d6bSBaptiste Daroussin 	 * Search for a key that already exists.
61661d06d6bSBaptiste Daroussin 	 * Create a new key if none is found.
61761d06d6bSBaptiste Daroussin 	 */
61861d06d6bSBaptiste Daroussin 	if ((def = eqn_def_find(ep)) == NULL) {
61961d06d6bSBaptiste Daroussin 		/* Find holes in string array. */
62061d06d6bSBaptiste Daroussin 		for (i = 0; i < (int)ep->defsz; i++)
62161d06d6bSBaptiste Daroussin 			if (0 == ep->defs[i].keysz)
62261d06d6bSBaptiste Daroussin 				break;
62361d06d6bSBaptiste Daroussin 
62461d06d6bSBaptiste Daroussin 		if (i == (int)ep->defsz) {
62561d06d6bSBaptiste Daroussin 			ep->defsz++;
62661d06d6bSBaptiste Daroussin 			ep->defs = mandoc_reallocarray(ep->defs,
62761d06d6bSBaptiste Daroussin 			    ep->defsz, sizeof(struct eqn_def));
62861d06d6bSBaptiste Daroussin 			ep->defs[i].key = ep->defs[i].val = NULL;
62961d06d6bSBaptiste Daroussin 		}
63061d06d6bSBaptiste Daroussin 
63161d06d6bSBaptiste Daroussin 		def = ep->defs + i;
63261d06d6bSBaptiste Daroussin 		free(def->key);
63361d06d6bSBaptiste Daroussin 		def->key = mandoc_strndup(ep->start, ep->toksz);
63461d06d6bSBaptiste Daroussin 		def->keysz = ep->toksz;
63561d06d6bSBaptiste Daroussin 	}
63661d06d6bSBaptiste Daroussin 
63761d06d6bSBaptiste Daroussin 	if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
638*7295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_REQ_EMPTY,
63961d06d6bSBaptiste Daroussin 		    ep->node->line, ep->node->pos, "define %s", def->key);
64061d06d6bSBaptiste Daroussin 		free(def->key);
64161d06d6bSBaptiste Daroussin 		free(def->val);
64261d06d6bSBaptiste Daroussin 		def->key = def->val = NULL;
64361d06d6bSBaptiste Daroussin 		def->keysz = def->valsz = 0;
64461d06d6bSBaptiste Daroussin 		return;
64561d06d6bSBaptiste Daroussin 	}
64661d06d6bSBaptiste Daroussin 	free(def->val);
64761d06d6bSBaptiste Daroussin 	def->val = mandoc_strndup(ep->start, ep->toksz);
64861d06d6bSBaptiste Daroussin 	def->valsz = ep->toksz;
64961d06d6bSBaptiste Daroussin }
65061d06d6bSBaptiste Daroussin 
65161d06d6bSBaptiste Daroussin void
65261d06d6bSBaptiste Daroussin eqn_parse(struct eqn_node *ep)
65361d06d6bSBaptiste Daroussin {
65461d06d6bSBaptiste Daroussin 	struct eqn_box	*cur, *nbox, *parent, *split;
65561d06d6bSBaptiste Daroussin 	const char	*cp, *cpn;
65661d06d6bSBaptiste Daroussin 	char		*p;
65761d06d6bSBaptiste Daroussin 	enum eqn_tok	 tok;
65861d06d6bSBaptiste Daroussin 	enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
65961d06d6bSBaptiste Daroussin 	int		 size;
66061d06d6bSBaptiste Daroussin 
66161d06d6bSBaptiste Daroussin 	parent = ep->node->eqn;
66261d06d6bSBaptiste Daroussin 	assert(parent != NULL);
66361d06d6bSBaptiste Daroussin 
66461d06d6bSBaptiste Daroussin 	/*
66561d06d6bSBaptiste Daroussin 	 * Empty equation.
66661d06d6bSBaptiste Daroussin 	 * Do not add it to the high-level syntax tree.
66761d06d6bSBaptiste Daroussin 	 */
66861d06d6bSBaptiste Daroussin 
66961d06d6bSBaptiste Daroussin 	if (ep->data == NULL)
67061d06d6bSBaptiste Daroussin 		return;
67161d06d6bSBaptiste Daroussin 
67261d06d6bSBaptiste Daroussin 	ep->start = ep->end = ep->data + strspn(ep->data, " ^~");
67361d06d6bSBaptiste Daroussin 
67461d06d6bSBaptiste Daroussin next_tok:
67561d06d6bSBaptiste Daroussin 	tok = eqn_next(ep, MODE_TOK);
67661d06d6bSBaptiste Daroussin 	switch (tok) {
67761d06d6bSBaptiste Daroussin 	case EQN_TOK_UNDEF:
67861d06d6bSBaptiste Daroussin 		eqn_undef(ep);
67961d06d6bSBaptiste Daroussin 		break;
68061d06d6bSBaptiste Daroussin 	case EQN_TOK_NDEFINE:
68161d06d6bSBaptiste Daroussin 	case EQN_TOK_DEFINE:
68261d06d6bSBaptiste Daroussin 		eqn_def(ep);
68361d06d6bSBaptiste Daroussin 		break;
68461d06d6bSBaptiste Daroussin 	case EQN_TOK_TDEFINE:
68561d06d6bSBaptiste Daroussin 		if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
68661d06d6bSBaptiste Daroussin 		    eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
687*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_REQ_EMPTY,
68861d06d6bSBaptiste Daroussin 			    ep->node->line, ep->node->pos, "tdefine");
68961d06d6bSBaptiste Daroussin 		break;
69061d06d6bSBaptiste Daroussin 	case EQN_TOK_DELIM:
69161d06d6bSBaptiste Daroussin 		eqn_delim(ep);
69261d06d6bSBaptiste Daroussin 		break;
69361d06d6bSBaptiste Daroussin 	case EQN_TOK_GFONT:
69461d06d6bSBaptiste Daroussin 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
695*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
696*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
69761d06d6bSBaptiste Daroussin 		break;
69861d06d6bSBaptiste Daroussin 	case EQN_TOK_MARK:
69961d06d6bSBaptiste Daroussin 	case EQN_TOK_LINEUP:
70061d06d6bSBaptiste Daroussin 		/* Ignore these. */
70161d06d6bSBaptiste Daroussin 		break;
70261d06d6bSBaptiste Daroussin 	case EQN_TOK_DYAD:
70361d06d6bSBaptiste Daroussin 	case EQN_TOK_VEC:
70461d06d6bSBaptiste Daroussin 	case EQN_TOK_UNDER:
70561d06d6bSBaptiste Daroussin 	case EQN_TOK_BAR:
70661d06d6bSBaptiste Daroussin 	case EQN_TOK_TILDE:
70761d06d6bSBaptiste Daroussin 	case EQN_TOK_HAT:
70861d06d6bSBaptiste Daroussin 	case EQN_TOK_DOT:
70961d06d6bSBaptiste Daroussin 	case EQN_TOK_DOTDOT:
71061d06d6bSBaptiste Daroussin 		if (parent->last == NULL) {
711*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
712*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
71361d06d6bSBaptiste Daroussin 			cur = eqn_box_alloc(ep, parent);
71461d06d6bSBaptiste Daroussin 			cur->type = EQN_TEXT;
71561d06d6bSBaptiste Daroussin 			cur->text = mandoc_strdup("");
71661d06d6bSBaptiste Daroussin 		}
71761d06d6bSBaptiste Daroussin 		parent = eqn_box_makebinary(ep, parent);
71861d06d6bSBaptiste Daroussin 		parent->type = EQN_LIST;
71961d06d6bSBaptiste Daroussin 		parent->expectargs = 1;
72061d06d6bSBaptiste Daroussin 		parent->font = EQNFONT_ROMAN;
72161d06d6bSBaptiste Daroussin 		switch (tok) {
72261d06d6bSBaptiste Daroussin 		case EQN_TOK_DOTDOT:
72361d06d6bSBaptiste Daroussin 			parent->top = mandoc_strdup("\\[ad]");
72461d06d6bSBaptiste Daroussin 			break;
72561d06d6bSBaptiste Daroussin 		case EQN_TOK_VEC:
72661d06d6bSBaptiste Daroussin 			parent->top = mandoc_strdup("\\[->]");
72761d06d6bSBaptiste Daroussin 			break;
72861d06d6bSBaptiste Daroussin 		case EQN_TOK_DYAD:
72961d06d6bSBaptiste Daroussin 			parent->top = mandoc_strdup("\\[<>]");
73061d06d6bSBaptiste Daroussin 			break;
73161d06d6bSBaptiste Daroussin 		case EQN_TOK_TILDE:
73261d06d6bSBaptiste Daroussin 			parent->top = mandoc_strdup("\\[a~]");
73361d06d6bSBaptiste Daroussin 			break;
73461d06d6bSBaptiste Daroussin 		case EQN_TOK_UNDER:
73561d06d6bSBaptiste Daroussin 			parent->bottom = mandoc_strdup("\\[ul]");
73661d06d6bSBaptiste Daroussin 			break;
73761d06d6bSBaptiste Daroussin 		case EQN_TOK_BAR:
73861d06d6bSBaptiste Daroussin 			parent->top = mandoc_strdup("\\[rn]");
73961d06d6bSBaptiste Daroussin 			break;
74061d06d6bSBaptiste Daroussin 		case EQN_TOK_DOT:
74161d06d6bSBaptiste Daroussin 			parent->top = mandoc_strdup("\\[a.]");
74261d06d6bSBaptiste Daroussin 			break;
74361d06d6bSBaptiste Daroussin 		case EQN_TOK_HAT:
74461d06d6bSBaptiste Daroussin 			parent->top = mandoc_strdup("\\[ha]");
74561d06d6bSBaptiste Daroussin 			break;
74661d06d6bSBaptiste Daroussin 		default:
74761d06d6bSBaptiste Daroussin 			abort();
74861d06d6bSBaptiste Daroussin 		}
74961d06d6bSBaptiste Daroussin 		parent = parent->parent;
75061d06d6bSBaptiste Daroussin 		break;
75161d06d6bSBaptiste Daroussin 	case EQN_TOK_FWD:
75261d06d6bSBaptiste Daroussin 	case EQN_TOK_BACK:
75361d06d6bSBaptiste Daroussin 	case EQN_TOK_DOWN:
75461d06d6bSBaptiste Daroussin 	case EQN_TOK_UP:
75561d06d6bSBaptiste Daroussin 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
756*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
757*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
75861d06d6bSBaptiste Daroussin 		break;
75961d06d6bSBaptiste Daroussin 	case EQN_TOK_FAT:
76061d06d6bSBaptiste Daroussin 	case EQN_TOK_ROMAN:
76161d06d6bSBaptiste Daroussin 	case EQN_TOK_ITALIC:
76261d06d6bSBaptiste Daroussin 	case EQN_TOK_BOLD:
76361d06d6bSBaptiste Daroussin 		while (parent->args == parent->expectargs)
76461d06d6bSBaptiste Daroussin 			parent = parent->parent;
76561d06d6bSBaptiste Daroussin 		/*
76661d06d6bSBaptiste Daroussin 		 * These values apply to the next word or sequence of
76761d06d6bSBaptiste Daroussin 		 * words; thus, we mark that we'll have a child with
76861d06d6bSBaptiste Daroussin 		 * exactly one of those.
76961d06d6bSBaptiste Daroussin 		 */
77061d06d6bSBaptiste Daroussin 		parent = eqn_box_alloc(ep, parent);
77161d06d6bSBaptiste Daroussin 		parent->type = EQN_LIST;
77261d06d6bSBaptiste Daroussin 		parent->expectargs = 1;
77361d06d6bSBaptiste Daroussin 		switch (tok) {
77461d06d6bSBaptiste Daroussin 		case EQN_TOK_FAT:
77561d06d6bSBaptiste Daroussin 			parent->font = EQNFONT_FAT;
77661d06d6bSBaptiste Daroussin 			break;
77761d06d6bSBaptiste Daroussin 		case EQN_TOK_ROMAN:
77861d06d6bSBaptiste Daroussin 			parent->font = EQNFONT_ROMAN;
77961d06d6bSBaptiste Daroussin 			break;
78061d06d6bSBaptiste Daroussin 		case EQN_TOK_ITALIC:
78161d06d6bSBaptiste Daroussin 			parent->font = EQNFONT_ITALIC;
78261d06d6bSBaptiste Daroussin 			break;
78361d06d6bSBaptiste Daroussin 		case EQN_TOK_BOLD:
78461d06d6bSBaptiste Daroussin 			parent->font = EQNFONT_BOLD;
78561d06d6bSBaptiste Daroussin 			break;
78661d06d6bSBaptiste Daroussin 		default:
78761d06d6bSBaptiste Daroussin 			abort();
78861d06d6bSBaptiste Daroussin 		}
78961d06d6bSBaptiste Daroussin 		break;
79061d06d6bSBaptiste Daroussin 	case EQN_TOK_SIZE:
79161d06d6bSBaptiste Daroussin 	case EQN_TOK_GSIZE:
79261d06d6bSBaptiste Daroussin 		/* Accept two values: integral size and a single. */
79361d06d6bSBaptiste Daroussin 		if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
794*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
795*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
79661d06d6bSBaptiste Daroussin 			break;
79761d06d6bSBaptiste Daroussin 		}
79861d06d6bSBaptiste Daroussin 		size = mandoc_strntoi(ep->start, ep->toksz, 10);
79961d06d6bSBaptiste Daroussin 		if (-1 == size) {
800*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line,
801*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
80261d06d6bSBaptiste Daroussin 			break;
80361d06d6bSBaptiste Daroussin 		}
80461d06d6bSBaptiste Daroussin 		if (EQN_TOK_GSIZE == tok) {
80561d06d6bSBaptiste Daroussin 			ep->gsize = size;
80661d06d6bSBaptiste Daroussin 			break;
80761d06d6bSBaptiste Daroussin 		}
80861d06d6bSBaptiste Daroussin 		while (parent->args == parent->expectargs)
80961d06d6bSBaptiste Daroussin 			parent = parent->parent;
81061d06d6bSBaptiste Daroussin 		parent = eqn_box_alloc(ep, parent);
81161d06d6bSBaptiste Daroussin 		parent->type = EQN_LIST;
81261d06d6bSBaptiste Daroussin 		parent->expectargs = 1;
81361d06d6bSBaptiste Daroussin 		parent->size = size;
81461d06d6bSBaptiste Daroussin 		break;
81561d06d6bSBaptiste Daroussin 	case EQN_TOK_FROM:
81661d06d6bSBaptiste Daroussin 	case EQN_TOK_TO:
81761d06d6bSBaptiste Daroussin 	case EQN_TOK_SUB:
81861d06d6bSBaptiste Daroussin 	case EQN_TOK_SUP:
81961d06d6bSBaptiste Daroussin 		/*
82061d06d6bSBaptiste Daroussin 		 * We have a left-right-associative expression.
82161d06d6bSBaptiste Daroussin 		 * Repivot under a positional node, open a child scope
82261d06d6bSBaptiste Daroussin 		 * and keep on reading.
82361d06d6bSBaptiste Daroussin 		 */
82461d06d6bSBaptiste Daroussin 		if (parent->last == NULL) {
825*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
826*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
82761d06d6bSBaptiste Daroussin 			cur = eqn_box_alloc(ep, parent);
82861d06d6bSBaptiste Daroussin 			cur->type = EQN_TEXT;
82961d06d6bSBaptiste Daroussin 			cur->text = mandoc_strdup("");
83061d06d6bSBaptiste Daroussin 		}
83161d06d6bSBaptiste Daroussin 		while (parent->expectargs == 1 && parent->args == 1)
83261d06d6bSBaptiste Daroussin 			parent = parent->parent;
83361d06d6bSBaptiste Daroussin 		if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO)  {
83461d06d6bSBaptiste Daroussin 			for (cur = parent; cur != NULL; cur = cur->parent)
83561d06d6bSBaptiste Daroussin 				if (cur->pos == EQNPOS_SUB ||
83661d06d6bSBaptiste Daroussin 				    cur->pos == EQNPOS_SUP ||
83761d06d6bSBaptiste Daroussin 				    cur->pos == EQNPOS_SUBSUP ||
83861d06d6bSBaptiste Daroussin 				    cur->pos == EQNPOS_SQRT ||
83961d06d6bSBaptiste Daroussin 				    cur->pos == EQNPOS_OVER)
84061d06d6bSBaptiste Daroussin 					break;
84161d06d6bSBaptiste Daroussin 			if (cur != NULL)
84261d06d6bSBaptiste Daroussin 				parent = cur->parent;
84361d06d6bSBaptiste Daroussin 		}
84461d06d6bSBaptiste Daroussin 		if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
84561d06d6bSBaptiste Daroussin 			parent->expectargs = 3;
84661d06d6bSBaptiste Daroussin 			parent->pos = EQNPOS_SUBSUP;
84761d06d6bSBaptiste Daroussin 			break;
84861d06d6bSBaptiste Daroussin 		}
84961d06d6bSBaptiste Daroussin 		if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
85061d06d6bSBaptiste Daroussin 			parent->expectargs = 3;
85161d06d6bSBaptiste Daroussin 			parent->pos = EQNPOS_FROMTO;
85261d06d6bSBaptiste Daroussin 			break;
85361d06d6bSBaptiste Daroussin 		}
85461d06d6bSBaptiste Daroussin 		parent = eqn_box_makebinary(ep, parent);
85561d06d6bSBaptiste Daroussin 		switch (tok) {
85661d06d6bSBaptiste Daroussin 		case EQN_TOK_FROM:
85761d06d6bSBaptiste Daroussin 			parent->pos = EQNPOS_FROM;
85861d06d6bSBaptiste Daroussin 			break;
85961d06d6bSBaptiste Daroussin 		case EQN_TOK_TO:
86061d06d6bSBaptiste Daroussin 			parent->pos = EQNPOS_TO;
86161d06d6bSBaptiste Daroussin 			break;
86261d06d6bSBaptiste Daroussin 		case EQN_TOK_SUP:
86361d06d6bSBaptiste Daroussin 			parent->pos = EQNPOS_SUP;
86461d06d6bSBaptiste Daroussin 			break;
86561d06d6bSBaptiste Daroussin 		case EQN_TOK_SUB:
86661d06d6bSBaptiste Daroussin 			parent->pos = EQNPOS_SUB;
86761d06d6bSBaptiste Daroussin 			break;
86861d06d6bSBaptiste Daroussin 		default:
86961d06d6bSBaptiste Daroussin 			abort();
87061d06d6bSBaptiste Daroussin 		}
87161d06d6bSBaptiste Daroussin 		break;
87261d06d6bSBaptiste Daroussin 	case EQN_TOK_SQRT:
87361d06d6bSBaptiste Daroussin 		while (parent->args == parent->expectargs)
87461d06d6bSBaptiste Daroussin 			parent = parent->parent;
87561d06d6bSBaptiste Daroussin 		/*
87661d06d6bSBaptiste Daroussin 		 * Accept a left-right-associative set of arguments just
87761d06d6bSBaptiste Daroussin 		 * like sub and sup and friends but without rebalancing
87861d06d6bSBaptiste Daroussin 		 * under a pivot.
87961d06d6bSBaptiste Daroussin 		 */
88061d06d6bSBaptiste Daroussin 		parent = eqn_box_alloc(ep, parent);
88161d06d6bSBaptiste Daroussin 		parent->type = EQN_SUBEXPR;
88261d06d6bSBaptiste Daroussin 		parent->pos = EQNPOS_SQRT;
88361d06d6bSBaptiste Daroussin 		parent->expectargs = 1;
88461d06d6bSBaptiste Daroussin 		break;
88561d06d6bSBaptiste Daroussin 	case EQN_TOK_OVER:
88661d06d6bSBaptiste Daroussin 		/*
88761d06d6bSBaptiste Daroussin 		 * We have a right-left-associative fraction.
88861d06d6bSBaptiste Daroussin 		 * Close out anything that's currently open, then
88961d06d6bSBaptiste Daroussin 		 * rebalance and continue reading.
89061d06d6bSBaptiste Daroussin 		 */
89161d06d6bSBaptiste Daroussin 		if (parent->last == NULL) {
892*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
893*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
89461d06d6bSBaptiste Daroussin 			cur = eqn_box_alloc(ep, parent);
89561d06d6bSBaptiste Daroussin 			cur->type = EQN_TEXT;
89661d06d6bSBaptiste Daroussin 			cur->text = mandoc_strdup("");
89761d06d6bSBaptiste Daroussin 		}
89861d06d6bSBaptiste Daroussin 		while (parent->args == parent->expectargs)
89961d06d6bSBaptiste Daroussin 			parent = parent->parent;
90061d06d6bSBaptiste Daroussin 		while (EQN_SUBEXPR == parent->type)
90161d06d6bSBaptiste Daroussin 			parent = parent->parent;
90261d06d6bSBaptiste Daroussin 		parent = eqn_box_makebinary(ep, parent);
90361d06d6bSBaptiste Daroussin 		parent->pos = EQNPOS_OVER;
90461d06d6bSBaptiste Daroussin 		break;
90561d06d6bSBaptiste Daroussin 	case EQN_TOK_RIGHT:
90661d06d6bSBaptiste Daroussin 	case EQN_TOK_BRACE_CLOSE:
90761d06d6bSBaptiste Daroussin 		/*
90861d06d6bSBaptiste Daroussin 		 * Close out the existing brace.
90961d06d6bSBaptiste Daroussin 		 * FIXME: this is a shitty sentinel: we should really
91061d06d6bSBaptiste Daroussin 		 * have a native EQN_BRACE type or whatnot.
91161d06d6bSBaptiste Daroussin 		 */
91261d06d6bSBaptiste Daroussin 		for (cur = parent; cur != NULL; cur = cur->parent)
91361d06d6bSBaptiste Daroussin 			if (cur->type == EQN_LIST &&
91461d06d6bSBaptiste Daroussin 			    cur->expectargs > 1 &&
91561d06d6bSBaptiste Daroussin 			    (tok == EQN_TOK_BRACE_CLOSE ||
91661d06d6bSBaptiste Daroussin 			     cur->left != NULL))
91761d06d6bSBaptiste Daroussin 				break;
91861d06d6bSBaptiste Daroussin 		if (cur == NULL) {
919*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line,
920*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
92161d06d6bSBaptiste Daroussin 			break;
92261d06d6bSBaptiste Daroussin 		}
92361d06d6bSBaptiste Daroussin 		parent = cur;
92461d06d6bSBaptiste Daroussin 		if (EQN_TOK_RIGHT == tok) {
92561d06d6bSBaptiste Daroussin 			if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
92661d06d6bSBaptiste Daroussin 				mandoc_msg(MANDOCERR_REQ_EMPTY,
927*7295610fSBaptiste Daroussin 				    ep->node->line, ep->node->pos,
928*7295610fSBaptiste Daroussin 				    "%s", eqn_toks[tok]);
92961d06d6bSBaptiste Daroussin 				break;
93061d06d6bSBaptiste Daroussin 			}
93161d06d6bSBaptiste Daroussin 			/* Handling depends on right/left. */
93261d06d6bSBaptiste Daroussin 			if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
93361d06d6bSBaptiste Daroussin 				parent->right = mandoc_strdup("\\[rc]");
93461d06d6bSBaptiste Daroussin 			else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
93561d06d6bSBaptiste Daroussin 				parent->right = mandoc_strdup("\\[rf]");
93661d06d6bSBaptiste Daroussin 			else
93761d06d6bSBaptiste Daroussin 				parent->right =
93861d06d6bSBaptiste Daroussin 				    mandoc_strndup(ep->start, ep->toksz);
93961d06d6bSBaptiste Daroussin 		}
94061d06d6bSBaptiste Daroussin 		parent = parent->parent;
94161d06d6bSBaptiste Daroussin 		if (tok == EQN_TOK_BRACE_CLOSE &&
94261d06d6bSBaptiste Daroussin 		    (parent->type == EQN_PILE ||
94361d06d6bSBaptiste Daroussin 		     parent->type == EQN_MATRIX))
94461d06d6bSBaptiste Daroussin 			parent = parent->parent;
94561d06d6bSBaptiste Daroussin 		/* Close out any "singleton" lists. */
94661d06d6bSBaptiste Daroussin 		while (parent->type == EQN_LIST &&
94761d06d6bSBaptiste Daroussin 		    parent->expectargs == 1 &&
94861d06d6bSBaptiste Daroussin 		    parent->args == 1)
94961d06d6bSBaptiste Daroussin 			parent = parent->parent;
95061d06d6bSBaptiste Daroussin 		break;
95161d06d6bSBaptiste Daroussin 	case EQN_TOK_BRACE_OPEN:
95261d06d6bSBaptiste Daroussin 	case EQN_TOK_LEFT:
95361d06d6bSBaptiste Daroussin 		/*
95461d06d6bSBaptiste Daroussin 		 * If we already have something in the stack and we're
95561d06d6bSBaptiste Daroussin 		 * in an expression, then rewind til we're not any more
95661d06d6bSBaptiste Daroussin 		 * (just like with the text node).
95761d06d6bSBaptiste Daroussin 		 */
95861d06d6bSBaptiste Daroussin 		while (parent->args == parent->expectargs)
95961d06d6bSBaptiste Daroussin 			parent = parent->parent;
96061d06d6bSBaptiste Daroussin 		if (EQN_TOK_LEFT == tok &&
96161d06d6bSBaptiste Daroussin 		    eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
962*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
963*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
96461d06d6bSBaptiste Daroussin 			break;
96561d06d6bSBaptiste Daroussin 		}
96661d06d6bSBaptiste Daroussin 		parent = eqn_box_alloc(ep, parent);
96761d06d6bSBaptiste Daroussin 		parent->type = EQN_LIST;
96861d06d6bSBaptiste Daroussin 		if (EQN_TOK_LEFT == tok) {
96961d06d6bSBaptiste Daroussin 			if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
97061d06d6bSBaptiste Daroussin 				parent->left = mandoc_strdup("\\[lc]");
97161d06d6bSBaptiste Daroussin 			else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
97261d06d6bSBaptiste Daroussin 				parent->left = mandoc_strdup("\\[lf]");
97361d06d6bSBaptiste Daroussin 			else
97461d06d6bSBaptiste Daroussin 				parent->left =
97561d06d6bSBaptiste Daroussin 				    mandoc_strndup(ep->start, ep->toksz);
97661d06d6bSBaptiste Daroussin 		}
97761d06d6bSBaptiste Daroussin 		break;
97861d06d6bSBaptiste Daroussin 	case EQN_TOK_PILE:
97961d06d6bSBaptiste Daroussin 	case EQN_TOK_LPILE:
98061d06d6bSBaptiste Daroussin 	case EQN_TOK_RPILE:
98161d06d6bSBaptiste Daroussin 	case EQN_TOK_CPILE:
98261d06d6bSBaptiste Daroussin 	case EQN_TOK_CCOL:
98361d06d6bSBaptiste Daroussin 	case EQN_TOK_LCOL:
98461d06d6bSBaptiste Daroussin 	case EQN_TOK_RCOL:
98561d06d6bSBaptiste Daroussin 		while (parent->args == parent->expectargs)
98661d06d6bSBaptiste Daroussin 			parent = parent->parent;
98761d06d6bSBaptiste Daroussin 		parent = eqn_box_alloc(ep, parent);
98861d06d6bSBaptiste Daroussin 		parent->type = EQN_PILE;
98961d06d6bSBaptiste Daroussin 		parent->expectargs = 1;
99061d06d6bSBaptiste Daroussin 		break;
99161d06d6bSBaptiste Daroussin 	case EQN_TOK_ABOVE:
99261d06d6bSBaptiste Daroussin 		for (cur = parent; cur != NULL; cur = cur->parent)
99361d06d6bSBaptiste Daroussin 			if (cur->type == EQN_PILE)
99461d06d6bSBaptiste Daroussin 				break;
99561d06d6bSBaptiste Daroussin 		if (cur == NULL) {
996*7295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line,
997*7295610fSBaptiste Daroussin 			    ep->node->pos, "%s", eqn_toks[tok]);
99861d06d6bSBaptiste Daroussin 			break;
99961d06d6bSBaptiste Daroussin 		}
100061d06d6bSBaptiste Daroussin 		parent = eqn_box_alloc(ep, cur);
100161d06d6bSBaptiste Daroussin 		parent->type = EQN_LIST;
100261d06d6bSBaptiste Daroussin 		break;
100361d06d6bSBaptiste Daroussin 	case EQN_TOK_MATRIX:
100461d06d6bSBaptiste Daroussin 		while (parent->args == parent->expectargs)
100561d06d6bSBaptiste Daroussin 			parent = parent->parent;
100661d06d6bSBaptiste Daroussin 		parent = eqn_box_alloc(ep, parent);
100761d06d6bSBaptiste Daroussin 		parent->type = EQN_MATRIX;
100861d06d6bSBaptiste Daroussin 		parent->expectargs = 1;
100961d06d6bSBaptiste Daroussin 		break;
101061d06d6bSBaptiste Daroussin 	case EQN_TOK_EOF:
101161d06d6bSBaptiste Daroussin 		return;
101261d06d6bSBaptiste Daroussin 	case EQN_TOK__MAX:
101361d06d6bSBaptiste Daroussin 	case EQN_TOK_FUNC:
101461d06d6bSBaptiste Daroussin 	case EQN_TOK_QUOTED:
101561d06d6bSBaptiste Daroussin 	case EQN_TOK_SYM:
101661d06d6bSBaptiste Daroussin 		p = ep->start;
101761d06d6bSBaptiste Daroussin 		assert(p != NULL);
101861d06d6bSBaptiste Daroussin 		/*
101961d06d6bSBaptiste Daroussin 		 * If we already have something in the stack and we're
102061d06d6bSBaptiste Daroussin 		 * in an expression, then rewind til we're not any more.
102161d06d6bSBaptiste Daroussin 		 */
102261d06d6bSBaptiste Daroussin 		while (parent->args == parent->expectargs)
102361d06d6bSBaptiste Daroussin 			parent = parent->parent;
102461d06d6bSBaptiste Daroussin 		cur = eqn_box_alloc(ep, parent);
102561d06d6bSBaptiste Daroussin 		cur->type = EQN_TEXT;
102661d06d6bSBaptiste Daroussin 		cur->text = p;
102761d06d6bSBaptiste Daroussin 		switch (tok) {
102861d06d6bSBaptiste Daroussin 		case EQN_TOK_FUNC:
102961d06d6bSBaptiste Daroussin 			cur->font = EQNFONT_ROMAN;
103061d06d6bSBaptiste Daroussin 			break;
103161d06d6bSBaptiste Daroussin 		case EQN_TOK_QUOTED:
103261d06d6bSBaptiste Daroussin 			if (cur->font == EQNFONT_NONE)
103361d06d6bSBaptiste Daroussin 				cur->font = EQNFONT_ITALIC;
103461d06d6bSBaptiste Daroussin 			break;
103561d06d6bSBaptiste Daroussin 		case EQN_TOK_SYM:
103661d06d6bSBaptiste Daroussin 			break;
103761d06d6bSBaptiste Daroussin 		default:
103861d06d6bSBaptiste Daroussin 			if (cur->font != EQNFONT_NONE || *p == '\0')
103961d06d6bSBaptiste Daroussin 				break;
104061d06d6bSBaptiste Daroussin 			cpn = p - 1;
104161d06d6bSBaptiste Daroussin 			ccln = CCL_LET;
104261d06d6bSBaptiste Daroussin 			split = NULL;
104361d06d6bSBaptiste Daroussin 			for (;;) {
104461d06d6bSBaptiste Daroussin 				/* Advance to next character. */
104561d06d6bSBaptiste Daroussin 				cp = cpn++;
104661d06d6bSBaptiste Daroussin 				ccl = ccln;
104761d06d6bSBaptiste Daroussin 				ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
104861d06d6bSBaptiste Daroussin 				    isdigit((unsigned char)*cpn) ||
104961d06d6bSBaptiste Daroussin 				    (*cpn == '.' && (ccl == CCL_DIG ||
105061d06d6bSBaptiste Daroussin 				     isdigit((unsigned char)cpn[1]))) ?
105161d06d6bSBaptiste Daroussin 				    CCL_DIG : CCL_PUN;
105261d06d6bSBaptiste Daroussin 				/* No boundary before first character. */
105361d06d6bSBaptiste Daroussin 				if (cp < p)
105461d06d6bSBaptiste Daroussin 					continue;
105561d06d6bSBaptiste Daroussin 				cur->font = ccl == CCL_LET ?
105661d06d6bSBaptiste Daroussin 				    EQNFONT_ITALIC : EQNFONT_ROMAN;
105761d06d6bSBaptiste Daroussin 				if (*cp == '\\')
105861d06d6bSBaptiste Daroussin 					mandoc_escape(&cpn, NULL, NULL);
105961d06d6bSBaptiste Daroussin 				/* No boundary after last character. */
106061d06d6bSBaptiste Daroussin 				if (*cpn == '\0')
106161d06d6bSBaptiste Daroussin 					break;
106261d06d6bSBaptiste Daroussin 				if (ccln == ccl && *cp != ',' && *cpn != ',')
106361d06d6bSBaptiste Daroussin 					continue;
106461d06d6bSBaptiste Daroussin 				/* Boundary found, split the text. */
106561d06d6bSBaptiste Daroussin 				if (parent->args == parent->expectargs) {
106661d06d6bSBaptiste Daroussin 					/* Remove the text from the tree. */
106761d06d6bSBaptiste Daroussin 					if (cur->prev == NULL)
106861d06d6bSBaptiste Daroussin 						parent->first = cur->next;
106961d06d6bSBaptiste Daroussin 					else
107061d06d6bSBaptiste Daroussin 						cur->prev->next = NULL;
107161d06d6bSBaptiste Daroussin 					parent->last = cur->prev;
107261d06d6bSBaptiste Daroussin 					parent->args--;
107361d06d6bSBaptiste Daroussin 					/* Set up a list instead. */
107461d06d6bSBaptiste Daroussin 					split = eqn_box_alloc(ep, parent);
107561d06d6bSBaptiste Daroussin 					split->type = EQN_LIST;
107661d06d6bSBaptiste Daroussin 					/* Insert the word into the list. */
107761d06d6bSBaptiste Daroussin 					split->first = split->last = cur;
107861d06d6bSBaptiste Daroussin 					cur->parent = split;
107961d06d6bSBaptiste Daroussin 					cur->prev = NULL;
108061d06d6bSBaptiste Daroussin 					parent = split;
108161d06d6bSBaptiste Daroussin 				}
108261d06d6bSBaptiste Daroussin 				/* Append a new text box. */
108361d06d6bSBaptiste Daroussin 				nbox = eqn_box_alloc(ep, parent);
108461d06d6bSBaptiste Daroussin 				nbox->type = EQN_TEXT;
108561d06d6bSBaptiste Daroussin 				nbox->text = mandoc_strdup(cpn);
108661d06d6bSBaptiste Daroussin 				/* Truncate the old box. */
108761d06d6bSBaptiste Daroussin 				p = mandoc_strndup(cur->text,
108861d06d6bSBaptiste Daroussin 				    cpn - cur->text);
108961d06d6bSBaptiste Daroussin 				free(cur->text);
109061d06d6bSBaptiste Daroussin 				cur->text = p;
109161d06d6bSBaptiste Daroussin 				/* Setup to process the new box. */
109261d06d6bSBaptiste Daroussin 				cur = nbox;
109361d06d6bSBaptiste Daroussin 				p = nbox->text;
109461d06d6bSBaptiste Daroussin 				cpn = p - 1;
109561d06d6bSBaptiste Daroussin 				ccln = CCL_LET;
109661d06d6bSBaptiste Daroussin 			}
109761d06d6bSBaptiste Daroussin 			if (split != NULL)
109861d06d6bSBaptiste Daroussin 				parent = split->parent;
109961d06d6bSBaptiste Daroussin 			break;
110061d06d6bSBaptiste Daroussin 		}
110161d06d6bSBaptiste Daroussin 		break;
110261d06d6bSBaptiste Daroussin 	default:
110361d06d6bSBaptiste Daroussin 		abort();
110461d06d6bSBaptiste Daroussin 	}
110561d06d6bSBaptiste Daroussin 	goto next_tok;
110661d06d6bSBaptiste Daroussin }
110761d06d6bSBaptiste Daroussin 
110861d06d6bSBaptiste Daroussin void
110961d06d6bSBaptiste Daroussin eqn_free(struct eqn_node *p)
111061d06d6bSBaptiste Daroussin {
111161d06d6bSBaptiste Daroussin 	int		 i;
111261d06d6bSBaptiste Daroussin 
1113*7295610fSBaptiste Daroussin 	if (p == NULL)
1114*7295610fSBaptiste Daroussin 		return;
1115*7295610fSBaptiste Daroussin 
111661d06d6bSBaptiste Daroussin 	for (i = 0; i < (int)p->defsz; i++) {
111761d06d6bSBaptiste Daroussin 		free(p->defs[i].key);
111861d06d6bSBaptiste Daroussin 		free(p->defs[i].val);
111961d06d6bSBaptiste Daroussin 	}
112061d06d6bSBaptiste Daroussin 
112161d06d6bSBaptiste Daroussin 	free(p->data);
112261d06d6bSBaptiste Daroussin 	free(p->defs);
112361d06d6bSBaptiste Daroussin 	free(p);
112461d06d6bSBaptiste Daroussin }
1125