]>
git.cameronkatri.com Git - mandoc.git/blob - eqn.c
1 /* $Id: eqn.c,v 1.85 2022/04/13 20:26:19 schwarze Exp $ */
3 * Copyright (c) 2014, 2015, 2017, 2018, 2020, 2022
4 * Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/types.h>
31 #include "mandoc_aux.h"
35 #include "libmandoc.h"
36 #include "eqn_parse.h"
38 #define EQN_NEST_MAX 128 /* maximum nesting of defines */
39 #define STRNEQ(p1, sz1, p2, sz2) \
40 ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
95 static const char *eqn_toks
[EQN_TOK__MAX
] = {
96 "dyad", /* EQN_TOK_DYAD */
97 "vec", /* EQN_TOK_VEC */
98 "under", /* EQN_TOK_UNDER */
99 "bar", /* EQN_TOK_BAR */
100 "tilde", /* EQN_TOK_TILDE */
101 "hat", /* EQN_TOK_HAT */
102 "dot", /* EQN_TOK_DOT */
103 "dotdot", /* EQN_TOK_DOTDOT */
104 "fwd", /* EQN_TOK_FWD * */
105 "back", /* EQN_TOK_BACK */
106 "down", /* EQN_TOK_DOWN */
107 "up", /* EQN_TOK_UP */
108 "fat", /* EQN_TOK_FAT */
109 "roman", /* EQN_TOK_ROMAN */
110 "italic", /* EQN_TOK_ITALIC */
111 "bold", /* EQN_TOK_BOLD */
112 "size", /* EQN_TOK_SIZE */
113 "sub", /* EQN_TOK_SUB */
114 "sup", /* EQN_TOK_SUP */
115 "sqrt", /* EQN_TOK_SQRT */
116 "over", /* EQN_TOK_OVER */
117 "from", /* EQN_TOK_FROM */
118 "to", /* EQN_TOK_TO */
119 "{", /* EQN_TOK_BRACE_OPEN */
120 "}", /* EQN_TOK_BRACE_CLOSE */
121 "gsize", /* EQN_TOK_GSIZE */
122 "gfont", /* EQN_TOK_GFONT */
123 "mark", /* EQN_TOK_MARK */
124 "lineup", /* EQN_TOK_LINEUP */
125 "left", /* EQN_TOK_LEFT */
126 "right", /* EQN_TOK_RIGHT */
127 "pile", /* EQN_TOK_PILE */
128 "lpile", /* EQN_TOK_LPILE */
129 "rpile", /* EQN_TOK_RPILE */
130 "cpile", /* EQN_TOK_CPILE */
131 "matrix", /* EQN_TOK_MATRIX */
132 "ccol", /* EQN_TOK_CCOL */
133 "lcol", /* EQN_TOK_LCOL */
134 "rcol", /* EQN_TOK_RCOL */
135 "delim", /* EQN_TOK_DELIM */
136 "define", /* EQN_TOK_DEFINE */
137 "tdefine", /* EQN_TOK_TDEFINE */
138 "ndefine", /* EQN_TOK_NDEFINE */
139 "undef", /* EQN_TOK_UNDEF */
140 "above", /* EQN_TOK_ABOVE */
143 static const char *const eqn_func
[] = {
144 "acos", "acsc", "and", "arc", "asec", "asin", "atan",
145 "cos", "cosh", "coth", "csc", "det", "exp", "for",
146 "if", "lim", "ln", "log", "max", "min",
147 "sec", "sin", "sinh", "tan", "tanh", "Im", "Re",
219 static const struct eqnsym eqnsyms
[EQNSYM__MAX
] = {
220 { "alpha", "*a" }, /* EQNSYM_alpha */
221 { "beta", "*b" }, /* EQNSYM_beta */
222 { "chi", "*x" }, /* EQNSYM_chi */
223 { "delta", "*d" }, /* EQNSYM_delta */
224 { "epsilon", "*e" }, /* EQNSYM_epsilon */
225 { "eta", "*y" }, /* EQNSYM_eta */
226 { "gamma", "*g" }, /* EQNSYM_gamma */
227 { "iota", "*i" }, /* EQNSYM_iota */
228 { "kappa", "*k" }, /* EQNSYM_kappa */
229 { "lambda", "*l" }, /* EQNSYM_lambda */
230 { "mu", "*m" }, /* EQNSYM_mu */
231 { "nu", "*n" }, /* EQNSYM_nu */
232 { "omega", "*w" }, /* EQNSYM_omega */
233 { "omicron", "*o" }, /* EQNSYM_omicron */
234 { "phi", "*f" }, /* EQNSYM_phi */
235 { "pi", "*p" }, /* EQNSYM_pi */
236 { "psi", "*q" }, /* EQNSYM_psi */
237 { "rho", "*r" }, /* EQNSYM_rho */
238 { "sigma", "*s" }, /* EQNSYM_sigma */
239 { "tau", "*t" }, /* EQNSYM_tau */
240 { "theta", "*h" }, /* EQNSYM_theta */
241 { "upsilon", "*u" }, /* EQNSYM_upsilon */
242 { "xi", "*c" }, /* EQNSYM_xi */
243 { "zeta", "*z" }, /* EQNSYM_zeta */
244 { "DELTA", "*D" }, /* EQNSYM_DELTA */
245 { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
246 { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
247 { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
248 { "PHI", "*F" }, /* EQNSYM_PHI */
249 { "PI", "*P" }, /* EQNSYM_PI */
250 { "PSI", "*Q" }, /* EQNSYM_PSI */
251 { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
252 { "THETA", "*H" }, /* EQNSYM_THETA */
253 { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
254 { "XI", "*C" }, /* EQNSYM_XI */
255 { "inter", "ca" }, /* EQNSYM_inter */
256 { "union", "cu" }, /* EQNSYM_union */
257 { "prod", "product" }, /* EQNSYM_prod */
258 { "int", "integral" }, /* EQNSYM_int */
259 { "sum", "sum" }, /* EQNSYM_sum */
260 { "grad", "gr" }, /* EQNSYM_grad */
261 { "del", "gr" }, /* EQNSYM_del */
262 { "times", "mu" }, /* EQNSYM_times */
263 { "cdot", "pc" }, /* EQNSYM_cdot */
264 { "nothing", "&" }, /* EQNSYM_nothing */
265 { "approx", "~~" }, /* EQNSYM_approx */
266 { "prime", "fm" }, /* EQNSYM_prime */
267 { "half", "12" }, /* EQNSYM_half */
268 { "partial", "pd" }, /* EQNSYM_partial */
269 { "inf", "if" }, /* EQNSYM_inf */
270 { ">>", ">>" }, /* EQNSYM_muchgreat */
271 { "<<", "<<" }, /* EQNSYM_muchless */
272 { "<-", "<-" }, /* EQNSYM_larrow */
273 { "->", "->" }, /* EQNSYM_rarrow */
274 { "+-", "+-" }, /* EQNSYM_pm */
275 { "!=", "!=" }, /* EQNSYM_nequal */
276 { "==", "==" }, /* EQNSYM_equiv */
277 { "<=", "<=" }, /* EQNSYM_lessequal */
278 { ">=", ">=" }, /* EQNSYM_moreequal */
279 { "-", "mi" }, /* EQNSYM_minus */
296 static struct eqn_box
*eqn_box_alloc(struct eqn_node
*, struct eqn_box
*);
297 static struct eqn_box
*eqn_box_makebinary(struct eqn_node
*,
299 static void eqn_def(struct eqn_node
*);
300 static struct eqn_def
*eqn_def_find(struct eqn_node
*);
301 static void eqn_delim(struct eqn_node
*);
302 static enum eqn_tok
eqn_next(struct eqn_node
*, enum parse_mode
);
303 static void eqn_undef(struct eqn_node
*);
311 ep
= mandoc_calloc(1, sizeof(*ep
));
312 ep
->gsize
= EQN_DEFSIZE
;
317 eqn_reset(struct eqn_node
*ep
)
320 ep
->data
= ep
->start
= ep
->end
= NULL
;
321 ep
->sz
= ep
->toksz
= 0;
325 eqn_read(struct eqn_node
*ep
, const char *p
)
329 if (ep
->data
== NULL
) {
331 ep
->data
= mandoc_strdup(p
);
333 ep
->sz
= mandoc_asprintf(&cp
, "%s %s", ep
->data
, p
);
341 * Find the key "key" of the give size within our eqn-defined values.
343 static struct eqn_def
*
344 eqn_def_find(struct eqn_node
*ep
)
348 for (i
= 0; i
< (int)ep
->defsz
; i
++)
349 if (ep
->defs
[i
].keysz
&& STRNEQ(ep
->defs
[i
].key
,
350 ep
->defs
[i
].keysz
, ep
->start
, ep
->toksz
))
357 * Parse a token from the input text. The modes are:
358 * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
359 * before its next occurence. Do not interpret the token in any
360 * way and return EQN_TOK_QUOTED. All other modes behave like
361 * MODE_QUOTED when *ep->start is '"'.
362 * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
363 * otherwise, it ends before the next whitespace or brace.
364 * Do not interpret the token and return EQN_TOK__MAX.
365 * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
366 * alias created with define. If it is an alias, replace it with
367 * its string value and reparse.
368 * MODE_TOK: Like MODE_SUB, but also check the token against the list
369 * of tokens, and if there is a match, return that token. Otherwise,
370 * if the token matches a symbol, return EQN_TOK_SYM; if it matches
371 * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for
372 * a token match, *ep->start is set to an allocated string that the
373 * caller is expected to free.
374 * All modes skip whitespace following the end of the token.
377 eqn_next(struct eqn_node
*ep
, enum parse_mode mode
)
381 int diff
, i
, newlen
, quoted
;
385 * Reset the recursion counter after advancing
386 * beyond the end of the rightmost substitution.
388 if (ep
->end
- ep
->data
>= ep
->sublen
)
392 quoted
= mode
== MODE_QUOTED
;
394 switch (*ep
->start
) {
413 ep
->end
= strchr(ep
->start
+ 1, *ep
->start
);
414 ep
->start
++; /* Skip opening quote. */
415 if (ep
->end
== NULL
) {
416 mandoc_msg(MANDOCERR_ARG_QUOTE
,
417 ep
->node
->line
, ep
->node
->pos
, NULL
);
418 ep
->end
= strchr(ep
->start
, '\0');
421 ep
->end
= ep
->start
+ 1;
422 if (*ep
->start
!= '{' && *ep
->start
!= '}')
423 ep
->end
+= strcspn(ep
->end
, " ^~\"{}\t");
425 ep
->toksz
= ep
->end
- ep
->start
;
426 if (quoted
&& *ep
->end
!= '\0')
427 ep
->end
++; /* Skip closing quote. */
428 while (*ep
->end
!= '\0' && strchr(" \t^~", *ep
->end
) != NULL
)
430 if (quoted
) /* Cannot return, may have to strndup. */
432 if (mode
== MODE_NOSUB
)
434 if ((def
= eqn_def_find(ep
)) == NULL
)
436 if (++ep
->subcnt
> EQN_NEST_MAX
) {
437 mandoc_msg(MANDOCERR_ROFFLOOP
,
438 ep
->node
->line
, ep
->node
->pos
, NULL
);
442 /* Replace a defined name with its string value. */
443 if ((diff
= def
->valsz
- ep
->toksz
) > 0) {
444 start
= ep
->start
- ep
->data
;
446 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ 1);
447 ep
->start
= ep
->data
+ start
;
451 memmove(ep
->start
+ def
->valsz
, ep
->start
+ ep
->toksz
,
452 strlen(ep
->start
+ ep
->toksz
) + 1);
453 memcpy(ep
->start
, def
->val
, def
->valsz
);
454 newlen
= ep
->start
- ep
->data
+ def
->valsz
;
455 if (ep
->sublen
< newlen
)
458 if (mode
!= MODE_TOK
)
459 return quoted
? EQN_TOK_QUOTED
: EQN_TOK__MAX
;
461 ep
->start
= mandoc_strndup(ep
->start
, ep
->toksz
);
462 return EQN_TOK_QUOTED
;
464 for (tok
= 0; tok
< EQN_TOK__MAX
; tok
++)
465 if (STRNEQ(ep
->start
, ep
->toksz
,
466 eqn_toks
[tok
], strlen(eqn_toks
[tok
])))
469 for (i
= 0; i
< EQNSYM__MAX
; i
++) {
470 if (STRNEQ(ep
->start
, ep
->toksz
,
471 eqnsyms
[i
].str
, strlen(eqnsyms
[i
].str
))) {
472 mandoc_asprintf(&ep
->start
,
473 "\\[%s]", eqnsyms
[i
].sym
);
477 ep
->start
= mandoc_strndup(ep
->start
, ep
->toksz
);
478 for (i
= 0; i
< (int)(sizeof(eqn_func
)/sizeof(*eqn_func
)); i
++)
479 if (STRNEQ(ep
->start
, ep
->toksz
,
480 eqn_func
[i
], strlen(eqn_func
[i
])))
486 eqn_box_free(struct eqn_box
*bp
)
492 eqn_box_free(bp
->first
);
494 eqn_box_free(bp
->next
);
509 bp
= mandoc_calloc(1, sizeof(*bp
));
510 bp
->expectargs
= UINT_MAX
;
515 * Allocate a box as the last child of the parent node.
517 static struct eqn_box
*
518 eqn_box_alloc(struct eqn_node
*ep
, struct eqn_box
*parent
)
525 bp
->font
= bp
->parent
->font
;
526 bp
->size
= ep
->gsize
;
528 if (NULL
!= parent
->first
) {
529 parent
->last
->next
= bp
;
530 bp
->prev
= parent
->last
;
539 * Reparent the current last node (of the current parent) under a new
540 * EQN_SUBEXPR as the first element.
541 * Then return the new parent.
542 * The new EQN_SUBEXPR will have a two-child limit.
544 static struct eqn_box
*
545 eqn_box_makebinary(struct eqn_node
*ep
, struct eqn_box
*parent
)
547 struct eqn_box
*b
, *newb
;
549 assert(NULL
!= parent
->last
);
551 if (parent
->last
== parent
->first
)
552 parent
->first
= NULL
;
554 parent
->last
= b
->prev
;
556 newb
= eqn_box_alloc(ep
, parent
);
557 newb
->type
= EQN_SUBEXPR
;
558 newb
->expectargs
= 2;
560 newb
->first
= newb
->last
= b
;
561 newb
->first
->next
= NULL
;
567 * Parse the "delim" control statement.
570 eqn_delim(struct eqn_node
*ep
)
572 if (ep
->end
[0] == '\0' || ep
->end
[1] == '\0') {
573 mandoc_msg(MANDOCERR_REQ_EMPTY
,
574 ep
->node
->line
, ep
->node
->pos
, "delim");
575 if (ep
->end
[0] != '\0')
577 } else if (strncmp(ep
->end
, "off", 3) == 0) {
580 } else if (strncmp(ep
->end
, "on", 2) == 0) {
581 if (ep
->odelim
&& ep
->cdelim
)
585 ep
->odelim
= *ep
->end
++;
586 ep
->cdelim
= *ep
->end
++;
592 * Undefine a previously-defined string.
595 eqn_undef(struct eqn_node
*ep
)
599 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
) {
600 mandoc_msg(MANDOCERR_REQ_EMPTY
,
601 ep
->node
->line
, ep
->node
->pos
, "undef");
604 if ((def
= eqn_def_find(ep
)) == NULL
)
608 def
->key
= def
->val
= NULL
;
609 def
->keysz
= def
->valsz
= 0;
613 eqn_def(struct eqn_node
*ep
)
618 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
) {
619 mandoc_msg(MANDOCERR_REQ_EMPTY
,
620 ep
->node
->line
, ep
->node
->pos
, "define");
625 * Search for a key that already exists.
626 * Create a new key if none is found.
628 if ((def
= eqn_def_find(ep
)) == NULL
) {
629 /* Find holes in string array. */
630 for (i
= 0; i
< (int)ep
->defsz
; i
++)
631 if (0 == ep
->defs
[i
].keysz
)
634 if (i
== (int)ep
->defsz
) {
636 ep
->defs
= mandoc_reallocarray(ep
->defs
,
637 ep
->defsz
, sizeof(struct eqn_def
));
638 ep
->defs
[i
].key
= ep
->defs
[i
].val
= NULL
;
643 def
->key
= mandoc_strndup(ep
->start
, ep
->toksz
);
644 def
->keysz
= ep
->toksz
;
647 if (eqn_next(ep
, MODE_QUOTED
) == EQN_TOK_EOF
) {
648 mandoc_msg(MANDOCERR_REQ_EMPTY
,
649 ep
->node
->line
, ep
->node
->pos
, "define %s", def
->key
);
652 def
->key
= def
->val
= NULL
;
653 def
->keysz
= def
->valsz
= 0;
657 def
->val
= mandoc_strndup(ep
->start
, ep
->toksz
);
658 def
->valsz
= ep
->toksz
;
662 eqn_parse(struct eqn_node
*ep
)
664 struct eqn_box
*cur
, *nbox
, *parent
, *split
;
665 const char *cp
, *cpn
;
668 enum { CCL_LET
, CCL_DIG
, CCL_PUN
} ccl
, ccln
;
671 parent
= ep
->node
->eqn
;
672 assert(parent
!= NULL
);
676 * Do not add it to the high-level syntax tree.
679 if (ep
->data
== NULL
)
682 ep
->start
= ep
->end
= ep
->data
;
687 tok
= eqn_next(ep
, MODE_TOK
);
692 case EQN_TOK_NDEFINE
:
696 case EQN_TOK_TDEFINE
:
697 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
||
698 eqn_next(ep
, MODE_QUOTED
) == EQN_TOK_EOF
)
699 mandoc_msg(MANDOCERR_REQ_EMPTY
,
700 ep
->node
->line
, ep
->node
->pos
, "tdefine");
706 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
)
707 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->node
->line
,
708 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
722 if (parent
->last
== NULL
) {
723 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->node
->line
,
724 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
725 cur
= eqn_box_alloc(ep
, parent
);
726 cur
->type
= EQN_TEXT
;
727 cur
->text
= mandoc_strdup("");
729 parent
= eqn_box_makebinary(ep
, parent
);
730 parent
->type
= EQN_LIST
;
731 parent
->expectargs
= 1;
732 parent
->font
= EQNFONT_ROMAN
;
735 parent
->top
= mandoc_strdup("\\[ad]");
738 parent
->top
= mandoc_strdup("\\[->]");
741 parent
->top
= mandoc_strdup("\\[<>]");
744 parent
->top
= mandoc_strdup("\\[a~]");
747 parent
->bottom
= mandoc_strdup("\\[ul]");
750 parent
->top
= mandoc_strdup("\\[rn]");
753 parent
->top
= mandoc_strdup("\\[a.]");
756 parent
->top
= mandoc_strdup("\\[ha]");
761 parent
= parent
->parent
;
767 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
)
768 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->node
->line
,
769 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
775 while (parent
->args
== parent
->expectargs
)
776 parent
= parent
->parent
;
778 * These values apply to the next word or sequence of
779 * words; thus, we mark that we'll have a child with
780 * exactly one of those.
782 parent
= eqn_box_alloc(ep
, parent
);
783 parent
->type
= EQN_LIST
;
784 parent
->expectargs
= 1;
787 parent
->font
= EQNFONT_FAT
;
790 parent
->font
= EQNFONT_ROMAN
;
793 parent
->font
= EQNFONT_ITALIC
;
796 parent
->font
= EQNFONT_BOLD
;
804 /* Accept two values: integral size and a single. */
805 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
806 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->node
->line
,
807 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
810 size
= mandoc_strntoi(ep
->start
, ep
->toksz
, 10);
812 mandoc_msg(MANDOCERR_IT_NONUM
, ep
->node
->line
,
813 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
816 if (EQN_TOK_GSIZE
== tok
) {
820 while (parent
->args
== parent
->expectargs
)
821 parent
= parent
->parent
;
822 parent
= eqn_box_alloc(ep
, parent
);
823 parent
->type
= EQN_LIST
;
824 parent
->expectargs
= 1;
832 * We have a left-right-associative expression.
833 * Repivot under a positional node, open a child scope
834 * and keep on reading.
836 if (parent
->last
== NULL
) {
837 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->node
->line
,
838 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
839 cur
= eqn_box_alloc(ep
, parent
);
840 cur
->type
= EQN_TEXT
;
841 cur
->text
= mandoc_strdup("");
843 while (parent
->expectargs
== 1 && parent
->args
== 1)
844 parent
= parent
->parent
;
845 if (tok
== EQN_TOK_FROM
|| tok
== EQN_TOK_TO
) {
846 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
847 if (cur
->pos
== EQNPOS_SUB
||
848 cur
->pos
== EQNPOS_SUP
||
849 cur
->pos
== EQNPOS_SUBSUP
||
850 cur
->pos
== EQNPOS_SQRT
||
851 cur
->pos
== EQNPOS_OVER
)
854 parent
= cur
->parent
;
856 if (tok
== EQN_TOK_SUP
&& parent
->pos
== EQNPOS_SUB
) {
857 parent
->expectargs
= 3;
858 parent
->pos
= EQNPOS_SUBSUP
;
861 if (tok
== EQN_TOK_TO
&& parent
->pos
== EQNPOS_FROM
) {
862 parent
->expectargs
= 3;
863 parent
->pos
= EQNPOS_FROMTO
;
866 parent
= eqn_box_makebinary(ep
, parent
);
869 parent
->pos
= EQNPOS_FROM
;
872 parent
->pos
= EQNPOS_TO
;
875 parent
->pos
= EQNPOS_SUP
;
878 parent
->pos
= EQNPOS_SUB
;
885 while (parent
->args
== parent
->expectargs
)
886 parent
= parent
->parent
;
888 * Accept a left-right-associative set of arguments just
889 * like sub and sup and friends but without rebalancing
892 parent
= eqn_box_alloc(ep
, parent
);
893 parent
->type
= EQN_SUBEXPR
;
894 parent
->pos
= EQNPOS_SQRT
;
895 parent
->expectargs
= 1;
899 * We have a right-left-associative fraction.
900 * Close out anything that's currently open, then
901 * rebalance and continue reading.
903 if (parent
->last
== NULL
) {
904 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->node
->line
,
905 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
906 cur
= eqn_box_alloc(ep
, parent
);
907 cur
->type
= EQN_TEXT
;
908 cur
->text
= mandoc_strdup("");
910 while (parent
->args
== parent
->expectargs
)
911 parent
= parent
->parent
;
912 while (EQN_SUBEXPR
== parent
->type
)
913 parent
= parent
->parent
;
914 parent
= eqn_box_makebinary(ep
, parent
);
915 parent
->pos
= EQNPOS_OVER
;
918 case EQN_TOK_BRACE_CLOSE
:
920 * Close out the existing brace.
921 * FIXME: this is a shitty sentinel: we should really
922 * have a native EQN_BRACE type or whatnot.
924 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
925 if (cur
->type
== EQN_LIST
&&
926 cur
->expectargs
> 1 &&
927 (tok
== EQN_TOK_BRACE_CLOSE
||
931 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ep
->node
->line
,
932 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
936 if (EQN_TOK_RIGHT
== tok
) {
937 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
938 mandoc_msg(MANDOCERR_REQ_EMPTY
,
939 ep
->node
->line
, ep
->node
->pos
,
940 "%s", eqn_toks
[tok
]);
943 /* Handling depends on right/left. */
944 if (STRNEQ(ep
->start
, ep
->toksz
, "ceiling", 7))
945 parent
->right
= mandoc_strdup("\\[rc]");
946 else if (STRNEQ(ep
->start
, ep
->toksz
, "floor", 5))
947 parent
->right
= mandoc_strdup("\\[rf]");
950 mandoc_strndup(ep
->start
, ep
->toksz
);
952 parent
= parent
->parent
;
953 if (tok
== EQN_TOK_BRACE_CLOSE
&&
954 (parent
->type
== EQN_PILE
||
955 parent
->type
== EQN_MATRIX
))
956 parent
= parent
->parent
;
957 /* Close out any "singleton" lists. */
958 while (parent
->type
== EQN_LIST
&&
959 parent
->expectargs
== 1 &&
961 parent
= parent
->parent
;
963 case EQN_TOK_BRACE_OPEN
:
966 * If we already have something in the stack and we're
967 * in an expression, then rewind til we're not any more
968 * (just like with the text node).
970 while (parent
->args
== parent
->expectargs
)
971 parent
= parent
->parent
;
972 if (EQN_TOK_LEFT
== tok
&&
973 eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
974 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->node
->line
,
975 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
978 parent
= eqn_box_alloc(ep
, parent
);
979 parent
->type
= EQN_LIST
;
980 if (EQN_TOK_LEFT
== tok
) {
981 if (STRNEQ(ep
->start
, ep
->toksz
, "ceiling", 7))
982 parent
->left
= mandoc_strdup("\\[lc]");
983 else if (STRNEQ(ep
->start
, ep
->toksz
, "floor", 5))
984 parent
->left
= mandoc_strdup("\\[lf]");
987 mandoc_strndup(ep
->start
, ep
->toksz
);
997 while (parent
->args
== parent
->expectargs
)
998 parent
= parent
->parent
;
999 parent
= eqn_box_alloc(ep
, parent
);
1000 parent
->type
= EQN_PILE
;
1001 parent
->expectargs
= 1;
1004 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
1005 if (cur
->type
== EQN_PILE
)
1008 mandoc_msg(MANDOCERR_IT_STRAY
, ep
->node
->line
,
1009 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
1012 parent
= eqn_box_alloc(ep
, cur
);
1013 parent
->type
= EQN_LIST
;
1015 case EQN_TOK_MATRIX
:
1016 while (parent
->args
== parent
->expectargs
)
1017 parent
= parent
->parent
;
1018 parent
= eqn_box_alloc(ep
, parent
);
1019 parent
->type
= EQN_MATRIX
;
1020 parent
->expectargs
= 1;
1026 case EQN_TOK_QUOTED
:
1031 * If we already have something in the stack and we're
1032 * in an expression, then rewind til we're not any more.
1034 while (parent
->args
== parent
->expectargs
)
1035 parent
= parent
->parent
;
1036 cur
= eqn_box_alloc(ep
, parent
);
1037 cur
->type
= EQN_TEXT
;
1041 cur
->font
= EQNFONT_ROMAN
;
1043 case EQN_TOK_QUOTED
:
1044 if (cur
->font
== EQNFONT_NONE
)
1045 cur
->font
= EQNFONT_ITALIC
;
1050 if (cur
->font
!= EQNFONT_NONE
|| *p
== '\0')
1056 /* Advance to next character. */
1059 ccln
= isalpha((unsigned char)*cpn
) ? CCL_LET
:
1060 isdigit((unsigned char)*cpn
) ||
1061 (*cpn
== '.' && (ccl
== CCL_DIG
||
1062 isdigit((unsigned char)cpn
[1]))) ?
1064 /* No boundary before first character. */
1067 cur
->font
= ccl
== CCL_LET
?
1068 EQNFONT_ITALIC
: EQNFONT_ROMAN
;
1070 mandoc_escape(&cpn
, NULL
, NULL
);
1071 /* No boundary after last character. */
1074 if (ccln
== ccl
&& *cp
!= ',' && *cpn
!= ',')
1076 /* Boundary found, split the text. */
1077 if (parent
->args
== parent
->expectargs
) {
1078 /* Remove the text from the tree. */
1079 if (cur
->prev
== NULL
)
1080 parent
->first
= cur
->next
;
1082 cur
->prev
->next
= NULL
;
1083 parent
->last
= cur
->prev
;
1085 /* Set up a list instead. */
1086 split
= eqn_box_alloc(ep
, parent
);
1087 split
->type
= EQN_LIST
;
1088 /* Insert the word into the list. */
1089 split
->first
= split
->last
= cur
;
1090 cur
->parent
= split
;
1094 /* Append a new text box. */
1095 nbox
= eqn_box_alloc(ep
, parent
);
1096 nbox
->type
= EQN_TEXT
;
1097 nbox
->text
= mandoc_strdup(cpn
);
1098 /* Truncate the old box. */
1099 p
= mandoc_strndup(cur
->text
,
1103 /* Setup to process the new box. */
1110 parent
= split
->parent
;
1121 eqn_free(struct eqn_node
*p
)
1128 for (i
= 0; i
< (int)p
->defsz
; i
++) {
1129 free(p
->defs
[i
].key
);
1130 free(p
->defs
[i
].val
);