]>
git.cameronkatri.com Git - mandoc.git/blob - eqn.c
1 /* $Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */
3 * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
30 #include "mandoc_aux.h"
34 #include "libmandoc.h"
35 #include "eqn_parse.h"
37 #define EQN_NEST_MAX 128 /* maximum nesting of defines */
38 #define STRNEQ(p1, sz1, p2, sz2) \
39 ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
94 static const char *eqn_toks
[EQN_TOK__MAX
] = {
95 "dyad", /* EQN_TOK_DYAD */
96 "vec", /* EQN_TOK_VEC */
97 "under", /* EQN_TOK_UNDER */
98 "bar", /* EQN_TOK_BAR */
99 "tilde", /* EQN_TOK_TILDE */
100 "hat", /* EQN_TOK_HAT */
101 "dot", /* EQN_TOK_DOT */
102 "dotdot", /* EQN_TOK_DOTDOT */
103 "fwd", /* EQN_TOK_FWD * */
104 "back", /* EQN_TOK_BACK */
105 "down", /* EQN_TOK_DOWN */
106 "up", /* EQN_TOK_UP */
107 "fat", /* EQN_TOK_FAT */
108 "roman", /* EQN_TOK_ROMAN */
109 "italic", /* EQN_TOK_ITALIC */
110 "bold", /* EQN_TOK_BOLD */
111 "size", /* EQN_TOK_SIZE */
112 "sub", /* EQN_TOK_SUB */
113 "sup", /* EQN_TOK_SUP */
114 "sqrt", /* EQN_TOK_SQRT */
115 "over", /* EQN_TOK_OVER */
116 "from", /* EQN_TOK_FROM */
117 "to", /* EQN_TOK_TO */
118 "{", /* EQN_TOK_BRACE_OPEN */
119 "}", /* EQN_TOK_BRACE_CLOSE */
120 "gsize", /* EQN_TOK_GSIZE */
121 "gfont", /* EQN_TOK_GFONT */
122 "mark", /* EQN_TOK_MARK */
123 "lineup", /* EQN_TOK_LINEUP */
124 "left", /* EQN_TOK_LEFT */
125 "right", /* EQN_TOK_RIGHT */
126 "pile", /* EQN_TOK_PILE */
127 "lpile", /* EQN_TOK_LPILE */
128 "rpile", /* EQN_TOK_RPILE */
129 "cpile", /* EQN_TOK_CPILE */
130 "matrix", /* EQN_TOK_MATRIX */
131 "ccol", /* EQN_TOK_CCOL */
132 "lcol", /* EQN_TOK_LCOL */
133 "rcol", /* EQN_TOK_RCOL */
134 "delim", /* EQN_TOK_DELIM */
135 "define", /* EQN_TOK_DEFINE */
136 "tdefine", /* EQN_TOK_TDEFINE */
137 "ndefine", /* EQN_TOK_NDEFINE */
138 "undef", /* EQN_TOK_UNDEF */
139 "above", /* EQN_TOK_ABOVE */
142 static const char *const eqn_func
[] = {
143 "acos", "acsc", "and", "arc", "asec", "asin", "atan",
144 "cos", "cosh", "coth", "csc", "det", "exp", "for",
145 "if", "lim", "ln", "log", "max", "min",
146 "sec", "sin", "sinh", "tan", "tanh", "Im", "Re",
218 static const struct eqnsym eqnsyms
[EQNSYM__MAX
] = {
219 { "alpha", "*a" }, /* EQNSYM_alpha */
220 { "beta", "*b" }, /* EQNSYM_beta */
221 { "chi", "*x" }, /* EQNSYM_chi */
222 { "delta", "*d" }, /* EQNSYM_delta */
223 { "epsilon", "*e" }, /* EQNSYM_epsilon */
224 { "eta", "*y" }, /* EQNSYM_eta */
225 { "gamma", "*g" }, /* EQNSYM_gamma */
226 { "iota", "*i" }, /* EQNSYM_iota */
227 { "kappa", "*k" }, /* EQNSYM_kappa */
228 { "lambda", "*l" }, /* EQNSYM_lambda */
229 { "mu", "*m" }, /* EQNSYM_mu */
230 { "nu", "*n" }, /* EQNSYM_nu */
231 { "omega", "*w" }, /* EQNSYM_omega */
232 { "omicron", "*o" }, /* EQNSYM_omicron */
233 { "phi", "*f" }, /* EQNSYM_phi */
234 { "pi", "*p" }, /* EQNSYM_pi */
235 { "psi", "*q" }, /* EQNSYM_psi */
236 { "rho", "*r" }, /* EQNSYM_rho */
237 { "sigma", "*s" }, /* EQNSYM_sigma */
238 { "tau", "*t" }, /* EQNSYM_tau */
239 { "theta", "*h" }, /* EQNSYM_theta */
240 { "upsilon", "*u" }, /* EQNSYM_upsilon */
241 { "xi", "*c" }, /* EQNSYM_xi */
242 { "zeta", "*z" }, /* EQNSYM_zeta */
243 { "DELTA", "*D" }, /* EQNSYM_DELTA */
244 { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
245 { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
246 { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
247 { "PHI", "*F" }, /* EQNSYM_PHI */
248 { "PI", "*P" }, /* EQNSYM_PI */
249 { "PSI", "*Q" }, /* EQNSYM_PSI */
250 { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
251 { "THETA", "*H" }, /* EQNSYM_THETA */
252 { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
253 { "XI", "*C" }, /* EQNSYM_XI */
254 { "inter", "ca" }, /* EQNSYM_inter */
255 { "union", "cu" }, /* EQNSYM_union */
256 { "prod", "product" }, /* EQNSYM_prod */
257 { "int", "integral" }, /* EQNSYM_int */
258 { "sum", "sum" }, /* EQNSYM_sum */
259 { "grad", "gr" }, /* EQNSYM_grad */
260 { "del", "gr" }, /* EQNSYM_del */
261 { "times", "mu" }, /* EQNSYM_times */
262 { "cdot", "pc" }, /* EQNSYM_cdot */
263 { "nothing", "&" }, /* EQNSYM_nothing */
264 { "approx", "~~" }, /* EQNSYM_approx */
265 { "prime", "fm" }, /* EQNSYM_prime */
266 { "half", "12" }, /* EQNSYM_half */
267 { "partial", "pd" }, /* EQNSYM_partial */
268 { "inf", "if" }, /* EQNSYM_inf */
269 { ">>", ">>" }, /* EQNSYM_muchgreat */
270 { "<<", "<<" }, /* EQNSYM_muchless */
271 { "<-", "<-" }, /* EQNSYM_larrow */
272 { "->", "->" }, /* EQNSYM_rarrow */
273 { "+-", "+-" }, /* EQNSYM_pm */
274 { "!=", "!=" }, /* EQNSYM_nequal */
275 { "==", "==" }, /* EQNSYM_equiv */
276 { "<=", "<=" }, /* EQNSYM_lessequal */
277 { ">=", ">=" }, /* EQNSYM_moreequal */
278 { "-", "mi" }, /* EQNSYM_minus */
295 static struct eqn_box
*eqn_box_alloc(struct eqn_node
*, struct eqn_box
*);
296 static struct eqn_box
*eqn_box_makebinary(struct eqn_node
*,
298 static void eqn_def(struct eqn_node
*);
299 static struct eqn_def
*eqn_def_find(struct eqn_node
*);
300 static void eqn_delim(struct eqn_node
*);
301 static enum eqn_tok
eqn_next(struct eqn_node
*, enum parse_mode
);
302 static void eqn_undef(struct eqn_node
*);
310 ep
= mandoc_calloc(1, sizeof(*ep
));
311 ep
->gsize
= EQN_DEFSIZE
;
316 eqn_reset(struct eqn_node
*ep
)
319 ep
->data
= ep
->start
= ep
->end
= NULL
;
320 ep
->sz
= ep
->toksz
= 0;
324 eqn_read(struct eqn_node
*ep
, const char *p
)
328 if (ep
->data
== NULL
) {
330 ep
->data
= mandoc_strdup(p
);
332 ep
->sz
= mandoc_asprintf(&cp
, "%s %s", ep
->data
, p
);
340 * Find the key "key" of the give size within our eqn-defined values.
342 static struct eqn_def
*
343 eqn_def_find(struct eqn_node
*ep
)
347 for (i
= 0; i
< (int)ep
->defsz
; i
++)
348 if (ep
->defs
[i
].keysz
&& STRNEQ(ep
->defs
[i
].key
,
349 ep
->defs
[i
].keysz
, ep
->start
, ep
->toksz
))
356 * Parse a token from the input text. The modes are:
357 * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
358 * before its next occurence. Do not interpret the token in any
359 * way and return EQN_TOK_QUOTED. All other modes behave like
360 * MODE_QUOTED when *ep->start is '"'.
361 * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
362 * otherwise, it ends before the next whitespace or brace.
363 * Do not interpret the token and return EQN_TOK__MAX.
364 * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
365 * alias created with define. If it is an alias, replace it with
366 * its string value and reparse.
367 * MODE_TOK: Like MODE_SUB, but also check the token against the list
368 * of tokens, and if there is a match, return that token. Otherwise,
369 * if the token matches a symbol, return EQN_TOK_SYM; if it matches
370 * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for
371 * a token match, *ep->start is set to an allocated string that the
372 * caller is expected to free.
373 * All modes skip whitespace following the end of the token.
376 eqn_next(struct eqn_node
*ep
, enum parse_mode mode
)
378 static int last_len
, lim
;
386 * Reset the recursion counter after advancing
387 * beyond the end of the previous substitution.
389 if (ep
->end
- ep
->data
>= last_len
)
393 quoted
= mode
== MODE_QUOTED
;
395 switch (*ep
->start
) {
406 ep
->end
= strchr(ep
->start
+ 1, *ep
->start
);
407 ep
->start
++; /* Skip opening quote. */
408 if (ep
->end
== NULL
) {
409 mandoc_msg(MANDOCERR_ARG_QUOTE
,
410 ep
->node
->line
, ep
->node
->pos
, NULL
);
411 ep
->end
= strchr(ep
->start
, '\0');
414 ep
->end
= ep
->start
+ 1;
415 if (*ep
->start
!= '{' && *ep
->start
!= '}')
416 ep
->end
+= strcspn(ep
->end
, " ^~\"{}\t");
418 ep
->toksz
= ep
->end
- ep
->start
;
419 if (quoted
&& *ep
->end
!= '\0')
420 ep
->end
++; /* Skip closing quote. */
421 while (*ep
->end
!= '\0' && strchr(" \t^~", *ep
->end
) != NULL
)
423 if (quoted
) /* Cannot return, may have to strndup. */
425 if (mode
== MODE_NOSUB
)
427 if ((def
= eqn_def_find(ep
)) == NULL
)
429 if (++lim
> EQN_NEST_MAX
) {
430 mandoc_msg(MANDOCERR_ROFFLOOP
,
431 ep
->node
->line
, ep
->node
->pos
, NULL
);
435 /* Replace a defined name with its string value. */
436 if ((diff
= def
->valsz
- ep
->toksz
) > 0) {
437 start
= ep
->start
- ep
->data
;
439 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ 1);
440 ep
->start
= ep
->data
+ start
;
443 memmove(ep
->start
+ def
->valsz
, ep
->start
+ ep
->toksz
,
444 strlen(ep
->start
+ ep
->toksz
) + 1);
445 memcpy(ep
->start
, def
->val
, def
->valsz
);
446 last_len
= ep
->start
- ep
->data
+ def
->valsz
;
448 if (mode
!= MODE_TOK
)
449 return quoted
? EQN_TOK_QUOTED
: EQN_TOK__MAX
;
451 ep
->start
= mandoc_strndup(ep
->start
, ep
->toksz
);
452 return EQN_TOK_QUOTED
;
454 for (tok
= 0; tok
< EQN_TOK__MAX
; tok
++)
455 if (STRNEQ(ep
->start
, ep
->toksz
,
456 eqn_toks
[tok
], strlen(eqn_toks
[tok
])))
459 for (i
= 0; i
< EQNSYM__MAX
; i
++) {
460 if (STRNEQ(ep
->start
, ep
->toksz
,
461 eqnsyms
[i
].str
, strlen(eqnsyms
[i
].str
))) {
462 mandoc_asprintf(&ep
->start
,
463 "\\[%s]", eqnsyms
[i
].sym
);
467 ep
->start
= mandoc_strndup(ep
->start
, ep
->toksz
);
468 for (i
= 0; i
< (int)(sizeof(eqn_func
)/sizeof(*eqn_func
)); i
++)
469 if (STRNEQ(ep
->start
, ep
->toksz
,
470 eqn_func
[i
], strlen(eqn_func
[i
])))
476 eqn_box_free(struct eqn_box
*bp
)
482 eqn_box_free(bp
->first
);
484 eqn_box_free(bp
->next
);
499 bp
= mandoc_calloc(1, sizeof(*bp
));
500 bp
->expectargs
= UINT_MAX
;
505 * Allocate a box as the last child of the parent node.
507 static struct eqn_box
*
508 eqn_box_alloc(struct eqn_node
*ep
, struct eqn_box
*parent
)
515 bp
->font
= bp
->parent
->font
;
516 bp
->size
= ep
->gsize
;
518 if (NULL
!= parent
->first
) {
519 parent
->last
->next
= bp
;
520 bp
->prev
= parent
->last
;
529 * Reparent the current last node (of the current parent) under a new
530 * EQN_SUBEXPR as the first element.
531 * Then return the new parent.
532 * The new EQN_SUBEXPR will have a two-child limit.
534 static struct eqn_box
*
535 eqn_box_makebinary(struct eqn_node
*ep
, struct eqn_box
*parent
)
537 struct eqn_box
*b
, *newb
;
539 assert(NULL
!= parent
->last
);
541 if (parent
->last
== parent
->first
)
542 parent
->first
= NULL
;
544 parent
->last
= b
->prev
;
546 newb
= eqn_box_alloc(ep
, parent
);
547 newb
->type
= EQN_SUBEXPR
;
548 newb
->expectargs
= 2;
550 newb
->first
= newb
->last
= b
;
551 newb
->first
->next
= NULL
;
557 * Parse the "delim" control statement.
560 eqn_delim(struct eqn_node
*ep
)
562 if (ep
->end
[0] == '\0' || ep
->end
[1] == '\0') {
563 mandoc_msg(MANDOCERR_REQ_EMPTY
,
564 ep
->node
->line
, ep
->node
->pos
, "delim");
565 if (ep
->end
[0] != '\0')
567 } else if (strncmp(ep
->end
, "off", 3) == 0) {
570 } else if (strncmp(ep
->end
, "on", 2) == 0) {
571 if (ep
->odelim
&& ep
->cdelim
)
575 ep
->odelim
= *ep
->end
++;
576 ep
->cdelim
= *ep
->end
++;
582 * Undefine a previously-defined string.
585 eqn_undef(struct eqn_node
*ep
)
589 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
) {
590 mandoc_msg(MANDOCERR_REQ_EMPTY
,
591 ep
->node
->line
, ep
->node
->pos
, "undef");
594 if ((def
= eqn_def_find(ep
)) == NULL
)
598 def
->key
= def
->val
= NULL
;
599 def
->keysz
= def
->valsz
= 0;
603 eqn_def(struct eqn_node
*ep
)
608 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
) {
609 mandoc_msg(MANDOCERR_REQ_EMPTY
,
610 ep
->node
->line
, ep
->node
->pos
, "define");
615 * Search for a key that already exists.
616 * Create a new key if none is found.
618 if ((def
= eqn_def_find(ep
)) == NULL
) {
619 /* Find holes in string array. */
620 for (i
= 0; i
< (int)ep
->defsz
; i
++)
621 if (0 == ep
->defs
[i
].keysz
)
624 if (i
== (int)ep
->defsz
) {
626 ep
->defs
= mandoc_reallocarray(ep
->defs
,
627 ep
->defsz
, sizeof(struct eqn_def
));
628 ep
->defs
[i
].key
= ep
->defs
[i
].val
= NULL
;
633 def
->key
= mandoc_strndup(ep
->start
, ep
->toksz
);
634 def
->keysz
= ep
->toksz
;
637 if (eqn_next(ep
, MODE_QUOTED
) == EQN_TOK_EOF
) {
638 mandoc_msg(MANDOCERR_REQ_EMPTY
,
639 ep
->node
->line
, ep
->node
->pos
, "define %s", def
->key
);
642 def
->key
= def
->val
= NULL
;
643 def
->keysz
= def
->valsz
= 0;
647 def
->val
= mandoc_strndup(ep
->start
, ep
->toksz
);
648 def
->valsz
= ep
->toksz
;
652 eqn_parse(struct eqn_node
*ep
)
654 struct eqn_box
*cur
, *nbox
, *parent
, *split
;
655 const char *cp
, *cpn
;
658 enum { CCL_LET
, CCL_DIG
, CCL_PUN
} ccl
, ccln
;
661 parent
= ep
->node
->eqn
;
662 assert(parent
!= NULL
);
666 * Do not add it to the high-level syntax tree.
669 if (ep
->data
== NULL
)
672 ep
->start
= ep
->end
= ep
->data
+ strspn(ep
->data
, " ^~");
675 tok
= eqn_next(ep
, MODE_TOK
);
680 case EQN_TOK_NDEFINE
:
684 case EQN_TOK_TDEFINE
:
685 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
||
686 eqn_next(ep
, MODE_QUOTED
) == EQN_TOK_EOF
)
687 mandoc_msg(MANDOCERR_REQ_EMPTY
,
688 ep
->node
->line
, ep
->node
->pos
, "tdefine");
694 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
)
695 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->node
->line
,
696 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
710 if (parent
->last
== NULL
) {
711 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->node
->line
,
712 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
713 cur
= eqn_box_alloc(ep
, parent
);
714 cur
->type
= EQN_TEXT
;
715 cur
->text
= mandoc_strdup("");
717 parent
= eqn_box_makebinary(ep
, parent
);
718 parent
->type
= EQN_LIST
;
719 parent
->expectargs
= 1;
720 parent
->font
= EQNFONT_ROMAN
;
723 parent
->top
= mandoc_strdup("\\[ad]");
726 parent
->top
= mandoc_strdup("\\[->]");
729 parent
->top
= mandoc_strdup("\\[<>]");
732 parent
->top
= mandoc_strdup("\\[a~]");
735 parent
->bottom
= mandoc_strdup("\\[ul]");
738 parent
->top
= mandoc_strdup("\\[rn]");
741 parent
->top
= mandoc_strdup("\\[a.]");
744 parent
->top
= mandoc_strdup("\\[ha]");
749 parent
= parent
->parent
;
755 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
)
756 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->node
->line
,
757 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
763 while (parent
->args
== parent
->expectargs
)
764 parent
= parent
->parent
;
766 * These values apply to the next word or sequence of
767 * words; thus, we mark that we'll have a child with
768 * exactly one of those.
770 parent
= eqn_box_alloc(ep
, parent
);
771 parent
->type
= EQN_LIST
;
772 parent
->expectargs
= 1;
775 parent
->font
= EQNFONT_FAT
;
778 parent
->font
= EQNFONT_ROMAN
;
781 parent
->font
= EQNFONT_ITALIC
;
784 parent
->font
= EQNFONT_BOLD
;
792 /* Accept two values: integral size and a single. */
793 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
794 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->node
->line
,
795 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
798 size
= mandoc_strntoi(ep
->start
, ep
->toksz
, 10);
800 mandoc_msg(MANDOCERR_IT_NONUM
, ep
->node
->line
,
801 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
804 if (EQN_TOK_GSIZE
== tok
) {
808 while (parent
->args
== parent
->expectargs
)
809 parent
= parent
->parent
;
810 parent
= eqn_box_alloc(ep
, parent
);
811 parent
->type
= EQN_LIST
;
812 parent
->expectargs
= 1;
820 * We have a left-right-associative expression.
821 * Repivot under a positional node, open a child scope
822 * and keep on reading.
824 if (parent
->last
== NULL
) {
825 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->node
->line
,
826 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
827 cur
= eqn_box_alloc(ep
, parent
);
828 cur
->type
= EQN_TEXT
;
829 cur
->text
= mandoc_strdup("");
831 while (parent
->expectargs
== 1 && parent
->args
== 1)
832 parent
= parent
->parent
;
833 if (tok
== EQN_TOK_FROM
|| tok
== EQN_TOK_TO
) {
834 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
835 if (cur
->pos
== EQNPOS_SUB
||
836 cur
->pos
== EQNPOS_SUP
||
837 cur
->pos
== EQNPOS_SUBSUP
||
838 cur
->pos
== EQNPOS_SQRT
||
839 cur
->pos
== EQNPOS_OVER
)
842 parent
= cur
->parent
;
844 if (tok
== EQN_TOK_SUP
&& parent
->pos
== EQNPOS_SUB
) {
845 parent
->expectargs
= 3;
846 parent
->pos
= EQNPOS_SUBSUP
;
849 if (tok
== EQN_TOK_TO
&& parent
->pos
== EQNPOS_FROM
) {
850 parent
->expectargs
= 3;
851 parent
->pos
= EQNPOS_FROMTO
;
854 parent
= eqn_box_makebinary(ep
, parent
);
857 parent
->pos
= EQNPOS_FROM
;
860 parent
->pos
= EQNPOS_TO
;
863 parent
->pos
= EQNPOS_SUP
;
866 parent
->pos
= EQNPOS_SUB
;
873 while (parent
->args
== parent
->expectargs
)
874 parent
= parent
->parent
;
876 * Accept a left-right-associative set of arguments just
877 * like sub and sup and friends but without rebalancing
880 parent
= eqn_box_alloc(ep
, parent
);
881 parent
->type
= EQN_SUBEXPR
;
882 parent
->pos
= EQNPOS_SQRT
;
883 parent
->expectargs
= 1;
887 * We have a right-left-associative fraction.
888 * Close out anything that's currently open, then
889 * rebalance and continue reading.
891 if (parent
->last
== NULL
) {
892 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->node
->line
,
893 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
894 cur
= eqn_box_alloc(ep
, parent
);
895 cur
->type
= EQN_TEXT
;
896 cur
->text
= mandoc_strdup("");
898 while (parent
->args
== parent
->expectargs
)
899 parent
= parent
->parent
;
900 while (EQN_SUBEXPR
== parent
->type
)
901 parent
= parent
->parent
;
902 parent
= eqn_box_makebinary(ep
, parent
);
903 parent
->pos
= EQNPOS_OVER
;
906 case EQN_TOK_BRACE_CLOSE
:
908 * Close out the existing brace.
909 * FIXME: this is a shitty sentinel: we should really
910 * have a native EQN_BRACE type or whatnot.
912 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
913 if (cur
->type
== EQN_LIST
&&
914 cur
->expectargs
> 1 &&
915 (tok
== EQN_TOK_BRACE_CLOSE
||
919 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ep
->node
->line
,
920 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
924 if (EQN_TOK_RIGHT
== tok
) {
925 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
926 mandoc_msg(MANDOCERR_REQ_EMPTY
,
927 ep
->node
->line
, ep
->node
->pos
,
928 "%s", eqn_toks
[tok
]);
931 /* Handling depends on right/left. */
932 if (STRNEQ(ep
->start
, ep
->toksz
, "ceiling", 7))
933 parent
->right
= mandoc_strdup("\\[rc]");
934 else if (STRNEQ(ep
->start
, ep
->toksz
, "floor", 5))
935 parent
->right
= mandoc_strdup("\\[rf]");
938 mandoc_strndup(ep
->start
, ep
->toksz
);
940 parent
= parent
->parent
;
941 if (tok
== EQN_TOK_BRACE_CLOSE
&&
942 (parent
->type
== EQN_PILE
||
943 parent
->type
== EQN_MATRIX
))
944 parent
= parent
->parent
;
945 /* Close out any "singleton" lists. */
946 while (parent
->type
== EQN_LIST
&&
947 parent
->expectargs
== 1 &&
949 parent
= parent
->parent
;
951 case EQN_TOK_BRACE_OPEN
:
954 * If we already have something in the stack and we're
955 * in an expression, then rewind til we're not any more
956 * (just like with the text node).
958 while (parent
->args
== parent
->expectargs
)
959 parent
= parent
->parent
;
960 if (EQN_TOK_LEFT
== tok
&&
961 eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
962 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->node
->line
,
963 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
966 parent
= eqn_box_alloc(ep
, parent
);
967 parent
->type
= EQN_LIST
;
968 if (EQN_TOK_LEFT
== tok
) {
969 if (STRNEQ(ep
->start
, ep
->toksz
, "ceiling", 7))
970 parent
->left
= mandoc_strdup("\\[lc]");
971 else if (STRNEQ(ep
->start
, ep
->toksz
, "floor", 5))
972 parent
->left
= mandoc_strdup("\\[lf]");
975 mandoc_strndup(ep
->start
, ep
->toksz
);
985 while (parent
->args
== parent
->expectargs
)
986 parent
= parent
->parent
;
987 parent
= eqn_box_alloc(ep
, parent
);
988 parent
->type
= EQN_PILE
;
989 parent
->expectargs
= 1;
992 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
993 if (cur
->type
== EQN_PILE
)
996 mandoc_msg(MANDOCERR_IT_STRAY
, ep
->node
->line
,
997 ep
->node
->pos
, "%s", eqn_toks
[tok
]);
1000 parent
= eqn_box_alloc(ep
, cur
);
1001 parent
->type
= EQN_LIST
;
1003 case EQN_TOK_MATRIX
:
1004 while (parent
->args
== parent
->expectargs
)
1005 parent
= parent
->parent
;
1006 parent
= eqn_box_alloc(ep
, parent
);
1007 parent
->type
= EQN_MATRIX
;
1008 parent
->expectargs
= 1;
1014 case EQN_TOK_QUOTED
:
1019 * If we already have something in the stack and we're
1020 * in an expression, then rewind til we're not any more.
1022 while (parent
->args
== parent
->expectargs
)
1023 parent
= parent
->parent
;
1024 cur
= eqn_box_alloc(ep
, parent
);
1025 cur
->type
= EQN_TEXT
;
1029 cur
->font
= EQNFONT_ROMAN
;
1031 case EQN_TOK_QUOTED
:
1032 if (cur
->font
== EQNFONT_NONE
)
1033 cur
->font
= EQNFONT_ITALIC
;
1038 if (cur
->font
!= EQNFONT_NONE
|| *p
== '\0')
1044 /* Advance to next character. */
1047 ccln
= isalpha((unsigned char)*cpn
) ? CCL_LET
:
1048 isdigit((unsigned char)*cpn
) ||
1049 (*cpn
== '.' && (ccl
== CCL_DIG
||
1050 isdigit((unsigned char)cpn
[1]))) ?
1052 /* No boundary before first character. */
1055 cur
->font
= ccl
== CCL_LET
?
1056 EQNFONT_ITALIC
: EQNFONT_ROMAN
;
1058 mandoc_escape(&cpn
, NULL
, NULL
);
1059 /* No boundary after last character. */
1062 if (ccln
== ccl
&& *cp
!= ',' && *cpn
!= ',')
1064 /* Boundary found, split the text. */
1065 if (parent
->args
== parent
->expectargs
) {
1066 /* Remove the text from the tree. */
1067 if (cur
->prev
== NULL
)
1068 parent
->first
= cur
->next
;
1070 cur
->prev
->next
= NULL
;
1071 parent
->last
= cur
->prev
;
1073 /* Set up a list instead. */
1074 split
= eqn_box_alloc(ep
, parent
);
1075 split
->type
= EQN_LIST
;
1076 /* Insert the word into the list. */
1077 split
->first
= split
->last
= cur
;
1078 cur
->parent
= split
;
1082 /* Append a new text box. */
1083 nbox
= eqn_box_alloc(ep
, parent
);
1084 nbox
->type
= EQN_TEXT
;
1085 nbox
->text
= mandoc_strdup(cpn
);
1086 /* Truncate the old box. */
1087 p
= mandoc_strndup(cur
->text
,
1091 /* Setup to process the new box. */
1098 parent
= split
->parent
;
1109 eqn_free(struct eqn_node
*p
)
1116 for (i
= 0; i
< (int)p
->defsz
; i
++) {
1117 free(p
->defs
[i
].key
);
1118 free(p
->defs
[i
].val
);