xref: /illumos-gate/usr/src/cmd/mandoc/eqn.c (revision 371584c2eae4cf827fd406ba26c14f021adaaa70)
1*371584c2SYuri Pankov /*	$Id: eqn.c,v 1.61 2016/01/08 00:50:45 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3260e9a87SYuri Pankov  * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4260e9a87SYuri Pankov  * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #include "config.h"
19260e9a87SYuri Pankov 
20260e9a87SYuri Pankov #include <sys/types.h>
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #include <assert.h>
2395c635efSGarrett D'Amore #include <limits.h>
2495c635efSGarrett D'Amore #include <stdio.h>
2595c635efSGarrett D'Amore #include <stdlib.h>
2695c635efSGarrett D'Amore #include <string.h>
2795c635efSGarrett D'Amore #include <time.h>
2895c635efSGarrett D'Amore 
2995c635efSGarrett D'Amore #include "mandoc.h"
30260e9a87SYuri Pankov #include "mandoc_aux.h"
3195c635efSGarrett D'Amore #include "libmandoc.h"
3295c635efSGarrett D'Amore #include "libroff.h"
3395c635efSGarrett D'Amore 
3495c635efSGarrett D'Amore #define	EQN_NEST_MAX	 128 /* maximum nesting of defines */
35260e9a87SYuri Pankov #define	STRNEQ(p1, sz1, p2, sz2) \
36260e9a87SYuri Pankov 	((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
3795c635efSGarrett D'Amore 
38260e9a87SYuri Pankov enum	eqn_tok {
39260e9a87SYuri Pankov 	EQN_TOK_DYAD = 0,
40260e9a87SYuri Pankov 	EQN_TOK_VEC,
41260e9a87SYuri Pankov 	EQN_TOK_UNDER,
42260e9a87SYuri Pankov 	EQN_TOK_BAR,
43260e9a87SYuri Pankov 	EQN_TOK_TILDE,
44260e9a87SYuri Pankov 	EQN_TOK_HAT,
45260e9a87SYuri Pankov 	EQN_TOK_DOT,
46260e9a87SYuri Pankov 	EQN_TOK_DOTDOT,
47260e9a87SYuri Pankov 	EQN_TOK_FWD,
48260e9a87SYuri Pankov 	EQN_TOK_BACK,
49260e9a87SYuri Pankov 	EQN_TOK_DOWN,
50260e9a87SYuri Pankov 	EQN_TOK_UP,
51260e9a87SYuri Pankov 	EQN_TOK_FAT,
52260e9a87SYuri Pankov 	EQN_TOK_ROMAN,
53260e9a87SYuri Pankov 	EQN_TOK_ITALIC,
54260e9a87SYuri Pankov 	EQN_TOK_BOLD,
55260e9a87SYuri Pankov 	EQN_TOK_SIZE,
56260e9a87SYuri Pankov 	EQN_TOK_SUB,
57260e9a87SYuri Pankov 	EQN_TOK_SUP,
58260e9a87SYuri Pankov 	EQN_TOK_SQRT,
59260e9a87SYuri Pankov 	EQN_TOK_OVER,
60260e9a87SYuri Pankov 	EQN_TOK_FROM,
61260e9a87SYuri Pankov 	EQN_TOK_TO,
62260e9a87SYuri Pankov 	EQN_TOK_BRACE_OPEN,
63260e9a87SYuri Pankov 	EQN_TOK_BRACE_CLOSE,
64260e9a87SYuri Pankov 	EQN_TOK_GSIZE,
65260e9a87SYuri Pankov 	EQN_TOK_GFONT,
66260e9a87SYuri Pankov 	EQN_TOK_MARK,
67260e9a87SYuri Pankov 	EQN_TOK_LINEUP,
68260e9a87SYuri Pankov 	EQN_TOK_LEFT,
69260e9a87SYuri Pankov 	EQN_TOK_RIGHT,
70260e9a87SYuri Pankov 	EQN_TOK_PILE,
71260e9a87SYuri Pankov 	EQN_TOK_LPILE,
72260e9a87SYuri Pankov 	EQN_TOK_RPILE,
73260e9a87SYuri Pankov 	EQN_TOK_CPILE,
74260e9a87SYuri Pankov 	EQN_TOK_MATRIX,
75260e9a87SYuri Pankov 	EQN_TOK_CCOL,
76260e9a87SYuri Pankov 	EQN_TOK_LCOL,
77260e9a87SYuri Pankov 	EQN_TOK_RCOL,
78260e9a87SYuri Pankov 	EQN_TOK_DELIM,
79260e9a87SYuri Pankov 	EQN_TOK_DEFINE,
80260e9a87SYuri Pankov 	EQN_TOK_TDEFINE,
81260e9a87SYuri Pankov 	EQN_TOK_NDEFINE,
82260e9a87SYuri Pankov 	EQN_TOK_UNDEF,
83260e9a87SYuri Pankov 	EQN_TOK_EOF,
84260e9a87SYuri Pankov 	EQN_TOK_ABOVE,
85260e9a87SYuri Pankov 	EQN_TOK__MAX
86260e9a87SYuri Pankov };
87260e9a87SYuri Pankov 
88260e9a87SYuri Pankov static	const char *eqn_toks[EQN_TOK__MAX] = {
89260e9a87SYuri Pankov 	"dyad", /* EQN_TOK_DYAD */
90260e9a87SYuri Pankov 	"vec", /* EQN_TOK_VEC */
91260e9a87SYuri Pankov 	"under", /* EQN_TOK_UNDER */
92260e9a87SYuri Pankov 	"bar", /* EQN_TOK_BAR */
93260e9a87SYuri Pankov 	"tilde", /* EQN_TOK_TILDE */
94260e9a87SYuri Pankov 	"hat", /* EQN_TOK_HAT */
95260e9a87SYuri Pankov 	"dot", /* EQN_TOK_DOT */
96260e9a87SYuri Pankov 	"dotdot", /* EQN_TOK_DOTDOT */
97260e9a87SYuri Pankov 	"fwd", /* EQN_TOK_FWD * */
98260e9a87SYuri Pankov 	"back", /* EQN_TOK_BACK */
99260e9a87SYuri Pankov 	"down", /* EQN_TOK_DOWN */
100260e9a87SYuri Pankov 	"up", /* EQN_TOK_UP */
101260e9a87SYuri Pankov 	"fat", /* EQN_TOK_FAT */
102260e9a87SYuri Pankov 	"roman", /* EQN_TOK_ROMAN */
103260e9a87SYuri Pankov 	"italic", /* EQN_TOK_ITALIC */
104260e9a87SYuri Pankov 	"bold", /* EQN_TOK_BOLD */
105260e9a87SYuri Pankov 	"size", /* EQN_TOK_SIZE */
106260e9a87SYuri Pankov 	"sub", /* EQN_TOK_SUB */
107260e9a87SYuri Pankov 	"sup", /* EQN_TOK_SUP */
108260e9a87SYuri Pankov 	"sqrt", /* EQN_TOK_SQRT */
109260e9a87SYuri Pankov 	"over", /* EQN_TOK_OVER */
110260e9a87SYuri Pankov 	"from", /* EQN_TOK_FROM */
111260e9a87SYuri Pankov 	"to", /* EQN_TOK_TO */
112260e9a87SYuri Pankov 	"{", /* EQN_TOK_BRACE_OPEN */
113260e9a87SYuri Pankov 	"}", /* EQN_TOK_BRACE_CLOSE */
114260e9a87SYuri Pankov 	"gsize", /* EQN_TOK_GSIZE */
115260e9a87SYuri Pankov 	"gfont", /* EQN_TOK_GFONT */
116260e9a87SYuri Pankov 	"mark", /* EQN_TOK_MARK */
117260e9a87SYuri Pankov 	"lineup", /* EQN_TOK_LINEUP */
118260e9a87SYuri Pankov 	"left", /* EQN_TOK_LEFT */
119260e9a87SYuri Pankov 	"right", /* EQN_TOK_RIGHT */
120260e9a87SYuri Pankov 	"pile", /* EQN_TOK_PILE */
121260e9a87SYuri Pankov 	"lpile", /* EQN_TOK_LPILE */
122260e9a87SYuri Pankov 	"rpile", /* EQN_TOK_RPILE */
123260e9a87SYuri Pankov 	"cpile", /* EQN_TOK_CPILE */
124260e9a87SYuri Pankov 	"matrix", /* EQN_TOK_MATRIX */
125260e9a87SYuri Pankov 	"ccol", /* EQN_TOK_CCOL */
126260e9a87SYuri Pankov 	"lcol", /* EQN_TOK_LCOL */
127260e9a87SYuri Pankov 	"rcol", /* EQN_TOK_RCOL */
128260e9a87SYuri Pankov 	"delim", /* EQN_TOK_DELIM */
129260e9a87SYuri Pankov 	"define", /* EQN_TOK_DEFINE */
130260e9a87SYuri Pankov 	"tdefine", /* EQN_TOK_TDEFINE */
131260e9a87SYuri Pankov 	"ndefine", /* EQN_TOK_NDEFINE */
132260e9a87SYuri Pankov 	"undef", /* EQN_TOK_UNDEF */
133260e9a87SYuri Pankov 	NULL, /* EQN_TOK_EOF */
134260e9a87SYuri Pankov 	"above", /* EQN_TOK_ABOVE */
13595c635efSGarrett D'Amore };
13695c635efSGarrett D'Amore 
13795c635efSGarrett D'Amore enum	eqn_symt {
13895c635efSGarrett D'Amore 	EQNSYM_alpha,
13995c635efSGarrett D'Amore 	EQNSYM_beta,
14095c635efSGarrett D'Amore 	EQNSYM_chi,
14195c635efSGarrett D'Amore 	EQNSYM_delta,
14295c635efSGarrett D'Amore 	EQNSYM_epsilon,
14395c635efSGarrett D'Amore 	EQNSYM_eta,
14495c635efSGarrett D'Amore 	EQNSYM_gamma,
14595c635efSGarrett D'Amore 	EQNSYM_iota,
14695c635efSGarrett D'Amore 	EQNSYM_kappa,
14795c635efSGarrett D'Amore 	EQNSYM_lambda,
14895c635efSGarrett D'Amore 	EQNSYM_mu,
14995c635efSGarrett D'Amore 	EQNSYM_nu,
15095c635efSGarrett D'Amore 	EQNSYM_omega,
15195c635efSGarrett D'Amore 	EQNSYM_omicron,
15295c635efSGarrett D'Amore 	EQNSYM_phi,
15395c635efSGarrett D'Amore 	EQNSYM_pi,
15495c635efSGarrett D'Amore 	EQNSYM_ps,
15595c635efSGarrett D'Amore 	EQNSYM_rho,
15695c635efSGarrett D'Amore 	EQNSYM_sigma,
15795c635efSGarrett D'Amore 	EQNSYM_tau,
15895c635efSGarrett D'Amore 	EQNSYM_theta,
15995c635efSGarrett D'Amore 	EQNSYM_upsilon,
16095c635efSGarrett D'Amore 	EQNSYM_xi,
16195c635efSGarrett D'Amore 	EQNSYM_zeta,
16295c635efSGarrett D'Amore 	EQNSYM_DELTA,
16395c635efSGarrett D'Amore 	EQNSYM_GAMMA,
16495c635efSGarrett D'Amore 	EQNSYM_LAMBDA,
16595c635efSGarrett D'Amore 	EQNSYM_OMEGA,
16695c635efSGarrett D'Amore 	EQNSYM_PHI,
16795c635efSGarrett D'Amore 	EQNSYM_PI,
16895c635efSGarrett D'Amore 	EQNSYM_PSI,
16995c635efSGarrett D'Amore 	EQNSYM_SIGMA,
17095c635efSGarrett D'Amore 	EQNSYM_THETA,
17195c635efSGarrett D'Amore 	EQNSYM_UPSILON,
17295c635efSGarrett D'Amore 	EQNSYM_XI,
17395c635efSGarrett D'Amore 	EQNSYM_inter,
17495c635efSGarrett D'Amore 	EQNSYM_union,
17595c635efSGarrett D'Amore 	EQNSYM_prod,
17695c635efSGarrett D'Amore 	EQNSYM_int,
17795c635efSGarrett D'Amore 	EQNSYM_sum,
17895c635efSGarrett D'Amore 	EQNSYM_grad,
17995c635efSGarrett D'Amore 	EQNSYM_del,
18095c635efSGarrett D'Amore 	EQNSYM_times,
18195c635efSGarrett D'Amore 	EQNSYM_cdot,
18295c635efSGarrett D'Amore 	EQNSYM_nothing,
18395c635efSGarrett D'Amore 	EQNSYM_approx,
18495c635efSGarrett D'Amore 	EQNSYM_prime,
18595c635efSGarrett D'Amore 	EQNSYM_half,
18695c635efSGarrett D'Amore 	EQNSYM_partial,
18795c635efSGarrett D'Amore 	EQNSYM_inf,
18895c635efSGarrett D'Amore 	EQNSYM_muchgreat,
18995c635efSGarrett D'Amore 	EQNSYM_muchless,
19095c635efSGarrett D'Amore 	EQNSYM_larrow,
19195c635efSGarrett D'Amore 	EQNSYM_rarrow,
19295c635efSGarrett D'Amore 	EQNSYM_pm,
19395c635efSGarrett D'Amore 	EQNSYM_nequal,
19495c635efSGarrett D'Amore 	EQNSYM_equiv,
19595c635efSGarrett D'Amore 	EQNSYM_lessequal,
19695c635efSGarrett D'Amore 	EQNSYM_moreequal,
197260e9a87SYuri Pankov 	EQNSYM_minus,
19895c635efSGarrett D'Amore 	EQNSYM__MAX
19995c635efSGarrett D'Amore };
20095c635efSGarrett D'Amore 
20195c635efSGarrett D'Amore struct	eqnsym {
202260e9a87SYuri Pankov 	const char	*str;
20395c635efSGarrett D'Amore 	const char	*sym;
20495c635efSGarrett D'Amore };
20595c635efSGarrett D'Amore 
20695c635efSGarrett D'Amore static	const struct eqnsym eqnsyms[EQNSYM__MAX] = {
207260e9a87SYuri Pankov 	{ "alpha", "*a" }, /* EQNSYM_alpha */
208260e9a87SYuri Pankov 	{ "beta", "*b" }, /* EQNSYM_beta */
209260e9a87SYuri Pankov 	{ "chi", "*x" }, /* EQNSYM_chi */
210260e9a87SYuri Pankov 	{ "delta", "*d" }, /* EQNSYM_delta */
211260e9a87SYuri Pankov 	{ "epsilon", "*e" }, /* EQNSYM_epsilon */
212260e9a87SYuri Pankov 	{ "eta", "*y" }, /* EQNSYM_eta */
213260e9a87SYuri Pankov 	{ "gamma", "*g" }, /* EQNSYM_gamma */
214260e9a87SYuri Pankov 	{ "iota", "*i" }, /* EQNSYM_iota */
215260e9a87SYuri Pankov 	{ "kappa", "*k" }, /* EQNSYM_kappa */
216260e9a87SYuri Pankov 	{ "lambda", "*l" }, /* EQNSYM_lambda */
217260e9a87SYuri Pankov 	{ "mu", "*m" }, /* EQNSYM_mu */
218260e9a87SYuri Pankov 	{ "nu", "*n" }, /* EQNSYM_nu */
219260e9a87SYuri Pankov 	{ "omega", "*w" }, /* EQNSYM_omega */
220260e9a87SYuri Pankov 	{ "omicron", "*o" }, /* EQNSYM_omicron */
221260e9a87SYuri Pankov 	{ "phi", "*f" }, /* EQNSYM_phi */
222260e9a87SYuri Pankov 	{ "pi", "*p" }, /* EQNSYM_pi */
223260e9a87SYuri Pankov 	{ "psi", "*q" }, /* EQNSYM_psi */
224260e9a87SYuri Pankov 	{ "rho", "*r" }, /* EQNSYM_rho */
225260e9a87SYuri Pankov 	{ "sigma", "*s" }, /* EQNSYM_sigma */
226260e9a87SYuri Pankov 	{ "tau", "*t" }, /* EQNSYM_tau */
227260e9a87SYuri Pankov 	{ "theta", "*h" }, /* EQNSYM_theta */
228260e9a87SYuri Pankov 	{ "upsilon", "*u" }, /* EQNSYM_upsilon */
229260e9a87SYuri Pankov 	{ "xi", "*c" }, /* EQNSYM_xi */
230260e9a87SYuri Pankov 	{ "zeta", "*z" }, /* EQNSYM_zeta */
231260e9a87SYuri Pankov 	{ "DELTA", "*D" }, /* EQNSYM_DELTA */
232260e9a87SYuri Pankov 	{ "GAMMA", "*G" }, /* EQNSYM_GAMMA */
233260e9a87SYuri Pankov 	{ "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
234260e9a87SYuri Pankov 	{ "OMEGA", "*W" }, /* EQNSYM_OMEGA */
235260e9a87SYuri Pankov 	{ "PHI", "*F" }, /* EQNSYM_PHI */
236260e9a87SYuri Pankov 	{ "PI", "*P" }, /* EQNSYM_PI */
237260e9a87SYuri Pankov 	{ "PSI", "*Q" }, /* EQNSYM_PSI */
238260e9a87SYuri Pankov 	{ "SIGMA", "*S" }, /* EQNSYM_SIGMA */
239260e9a87SYuri Pankov 	{ "THETA", "*H" }, /* EQNSYM_THETA */
240260e9a87SYuri Pankov 	{ "UPSILON", "*U" }, /* EQNSYM_UPSILON */
241260e9a87SYuri Pankov 	{ "XI", "*C" }, /* EQNSYM_XI */
242260e9a87SYuri Pankov 	{ "inter", "ca" }, /* EQNSYM_inter */
243260e9a87SYuri Pankov 	{ "union", "cu" }, /* EQNSYM_union */
244260e9a87SYuri Pankov 	{ "prod", "product" }, /* EQNSYM_prod */
245260e9a87SYuri Pankov 	{ "int", "integral" }, /* EQNSYM_int */
246260e9a87SYuri Pankov 	{ "sum", "sum" }, /* EQNSYM_sum */
247260e9a87SYuri Pankov 	{ "grad", "gr" }, /* EQNSYM_grad */
248260e9a87SYuri Pankov 	{ "del", "gr" }, /* EQNSYM_del */
249260e9a87SYuri Pankov 	{ "times", "mu" }, /* EQNSYM_times */
250260e9a87SYuri Pankov 	{ "cdot", "pc" }, /* EQNSYM_cdot */
251260e9a87SYuri Pankov 	{ "nothing", "&" }, /* EQNSYM_nothing */
252260e9a87SYuri Pankov 	{ "approx", "~~" }, /* EQNSYM_approx */
253260e9a87SYuri Pankov 	{ "prime", "fm" }, /* EQNSYM_prime */
254260e9a87SYuri Pankov 	{ "half", "12" }, /* EQNSYM_half */
255260e9a87SYuri Pankov 	{ "partial", "pd" }, /* EQNSYM_partial */
256260e9a87SYuri Pankov 	{ "inf", "if" }, /* EQNSYM_inf */
257260e9a87SYuri Pankov 	{ ">>", ">>" }, /* EQNSYM_muchgreat */
258260e9a87SYuri Pankov 	{ "<<", "<<" }, /* EQNSYM_muchless */
259260e9a87SYuri Pankov 	{ "<-", "<-" }, /* EQNSYM_larrow */
260260e9a87SYuri Pankov 	{ "->", "->" }, /* EQNSYM_rarrow */
261260e9a87SYuri Pankov 	{ "+-", "+-" }, /* EQNSYM_pm */
262260e9a87SYuri Pankov 	{ "!=", "!=" }, /* EQNSYM_nequal */
263260e9a87SYuri Pankov 	{ "==", "==" }, /* EQNSYM_equiv */
264260e9a87SYuri Pankov 	{ "<=", "<=" }, /* EQNSYM_lessequal */
265260e9a87SYuri Pankov 	{ ">=", ">=" }, /* EQNSYM_moreequal */
266260e9a87SYuri Pankov 	{ "-", "mi" }, /* EQNSYM_minus */
26795c635efSGarrett D'Amore };
26895c635efSGarrett D'Amore 
269260e9a87SYuri Pankov static	struct eqn_box	*eqn_box_alloc(struct eqn_node *, struct eqn_box *);
270260e9a87SYuri Pankov static	void		 eqn_box_free(struct eqn_box *);
271260e9a87SYuri Pankov static	struct eqn_box	*eqn_box_makebinary(struct eqn_node *,
272260e9a87SYuri Pankov 				enum eqn_post, struct eqn_box *);
273260e9a87SYuri Pankov static	void		 eqn_def(struct eqn_node *);
274260e9a87SYuri Pankov static	struct eqn_def	*eqn_def_find(struct eqn_node *, const char *, size_t);
275260e9a87SYuri Pankov static	void		 eqn_delim(struct eqn_node *);
276260e9a87SYuri Pankov static	const char	*eqn_next(struct eqn_node *, char, size_t *, int);
277260e9a87SYuri Pankov static	const char	*eqn_nextrawtok(struct eqn_node *, size_t *);
278260e9a87SYuri Pankov static	const char	*eqn_nexttok(struct eqn_node *, size_t *);
279260e9a87SYuri Pankov static	enum rofferr	 eqn_parse(struct eqn_node *, struct eqn_box *);
280260e9a87SYuri Pankov static	enum eqn_tok	 eqn_tok_parse(struct eqn_node *, char **);
281260e9a87SYuri Pankov static	void		 eqn_undef(struct eqn_node *);
282260e9a87SYuri Pankov 
283260e9a87SYuri Pankov 
28495c635efSGarrett D'Amore enum rofferr
28595c635efSGarrett D'Amore eqn_read(struct eqn_node **epp, int ln,
28695c635efSGarrett D'Amore 		const char *p, int pos, int *offs)
28795c635efSGarrett D'Amore {
28895c635efSGarrett D'Amore 	size_t		 sz;
28995c635efSGarrett D'Amore 	struct eqn_node	*ep;
29095c635efSGarrett D'Amore 	enum rofferr	 er;
29195c635efSGarrett D'Amore 
29295c635efSGarrett D'Amore 	ep = *epp;
29395c635efSGarrett D'Amore 
29495c635efSGarrett D'Amore 	/*
29595c635efSGarrett D'Amore 	 * If we're the terminating mark, unset our equation status and
29695c635efSGarrett D'Amore 	 * validate the full equation.
29795c635efSGarrett D'Amore 	 */
29895c635efSGarrett D'Amore 
29995c635efSGarrett D'Amore 	if (0 == strncmp(p, ".EN", 3)) {
30095c635efSGarrett D'Amore 		er = eqn_end(epp);
30195c635efSGarrett D'Amore 		p += 3;
30295c635efSGarrett D'Amore 		while (' ' == *p || '\t' == *p)
30395c635efSGarrett D'Amore 			p++;
30495c635efSGarrett D'Amore 		if ('\0' == *p)
305*371584c2SYuri Pankov 			return er;
306260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse,
307260e9a87SYuri Pankov 		    ln, pos, "EN %s", p);
308*371584c2SYuri Pankov 		return er;
30995c635efSGarrett D'Amore 	}
31095c635efSGarrett D'Amore 
31195c635efSGarrett D'Amore 	/*
31295c635efSGarrett D'Amore 	 * Build up the full string, replacing all newlines with regular
31395c635efSGarrett D'Amore 	 * whitespace.
31495c635efSGarrett D'Amore 	 */
31595c635efSGarrett D'Amore 
31695c635efSGarrett D'Amore 	sz = strlen(p + pos) + 1;
31795c635efSGarrett D'Amore 	ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1);
31895c635efSGarrett D'Amore 
31995c635efSGarrett D'Amore 	/* First invocation: nil terminate the string. */
32095c635efSGarrett D'Amore 
32195c635efSGarrett D'Amore 	if (0 == ep->sz)
32295c635efSGarrett D'Amore 		*ep->data = '\0';
32395c635efSGarrett D'Amore 
32495c635efSGarrett D'Amore 	ep->sz += sz;
32595c635efSGarrett D'Amore 	strlcat(ep->data, p + pos, ep->sz + 1);
32695c635efSGarrett D'Amore 	strlcat(ep->data, " ", ep->sz + 1);
327*371584c2SYuri Pankov 	return ROFF_IGN;
32895c635efSGarrett D'Amore }
32995c635efSGarrett D'Amore 
33095c635efSGarrett D'Amore struct eqn_node *
331260e9a87SYuri Pankov eqn_alloc(int pos, int line, struct mparse *parse)
33295c635efSGarrett D'Amore {
33395c635efSGarrett D'Amore 	struct eqn_node	*p;
33495c635efSGarrett D'Amore 
33595c635efSGarrett D'Amore 	p = mandoc_calloc(1, sizeof(struct eqn_node));
33695c635efSGarrett D'Amore 
33795c635efSGarrett D'Amore 	p->parse = parse;
33895c635efSGarrett D'Amore 	p->eqn.ln = line;
33995c635efSGarrett D'Amore 	p->eqn.pos = pos;
34095c635efSGarrett D'Amore 	p->gsize = EQN_DEFSIZE;
34195c635efSGarrett D'Amore 
342*371584c2SYuri Pankov 	return p;
34395c635efSGarrett D'Amore }
34495c635efSGarrett D'Amore 
345260e9a87SYuri Pankov /*
346260e9a87SYuri Pankov  * Find the key "key" of the give size within our eqn-defined values.
347260e9a87SYuri Pankov  */
348260e9a87SYuri Pankov static struct eqn_def *
349260e9a87SYuri Pankov eqn_def_find(struct eqn_node *ep, const char *key, size_t sz)
35095c635efSGarrett D'Amore {
35195c635efSGarrett D'Amore 	int		 i;
35295c635efSGarrett D'Amore 
353260e9a87SYuri Pankov 	for (i = 0; i < (int)ep->defsz; i++)
354260e9a87SYuri Pankov 		if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
355260e9a87SYuri Pankov 		    ep->defs[i].keysz, key, sz))
356*371584c2SYuri Pankov 			return &ep->defs[i];
35795c635efSGarrett D'Amore 
358*371584c2SYuri Pankov 	return NULL;
35995c635efSGarrett D'Amore }
36095c635efSGarrett D'Amore 
361260e9a87SYuri Pankov /*
362260e9a87SYuri Pankov  * Get the next token from the input stream using the given quote
363260e9a87SYuri Pankov  * character.
364260e9a87SYuri Pankov  * Optionally make any replacements.
365260e9a87SYuri Pankov  */
36695c635efSGarrett D'Amore static const char *
36795c635efSGarrett D'Amore eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl)
36895c635efSGarrett D'Amore {
36995c635efSGarrett D'Amore 	char		*start, *next;
37095c635efSGarrett D'Amore 	int		 q, diff, lim;
37195c635efSGarrett D'Amore 	size_t		 ssz, dummy;
37295c635efSGarrett D'Amore 	struct eqn_def	*def;
37395c635efSGarrett D'Amore 
37495c635efSGarrett D'Amore 	if (NULL == sz)
37595c635efSGarrett D'Amore 		sz = &dummy;
37695c635efSGarrett D'Amore 
37795c635efSGarrett D'Amore 	lim = 0;
37895c635efSGarrett D'Amore 	ep->rew = ep->cur;
37995c635efSGarrett D'Amore again:
38095c635efSGarrett D'Amore 	/* Prevent self-definitions. */
38195c635efSGarrett D'Amore 
38295c635efSGarrett D'Amore 	if (lim >= EQN_NEST_MAX) {
383260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,
384260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, NULL);
385*371584c2SYuri Pankov 		return NULL;
38695c635efSGarrett D'Amore 	}
38795c635efSGarrett D'Amore 
38895c635efSGarrett D'Amore 	ep->cur = ep->rew;
38995c635efSGarrett D'Amore 	start = &ep->data[(int)ep->cur];
39095c635efSGarrett D'Amore 	q = 0;
39195c635efSGarrett D'Amore 
39295c635efSGarrett D'Amore 	if ('\0' == *start)
393*371584c2SYuri Pankov 		return NULL;
39495c635efSGarrett D'Amore 
39595c635efSGarrett D'Amore 	if (quote == *start) {
39695c635efSGarrett D'Amore 		ep->cur++;
39795c635efSGarrett D'Amore 		q = 1;
39895c635efSGarrett D'Amore 	}
39995c635efSGarrett D'Amore 
40095c635efSGarrett D'Amore 	start = &ep->data[(int)ep->cur];
40195c635efSGarrett D'Amore 
40295c635efSGarrett D'Amore 	if ( ! q) {
40395c635efSGarrett D'Amore 		if ('{' == *start || '}' == *start)
40495c635efSGarrett D'Amore 			ssz = 1;
40595c635efSGarrett D'Amore 		else
40695c635efSGarrett D'Amore 			ssz = strcspn(start + 1, " ^~\"{}\t") + 1;
40795c635efSGarrett D'Amore 		next = start + (int)ssz;
40895c635efSGarrett D'Amore 		if ('\0' == *next)
40995c635efSGarrett D'Amore 			next = NULL;
41095c635efSGarrett D'Amore 	} else
41195c635efSGarrett D'Amore 		next = strchr(start, quote);
41295c635efSGarrett D'Amore 
41395c635efSGarrett D'Amore 	if (NULL != next) {
41495c635efSGarrett D'Amore 		*sz = (size_t)(next - start);
41595c635efSGarrett D'Amore 		ep->cur += *sz;
41695c635efSGarrett D'Amore 		if (q)
41795c635efSGarrett D'Amore 			ep->cur++;
41895c635efSGarrett D'Amore 		while (' ' == ep->data[(int)ep->cur] ||
41995c635efSGarrett D'Amore 		    '\t' == ep->data[(int)ep->cur] ||
42095c635efSGarrett D'Amore 		    '^' == ep->data[(int)ep->cur] ||
42195c635efSGarrett D'Amore 		    '~' == ep->data[(int)ep->cur])
42295c635efSGarrett D'Amore 			ep->cur++;
42395c635efSGarrett D'Amore 	} else {
42495c635efSGarrett D'Amore 		if (q)
425260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,
426260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, NULL);
42795c635efSGarrett D'Amore 		next = strchr(start, '\0');
42895c635efSGarrett D'Amore 		*sz = (size_t)(next - start);
42995c635efSGarrett D'Amore 		ep->cur += *sz;
43095c635efSGarrett D'Amore 	}
43195c635efSGarrett D'Amore 
43295c635efSGarrett D'Amore 	/* Quotes aren't expanded for values. */
43395c635efSGarrett D'Amore 
43495c635efSGarrett D'Amore 	if (q || ! repl)
435*371584c2SYuri Pankov 		return start;
43695c635efSGarrett D'Amore 
43795c635efSGarrett D'Amore 	if (NULL != (def = eqn_def_find(ep, start, *sz))) {
43895c635efSGarrett D'Amore 		diff = def->valsz - *sz;
43995c635efSGarrett D'Amore 
44095c635efSGarrett D'Amore 		if (def->valsz > *sz) {
44195c635efSGarrett D'Amore 			ep->sz += diff;
44295c635efSGarrett D'Amore 			ep->data = mandoc_realloc(ep->data, ep->sz + 1);
44395c635efSGarrett D'Amore 			ep->data[ep->sz] = '\0';
44495c635efSGarrett D'Amore 			start = &ep->data[(int)ep->rew];
44595c635efSGarrett D'Amore 		}
44695c635efSGarrett D'Amore 
44795c635efSGarrett D'Amore 		diff = def->valsz - *sz;
44895c635efSGarrett D'Amore 		memmove(start + *sz + diff, start + *sz,
44995c635efSGarrett D'Amore 		    (strlen(start) - *sz) + 1);
45095c635efSGarrett D'Amore 		memcpy(start, def->val, def->valsz);
451*371584c2SYuri Pankov 		lim++;
45295c635efSGarrett D'Amore 		goto again;
45395c635efSGarrett D'Amore 	}
45495c635efSGarrett D'Amore 
455*371584c2SYuri Pankov 	return start;
45695c635efSGarrett D'Amore }
45795c635efSGarrett D'Amore 
458260e9a87SYuri Pankov /*
459260e9a87SYuri Pankov  * Get the next delimited token using the default current quote
460260e9a87SYuri Pankov  * character.
461260e9a87SYuri Pankov  */
462260e9a87SYuri Pankov static const char *
463260e9a87SYuri Pankov eqn_nexttok(struct eqn_node *ep, size_t *sz)
46495c635efSGarrett D'Amore {
46595c635efSGarrett D'Amore 
466*371584c2SYuri Pankov 	return eqn_next(ep, '"', sz, 1);
46795c635efSGarrett D'Amore }
46895c635efSGarrett D'Amore 
469260e9a87SYuri Pankov /*
470260e9a87SYuri Pankov  * Get next token without replacement.
471260e9a87SYuri Pankov  */
472260e9a87SYuri Pankov static const char *
473260e9a87SYuri Pankov eqn_nextrawtok(struct eqn_node *ep, size_t *sz)
47495c635efSGarrett D'Amore {
47595c635efSGarrett D'Amore 
476*371584c2SYuri Pankov 	return eqn_next(ep, '"', sz, 0);
47795c635efSGarrett D'Amore }
47895c635efSGarrett D'Amore 
479260e9a87SYuri Pankov /*
480260e9a87SYuri Pankov  * Parse a token from the stream of text.
481260e9a87SYuri Pankov  * A token consists of one of the recognised eqn(7) strings.
482260e9a87SYuri Pankov  * Strings are separated by delimiting marks.
483260e9a87SYuri Pankov  * This returns EQN_TOK_EOF when there are no more tokens.
484260e9a87SYuri Pankov  * If the token is an unrecognised string literal, then it returns
485260e9a87SYuri Pankov  * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated
486260e9a87SYuri Pankov  * string.
487260e9a87SYuri Pankov  * This must be later freed with free(3).
488260e9a87SYuri Pankov  */
489260e9a87SYuri Pankov static enum eqn_tok
490260e9a87SYuri Pankov eqn_tok_parse(struct eqn_node *ep, char **p)
491260e9a87SYuri Pankov {
492260e9a87SYuri Pankov 	const char	*start;
493260e9a87SYuri Pankov 	size_t		 i, sz;
494260e9a87SYuri Pankov 	int		 quoted;
495260e9a87SYuri Pankov 
496260e9a87SYuri Pankov 	if (NULL != p)
497260e9a87SYuri Pankov 		*p = NULL;
498260e9a87SYuri Pankov 
499260e9a87SYuri Pankov 	quoted = ep->data[ep->cur] == '"';
500260e9a87SYuri Pankov 
501260e9a87SYuri Pankov 	if (NULL == (start = eqn_nexttok(ep, &sz)))
502*371584c2SYuri Pankov 		return EQN_TOK_EOF;
503260e9a87SYuri Pankov 
504260e9a87SYuri Pankov 	if (quoted) {
505260e9a87SYuri Pankov 		if (p != NULL)
506260e9a87SYuri Pankov 			*p = mandoc_strndup(start, sz);
507*371584c2SYuri Pankov 		return EQN_TOK__MAX;
508260e9a87SYuri Pankov 	}
509260e9a87SYuri Pankov 
510260e9a87SYuri Pankov 	for (i = 0; i < EQN_TOK__MAX; i++) {
511260e9a87SYuri Pankov 		if (NULL == eqn_toks[i])
512260e9a87SYuri Pankov 			continue;
513260e9a87SYuri Pankov 		if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i])))
514260e9a87SYuri Pankov 			break;
515260e9a87SYuri Pankov 	}
516260e9a87SYuri Pankov 
517260e9a87SYuri Pankov 	if (i == EQN_TOK__MAX && NULL != p)
518260e9a87SYuri Pankov 		*p = mandoc_strndup(start, sz);
519260e9a87SYuri Pankov 
520*371584c2SYuri Pankov 	return i;
521260e9a87SYuri Pankov }
522260e9a87SYuri Pankov 
523260e9a87SYuri Pankov static void
524260e9a87SYuri Pankov eqn_box_free(struct eqn_box *bp)
52595c635efSGarrett D'Amore {
52695c635efSGarrett D'Amore 
527260e9a87SYuri Pankov 	if (bp->first)
528260e9a87SYuri Pankov 		eqn_box_free(bp->first);
529260e9a87SYuri Pankov 	if (bp->next)
530260e9a87SYuri Pankov 		eqn_box_free(bp->next);
53195c635efSGarrett D'Amore 
532260e9a87SYuri Pankov 	free(bp->text);
533260e9a87SYuri Pankov 	free(bp->left);
534260e9a87SYuri Pankov 	free(bp->right);
535260e9a87SYuri Pankov 	free(bp->top);
536260e9a87SYuri Pankov 	free(bp->bottom);
537260e9a87SYuri Pankov 	free(bp);
53895c635efSGarrett D'Amore }
53995c635efSGarrett D'Amore 
540260e9a87SYuri Pankov /*
541260e9a87SYuri Pankov  * Allocate a box as the last child of the parent node.
542260e9a87SYuri Pankov  */
543260e9a87SYuri Pankov static struct eqn_box *
544260e9a87SYuri Pankov eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
545260e9a87SYuri Pankov {
546260e9a87SYuri Pankov 	struct eqn_box	*bp;
547260e9a87SYuri Pankov 
548260e9a87SYuri Pankov 	bp = mandoc_calloc(1, sizeof(struct eqn_box));
549260e9a87SYuri Pankov 	bp->parent = parent;
550260e9a87SYuri Pankov 	bp->parent->args++;
551260e9a87SYuri Pankov 	bp->expectargs = UINT_MAX;
552260e9a87SYuri Pankov 	bp->size = ep->gsize;
553260e9a87SYuri Pankov 
554260e9a87SYuri Pankov 	if (NULL != parent->first) {
555260e9a87SYuri Pankov 		parent->last->next = bp;
556260e9a87SYuri Pankov 		bp->prev = parent->last;
557260e9a87SYuri Pankov 	} else
558260e9a87SYuri Pankov 		parent->first = bp;
559260e9a87SYuri Pankov 
560260e9a87SYuri Pankov 	parent->last = bp;
561*371584c2SYuri Pankov 	return bp;
562260e9a87SYuri Pankov }
563260e9a87SYuri Pankov 
564260e9a87SYuri Pankov /*
565260e9a87SYuri Pankov  * Reparent the current last node (of the current parent) under a new
566260e9a87SYuri Pankov  * EQN_SUBEXPR as the first element.
567260e9a87SYuri Pankov  * Then return the new parent.
568260e9a87SYuri Pankov  * The new EQN_SUBEXPR will have a two-child limit.
569260e9a87SYuri Pankov  */
570260e9a87SYuri Pankov static struct eqn_box *
571260e9a87SYuri Pankov eqn_box_makebinary(struct eqn_node *ep,
572260e9a87SYuri Pankov 	enum eqn_post pos, struct eqn_box *parent)
573260e9a87SYuri Pankov {
574260e9a87SYuri Pankov 	struct eqn_box	*b, *newb;
575260e9a87SYuri Pankov 
576260e9a87SYuri Pankov 	assert(NULL != parent->last);
577260e9a87SYuri Pankov 	b = parent->last;
578260e9a87SYuri Pankov 	if (parent->last == parent->first)
579260e9a87SYuri Pankov 		parent->first = NULL;
580260e9a87SYuri Pankov 	parent->args--;
581260e9a87SYuri Pankov 	parent->last = b->prev;
582260e9a87SYuri Pankov 	b->prev = NULL;
583260e9a87SYuri Pankov 	newb = eqn_box_alloc(ep, parent);
584260e9a87SYuri Pankov 	newb->pos = pos;
585260e9a87SYuri Pankov 	newb->type = EQN_SUBEXPR;
586260e9a87SYuri Pankov 	newb->expectargs = 2;
587260e9a87SYuri Pankov 	newb->args = 1;
588260e9a87SYuri Pankov 	newb->first = newb->last = b;
589260e9a87SYuri Pankov 	newb->first->next = NULL;
590260e9a87SYuri Pankov 	b->parent = newb;
591*371584c2SYuri Pankov 	return newb;
592260e9a87SYuri Pankov }
593260e9a87SYuri Pankov 
594260e9a87SYuri Pankov /*
595260e9a87SYuri Pankov  * Parse the "delim" control statement.
596260e9a87SYuri Pankov  */
597260e9a87SYuri Pankov static void
598260e9a87SYuri Pankov eqn_delim(struct eqn_node *ep)
599260e9a87SYuri Pankov {
600260e9a87SYuri Pankov 	const char	*start;
601260e9a87SYuri Pankov 	size_t		 sz;
602260e9a87SYuri Pankov 
603260e9a87SYuri Pankov 	if ((start = eqn_nextrawtok(ep, &sz)) == NULL)
604260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
605260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, "delim");
606260e9a87SYuri Pankov 	else if (strncmp(start, "off", 3) == 0)
607260e9a87SYuri Pankov 		ep->delim = 0;
608260e9a87SYuri Pankov 	else if (strncmp(start, "on", 2) == 0) {
609260e9a87SYuri Pankov 		if (ep->odelim && ep->cdelim)
610260e9a87SYuri Pankov 			ep->delim = 1;
611260e9a87SYuri Pankov 	} else if (start[1] != '\0') {
612260e9a87SYuri Pankov 		ep->odelim = start[0];
613260e9a87SYuri Pankov 		ep->cdelim = start[1];
614260e9a87SYuri Pankov 		ep->delim = 1;
615260e9a87SYuri Pankov 	}
616260e9a87SYuri Pankov }
617260e9a87SYuri Pankov 
618260e9a87SYuri Pankov /*
619260e9a87SYuri Pankov  * Undefine a previously-defined string.
620260e9a87SYuri Pankov  */
621260e9a87SYuri Pankov static void
622260e9a87SYuri Pankov eqn_undef(struct eqn_node *ep)
623260e9a87SYuri Pankov {
624260e9a87SYuri Pankov 	const char	*start;
625260e9a87SYuri Pankov 	struct eqn_def	*def;
626260e9a87SYuri Pankov 	size_t		 sz;
627260e9a87SYuri Pankov 
628260e9a87SYuri Pankov 	if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {
629260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
630260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, "undef");
631260e9a87SYuri Pankov 		return;
632260e9a87SYuri Pankov 	}
633260e9a87SYuri Pankov 	if ((def = eqn_def_find(ep, start, sz)) == NULL)
634260e9a87SYuri Pankov 		return;
635260e9a87SYuri Pankov 	free(def->key);
636260e9a87SYuri Pankov 	free(def->val);
637260e9a87SYuri Pankov 	def->key = def->val = NULL;
638260e9a87SYuri Pankov 	def->keysz = def->valsz = 0;
639260e9a87SYuri Pankov }
640260e9a87SYuri Pankov 
641260e9a87SYuri Pankov static void
642260e9a87SYuri Pankov eqn_def(struct eqn_node *ep)
64395c635efSGarrett D'Amore {
64495c635efSGarrett D'Amore 	const char	*start;
64595c635efSGarrett D'Amore 	size_t		 sz;
64695c635efSGarrett D'Amore 	struct eqn_def	*def;
64795c635efSGarrett D'Amore 	int		 i;
64895c635efSGarrett D'Amore 
649260e9a87SYuri Pankov 	if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {
650260e9a87SYuri Pankov 		mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
651260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, "define");
652260e9a87SYuri Pankov 		return;
65395c635efSGarrett D'Amore 	}
65495c635efSGarrett D'Amore 
65595c635efSGarrett D'Amore 	/*
65695c635efSGarrett D'Amore 	 * Search for a key that already exists.
65795c635efSGarrett D'Amore 	 * Create a new key if none is found.
65895c635efSGarrett D'Amore 	 */
65995c635efSGarrett D'Amore 	if (NULL == (def = eqn_def_find(ep, start, sz))) {
66095c635efSGarrett D'Amore 		/* Find holes in string array. */
66195c635efSGarrett D'Amore 		for (i = 0; i < (int)ep->defsz; i++)
66295c635efSGarrett D'Amore 			if (0 == ep->defs[i].keysz)
66395c635efSGarrett D'Amore 				break;
66495c635efSGarrett D'Amore 
66595c635efSGarrett D'Amore 		if (i == (int)ep->defsz) {
66695c635efSGarrett D'Amore 			ep->defsz++;
667260e9a87SYuri Pankov 			ep->defs = mandoc_reallocarray(ep->defs,
668260e9a87SYuri Pankov 			    ep->defsz, sizeof(struct eqn_def));
66995c635efSGarrett D'Amore 			ep->defs[i].key = ep->defs[i].val = NULL;
67095c635efSGarrett D'Amore 		}
67195c635efSGarrett D'Amore 
672260e9a87SYuri Pankov 		def = ep->defs + i;
673260e9a87SYuri Pankov 		free(def->key);
674260e9a87SYuri Pankov 		def->key = mandoc_strndup(start, sz);
675260e9a87SYuri Pankov 		def->keysz = sz;
67695c635efSGarrett D'Amore 	}
67795c635efSGarrett D'Amore 
67895c635efSGarrett D'Amore 	start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);
679260e9a87SYuri Pankov 	if (start == NULL) {
680260e9a87SYuri Pankov 		mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,
681260e9a87SYuri Pankov 		    ep->eqn.ln, ep->eqn.pos, "define %s", def->key);
682260e9a87SYuri Pankov 		free(def->key);
683260e9a87SYuri Pankov 		free(def->val);
684260e9a87SYuri Pankov 		def->key = def->val = NULL;
685260e9a87SYuri Pankov 		def->keysz = def->valsz = 0;
686260e9a87SYuri Pankov 		return;
68795c635efSGarrett D'Amore 	}
688260e9a87SYuri Pankov 	free(def->val);
689260e9a87SYuri Pankov 	def->val = mandoc_strndup(start, sz);
69095c635efSGarrett D'Amore 	def->valsz = sz;
69195c635efSGarrett D'Amore }
69295c635efSGarrett D'Amore 
693260e9a87SYuri Pankov /*
694260e9a87SYuri Pankov  * Recursively parse an eqn(7) expression.
695260e9a87SYuri Pankov  */
696260e9a87SYuri Pankov static enum rofferr
697260e9a87SYuri Pankov eqn_parse(struct eqn_node *ep, struct eqn_box *parent)
69895c635efSGarrett D'Amore {
699260e9a87SYuri Pankov 	char		 sym[64];
700260e9a87SYuri Pankov 	struct eqn_box	*cur;
70195c635efSGarrett D'Amore 	const char	*start;
702260e9a87SYuri Pankov 	char		*p;
703260e9a87SYuri Pankov 	size_t		 i, sz;
704260e9a87SYuri Pankov 	enum eqn_tok	 tok, subtok;
705260e9a87SYuri Pankov 	enum eqn_post	 pos;
706260e9a87SYuri Pankov 	int		 size;
70795c635efSGarrett D'Amore 
708260e9a87SYuri Pankov 	assert(parent != NULL);
709260e9a87SYuri Pankov 
710260e9a87SYuri Pankov 	/*
711260e9a87SYuri Pankov 	 * Empty equation.
712260e9a87SYuri Pankov 	 * Do not add it to the high-level syntax tree.
713260e9a87SYuri Pankov 	 */
714260e9a87SYuri Pankov 
715260e9a87SYuri Pankov 	if (ep->data == NULL)
716*371584c2SYuri Pankov 		return ROFF_IGN;
717260e9a87SYuri Pankov 
718260e9a87SYuri Pankov next_tok:
719260e9a87SYuri Pankov 	tok = eqn_tok_parse(ep, &p);
720260e9a87SYuri Pankov 
721260e9a87SYuri Pankov this_tok:
722260e9a87SYuri Pankov 	switch (tok) {
723260e9a87SYuri Pankov 	case (EQN_TOK_UNDEF):
724260e9a87SYuri Pankov 		eqn_undef(ep);
725260e9a87SYuri Pankov 		break;
726260e9a87SYuri Pankov 	case (EQN_TOK_NDEFINE):
727260e9a87SYuri Pankov 	case (EQN_TOK_DEFINE):
728260e9a87SYuri Pankov 		eqn_def(ep);
729260e9a87SYuri Pankov 		break;
730260e9a87SYuri Pankov 	case (EQN_TOK_TDEFINE):
731260e9a87SYuri Pankov 		if (eqn_nextrawtok(ep, NULL) == NULL ||
732260e9a87SYuri Pankov 		    eqn_next(ep, ep->data[(int)ep->cur], NULL, 0) == NULL)
733260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
734260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, "tdefine");
735260e9a87SYuri Pankov 		break;
736260e9a87SYuri Pankov 	case (EQN_TOK_DELIM):
737260e9a87SYuri Pankov 		eqn_delim(ep);
738260e9a87SYuri Pankov 		break;
739260e9a87SYuri Pankov 	case (EQN_TOK_GFONT):
740260e9a87SYuri Pankov 		if (eqn_nextrawtok(ep, NULL) == NULL)
741260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
742260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
743260e9a87SYuri Pankov 		break;
744260e9a87SYuri Pankov 	case (EQN_TOK_MARK):
745260e9a87SYuri Pankov 	case (EQN_TOK_LINEUP):
746260e9a87SYuri Pankov 		/* Ignore these. */
747260e9a87SYuri Pankov 		break;
748260e9a87SYuri Pankov 	case (EQN_TOK_DYAD):
749260e9a87SYuri Pankov 	case (EQN_TOK_VEC):
750260e9a87SYuri Pankov 	case (EQN_TOK_UNDER):
751260e9a87SYuri Pankov 	case (EQN_TOK_BAR):
752260e9a87SYuri Pankov 	case (EQN_TOK_TILDE):
753260e9a87SYuri Pankov 	case (EQN_TOK_HAT):
754260e9a87SYuri Pankov 	case (EQN_TOK_DOT):
755260e9a87SYuri Pankov 	case (EQN_TOK_DOTDOT):
756260e9a87SYuri Pankov 		if (parent->last == NULL) {
757260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
758260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
759260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
760260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
761260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
76295c635efSGarrett D'Amore 		}
763260e9a87SYuri Pankov 		parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent);
764260e9a87SYuri Pankov 		parent->type = EQN_LISTONE;
765260e9a87SYuri Pankov 		parent->expectargs = 1;
766260e9a87SYuri Pankov 		switch (tok) {
767260e9a87SYuri Pankov 		case (EQN_TOK_DOTDOT):
768260e9a87SYuri Pankov 			strlcpy(sym, "\\[ad]", sizeof(sym));
769260e9a87SYuri Pankov 			break;
770260e9a87SYuri Pankov 		case (EQN_TOK_VEC):
771260e9a87SYuri Pankov 			strlcpy(sym, "\\[->]", sizeof(sym));
772260e9a87SYuri Pankov 			break;
773260e9a87SYuri Pankov 		case (EQN_TOK_DYAD):
774260e9a87SYuri Pankov 			strlcpy(sym, "\\[<>]", sizeof(sym));
775260e9a87SYuri Pankov 			break;
776260e9a87SYuri Pankov 		case (EQN_TOK_TILDE):
777260e9a87SYuri Pankov 			strlcpy(sym, "\\[a~]", sizeof(sym));
778260e9a87SYuri Pankov 			break;
779260e9a87SYuri Pankov 		case (EQN_TOK_UNDER):
780260e9a87SYuri Pankov 			strlcpy(sym, "\\[ul]", sizeof(sym));
781260e9a87SYuri Pankov 			break;
782260e9a87SYuri Pankov 		case (EQN_TOK_BAR):
783260e9a87SYuri Pankov 			strlcpy(sym, "\\[rl]", sizeof(sym));
784260e9a87SYuri Pankov 			break;
785260e9a87SYuri Pankov 		case (EQN_TOK_DOT):
786260e9a87SYuri Pankov 			strlcpy(sym, "\\[a.]", sizeof(sym));
787260e9a87SYuri Pankov 			break;
788260e9a87SYuri Pankov 		case (EQN_TOK_HAT):
789260e9a87SYuri Pankov 			strlcpy(sym, "\\[ha]", sizeof(sym));
790260e9a87SYuri Pankov 			break;
791260e9a87SYuri Pankov 		default:
792260e9a87SYuri Pankov 			abort();
79395c635efSGarrett D'Amore 		}
79495c635efSGarrett D'Amore 
795260e9a87SYuri Pankov 		switch (tok) {
796260e9a87SYuri Pankov 		case (EQN_TOK_DOTDOT):
797260e9a87SYuri Pankov 		case (EQN_TOK_VEC):
798260e9a87SYuri Pankov 		case (EQN_TOK_DYAD):
799260e9a87SYuri Pankov 		case (EQN_TOK_TILDE):
800260e9a87SYuri Pankov 		case (EQN_TOK_BAR):
801260e9a87SYuri Pankov 		case (EQN_TOK_DOT):
802260e9a87SYuri Pankov 		case (EQN_TOK_HAT):
803260e9a87SYuri Pankov 			parent->top = mandoc_strdup(sym);
804260e9a87SYuri Pankov 			break;
805260e9a87SYuri Pankov 		case (EQN_TOK_UNDER):
806260e9a87SYuri Pankov 			parent->bottom = mandoc_strdup(sym);
807260e9a87SYuri Pankov 			break;
808260e9a87SYuri Pankov 		default:
809260e9a87SYuri Pankov 			abort();
810260e9a87SYuri Pankov 		}
811260e9a87SYuri Pankov 		parent = parent->parent;
812260e9a87SYuri Pankov 		break;
813260e9a87SYuri Pankov 	case (EQN_TOK_FWD):
814260e9a87SYuri Pankov 	case (EQN_TOK_BACK):
815260e9a87SYuri Pankov 	case (EQN_TOK_DOWN):
816260e9a87SYuri Pankov 	case (EQN_TOK_UP):
817260e9a87SYuri Pankov 		subtok = eqn_tok_parse(ep, NULL);
818260e9a87SYuri Pankov 		if (subtok != EQN_TOK__MAX) {
819260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
820260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
821260e9a87SYuri Pankov 			tok = subtok;
822260e9a87SYuri Pankov 			goto this_tok;
823260e9a87SYuri Pankov 		}
824260e9a87SYuri Pankov 		break;
825260e9a87SYuri Pankov 	case (EQN_TOK_FAT):
826260e9a87SYuri Pankov 	case (EQN_TOK_ROMAN):
827260e9a87SYuri Pankov 	case (EQN_TOK_ITALIC):
828260e9a87SYuri Pankov 	case (EQN_TOK_BOLD):
829260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
830260e9a87SYuri Pankov 			parent = parent->parent;
831260e9a87SYuri Pankov 		/*
832260e9a87SYuri Pankov 		 * These values apply to the next word or sequence of
833260e9a87SYuri Pankov 		 * words; thus, we mark that we'll have a child with
834260e9a87SYuri Pankov 		 * exactly one of those.
835260e9a87SYuri Pankov 		 */
836260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
837260e9a87SYuri Pankov 		parent->type = EQN_LISTONE;
838260e9a87SYuri Pankov 		parent->expectargs = 1;
839260e9a87SYuri Pankov 		switch (tok) {
840260e9a87SYuri Pankov 		case (EQN_TOK_FAT):
841260e9a87SYuri Pankov 			parent->font = EQNFONT_FAT;
842260e9a87SYuri Pankov 			break;
843260e9a87SYuri Pankov 		case (EQN_TOK_ROMAN):
844260e9a87SYuri Pankov 			parent->font = EQNFONT_ROMAN;
845260e9a87SYuri Pankov 			break;
846260e9a87SYuri Pankov 		case (EQN_TOK_ITALIC):
847260e9a87SYuri Pankov 			parent->font = EQNFONT_ITALIC;
848260e9a87SYuri Pankov 			break;
849260e9a87SYuri Pankov 		case (EQN_TOK_BOLD):
850260e9a87SYuri Pankov 			parent->font = EQNFONT_BOLD;
851260e9a87SYuri Pankov 			break;
852260e9a87SYuri Pankov 		default:
853260e9a87SYuri Pankov 			abort();
854260e9a87SYuri Pankov 		}
855260e9a87SYuri Pankov 		break;
856260e9a87SYuri Pankov 	case (EQN_TOK_SIZE):
857260e9a87SYuri Pankov 	case (EQN_TOK_GSIZE):
858260e9a87SYuri Pankov 		/* Accept two values: integral size and a single. */
859260e9a87SYuri Pankov 		if (NULL == (start = eqn_nexttok(ep, &sz))) {
860260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
861260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
862260e9a87SYuri Pankov 			break;
863260e9a87SYuri Pankov 		}
864260e9a87SYuri Pankov 		size = mandoc_strntoi(start, sz, 10);
865260e9a87SYuri Pankov 		if (-1 == size) {
866260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
867260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
868260e9a87SYuri Pankov 			break;
869260e9a87SYuri Pankov 		}
870260e9a87SYuri Pankov 		if (EQN_TOK_GSIZE == tok) {
871260e9a87SYuri Pankov 			ep->gsize = size;
872260e9a87SYuri Pankov 			break;
873260e9a87SYuri Pankov 		}
874260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
875260e9a87SYuri Pankov 		parent->type = EQN_LISTONE;
876260e9a87SYuri Pankov 		parent->expectargs = 1;
877260e9a87SYuri Pankov 		parent->size = size;
878260e9a87SYuri Pankov 		break;
879260e9a87SYuri Pankov 	case (EQN_TOK_FROM):
880260e9a87SYuri Pankov 	case (EQN_TOK_TO):
881260e9a87SYuri Pankov 	case (EQN_TOK_SUB):
882260e9a87SYuri Pankov 	case (EQN_TOK_SUP):
883260e9a87SYuri Pankov 		/*
884260e9a87SYuri Pankov 		 * We have a left-right-associative expression.
885260e9a87SYuri Pankov 		 * Repivot under a positional node, open a child scope
886260e9a87SYuri Pankov 		 * and keep on reading.
887260e9a87SYuri Pankov 		 */
888260e9a87SYuri Pankov 		if (parent->last == NULL) {
889260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
890260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
891260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
892260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
893260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
894260e9a87SYuri Pankov 		}
895260e9a87SYuri Pankov 		/* Handle the "subsup" and "fromto" positions. */
896260e9a87SYuri Pankov 		if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) {
897260e9a87SYuri Pankov 			parent->expectargs = 3;
898260e9a87SYuri Pankov 			parent->pos = EQNPOS_SUBSUP;
899260e9a87SYuri Pankov 			break;
900260e9a87SYuri Pankov 		}
901260e9a87SYuri Pankov 		if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) {
902260e9a87SYuri Pankov 			parent->expectargs = 3;
903260e9a87SYuri Pankov 			parent->pos = EQNPOS_FROMTO;
904260e9a87SYuri Pankov 			break;
905260e9a87SYuri Pankov 		}
906260e9a87SYuri Pankov 		switch (tok) {
907260e9a87SYuri Pankov 		case (EQN_TOK_FROM):
908260e9a87SYuri Pankov 			pos = EQNPOS_FROM;
909260e9a87SYuri Pankov 			break;
910260e9a87SYuri Pankov 		case (EQN_TOK_TO):
911260e9a87SYuri Pankov 			pos = EQNPOS_TO;
912260e9a87SYuri Pankov 			break;
913260e9a87SYuri Pankov 		case (EQN_TOK_SUP):
914260e9a87SYuri Pankov 			pos = EQNPOS_SUP;
915260e9a87SYuri Pankov 			break;
916260e9a87SYuri Pankov 		case (EQN_TOK_SUB):
917260e9a87SYuri Pankov 			pos = EQNPOS_SUB;
918260e9a87SYuri Pankov 			break;
919260e9a87SYuri Pankov 		default:
920260e9a87SYuri Pankov 			abort();
921260e9a87SYuri Pankov 		}
922260e9a87SYuri Pankov 		parent = eqn_box_makebinary(ep, pos, parent);
923260e9a87SYuri Pankov 		break;
924260e9a87SYuri Pankov 	case (EQN_TOK_SQRT):
925260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
926260e9a87SYuri Pankov 			parent = parent->parent;
927260e9a87SYuri Pankov 		/*
928260e9a87SYuri Pankov 		 * Accept a left-right-associative set of arguments just
929260e9a87SYuri Pankov 		 * like sub and sup and friends but without rebalancing
930260e9a87SYuri Pankov 		 * under a pivot.
931260e9a87SYuri Pankov 		 */
932260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
933260e9a87SYuri Pankov 		parent->type = EQN_SUBEXPR;
934260e9a87SYuri Pankov 		parent->pos = EQNPOS_SQRT;
935260e9a87SYuri Pankov 		parent->expectargs = 1;
936260e9a87SYuri Pankov 		break;
937260e9a87SYuri Pankov 	case (EQN_TOK_OVER):
938260e9a87SYuri Pankov 		/*
939260e9a87SYuri Pankov 		 * We have a right-left-associative fraction.
940260e9a87SYuri Pankov 		 * Close out anything that's currently open, then
941260e9a87SYuri Pankov 		 * rebalance and continue reading.
942260e9a87SYuri Pankov 		 */
943260e9a87SYuri Pankov 		if (parent->last == NULL) {
944260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
945260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
946260e9a87SYuri Pankov 			cur = eqn_box_alloc(ep, parent);
947260e9a87SYuri Pankov 			cur->type = EQN_TEXT;
948260e9a87SYuri Pankov 			cur->text = mandoc_strdup("");
949260e9a87SYuri Pankov 		}
950260e9a87SYuri Pankov 		while (EQN_SUBEXPR == parent->type)
951260e9a87SYuri Pankov 			parent = parent->parent;
952260e9a87SYuri Pankov 		parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent);
953260e9a87SYuri Pankov 		break;
954260e9a87SYuri Pankov 	case (EQN_TOK_RIGHT):
955260e9a87SYuri Pankov 	case (EQN_TOK_BRACE_CLOSE):
956260e9a87SYuri Pankov 		/*
957260e9a87SYuri Pankov 		 * Close out the existing brace.
958260e9a87SYuri Pankov 		 * FIXME: this is a shitty sentinel: we should really
959260e9a87SYuri Pankov 		 * have a native EQN_BRACE type or whatnot.
960260e9a87SYuri Pankov 		 */
961260e9a87SYuri Pankov 		for (cur = parent; cur != NULL; cur = cur->parent)
962260e9a87SYuri Pankov 			if (cur->type == EQN_LIST &&
963260e9a87SYuri Pankov 			    (tok == EQN_TOK_BRACE_CLOSE ||
964260e9a87SYuri Pankov 			     cur->left != NULL))
965260e9a87SYuri Pankov 				break;
966260e9a87SYuri Pankov 		if (cur == NULL) {
967260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
968260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
969260e9a87SYuri Pankov 			break;
970260e9a87SYuri Pankov 		}
971260e9a87SYuri Pankov 		parent = cur;
972260e9a87SYuri Pankov 		if (EQN_TOK_RIGHT == tok) {
973260e9a87SYuri Pankov 			if (NULL == (start = eqn_nexttok(ep, &sz))) {
974260e9a87SYuri Pankov 				mandoc_msg(MANDOCERR_REQ_EMPTY,
975260e9a87SYuri Pankov 				    ep->parse, ep->eqn.ln,
976260e9a87SYuri Pankov 				    ep->eqn.pos, eqn_toks[tok]);
977260e9a87SYuri Pankov 				break;
978260e9a87SYuri Pankov 			}
979260e9a87SYuri Pankov 			/* Handling depends on right/left. */
980260e9a87SYuri Pankov 			if (STRNEQ(start, sz, "ceiling", 7)) {
981260e9a87SYuri Pankov 				strlcpy(sym, "\\[rc]", sizeof(sym));
982260e9a87SYuri Pankov 				parent->right = mandoc_strdup(sym);
983260e9a87SYuri Pankov 			} else if (STRNEQ(start, sz, "floor", 5)) {
984260e9a87SYuri Pankov 				strlcpy(sym, "\\[rf]", sizeof(sym));
985260e9a87SYuri Pankov 				parent->right = mandoc_strdup(sym);
986260e9a87SYuri Pankov 			} else
987260e9a87SYuri Pankov 				parent->right = mandoc_strndup(start, sz);
988260e9a87SYuri Pankov 		}
989260e9a87SYuri Pankov 		parent = parent->parent;
990*371584c2SYuri Pankov 		if (tok == EQN_TOK_BRACE_CLOSE &&
991260e9a87SYuri Pankov 		    (parent->type == EQN_PILE ||
992260e9a87SYuri Pankov 		     parent->type == EQN_MATRIX))
993260e9a87SYuri Pankov 			parent = parent->parent;
994260e9a87SYuri Pankov 		/* Close out any "singleton" lists. */
995260e9a87SYuri Pankov 		while (parent->type == EQN_LISTONE &&
996260e9a87SYuri Pankov 		    parent->args == parent->expectargs)
997260e9a87SYuri Pankov 			parent = parent->parent;
998260e9a87SYuri Pankov 		break;
999260e9a87SYuri Pankov 	case (EQN_TOK_BRACE_OPEN):
1000260e9a87SYuri Pankov 	case (EQN_TOK_LEFT):
1001260e9a87SYuri Pankov 		/*
1002260e9a87SYuri Pankov 		 * If we already have something in the stack and we're
1003260e9a87SYuri Pankov 		 * in an expression, then rewind til we're not any more
1004260e9a87SYuri Pankov 		 * (just like with the text node).
1005260e9a87SYuri Pankov 		 */
1006260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1007260e9a87SYuri Pankov 			parent = parent->parent;
1008260e9a87SYuri Pankov 		if (EQN_TOK_LEFT == tok &&
1009260e9a87SYuri Pankov 		    (start = eqn_nexttok(ep, &sz)) == NULL) {
1010260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
1011260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
1012260e9a87SYuri Pankov 			break;
1013260e9a87SYuri Pankov 		}
1014260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
1015260e9a87SYuri Pankov 		parent->type = EQN_LIST;
1016260e9a87SYuri Pankov 		if (EQN_TOK_LEFT == tok) {
1017260e9a87SYuri Pankov 			if (STRNEQ(start, sz, "ceiling", 7)) {
1018260e9a87SYuri Pankov 				strlcpy(sym, "\\[lc]", sizeof(sym));
1019260e9a87SYuri Pankov 				parent->left = mandoc_strdup(sym);
1020260e9a87SYuri Pankov 			} else if (STRNEQ(start, sz, "floor", 5)) {
1021260e9a87SYuri Pankov 				strlcpy(sym, "\\[lf]", sizeof(sym));
1022260e9a87SYuri Pankov 				parent->left = mandoc_strdup(sym);
1023260e9a87SYuri Pankov 			} else
1024260e9a87SYuri Pankov 				parent->left = mandoc_strndup(start, sz);
1025260e9a87SYuri Pankov 		}
1026260e9a87SYuri Pankov 		break;
1027260e9a87SYuri Pankov 	case (EQN_TOK_PILE):
1028260e9a87SYuri Pankov 	case (EQN_TOK_LPILE):
1029260e9a87SYuri Pankov 	case (EQN_TOK_RPILE):
1030260e9a87SYuri Pankov 	case (EQN_TOK_CPILE):
1031260e9a87SYuri Pankov 	case (EQN_TOK_CCOL):
1032260e9a87SYuri Pankov 	case (EQN_TOK_LCOL):
1033260e9a87SYuri Pankov 	case (EQN_TOK_RCOL):
1034260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1035260e9a87SYuri Pankov 			parent = parent->parent;
1036260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
1037260e9a87SYuri Pankov 		parent->type = EQN_PILE;
1038260e9a87SYuri Pankov 		parent->expectargs = 1;
1039260e9a87SYuri Pankov 		break;
1040260e9a87SYuri Pankov 	case (EQN_TOK_ABOVE):
1041260e9a87SYuri Pankov 		for (cur = parent; cur != NULL; cur = cur->parent)
1042260e9a87SYuri Pankov 			if (cur->type == EQN_PILE)
1043260e9a87SYuri Pankov 				break;
1044260e9a87SYuri Pankov 		if (cur == NULL) {
1045260e9a87SYuri Pankov 			mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
1046260e9a87SYuri Pankov 			    ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
1047260e9a87SYuri Pankov 			break;
1048260e9a87SYuri Pankov 		}
1049260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, cur);
1050260e9a87SYuri Pankov 		parent->type = EQN_LIST;
1051260e9a87SYuri Pankov 		break;
1052260e9a87SYuri Pankov 	case (EQN_TOK_MATRIX):
1053260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1054260e9a87SYuri Pankov 			parent = parent->parent;
1055260e9a87SYuri Pankov 		parent = eqn_box_alloc(ep, parent);
1056260e9a87SYuri Pankov 		parent->type = EQN_MATRIX;
1057260e9a87SYuri Pankov 		parent->expectargs = 1;
1058260e9a87SYuri Pankov 		break;
1059260e9a87SYuri Pankov 	case (EQN_TOK_EOF):
1060260e9a87SYuri Pankov 		/*
1061260e9a87SYuri Pankov 		 * End of file!
1062260e9a87SYuri Pankov 		 * TODO: make sure we're not in an open subexpression.
1063260e9a87SYuri Pankov 		 */
1064*371584c2SYuri Pankov 		return ROFF_EQN;
1065260e9a87SYuri Pankov 	default:
1066260e9a87SYuri Pankov 		assert(tok == EQN_TOK__MAX);
1067260e9a87SYuri Pankov 		assert(NULL != p);
1068260e9a87SYuri Pankov 		/*
1069260e9a87SYuri Pankov 		 * If we already have something in the stack and we're
1070260e9a87SYuri Pankov 		 * in an expression, then rewind til we're not any more.
1071260e9a87SYuri Pankov 		 */
1072260e9a87SYuri Pankov 		while (parent->args == parent->expectargs)
1073260e9a87SYuri Pankov 			parent = parent->parent;
1074260e9a87SYuri Pankov 		cur = eqn_box_alloc(ep, parent);
1075260e9a87SYuri Pankov 		cur->type = EQN_TEXT;
1076260e9a87SYuri Pankov 		for (i = 0; i < EQNSYM__MAX; i++)
1077260e9a87SYuri Pankov 			if (0 == strcmp(eqnsyms[i].str, p)) {
1078260e9a87SYuri Pankov 				(void)snprintf(sym, sizeof(sym),
1079260e9a87SYuri Pankov 					"\\[%s]", eqnsyms[i].sym);
1080260e9a87SYuri Pankov 				cur->text = mandoc_strdup(sym);
1081260e9a87SYuri Pankov 				free(p);
1082260e9a87SYuri Pankov 				break;
1083260e9a87SYuri Pankov 			}
1084260e9a87SYuri Pankov 
1085260e9a87SYuri Pankov 		if (i == EQNSYM__MAX)
1086260e9a87SYuri Pankov 			cur->text = p;
1087260e9a87SYuri Pankov 		/*
1088260e9a87SYuri Pankov 		 * Post-process list status.
1089260e9a87SYuri Pankov 		 */
1090260e9a87SYuri Pankov 		while (parent->type == EQN_LISTONE &&
1091260e9a87SYuri Pankov 		    parent->args == parent->expectargs)
1092260e9a87SYuri Pankov 			parent = parent->parent;
1093260e9a87SYuri Pankov 		break;
1094260e9a87SYuri Pankov 	}
1095260e9a87SYuri Pankov 	goto next_tok;
1096260e9a87SYuri Pankov }
1097260e9a87SYuri Pankov 
1098260e9a87SYuri Pankov enum rofferr
1099260e9a87SYuri Pankov eqn_end(struct eqn_node **epp)
110095c635efSGarrett D'Amore {
1101260e9a87SYuri Pankov 	struct eqn_node	*ep;
110295c635efSGarrett D'Amore 
1103260e9a87SYuri Pankov 	ep = *epp;
1104260e9a87SYuri Pankov 	*epp = NULL;
110595c635efSGarrett D'Amore 
1106260e9a87SYuri Pankov 	ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));
1107260e9a87SYuri Pankov 	ep->eqn.root->expectargs = UINT_MAX;
1108*371584c2SYuri Pankov 	return eqn_parse(ep, ep->eqn.root);
110995c635efSGarrett D'Amore }
111095c635efSGarrett D'Amore 
1111260e9a87SYuri Pankov void
1112260e9a87SYuri Pankov eqn_free(struct eqn_node *p)
111395c635efSGarrett D'Amore {
111495c635efSGarrett D'Amore 	int		 i;
111595c635efSGarrett D'Amore 
1116260e9a87SYuri Pankov 	eqn_box_free(p->eqn.root);
111795c635efSGarrett D'Amore 
1118260e9a87SYuri Pankov 	for (i = 0; i < (int)p->defsz; i++) {
1119260e9a87SYuri Pankov 		free(p->defs[i].key);
1120260e9a87SYuri Pankov 		free(p->defs[i].val);
1121260e9a87SYuri Pankov 	}
1122260e9a87SYuri Pankov 
1123260e9a87SYuri Pankov 	free(p->data);
1124260e9a87SYuri Pankov 	free(p->defs);
1125260e9a87SYuri Pankov 	free(p);
112695c635efSGarrett D'Amore }
1127