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