1*d9a51c35Sjmc /* $OpenBSD: eqn.c,v 1.49 2022/12/26 19:16:02 jmc Exp $ */
2883d305eSschwarze /*
35030fd5bSschwarze * Copyright (c) 2014, 2015, 2017, 2018, 2020, 2022
45030fd5bSschwarze * Ingo Schwarze <schwarze@openbsd.org>
5a3108a0aSschwarze * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
6883d305eSschwarze *
7883d305eSschwarze * Permission to use, copy, modify, and distribute this software for any
8883d305eSschwarze * purpose with or without fee is hereby granted, provided that the above
9883d305eSschwarze * copyright notice and this permission notice appear in all copies.
10883d305eSschwarze *
11883d305eSschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12883d305eSschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13883d305eSschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14883d305eSschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15883d305eSschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16883d305eSschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17883d305eSschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18883d305eSschwarze */
19f8617809Sschwarze #include <sys/types.h>
20f8617809Sschwarze
21883d305eSschwarze #include <assert.h>
2259c226a5Sschwarze #include <ctype.h>
23f8618d99Sschwarze #include <limits.h>
24f8618d99Sschwarze #include <stdio.h>
25883d305eSschwarze #include <stdlib.h>
26883d305eSschwarze #include <string.h>
2704e980cbSschwarze #include <time.h>
28883d305eSschwarze
294f4f7972Sschwarze #include "mandoc_aux.h"
30bf9acac6Sschwarze #include "mandoc.h"
31bf9acac6Sschwarze #include "roff.h"
32d4c8d4a3Sschwarze #include "eqn.h"
33883d305eSschwarze #include "libmandoc.h"
34abe31b56Sschwarze #include "eqn_parse.h"
35883d305eSschwarze
36f8618d99Sschwarze #define EQN_NEST_MAX 128 /* maximum nesting of defines */
37f8617809Sschwarze #define STRNEQ(p1, sz1, p2, sz2) \
38f8617809Sschwarze ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
39f8618d99Sschwarze
40f8617809Sschwarze enum eqn_tok {
41f8617809Sschwarze EQN_TOK_DYAD = 0,
42f8617809Sschwarze EQN_TOK_VEC,
43f8617809Sschwarze EQN_TOK_UNDER,
44f8617809Sschwarze EQN_TOK_BAR,
45f8617809Sschwarze EQN_TOK_TILDE,
46f8617809Sschwarze EQN_TOK_HAT,
47f8617809Sschwarze EQN_TOK_DOT,
48f8617809Sschwarze EQN_TOK_DOTDOT,
49f8617809Sschwarze EQN_TOK_FWD,
50f8617809Sschwarze EQN_TOK_BACK,
51f8617809Sschwarze EQN_TOK_DOWN,
52f8617809Sschwarze EQN_TOK_UP,
53f8617809Sschwarze EQN_TOK_FAT,
54f8617809Sschwarze EQN_TOK_ROMAN,
55f8617809Sschwarze EQN_TOK_ITALIC,
56f8617809Sschwarze EQN_TOK_BOLD,
57f8617809Sschwarze EQN_TOK_SIZE,
58f8617809Sschwarze EQN_TOK_SUB,
59f8617809Sschwarze EQN_TOK_SUP,
60f8617809Sschwarze EQN_TOK_SQRT,
61f8617809Sschwarze EQN_TOK_OVER,
62f8617809Sschwarze EQN_TOK_FROM,
63f8617809Sschwarze EQN_TOK_TO,
64f8617809Sschwarze EQN_TOK_BRACE_OPEN,
65f8617809Sschwarze EQN_TOK_BRACE_CLOSE,
66f8617809Sschwarze EQN_TOK_GSIZE,
67f8617809Sschwarze EQN_TOK_GFONT,
68f8617809Sschwarze EQN_TOK_MARK,
69f8617809Sschwarze EQN_TOK_LINEUP,
70f8617809Sschwarze EQN_TOK_LEFT,
71f8617809Sschwarze EQN_TOK_RIGHT,
72f8617809Sschwarze EQN_TOK_PILE,
73f8617809Sschwarze EQN_TOK_LPILE,
74f8617809Sschwarze EQN_TOK_RPILE,
75f8617809Sschwarze EQN_TOK_CPILE,
76f8617809Sschwarze EQN_TOK_MATRIX,
77f8617809Sschwarze EQN_TOK_CCOL,
78f8617809Sschwarze EQN_TOK_LCOL,
79f8617809Sschwarze EQN_TOK_RCOL,
80f8617809Sschwarze EQN_TOK_DELIM,
81f8617809Sschwarze EQN_TOK_DEFINE,
82f8617809Sschwarze EQN_TOK_TDEFINE,
83f8617809Sschwarze EQN_TOK_NDEFINE,
84f8617809Sschwarze EQN_TOK_UNDEF,
85f8617809Sschwarze EQN_TOK_ABOVE,
8666b13c11Sschwarze EQN_TOK__MAX,
8766b13c11Sschwarze EQN_TOK_FUNC,
8821b46522Sschwarze EQN_TOK_QUOTED,
8921b46522Sschwarze EQN_TOK_SYM,
9066b13c11Sschwarze EQN_TOK_EOF
91f8617809Sschwarze };
92f8617809Sschwarze
93f8617809Sschwarze static const char *eqn_toks[EQN_TOK__MAX] = {
94f8617809Sschwarze "dyad", /* EQN_TOK_DYAD */
95f8617809Sschwarze "vec", /* EQN_TOK_VEC */
96f8617809Sschwarze "under", /* EQN_TOK_UNDER */
97f8617809Sschwarze "bar", /* EQN_TOK_BAR */
98f8617809Sschwarze "tilde", /* EQN_TOK_TILDE */
99f8617809Sschwarze "hat", /* EQN_TOK_HAT */
100f8617809Sschwarze "dot", /* EQN_TOK_DOT */
101f8617809Sschwarze "dotdot", /* EQN_TOK_DOTDOT */
102f8617809Sschwarze "fwd", /* EQN_TOK_FWD * */
103f8617809Sschwarze "back", /* EQN_TOK_BACK */
104f8617809Sschwarze "down", /* EQN_TOK_DOWN */
105f8617809Sschwarze "up", /* EQN_TOK_UP */
106f8617809Sschwarze "fat", /* EQN_TOK_FAT */
107f8617809Sschwarze "roman", /* EQN_TOK_ROMAN */
108f8617809Sschwarze "italic", /* EQN_TOK_ITALIC */
109f8617809Sschwarze "bold", /* EQN_TOK_BOLD */
110f8617809Sschwarze "size", /* EQN_TOK_SIZE */
111f8617809Sschwarze "sub", /* EQN_TOK_SUB */
112f8617809Sschwarze "sup", /* EQN_TOK_SUP */
113f8617809Sschwarze "sqrt", /* EQN_TOK_SQRT */
114f8617809Sschwarze "over", /* EQN_TOK_OVER */
115f8617809Sschwarze "from", /* EQN_TOK_FROM */
116f8617809Sschwarze "to", /* EQN_TOK_TO */
117f8617809Sschwarze "{", /* EQN_TOK_BRACE_OPEN */
118f8617809Sschwarze "}", /* EQN_TOK_BRACE_CLOSE */
119f8617809Sschwarze "gsize", /* EQN_TOK_GSIZE */
120f8617809Sschwarze "gfont", /* EQN_TOK_GFONT */
121f8617809Sschwarze "mark", /* EQN_TOK_MARK */
122f8617809Sschwarze "lineup", /* EQN_TOK_LINEUP */
123f8617809Sschwarze "left", /* EQN_TOK_LEFT */
124f8617809Sschwarze "right", /* EQN_TOK_RIGHT */
125f8617809Sschwarze "pile", /* EQN_TOK_PILE */
126f8617809Sschwarze "lpile", /* EQN_TOK_LPILE */
127f8617809Sschwarze "rpile", /* EQN_TOK_RPILE */
128f8617809Sschwarze "cpile", /* EQN_TOK_CPILE */
129f8617809Sschwarze "matrix", /* EQN_TOK_MATRIX */
130f8617809Sschwarze "ccol", /* EQN_TOK_CCOL */
131f8617809Sschwarze "lcol", /* EQN_TOK_LCOL */
132f8617809Sschwarze "rcol", /* EQN_TOK_RCOL */
133f8617809Sschwarze "delim", /* EQN_TOK_DELIM */
134f8617809Sschwarze "define", /* EQN_TOK_DEFINE */
135f8617809Sschwarze "tdefine", /* EQN_TOK_TDEFINE */
136f8617809Sschwarze "ndefine", /* EQN_TOK_NDEFINE */
137f8617809Sschwarze "undef", /* EQN_TOK_UNDEF */
138f8617809Sschwarze "above", /* EQN_TOK_ABOVE */
139f8618d99Sschwarze };
140f8618d99Sschwarze
14166b13c11Sschwarze static const char *const eqn_func[] = {
14266b13c11Sschwarze "acos", "acsc", "and", "arc", "asec", "asin", "atan",
14366b13c11Sschwarze "cos", "cosh", "coth", "csc", "det", "exp", "for",
14466b13c11Sschwarze "if", "lim", "ln", "log", "max", "min",
14566b13c11Sschwarze "sec", "sin", "sinh", "tan", "tanh", "Im", "Re",
14666b13c11Sschwarze };
14766b13c11Sschwarze
148f8618d99Sschwarze enum eqn_symt {
1496538659fSschwarze EQNSYM_alpha = 0,
150f8618d99Sschwarze EQNSYM_beta,
151f8618d99Sschwarze EQNSYM_chi,
152f8618d99Sschwarze EQNSYM_delta,
153f8618d99Sschwarze EQNSYM_epsilon,
154f8618d99Sschwarze EQNSYM_eta,
155f8618d99Sschwarze EQNSYM_gamma,
156f8618d99Sschwarze EQNSYM_iota,
157f8618d99Sschwarze EQNSYM_kappa,
158f8618d99Sschwarze EQNSYM_lambda,
159f8618d99Sschwarze EQNSYM_mu,
160f8618d99Sschwarze EQNSYM_nu,
161f8618d99Sschwarze EQNSYM_omega,
162f8618d99Sschwarze EQNSYM_omicron,
163f8618d99Sschwarze EQNSYM_phi,
164f8618d99Sschwarze EQNSYM_pi,
165f8618d99Sschwarze EQNSYM_ps,
166f8618d99Sschwarze EQNSYM_rho,
167f8618d99Sschwarze EQNSYM_sigma,
168f8618d99Sschwarze EQNSYM_tau,
169f8618d99Sschwarze EQNSYM_theta,
170f8618d99Sschwarze EQNSYM_upsilon,
171f8618d99Sschwarze EQNSYM_xi,
172f8618d99Sschwarze EQNSYM_zeta,
173f8618d99Sschwarze EQNSYM_DELTA,
174f8618d99Sschwarze EQNSYM_GAMMA,
175f8618d99Sschwarze EQNSYM_LAMBDA,
176f8618d99Sschwarze EQNSYM_OMEGA,
177f8618d99Sschwarze EQNSYM_PHI,
178f8618d99Sschwarze EQNSYM_PI,
179f8618d99Sschwarze EQNSYM_PSI,
180f8618d99Sschwarze EQNSYM_SIGMA,
181f8618d99Sschwarze EQNSYM_THETA,
182f8618d99Sschwarze EQNSYM_UPSILON,
183f8618d99Sschwarze EQNSYM_XI,
184f8618d99Sschwarze EQNSYM_inter,
185f8618d99Sschwarze EQNSYM_union,
186f8618d99Sschwarze EQNSYM_prod,
187f8618d99Sschwarze EQNSYM_int,
188f8618d99Sschwarze EQNSYM_sum,
189f8618d99Sschwarze EQNSYM_grad,
190f8618d99Sschwarze EQNSYM_del,
191f8618d99Sschwarze EQNSYM_times,
192f8618d99Sschwarze EQNSYM_cdot,
193f8618d99Sschwarze EQNSYM_nothing,
194f8618d99Sschwarze EQNSYM_approx,
195f8618d99Sschwarze EQNSYM_prime,
196f8618d99Sschwarze EQNSYM_half,
197f8618d99Sschwarze EQNSYM_partial,
198f8618d99Sschwarze EQNSYM_inf,
199f8618d99Sschwarze EQNSYM_muchgreat,
200f8618d99Sschwarze EQNSYM_muchless,
201f8618d99Sschwarze EQNSYM_larrow,
202f8618d99Sschwarze EQNSYM_rarrow,
203f8618d99Sschwarze EQNSYM_pm,
204f8618d99Sschwarze EQNSYM_nequal,
205f8618d99Sschwarze EQNSYM_equiv,
206f8618d99Sschwarze EQNSYM_lessequal,
207f8618d99Sschwarze EQNSYM_moreequal,
208edec86bbSbentley EQNSYM_minus,
209f8618d99Sschwarze EQNSYM__MAX
210f8618d99Sschwarze };
211f8618d99Sschwarze
212f8618d99Sschwarze struct eqnsym {
213f8617809Sschwarze const char *str;
214f8618d99Sschwarze const char *sym;
215f8618d99Sschwarze };
216f8618d99Sschwarze
217f8618d99Sschwarze static const struct eqnsym eqnsyms[EQNSYM__MAX] = {
218f8617809Sschwarze { "alpha", "*a" }, /* EQNSYM_alpha */
219f8617809Sschwarze { "beta", "*b" }, /* EQNSYM_beta */
220f8617809Sschwarze { "chi", "*x" }, /* EQNSYM_chi */
221f8617809Sschwarze { "delta", "*d" }, /* EQNSYM_delta */
222f8617809Sschwarze { "epsilon", "*e" }, /* EQNSYM_epsilon */
223f8617809Sschwarze { "eta", "*y" }, /* EQNSYM_eta */
224f8617809Sschwarze { "gamma", "*g" }, /* EQNSYM_gamma */
225f8617809Sschwarze { "iota", "*i" }, /* EQNSYM_iota */
226f8617809Sschwarze { "kappa", "*k" }, /* EQNSYM_kappa */
227f8617809Sschwarze { "lambda", "*l" }, /* EQNSYM_lambda */
228f8617809Sschwarze { "mu", "*m" }, /* EQNSYM_mu */
229f8617809Sschwarze { "nu", "*n" }, /* EQNSYM_nu */
230f8617809Sschwarze { "omega", "*w" }, /* EQNSYM_omega */
231f8617809Sschwarze { "omicron", "*o" }, /* EQNSYM_omicron */
232f8617809Sschwarze { "phi", "*f" }, /* EQNSYM_phi */
233f8617809Sschwarze { "pi", "*p" }, /* EQNSYM_pi */
234f8617809Sschwarze { "psi", "*q" }, /* EQNSYM_psi */
235f8617809Sschwarze { "rho", "*r" }, /* EQNSYM_rho */
236f8617809Sschwarze { "sigma", "*s" }, /* EQNSYM_sigma */
237f8617809Sschwarze { "tau", "*t" }, /* EQNSYM_tau */
238f8617809Sschwarze { "theta", "*h" }, /* EQNSYM_theta */
239f8617809Sschwarze { "upsilon", "*u" }, /* EQNSYM_upsilon */
240f8617809Sschwarze { "xi", "*c" }, /* EQNSYM_xi */
241f8617809Sschwarze { "zeta", "*z" }, /* EQNSYM_zeta */
242f8617809Sschwarze { "DELTA", "*D" }, /* EQNSYM_DELTA */
243f8617809Sschwarze { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
244f8617809Sschwarze { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
245f8617809Sschwarze { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
246f8617809Sschwarze { "PHI", "*F" }, /* EQNSYM_PHI */
247f8617809Sschwarze { "PI", "*P" }, /* EQNSYM_PI */
248f8617809Sschwarze { "PSI", "*Q" }, /* EQNSYM_PSI */
249f8617809Sschwarze { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
250f8617809Sschwarze { "THETA", "*H" }, /* EQNSYM_THETA */
251f8617809Sschwarze { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
252f8617809Sschwarze { "XI", "*C" }, /* EQNSYM_XI */
253f8617809Sschwarze { "inter", "ca" }, /* EQNSYM_inter */
254f8617809Sschwarze { "union", "cu" }, /* EQNSYM_union */
255f8617809Sschwarze { "prod", "product" }, /* EQNSYM_prod */
256f8617809Sschwarze { "int", "integral" }, /* EQNSYM_int */
257f8617809Sschwarze { "sum", "sum" }, /* EQNSYM_sum */
258f8617809Sschwarze { "grad", "gr" }, /* EQNSYM_grad */
259f8617809Sschwarze { "del", "gr" }, /* EQNSYM_del */
260f8617809Sschwarze { "times", "mu" }, /* EQNSYM_times */
261f8617809Sschwarze { "cdot", "pc" }, /* EQNSYM_cdot */
262f8617809Sschwarze { "nothing", "&" }, /* EQNSYM_nothing */
263f8617809Sschwarze { "approx", "~~" }, /* EQNSYM_approx */
264edec86bbSbentley { "prime", "fm" }, /* EQNSYM_prime */
265f8617809Sschwarze { "half", "12" }, /* EQNSYM_half */
266f8617809Sschwarze { "partial", "pd" }, /* EQNSYM_partial */
267f8617809Sschwarze { "inf", "if" }, /* EQNSYM_inf */
268f8617809Sschwarze { ">>", ">>" }, /* EQNSYM_muchgreat */
269f8617809Sschwarze { "<<", "<<" }, /* EQNSYM_muchless */
270f8617809Sschwarze { "<-", "<-" }, /* EQNSYM_larrow */
271f8617809Sschwarze { "->", "->" }, /* EQNSYM_rarrow */
272f8617809Sschwarze { "+-", "+-" }, /* EQNSYM_pm */
273f8617809Sschwarze { "!=", "!=" }, /* EQNSYM_nequal */
274f8617809Sschwarze { "==", "==" }, /* EQNSYM_equiv */
275f8617809Sschwarze { "<=", "<=" }, /* EQNSYM_lessequal */
276f8617809Sschwarze { ">=", ">=" }, /* EQNSYM_moreequal */
277edec86bbSbentley { "-", "mi" }, /* EQNSYM_minus */
278f8618d99Sschwarze };
279f8618d99Sschwarze
2806538659fSschwarze enum parse_mode {
2816538659fSschwarze MODE_QUOTED,
2826538659fSschwarze MODE_NOSUB,
2836538659fSschwarze MODE_SUB,
2846538659fSschwarze MODE_TOK
2856538659fSschwarze };
2866538659fSschwarze
287abe31b56Sschwarze struct eqn_def {
288abe31b56Sschwarze char *key;
289abe31b56Sschwarze size_t keysz;
290abe31b56Sschwarze char *val;
291abe31b56Sschwarze size_t valsz;
292abe31b56Sschwarze };
293abe31b56Sschwarze
2943578e616Sschwarze static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *);
2953578e616Sschwarze static struct eqn_box *eqn_box_makebinary(struct eqn_node *,
296244ed75aSschwarze struct eqn_box *);
2973578e616Sschwarze static void eqn_def(struct eqn_node *);
2986538659fSschwarze static struct eqn_def *eqn_def_find(struct eqn_node *);
2993578e616Sschwarze static void eqn_delim(struct eqn_node *);
3006538659fSschwarze static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode);
3013578e616Sschwarze static void eqn_undef(struct eqn_node *);
3023578e616Sschwarze
3033578e616Sschwarze
304883d305eSschwarze struct eqn_node *
eqn_alloc(void)30591305757Sschwarze eqn_alloc(void)
306883d305eSschwarze {
307bf9acac6Sschwarze struct eqn_node *ep;
308883d305eSschwarze
309bf9acac6Sschwarze ep = mandoc_calloc(1, sizeof(*ep));
310bf9acac6Sschwarze ep->gsize = EQN_DEFSIZE;
311bf9acac6Sschwarze return ep;
312bf9acac6Sschwarze }
313f8618d99Sschwarze
314bf9acac6Sschwarze void
eqn_reset(struct eqn_node * ep)315bf9acac6Sschwarze eqn_reset(struct eqn_node *ep)
316bf9acac6Sschwarze {
317bf9acac6Sschwarze free(ep->data);
318bf9acac6Sschwarze ep->data = ep->start = ep->end = NULL;
319bf9acac6Sschwarze ep->sz = ep->toksz = 0;
320bf9acac6Sschwarze }
321883d305eSschwarze
322bf9acac6Sschwarze void
eqn_read(struct eqn_node * ep,const char * p)323bf9acac6Sschwarze eqn_read(struct eqn_node *ep, const char *p)
324bf9acac6Sschwarze {
325bf9acac6Sschwarze char *cp;
326bf9acac6Sschwarze
327bf9acac6Sschwarze if (ep->data == NULL) {
328bf9acac6Sschwarze ep->sz = strlen(p);
329bf9acac6Sschwarze ep->data = mandoc_strdup(p);
330bf9acac6Sschwarze } else {
331bf9acac6Sschwarze ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p);
332bf9acac6Sschwarze free(ep->data);
333bf9acac6Sschwarze ep->data = cp;
334bf9acac6Sschwarze }
335bf9acac6Sschwarze ep->sz += 1;
336883d305eSschwarze }
337883d305eSschwarze
3388aae8206Sschwarze /*
339f8617809Sschwarze * Find the key "key" of the give size within our eqn-defined values.
3408aae8206Sschwarze */
341f8617809Sschwarze static struct eqn_def *
eqn_def_find(struct eqn_node * ep)3426538659fSschwarze eqn_def_find(struct eqn_node *ep)
343883d305eSschwarze {
344f8618d99Sschwarze int i;
345883d305eSschwarze
346f8617809Sschwarze for (i = 0; i < (int)ep->defsz; i++)
347f8617809Sschwarze if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key,
3486538659fSschwarze ep->defs[i].keysz, ep->start, ep->toksz))
349526e306bSschwarze return &ep->defs[i];
350f8618d99Sschwarze
351526e306bSschwarze return NULL;
352f8618d99Sschwarze }
353f8618d99Sschwarze
354f8617809Sschwarze /*
3556538659fSschwarze * Parse a token from the input text. The modes are:
3566538659fSschwarze * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
357*d9a51c35Sjmc * before its next occurrence. Do not interpret the token in any
3586538659fSschwarze * way and return EQN_TOK_QUOTED. All other modes behave like
3596538659fSschwarze * MODE_QUOTED when *ep->start is '"'.
3606538659fSschwarze * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
3616538659fSschwarze * otherwise, it ends before the next whitespace or brace.
3626538659fSschwarze * Do not interpret the token and return EQN_TOK__MAX.
3636538659fSschwarze * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
3646538659fSschwarze * alias created with define. If it is an alias, replace it with
3656538659fSschwarze * its string value and reparse.
3666538659fSschwarze * MODE_TOK: Like MODE_SUB, but also check the token against the list
3676538659fSschwarze * of tokens, and if there is a match, return that token. Otherwise,
3686538659fSschwarze * if the token matches a symbol, return EQN_TOK_SYM; if it matches
3696538659fSschwarze * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for
3706538659fSschwarze * a token match, *ep->start is set to an allocated string that the
3716538659fSschwarze * caller is expected to free.
3726538659fSschwarze * All modes skip whitespace following the end of the token.
373f8617809Sschwarze */
374f8617809Sschwarze static enum eqn_tok
eqn_next(struct eqn_node * ep,enum parse_mode mode)3756538659fSschwarze eqn_next(struct eqn_node *ep, enum parse_mode mode)
376f8617809Sschwarze {
3776538659fSschwarze struct eqn_def *def;
3786538659fSschwarze size_t start;
3795030fd5bSschwarze int diff, i, newlen, quoted;
3806538659fSschwarze enum eqn_tok tok;
381f8617809Sschwarze
3826538659fSschwarze /*
3836538659fSschwarze * Reset the recursion counter after advancing
3845030fd5bSschwarze * beyond the end of the rightmost substitution.
3856538659fSschwarze */
3865030fd5bSschwarze if (ep->end - ep->data >= ep->sublen)
3875030fd5bSschwarze ep->subcnt = 0;
3886c081f56Sschwarze
3896538659fSschwarze ep->start = ep->end;
3906538659fSschwarze quoted = mode == MODE_QUOTED;
3916538659fSschwarze for (;;) {
3926538659fSschwarze switch (*ep->start) {
3936538659fSschwarze case '\0':
3946538659fSschwarze ep->toksz = 0;
395526e306bSschwarze return EQN_TOK_EOF;
3966538659fSschwarze case '"':
3976538659fSschwarze quoted = 1;
3986538659fSschwarze break;
3990f5f5b53Sschwarze case ' ':
4000f5f5b53Sschwarze case '\t':
4010f5f5b53Sschwarze case '~':
4020f5f5b53Sschwarze case '^':
4030f5f5b53Sschwarze if (quoted)
4040f5f5b53Sschwarze break;
4050f5f5b53Sschwarze ep->start++;
4060f5f5b53Sschwarze continue;
4076538659fSschwarze default:
4086538659fSschwarze break;
4096538659fSschwarze }
4106c081f56Sschwarze if (quoted) {
4116538659fSschwarze ep->end = strchr(ep->start + 1, *ep->start);
4126538659fSschwarze ep->start++; /* Skip opening quote. */
4136538659fSschwarze if (ep->end == NULL) {
414a5a5f808Sschwarze mandoc_msg(MANDOCERR_ARG_QUOTE,
415bf9acac6Sschwarze ep->node->line, ep->node->pos, NULL);
4166538659fSschwarze ep->end = strchr(ep->start, '\0');
4176538659fSschwarze }
4186538659fSschwarze } else {
4196538659fSschwarze ep->end = ep->start + 1;
4206538659fSschwarze if (*ep->start != '{' && *ep->start != '}')
4216538659fSschwarze ep->end += strcspn(ep->end, " ^~\"{}\t");
4226538659fSschwarze }
4236538659fSschwarze ep->toksz = ep->end - ep->start;
4246538659fSschwarze if (quoted && *ep->end != '\0')
4256538659fSschwarze ep->end++; /* Skip closing quote. */
4266538659fSschwarze while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL)
4276538659fSschwarze ep->end++;
4286538659fSschwarze if (quoted) /* Cannot return, may have to strndup. */
4296538659fSschwarze break;
4306538659fSschwarze if (mode == MODE_NOSUB)
4316538659fSschwarze return EQN_TOK__MAX;
4326538659fSschwarze if ((def = eqn_def_find(ep)) == NULL)
4336538659fSschwarze break;
4345030fd5bSschwarze if (++ep->subcnt > EQN_NEST_MAX) {
435a5a5f808Sschwarze mandoc_msg(MANDOCERR_ROFFLOOP,
436bf9acac6Sschwarze ep->node->line, ep->node->pos, NULL);
4375030fd5bSschwarze break;
4386c081f56Sschwarze }
4396c081f56Sschwarze
4406538659fSschwarze /* Replace a defined name with its string value. */
4416538659fSschwarze if ((diff = def->valsz - ep->toksz) > 0) {
4426538659fSschwarze start = ep->start - ep->data;
4436538659fSschwarze ep->sz += diff;
4446538659fSschwarze ep->data = mandoc_realloc(ep->data, ep->sz + 1);
4456538659fSschwarze ep->start = ep->data + start;
4465030fd5bSschwarze ep->sublen += diff;
4476538659fSschwarze }
4486538659fSschwarze if (diff)
4496538659fSschwarze memmove(ep->start + def->valsz, ep->start + ep->toksz,
4506538659fSschwarze strlen(ep->start + ep->toksz) + 1);
4516538659fSschwarze memcpy(ep->start, def->val, def->valsz);
4525030fd5bSschwarze newlen = ep->start - ep->data + def->valsz;
4535030fd5bSschwarze if (ep->sublen < newlen)
4545030fd5bSschwarze ep->sublen = newlen;
4556538659fSschwarze }
4566538659fSschwarze if (mode != MODE_TOK)
4576538659fSschwarze return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX;
4586538659fSschwarze if (quoted) {
4596538659fSschwarze ep->start = mandoc_strndup(ep->start, ep->toksz);
4606538659fSschwarze return EQN_TOK_QUOTED;
4616538659fSschwarze }
4626538659fSschwarze for (tok = 0; tok < EQN_TOK__MAX; tok++)
4636538659fSschwarze if (STRNEQ(ep->start, ep->toksz,
4646538659fSschwarze eqn_toks[tok], strlen(eqn_toks[tok])))
4656538659fSschwarze return tok;
466f8617809Sschwarze
467d5ed6f1bSschwarze for (i = 0; i < EQNSYM__MAX; i++) {
4686538659fSschwarze if (STRNEQ(ep->start, ep->toksz,
469d5ed6f1bSschwarze eqnsyms[i].str, strlen(eqnsyms[i].str))) {
4706538659fSschwarze mandoc_asprintf(&ep->start,
4716538659fSschwarze "\\[%s]", eqnsyms[i].sym);
47221b46522Sschwarze return EQN_TOK_SYM;
473d5ed6f1bSschwarze }
474d5ed6f1bSschwarze }
4756538659fSschwarze ep->start = mandoc_strndup(ep->start, ep->toksz);
4766538659fSschwarze for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++)
4776538659fSschwarze if (STRNEQ(ep->start, ep->toksz,
4786538659fSschwarze eqn_func[i], strlen(eqn_func[i])))
47966b13c11Sschwarze return EQN_TOK_FUNC;
48066b13c11Sschwarze return EQN_TOK__MAX;
481f8617809Sschwarze }
482f8617809Sschwarze
483bf9acac6Sschwarze void
eqn_box_free(struct eqn_box * bp)484f8617809Sschwarze eqn_box_free(struct eqn_box *bp)
485f8617809Sschwarze {
486abe31b56Sschwarze if (bp == NULL)
487abe31b56Sschwarze return;
488f8617809Sschwarze
489f8617809Sschwarze if (bp->first)
490f8617809Sschwarze eqn_box_free(bp->first);
491f8617809Sschwarze if (bp->next)
492f8617809Sschwarze eqn_box_free(bp->next);
493f8617809Sschwarze
494f8617809Sschwarze free(bp->text);
495f8617809Sschwarze free(bp->left);
496f8617809Sschwarze free(bp->right);
497f8617809Sschwarze free(bp->top);
498f8617809Sschwarze free(bp->bottom);
499f8617809Sschwarze free(bp);
500f8617809Sschwarze }
501f8617809Sschwarze
502d4c8d4a3Sschwarze struct eqn_box *
eqn_box_new(void)503d4c8d4a3Sschwarze eqn_box_new(void)
504d4c8d4a3Sschwarze {
505d4c8d4a3Sschwarze struct eqn_box *bp;
506d4c8d4a3Sschwarze
507d4c8d4a3Sschwarze bp = mandoc_calloc(1, sizeof(*bp));
508d4c8d4a3Sschwarze bp->expectargs = UINT_MAX;
509d4c8d4a3Sschwarze return bp;
510d4c8d4a3Sschwarze }
511d4c8d4a3Sschwarze
512f8617809Sschwarze /*
513f8617809Sschwarze * Allocate a box as the last child of the parent node.
514f8617809Sschwarze */
515f8617809Sschwarze static struct eqn_box *
eqn_box_alloc(struct eqn_node * ep,struct eqn_box * parent)516f8617809Sschwarze eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent)
517f8617809Sschwarze {
518f8617809Sschwarze struct eqn_box *bp;
519f8617809Sschwarze
520d4c8d4a3Sschwarze bp = eqn_box_new();
521f8617809Sschwarze bp->parent = parent;
522f8617809Sschwarze bp->parent->args++;
523a8c898eaSschwarze bp->font = bp->parent->font;
524f8617809Sschwarze bp->size = ep->gsize;
525f8617809Sschwarze
526f8617809Sschwarze if (NULL != parent->first) {
527f8617809Sschwarze parent->last->next = bp;
528f8617809Sschwarze bp->prev = parent->last;
529f8617809Sschwarze } else
530f8617809Sschwarze parent->first = bp;
531f8617809Sschwarze
532f8617809Sschwarze parent->last = bp;
533526e306bSschwarze return bp;
534f8617809Sschwarze }
535f8617809Sschwarze
536f8617809Sschwarze /*
537f8617809Sschwarze * Reparent the current last node (of the current parent) under a new
538f8617809Sschwarze * EQN_SUBEXPR as the first element.
539f8617809Sschwarze * Then return the new parent.
540f8617809Sschwarze * The new EQN_SUBEXPR will have a two-child limit.
541f8617809Sschwarze */
542f8617809Sschwarze static struct eqn_box *
eqn_box_makebinary(struct eqn_node * ep,struct eqn_box * parent)543244ed75aSschwarze eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent)
544f8617809Sschwarze {
545f8617809Sschwarze struct eqn_box *b, *newb;
546f8617809Sschwarze
547f8617809Sschwarze assert(NULL != parent->last);
548f8617809Sschwarze b = parent->last;
549f8617809Sschwarze if (parent->last == parent->first)
550f8617809Sschwarze parent->first = NULL;
551f8617809Sschwarze parent->args--;
552f8617809Sschwarze parent->last = b->prev;
553f8617809Sschwarze b->prev = NULL;
554f8617809Sschwarze newb = eqn_box_alloc(ep, parent);
555f8617809Sschwarze newb->type = EQN_SUBEXPR;
556f8617809Sschwarze newb->expectargs = 2;
557f8617809Sschwarze newb->args = 1;
558f8617809Sschwarze newb->first = newb->last = b;
559f8617809Sschwarze newb->first->next = NULL;
560f8617809Sschwarze b->parent = newb;
561526e306bSschwarze return newb;
562f8617809Sschwarze }
563f8617809Sschwarze
564f8617809Sschwarze /*
56557ca9bd5Sschwarze * Parse the "delim" control statement.
56657ca9bd5Sschwarze */
56757ca9bd5Sschwarze static void
eqn_delim(struct eqn_node * ep)56857ca9bd5Sschwarze eqn_delim(struct eqn_node *ep)
56957ca9bd5Sschwarze {
5706538659fSschwarze if (ep->end[0] == '\0' || ep->end[1] == '\0') {
571a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY,
572bf9acac6Sschwarze ep->node->line, ep->node->pos, "delim");
5736538659fSschwarze if (ep->end[0] != '\0')
5746538659fSschwarze ep->end++;
5756538659fSschwarze } else if (strncmp(ep->end, "off", 3) == 0) {
57657ca9bd5Sschwarze ep->delim = 0;
5776538659fSschwarze ep->end += 3;
5786538659fSschwarze } else if (strncmp(ep->end, "on", 2) == 0) {
57957ca9bd5Sschwarze if (ep->odelim && ep->cdelim)
58057ca9bd5Sschwarze ep->delim = 1;
5816538659fSschwarze ep->end += 2;
5826538659fSschwarze } else {
5836538659fSschwarze ep->odelim = *ep->end++;
5846538659fSschwarze ep->cdelim = *ep->end++;
58557ca9bd5Sschwarze ep->delim = 1;
58657ca9bd5Sschwarze }
58757ca9bd5Sschwarze }
58857ca9bd5Sschwarze
58957ca9bd5Sschwarze /*
590f8617809Sschwarze * Undefine a previously-defined string.
591f8617809Sschwarze */
5923578e616Sschwarze static void
eqn_undef(struct eqn_node * ep)593f8617809Sschwarze eqn_undef(struct eqn_node *ep)
594f8617809Sschwarze {
595f8617809Sschwarze struct eqn_def *def;
596f8617809Sschwarze
5976538659fSschwarze if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
598a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY,
599bf9acac6Sschwarze ep->node->line, ep->node->pos, "undef");
6003578e616Sschwarze return;
6013578e616Sschwarze }
6026538659fSschwarze if ((def = eqn_def_find(ep)) == NULL)
6033578e616Sschwarze return;
6043578e616Sschwarze free(def->key);
6053578e616Sschwarze free(def->val);
6063578e616Sschwarze def->key = def->val = NULL;
6073578e616Sschwarze def->keysz = def->valsz = 0;
608f8618d99Sschwarze }
609f8618d99Sschwarze
6103578e616Sschwarze static void
eqn_def(struct eqn_node * ep)611f8617809Sschwarze eqn_def(struct eqn_node *ep)
612f8618d99Sschwarze {
613f8618d99Sschwarze struct eqn_def *def;
614f8618d99Sschwarze int i;
615f8618d99Sschwarze
6166538659fSschwarze if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) {
617a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY,
618bf9acac6Sschwarze ep->node->line, ep->node->pos, "define");
6193578e616Sschwarze return;
620f8618d99Sschwarze }
621f8618d99Sschwarze
622f8618d99Sschwarze /*
623f8618d99Sschwarze * Search for a key that already exists.
624f8618d99Sschwarze * Create a new key if none is found.
625f8618d99Sschwarze */
6266538659fSschwarze if ((def = eqn_def_find(ep)) == NULL) {
627f8618d99Sschwarze /* Find holes in string array. */
628f8618d99Sschwarze for (i = 0; i < (int)ep->defsz; i++)
629f8618d99Sschwarze if (0 == ep->defs[i].keysz)
630f8618d99Sschwarze break;
631f8618d99Sschwarze
632f8618d99Sschwarze if (i == (int)ep->defsz) {
633f8618d99Sschwarze ep->defsz++;
6348286bf36Sschwarze ep->defs = mandoc_reallocarray(ep->defs,
6358286bf36Sschwarze ep->defsz, sizeof(struct eqn_def));
636f8618d99Sschwarze ep->defs[i].key = ep->defs[i].val = NULL;
637f8618d99Sschwarze }
638f8618d99Sschwarze
6393578e616Sschwarze def = ep->defs + i;
6403578e616Sschwarze free(def->key);
6416538659fSschwarze def->key = mandoc_strndup(ep->start, ep->toksz);
6426538659fSschwarze def->keysz = ep->toksz;
643f8618d99Sschwarze }
644f8618d99Sschwarze
6456538659fSschwarze if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) {
646a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY,
647bf9acac6Sschwarze ep->node->line, ep->node->pos, "define %s", def->key);
6483578e616Sschwarze free(def->key);
6493578e616Sschwarze free(def->val);
6503578e616Sschwarze def->key = def->val = NULL;
6513578e616Sschwarze def->keysz = def->valsz = 0;
6523578e616Sschwarze return;
653f8618d99Sschwarze }
6543578e616Sschwarze free(def->val);
6556538659fSschwarze def->val = mandoc_strndup(ep->start, ep->toksz);
6566538659fSschwarze def->valsz = ep->toksz;
657f8618d99Sschwarze }
658f8618d99Sschwarze
659bf9acac6Sschwarze void
eqn_parse(struct eqn_node * ep)660bf9acac6Sschwarze eqn_parse(struct eqn_node *ep)
661f8618d99Sschwarze {
662bf9acac6Sschwarze struct eqn_box *cur, *nbox, *parent, *split;
6636538659fSschwarze const char *cp, *cpn;
664f8617809Sschwarze char *p;
6656538659fSschwarze enum eqn_tok tok;
666a8c898eaSschwarze enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln;
6673578e616Sschwarze int size;
668f8618d99Sschwarze
669bf9acac6Sschwarze parent = ep->node->eqn;
6708b6ab528Sschwarze assert(parent != NULL);
6713578e616Sschwarze
6723578e616Sschwarze /*
6733578e616Sschwarze * Empty equation.
6743578e616Sschwarze * Do not add it to the high-level syntax tree.
6753578e616Sschwarze */
6763578e616Sschwarze
6778b6ab528Sschwarze if (ep->data == NULL)
678bf9acac6Sschwarze return;
679f8617809Sschwarze
6800f5f5b53Sschwarze ep->start = ep->end = ep->data;
6815030fd5bSschwarze ep->sublen = 0;
6825030fd5bSschwarze ep->subcnt = 0;
683a4dfe1bcSschwarze
6846538659fSschwarze next_tok:
6856538659fSschwarze tok = eqn_next(ep, MODE_TOK);
686a4dfe1bcSschwarze switch (tok) {
687e74fa2aeSschwarze case EQN_TOK_UNDEF:
6883578e616Sschwarze eqn_undef(ep);
689f8617809Sschwarze break;
690e74fa2aeSschwarze case EQN_TOK_NDEFINE:
691e74fa2aeSschwarze case EQN_TOK_DEFINE:
6923578e616Sschwarze eqn_def(ep);
693f8617809Sschwarze break;
694e74fa2aeSschwarze case EQN_TOK_TDEFINE:
6956538659fSschwarze if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF ||
6966538659fSschwarze eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF)
697a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY,
698bf9acac6Sschwarze ep->node->line, ep->node->pos, "tdefine");
699f8617809Sschwarze break;
700e74fa2aeSschwarze case EQN_TOK_DELIM:
70157ca9bd5Sschwarze eqn_delim(ep);
70257ca9bd5Sschwarze break;
703e74fa2aeSschwarze case EQN_TOK_GFONT:
7046538659fSschwarze if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
705a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
706a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
707f8617809Sschwarze break;
708e74fa2aeSschwarze case EQN_TOK_MARK:
709e74fa2aeSschwarze case EQN_TOK_LINEUP:
710f8617809Sschwarze /* Ignore these. */
711f8617809Sschwarze break;
712e74fa2aeSschwarze case EQN_TOK_DYAD:
713e74fa2aeSschwarze case EQN_TOK_VEC:
714e74fa2aeSschwarze case EQN_TOK_UNDER:
715e74fa2aeSschwarze case EQN_TOK_BAR:
716e74fa2aeSschwarze case EQN_TOK_TILDE:
717e74fa2aeSschwarze case EQN_TOK_HAT:
718e74fa2aeSschwarze case EQN_TOK_DOT:
719e74fa2aeSschwarze case EQN_TOK_DOTDOT:
720a4dfe1bcSschwarze if (parent->last == NULL) {
721a5a5f808Sschwarze mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
722a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
723a4dfe1bcSschwarze cur = eqn_box_alloc(ep, parent);
724a4dfe1bcSschwarze cur->type = EQN_TEXT;
725a4dfe1bcSschwarze cur->text = mandoc_strdup("");
726f8617809Sschwarze }
727244ed75aSschwarze parent = eqn_box_makebinary(ep, parent);
72832e49996Sschwarze parent->type = EQN_LIST;
729f8617809Sschwarze parent->expectargs = 1;
730a8c898eaSschwarze parent->font = EQNFONT_ROMAN;
731f8617809Sschwarze switch (tok) {
732e74fa2aeSschwarze case EQN_TOK_DOTDOT:
7336538659fSschwarze parent->top = mandoc_strdup("\\[ad]");
734f8617809Sschwarze break;
735e74fa2aeSschwarze case EQN_TOK_VEC:
7366538659fSschwarze parent->top = mandoc_strdup("\\[->]");
737f8617809Sschwarze break;
738e74fa2aeSschwarze case EQN_TOK_DYAD:
7396538659fSschwarze parent->top = mandoc_strdup("\\[<>]");
740f8617809Sschwarze break;
741e74fa2aeSschwarze case EQN_TOK_TILDE:
7426538659fSschwarze parent->top = mandoc_strdup("\\[a~]");
743f8617809Sschwarze break;
744e74fa2aeSschwarze case EQN_TOK_UNDER:
7456538659fSschwarze parent->bottom = mandoc_strdup("\\[ul]");
746f8617809Sschwarze break;
747e74fa2aeSschwarze case EQN_TOK_BAR:
7487779627fSbentley parent->top = mandoc_strdup("\\[rn]");
749f8617809Sschwarze break;
750e74fa2aeSschwarze case EQN_TOK_DOT:
7516538659fSschwarze parent->top = mandoc_strdup("\\[a.]");
752f8617809Sschwarze break;
753e74fa2aeSschwarze case EQN_TOK_HAT:
7546538659fSschwarze parent->top = mandoc_strdup("\\[ha]");
755f8617809Sschwarze break;
756f8617809Sschwarze default:
757f8617809Sschwarze abort();
758f8617809Sschwarze }
759f8617809Sschwarze parent = parent->parent;
760f8617809Sschwarze break;
761e74fa2aeSschwarze case EQN_TOK_FWD:
762e74fa2aeSschwarze case EQN_TOK_BACK:
763e74fa2aeSschwarze case EQN_TOK_DOWN:
764e74fa2aeSschwarze case EQN_TOK_UP:
7656538659fSschwarze if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF)
766a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
767a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
768f8617809Sschwarze break;
769e74fa2aeSschwarze case EQN_TOK_FAT:
770e74fa2aeSschwarze case EQN_TOK_ROMAN:
771e74fa2aeSschwarze case EQN_TOK_ITALIC:
772e74fa2aeSschwarze case EQN_TOK_BOLD:
773f8617809Sschwarze while (parent->args == parent->expectargs)
774a4dfe1bcSschwarze parent = parent->parent;
775f8617809Sschwarze /*
776f8617809Sschwarze * These values apply to the next word or sequence of
777f8617809Sschwarze * words; thus, we mark that we'll have a child with
778f8617809Sschwarze * exactly one of those.
779f8617809Sschwarze */
780f8617809Sschwarze parent = eqn_box_alloc(ep, parent);
78132e49996Sschwarze parent->type = EQN_LIST;
782f8617809Sschwarze parent->expectargs = 1;
783f8617809Sschwarze switch (tok) {
784e74fa2aeSschwarze case EQN_TOK_FAT:
785f8617809Sschwarze parent->font = EQNFONT_FAT;
786f8617809Sschwarze break;
787e74fa2aeSschwarze case EQN_TOK_ROMAN:
788f8617809Sschwarze parent->font = EQNFONT_ROMAN;
789f8617809Sschwarze break;
790e74fa2aeSschwarze case EQN_TOK_ITALIC:
791f8617809Sschwarze parent->font = EQNFONT_ITALIC;
792f8617809Sschwarze break;
793e74fa2aeSschwarze case EQN_TOK_BOLD:
794f8617809Sschwarze parent->font = EQNFONT_BOLD;
795f8617809Sschwarze break;
796f8617809Sschwarze default:
797f8617809Sschwarze abort();
798f8617809Sschwarze }
799f8617809Sschwarze break;
800e74fa2aeSschwarze case EQN_TOK_SIZE:
801e74fa2aeSschwarze case EQN_TOK_GSIZE:
802f8617809Sschwarze /* Accept two values: integral size and a single. */
8036538659fSschwarze if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
804a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
805a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
806a4dfe1bcSschwarze break;
807f8617809Sschwarze }
8086538659fSschwarze size = mandoc_strntoi(ep->start, ep->toksz, 10);
809f8617809Sschwarze if (-1 == size) {
810a5a5f808Sschwarze mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line,
811a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
812a4dfe1bcSschwarze break;
813f8617809Sschwarze }
814f8617809Sschwarze if (EQN_TOK_GSIZE == tok) {
815f8617809Sschwarze ep->gsize = size;
816f8617809Sschwarze break;
817f8617809Sschwarze }
8182fe51f68Sschwarze while (parent->args == parent->expectargs)
8192fe51f68Sschwarze parent = parent->parent;
820f8617809Sschwarze parent = eqn_box_alloc(ep, parent);
82132e49996Sschwarze parent->type = EQN_LIST;
822f8617809Sschwarze parent->expectargs = 1;
823f8617809Sschwarze parent->size = size;
824f8617809Sschwarze break;
825e74fa2aeSschwarze case EQN_TOK_FROM:
826e74fa2aeSschwarze case EQN_TOK_TO:
827e74fa2aeSschwarze case EQN_TOK_SUB:
828e74fa2aeSschwarze case EQN_TOK_SUP:
829f8617809Sschwarze /*
830f8617809Sschwarze * We have a left-right-associative expression.
831f8617809Sschwarze * Repivot under a positional node, open a child scope
832f8617809Sschwarze * and keep on reading.
833f8617809Sschwarze */
834a4dfe1bcSschwarze if (parent->last == NULL) {
835a5a5f808Sschwarze mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
836a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
837a4dfe1bcSschwarze cur = eqn_box_alloc(ep, parent);
838a4dfe1bcSschwarze cur->type = EQN_TEXT;
839a4dfe1bcSschwarze cur->text = mandoc_strdup("");
840f8617809Sschwarze }
8412fe51f68Sschwarze while (parent->expectargs == 1 && parent->args == 1)
8422fe51f68Sschwarze parent = parent->parent;
8432fe51f68Sschwarze if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) {
8442fe51f68Sschwarze for (cur = parent; cur != NULL; cur = cur->parent)
8452fe51f68Sschwarze if (cur->pos == EQNPOS_SUB ||
8462fe51f68Sschwarze cur->pos == EQNPOS_SUP ||
8472fe51f68Sschwarze cur->pos == EQNPOS_SUBSUP ||
8482fe51f68Sschwarze cur->pos == EQNPOS_SQRT ||
8492fe51f68Sschwarze cur->pos == EQNPOS_OVER)
8502fe51f68Sschwarze break;
8512fe51f68Sschwarze if (cur != NULL)
8522fe51f68Sschwarze parent = cur->parent;
8532fe51f68Sschwarze }
8542fe51f68Sschwarze if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) {
855f8617809Sschwarze parent->expectargs = 3;
856f8617809Sschwarze parent->pos = EQNPOS_SUBSUP;
857f8617809Sschwarze break;
858f8617809Sschwarze }
8592fe51f68Sschwarze if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) {
860f8617809Sschwarze parent->expectargs = 3;
861f8617809Sschwarze parent->pos = EQNPOS_FROMTO;
862f8617809Sschwarze break;
863f8617809Sschwarze }
864244ed75aSschwarze parent = eqn_box_makebinary(ep, parent);
865f8617809Sschwarze switch (tok) {
866e74fa2aeSschwarze case EQN_TOK_FROM:
867244ed75aSschwarze parent->pos = EQNPOS_FROM;
868f8617809Sschwarze break;
869e74fa2aeSschwarze case EQN_TOK_TO:
870244ed75aSschwarze parent->pos = EQNPOS_TO;
871f8617809Sschwarze break;
872e74fa2aeSschwarze case EQN_TOK_SUP:
873244ed75aSschwarze parent->pos = EQNPOS_SUP;
874f8617809Sschwarze break;
875e74fa2aeSschwarze case EQN_TOK_SUB:
876244ed75aSschwarze parent->pos = EQNPOS_SUB;
877f8617809Sschwarze break;
878f8617809Sschwarze default:
879f8617809Sschwarze abort();
880f8617809Sschwarze }
881f8617809Sschwarze break;
882e74fa2aeSschwarze case EQN_TOK_SQRT:
883f8617809Sschwarze while (parent->args == parent->expectargs)
884a4dfe1bcSschwarze parent = parent->parent;
885f8617809Sschwarze /*
886f8617809Sschwarze * Accept a left-right-associative set of arguments just
887f8617809Sschwarze * like sub and sup and friends but without rebalancing
888f8617809Sschwarze * under a pivot.
889f8617809Sschwarze */
890f8617809Sschwarze parent = eqn_box_alloc(ep, parent);
891f8617809Sschwarze parent->type = EQN_SUBEXPR;
892f8617809Sschwarze parent->pos = EQNPOS_SQRT;
893f8617809Sschwarze parent->expectargs = 1;
894f8617809Sschwarze break;
895e74fa2aeSschwarze case EQN_TOK_OVER:
896f8617809Sschwarze /*
897f8617809Sschwarze * We have a right-left-associative fraction.
898f8617809Sschwarze * Close out anything that's currently open, then
899f8617809Sschwarze * rebalance and continue reading.
900f8617809Sschwarze */
901a4dfe1bcSschwarze if (parent->last == NULL) {
902a5a5f808Sschwarze mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line,
903a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
904a4dfe1bcSschwarze cur = eqn_box_alloc(ep, parent);
905a4dfe1bcSschwarze cur->type = EQN_TEXT;
906a4dfe1bcSschwarze cur->text = mandoc_strdup("");
907f8617809Sschwarze }
9082fe51f68Sschwarze while (parent->args == parent->expectargs)
9092fe51f68Sschwarze parent = parent->parent;
910f8617809Sschwarze while (EQN_SUBEXPR == parent->type)
911a4dfe1bcSschwarze parent = parent->parent;
912244ed75aSschwarze parent = eqn_box_makebinary(ep, parent);
913244ed75aSschwarze parent->pos = EQNPOS_OVER;
914f8617809Sschwarze break;
915e74fa2aeSschwarze case EQN_TOK_RIGHT:
916e74fa2aeSschwarze case EQN_TOK_BRACE_CLOSE:
917f8617809Sschwarze /*
918f8617809Sschwarze * Close out the existing brace.
919f8617809Sschwarze * FIXME: this is a shitty sentinel: we should really
920f8617809Sschwarze * have a native EQN_BRACE type or whatnot.
921f8617809Sschwarze */
922a4dfe1bcSschwarze for (cur = parent; cur != NULL; cur = cur->parent)
923a4dfe1bcSschwarze if (cur->type == EQN_LIST &&
92432e49996Sschwarze cur->expectargs > 1 &&
925a4dfe1bcSschwarze (tok == EQN_TOK_BRACE_CLOSE ||
926a4dfe1bcSschwarze cur->left != NULL))
927a4dfe1bcSschwarze break;
928a4dfe1bcSschwarze if (cur == NULL) {
929a5a5f808Sschwarze mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line,
930a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
931a4dfe1bcSschwarze break;
932f8617809Sschwarze }
933a4dfe1bcSschwarze parent = cur;
934f8617809Sschwarze if (EQN_TOK_RIGHT == tok) {
9356538659fSschwarze if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
936a4dfe1bcSschwarze mandoc_msg(MANDOCERR_REQ_EMPTY,
937a5a5f808Sschwarze ep->node->line, ep->node->pos,
938a5a5f808Sschwarze "%s", eqn_toks[tok]);
939a4dfe1bcSschwarze break;
940f8617809Sschwarze }
941f8617809Sschwarze /* Handling depends on right/left. */
9426538659fSschwarze if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
9436538659fSschwarze parent->right = mandoc_strdup("\\[rc]");
9446538659fSschwarze else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
9456538659fSschwarze parent->right = mandoc_strdup("\\[rf]");
9466538659fSschwarze else
9476538659fSschwarze parent->right =
9486538659fSschwarze mandoc_strndup(ep->start, ep->toksz);
949f8617809Sschwarze }
950a4dfe1bcSschwarze parent = parent->parent;
95152888185Sschwarze if (tok == EQN_TOK_BRACE_CLOSE &&
952f8617809Sschwarze (parent->type == EQN_PILE ||
953f8617809Sschwarze parent->type == EQN_MATRIX))
954f8617809Sschwarze parent = parent->parent;
955f8617809Sschwarze /* Close out any "singleton" lists. */
95632e49996Sschwarze while (parent->type == EQN_LIST &&
95732e49996Sschwarze parent->expectargs == 1 &&
95832e49996Sschwarze parent->args == 1)
959a4dfe1bcSschwarze parent = parent->parent;
960f8617809Sschwarze break;
961e74fa2aeSschwarze case EQN_TOK_BRACE_OPEN:
962e74fa2aeSschwarze case EQN_TOK_LEFT:
963f8617809Sschwarze /*
964f8617809Sschwarze * If we already have something in the stack and we're
965f8617809Sschwarze * in an expression, then rewind til we're not any more
966f8617809Sschwarze * (just like with the text node).
967f8617809Sschwarze */
968f8617809Sschwarze while (parent->args == parent->expectargs)
969a4dfe1bcSschwarze parent = parent->parent;
970a4dfe1bcSschwarze if (EQN_TOK_LEFT == tok &&
9716538659fSschwarze eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) {
972a5a5f808Sschwarze mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line,
973a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
974a4dfe1bcSschwarze break;
975f8617809Sschwarze }
976f8617809Sschwarze parent = eqn_box_alloc(ep, parent);
977f8617809Sschwarze parent->type = EQN_LIST;
978f8617809Sschwarze if (EQN_TOK_LEFT == tok) {
9796538659fSschwarze if (STRNEQ(ep->start, ep->toksz, "ceiling", 7))
9806538659fSschwarze parent->left = mandoc_strdup("\\[lc]");
9816538659fSschwarze else if (STRNEQ(ep->start, ep->toksz, "floor", 5))
9826538659fSschwarze parent->left = mandoc_strdup("\\[lf]");
9836538659fSschwarze else
9846538659fSschwarze parent->left =
9856538659fSschwarze mandoc_strndup(ep->start, ep->toksz);
986f8617809Sschwarze }
987f8617809Sschwarze break;
988e74fa2aeSschwarze case EQN_TOK_PILE:
989e74fa2aeSschwarze case EQN_TOK_LPILE:
990e74fa2aeSschwarze case EQN_TOK_RPILE:
991e74fa2aeSschwarze case EQN_TOK_CPILE:
992e74fa2aeSschwarze case EQN_TOK_CCOL:
993e74fa2aeSschwarze case EQN_TOK_LCOL:
994e74fa2aeSschwarze case EQN_TOK_RCOL:
995f8617809Sschwarze while (parent->args == parent->expectargs)
996a4dfe1bcSschwarze parent = parent->parent;
997f8617809Sschwarze parent = eqn_box_alloc(ep, parent);
998f8617809Sschwarze parent->type = EQN_PILE;
999a4dfe1bcSschwarze parent->expectargs = 1;
1000f8617809Sschwarze break;
1001e74fa2aeSschwarze case EQN_TOK_ABOVE:
1002a4dfe1bcSschwarze for (cur = parent; cur != NULL; cur = cur->parent)
1003a4dfe1bcSschwarze if (cur->type == EQN_PILE)
1004a4dfe1bcSschwarze break;
1005a4dfe1bcSschwarze if (cur == NULL) {
1006a5a5f808Sschwarze mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line,
1007a5a5f808Sschwarze ep->node->pos, "%s", eqn_toks[tok]);
1008a4dfe1bcSschwarze break;
1009f8617809Sschwarze }
1010a4dfe1bcSschwarze parent = eqn_box_alloc(ep, cur);
1011f8617809Sschwarze parent->type = EQN_LIST;
1012f8617809Sschwarze break;
1013e74fa2aeSschwarze case EQN_TOK_MATRIX:
1014f8617809Sschwarze while (parent->args == parent->expectargs)
1015a4dfe1bcSschwarze parent = parent->parent;
1016f8617809Sschwarze parent = eqn_box_alloc(ep, parent);
1017f8617809Sschwarze parent->type = EQN_MATRIX;
1018a4dfe1bcSschwarze parent->expectargs = 1;
1019f8617809Sschwarze break;
1020e74fa2aeSschwarze case EQN_TOK_EOF:
1021bf9acac6Sschwarze return;
102266b13c11Sschwarze case EQN_TOK__MAX:
102321b46522Sschwarze case EQN_TOK_FUNC:
102421b46522Sschwarze case EQN_TOK_QUOTED:
102521b46522Sschwarze case EQN_TOK_SYM:
10266538659fSschwarze p = ep->start;
102766b13c11Sschwarze assert(p != NULL);
1028f8617809Sschwarze /*
1029f8617809Sschwarze * If we already have something in the stack and we're
1030f8617809Sschwarze * in an expression, then rewind til we're not any more.
1031f8617809Sschwarze */
1032f8617809Sschwarze while (parent->args == parent->expectargs)
1033a4dfe1bcSschwarze parent = parent->parent;
1034f8617809Sschwarze cur = eqn_box_alloc(ep, parent);
1035f8617809Sschwarze cur->type = EQN_TEXT;
1036f8617809Sschwarze cur->text = p;
1037a8c898eaSschwarze switch (tok) {
1038a8c898eaSschwarze case EQN_TOK_FUNC:
1039a8c898eaSschwarze cur->font = EQNFONT_ROMAN;
104021b46522Sschwarze break;
1041a8c898eaSschwarze case EQN_TOK_QUOTED:
1042a8c898eaSschwarze if (cur->font == EQNFONT_NONE)
104321b46522Sschwarze cur->font = EQNFONT_ITALIC;
104421b46522Sschwarze break;
1045a8c898eaSschwarze case EQN_TOK_SYM:
1046a8c898eaSschwarze break;
1047a8c898eaSschwarze default:
1048a8c898eaSschwarze if (cur->font != EQNFONT_NONE || *p == '\0')
1049a8c898eaSschwarze break;
1050a8c898eaSschwarze cpn = p - 1;
1051a8c898eaSschwarze ccln = CCL_LET;
1052698f1840Sschwarze split = NULL;
105359c226a5Sschwarze for (;;) {
1054a8c898eaSschwarze /* Advance to next character. */
1055a8c898eaSschwarze cp = cpn++;
1056a8c898eaSschwarze ccl = ccln;
1057a8c898eaSschwarze ccln = isalpha((unsigned char)*cpn) ? CCL_LET :
1058a8c898eaSschwarze isdigit((unsigned char)*cpn) ||
1059a8c898eaSschwarze (*cpn == '.' && (ccl == CCL_DIG ||
1060a8c898eaSschwarze isdigit((unsigned char)cpn[1]))) ?
1061a8c898eaSschwarze CCL_DIG : CCL_PUN;
1062a8c898eaSschwarze /* No boundary before first character. */
1063a8c898eaSschwarze if (cp < p)
1064a8c898eaSschwarze continue;
1065a8c898eaSschwarze cur->font = ccl == CCL_LET ?
1066a8c898eaSschwarze EQNFONT_ITALIC : EQNFONT_ROMAN;
106759c226a5Sschwarze if (*cp == '\\')
106859c226a5Sschwarze mandoc_escape(&cpn, NULL, NULL);
1069a8c898eaSschwarze /* No boundary after last character. */
107059c226a5Sschwarze if (*cpn == '\0')
107159c226a5Sschwarze break;
10726898e1e9Sschwarze if (ccln == ccl && *cp != ',' && *cpn != ',')
107359c226a5Sschwarze continue;
107426deb957Sschwarze /* Boundary found, split the text. */
107526deb957Sschwarze if (parent->args == parent->expectargs) {
107626deb957Sschwarze /* Remove the text from the tree. */
107726deb957Sschwarze if (cur->prev == NULL)
107826deb957Sschwarze parent->first = cur->next;
107926deb957Sschwarze else
108026deb957Sschwarze cur->prev->next = NULL;
108126deb957Sschwarze parent->last = cur->prev;
108226deb957Sschwarze parent->args--;
108326deb957Sschwarze /* Set up a list instead. */
1084698f1840Sschwarze split = eqn_box_alloc(ep, parent);
1085698f1840Sschwarze split->type = EQN_LIST;
108626deb957Sschwarze /* Insert the word into the list. */
1087698f1840Sschwarze split->first = split->last = cur;
1088698f1840Sschwarze cur->parent = split;
108926deb957Sschwarze cur->prev = NULL;
1090698f1840Sschwarze parent = split;
109126deb957Sschwarze }
109226deb957Sschwarze /* Append a new text box. */
109359c226a5Sschwarze nbox = eqn_box_alloc(ep, parent);
109459c226a5Sschwarze nbox->type = EQN_TEXT;
109559c226a5Sschwarze nbox->text = mandoc_strdup(cpn);
1096a8c898eaSschwarze /* Truncate the old box. */
109759c226a5Sschwarze p = mandoc_strndup(cur->text,
109859c226a5Sschwarze cpn - cur->text);
109959c226a5Sschwarze free(cur->text);
110059c226a5Sschwarze cur->text = p;
1101a8c898eaSschwarze /* Setup to process the new box. */
110259c226a5Sschwarze cur = nbox;
1103a8c898eaSschwarze p = nbox->text;
1104a8c898eaSschwarze cpn = p - 1;
1105a8c898eaSschwarze ccln = CCL_LET;
110659c226a5Sschwarze }
1107698f1840Sschwarze if (split != NULL)
1108698f1840Sschwarze parent = split->parent;
1109a8c898eaSschwarze break;
1110a8c898eaSschwarze }
1111f8617809Sschwarze break;
111266b13c11Sschwarze default:
111366b13c11Sschwarze abort();
1114f8617809Sschwarze }
1115a4dfe1bcSschwarze goto next_tok;
1116f8617809Sschwarze }
1117f8617809Sschwarze
1118f8617809Sschwarze void
eqn_free(struct eqn_node * p)1119f8617809Sschwarze eqn_free(struct eqn_node *p)
1120f8618d99Sschwarze {
1121f8618d99Sschwarze int i;
1122f8618d99Sschwarze
1123abe31b56Sschwarze if (p == NULL)
1124abe31b56Sschwarze return;
1125abe31b56Sschwarze
1126f8617809Sschwarze for (i = 0; i < (int)p->defsz; i++) {
1127f8617809Sschwarze free(p->defs[i].key);
1128f8617809Sschwarze free(p->defs[i].val);
1129f8617809Sschwarze }
1130f8617809Sschwarze
1131f8617809Sschwarze free(p->data);
1132f8617809Sschwarze free(p->defs);
1133f8617809Sschwarze free(p);
1134f8618d99Sschwarze }
1135