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