]>
git.cameronkatri.com Git - mandoc.git/blob - eqn.c
1 /* $Id: eqn.c,v 1.72 2017/06/29 16:31:15 schwarze Exp $ */
3 * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014, 2015, 2017 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>
31 #include "mandoc_aux.h"
32 #include "libmandoc.h"
35 #define EQN_NEST_MAX 128 /* maximum nesting of defines */
36 #define STRNEQ(p1, sz1, p2, sz2) \
37 ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
92 static const char *eqn_toks
[EQN_TOK__MAX
] = {
93 "dyad", /* EQN_TOK_DYAD */
94 "vec", /* EQN_TOK_VEC */
95 "under", /* EQN_TOK_UNDER */
96 "bar", /* EQN_TOK_BAR */
97 "tilde", /* EQN_TOK_TILDE */
98 "hat", /* EQN_TOK_HAT */
99 "dot", /* EQN_TOK_DOT */
100 "dotdot", /* EQN_TOK_DOTDOT */
101 "fwd", /* EQN_TOK_FWD * */
102 "back", /* EQN_TOK_BACK */
103 "down", /* EQN_TOK_DOWN */
104 "up", /* EQN_TOK_UP */
105 "fat", /* EQN_TOK_FAT */
106 "roman", /* EQN_TOK_ROMAN */
107 "italic", /* EQN_TOK_ITALIC */
108 "bold", /* EQN_TOK_BOLD */
109 "size", /* EQN_TOK_SIZE */
110 "sub", /* EQN_TOK_SUB */
111 "sup", /* EQN_TOK_SUP */
112 "sqrt", /* EQN_TOK_SQRT */
113 "over", /* EQN_TOK_OVER */
114 "from", /* EQN_TOK_FROM */
115 "to", /* EQN_TOK_TO */
116 "{", /* EQN_TOK_BRACE_OPEN */
117 "}", /* EQN_TOK_BRACE_CLOSE */
118 "gsize", /* EQN_TOK_GSIZE */
119 "gfont", /* EQN_TOK_GFONT */
120 "mark", /* EQN_TOK_MARK */
121 "lineup", /* EQN_TOK_LINEUP */
122 "left", /* EQN_TOK_LEFT */
123 "right", /* EQN_TOK_RIGHT */
124 "pile", /* EQN_TOK_PILE */
125 "lpile", /* EQN_TOK_LPILE */
126 "rpile", /* EQN_TOK_RPILE */
127 "cpile", /* EQN_TOK_CPILE */
128 "matrix", /* EQN_TOK_MATRIX */
129 "ccol", /* EQN_TOK_CCOL */
130 "lcol", /* EQN_TOK_LCOL */
131 "rcol", /* EQN_TOK_RCOL */
132 "delim", /* EQN_TOK_DELIM */
133 "define", /* EQN_TOK_DEFINE */
134 "tdefine", /* EQN_TOK_TDEFINE */
135 "ndefine", /* EQN_TOK_NDEFINE */
136 "undef", /* EQN_TOK_UNDEF */
137 "above", /* EQN_TOK_ABOVE */
140 static const char *const eqn_func
[] = {
141 "acos", "acsc", "and", "arc", "asec", "asin", "atan",
142 "cos", "cosh", "coth", "csc", "det", "exp", "for",
143 "if", "lim", "ln", "log", "max", "min",
144 "sec", "sin", "sinh", "tan", "tanh", "Im", "Re",
216 static const struct eqnsym eqnsyms
[EQNSYM__MAX
] = {
217 { "alpha", "*a" }, /* EQNSYM_alpha */
218 { "beta", "*b" }, /* EQNSYM_beta */
219 { "chi", "*x" }, /* EQNSYM_chi */
220 { "delta", "*d" }, /* EQNSYM_delta */
221 { "epsilon", "*e" }, /* EQNSYM_epsilon */
222 { "eta", "*y" }, /* EQNSYM_eta */
223 { "gamma", "*g" }, /* EQNSYM_gamma */
224 { "iota", "*i" }, /* EQNSYM_iota */
225 { "kappa", "*k" }, /* EQNSYM_kappa */
226 { "lambda", "*l" }, /* EQNSYM_lambda */
227 { "mu", "*m" }, /* EQNSYM_mu */
228 { "nu", "*n" }, /* EQNSYM_nu */
229 { "omega", "*w" }, /* EQNSYM_omega */
230 { "omicron", "*o" }, /* EQNSYM_omicron */
231 { "phi", "*f" }, /* EQNSYM_phi */
232 { "pi", "*p" }, /* EQNSYM_pi */
233 { "psi", "*q" }, /* EQNSYM_psi */
234 { "rho", "*r" }, /* EQNSYM_rho */
235 { "sigma", "*s" }, /* EQNSYM_sigma */
236 { "tau", "*t" }, /* EQNSYM_tau */
237 { "theta", "*h" }, /* EQNSYM_theta */
238 { "upsilon", "*u" }, /* EQNSYM_upsilon */
239 { "xi", "*c" }, /* EQNSYM_xi */
240 { "zeta", "*z" }, /* EQNSYM_zeta */
241 { "DELTA", "*D" }, /* EQNSYM_DELTA */
242 { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
243 { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
244 { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
245 { "PHI", "*F" }, /* EQNSYM_PHI */
246 { "PI", "*P" }, /* EQNSYM_PI */
247 { "PSI", "*Q" }, /* EQNSYM_PSI */
248 { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
249 { "THETA", "*H" }, /* EQNSYM_THETA */
250 { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
251 { "XI", "*C" }, /* EQNSYM_XI */
252 { "inter", "ca" }, /* EQNSYM_inter */
253 { "union", "cu" }, /* EQNSYM_union */
254 { "prod", "product" }, /* EQNSYM_prod */
255 { "int", "integral" }, /* EQNSYM_int */
256 { "sum", "sum" }, /* EQNSYM_sum */
257 { "grad", "gr" }, /* EQNSYM_grad */
258 { "del", "gr" }, /* EQNSYM_del */
259 { "times", "mu" }, /* EQNSYM_times */
260 { "cdot", "pc" }, /* EQNSYM_cdot */
261 { "nothing", "&" }, /* EQNSYM_nothing */
262 { "approx", "~~" }, /* EQNSYM_approx */
263 { "prime", "fm" }, /* EQNSYM_prime */
264 { "half", "12" }, /* EQNSYM_half */
265 { "partial", "pd" }, /* EQNSYM_partial */
266 { "inf", "if" }, /* EQNSYM_inf */
267 { ">>", ">>" }, /* EQNSYM_muchgreat */
268 { "<<", "<<" }, /* EQNSYM_muchless */
269 { "<-", "<-" }, /* EQNSYM_larrow */
270 { "->", "->" }, /* EQNSYM_rarrow */
271 { "+-", "+-" }, /* EQNSYM_pm */
272 { "!=", "!=" }, /* EQNSYM_nequal */
273 { "==", "==" }, /* EQNSYM_equiv */
274 { "<=", "<=" }, /* EQNSYM_lessequal */
275 { ">=", ">=" }, /* EQNSYM_moreequal */
276 { "-", "mi" }, /* EQNSYM_minus */
286 static struct eqn_box
*eqn_box_alloc(struct eqn_node
*, struct eqn_box
*);
287 static void eqn_box_free(struct eqn_box
*);
288 static struct eqn_box
*eqn_box_makebinary(struct eqn_node
*,
289 enum eqn_post
, struct eqn_box
*);
290 static void eqn_def(struct eqn_node
*);
291 static struct eqn_def
*eqn_def_find(struct eqn_node
*);
292 static void eqn_delim(struct eqn_node
*);
293 static enum eqn_tok
eqn_next(struct eqn_node
*, enum parse_mode
);
294 static enum rofferr
eqn_parse(struct eqn_node
*, struct eqn_box
*);
295 static void eqn_undef(struct eqn_node
*);
299 eqn_read(struct eqn_node
**epp
, int ln
,
300 const char *p
, int pos
, int *offs
)
309 * If we're the terminating mark, unset our equation status and
310 * validate the full equation.
313 if (0 == strncmp(p
, ".EN", 3)) {
316 while (' ' == *p
|| '\t' == *p
)
320 mandoc_vmsg(MANDOCERR_ARG_SKIP
, ep
->parse
,
321 ln
, pos
, "EN %s", p
);
326 * Build up the full string, replacing all newlines with regular
330 sz
= strlen(p
+ pos
) + 1;
331 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ sz
+ 1);
333 /* First invocation: nil terminate the string. */
339 strlcat(ep
->data
, p
+ pos
, ep
->sz
+ 1);
340 strlcat(ep
->data
, " ", ep
->sz
+ 1);
345 eqn_alloc(int pos
, int line
, struct mparse
*parse
)
349 p
= mandoc_calloc(1, sizeof(struct eqn_node
));
354 p
->gsize
= EQN_DEFSIZE
;
360 * Find the key "key" of the give size within our eqn-defined values.
362 static struct eqn_def
*
363 eqn_def_find(struct eqn_node
*ep
)
367 for (i
= 0; i
< (int)ep
->defsz
; i
++)
368 if (ep
->defs
[i
].keysz
&& STRNEQ(ep
->defs
[i
].key
,
369 ep
->defs
[i
].keysz
, ep
->start
, ep
->toksz
))
376 * Parse a token from the input text. The modes are:
377 * MODE_QUOTED: Use *ep->start as the delimiter; the token ends
378 * before its next occurence. Do not interpret the token in any
379 * way and return EQN_TOK_QUOTED. All other modes behave like
380 * MODE_QUOTED when *ep->start is '"'.
381 * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it;
382 * otherwise, it ends before the next whitespace or brace.
383 * Do not interpret the token and return EQN_TOK__MAX.
384 * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an
385 * alias created with define. If it is an alias, replace it with
386 * its string value and reparse.
387 * MODE_TOK: Like MODE_SUB, but also check the token against the list
388 * of tokens, and if there is a match, return that token. Otherwise,
389 * if the token matches a symbol, return EQN_TOK_SYM; if it matches
390 * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for
391 * a token match, *ep->start is set to an allocated string that the
392 * caller is expected to free.
393 * All modes skip whitespace following the end of the token.
396 eqn_next(struct eqn_node
*ep
, enum parse_mode mode
)
398 static int last_len
, lim
;
406 * Reset the recursion counter after advancing
407 * beyond the end of the previous substitution.
409 if (ep
->end
- ep
->data
>= last_len
)
413 quoted
= mode
== MODE_QUOTED
;
415 switch (*ep
->start
) {
426 ep
->end
= strchr(ep
->start
+ 1, *ep
->start
);
427 ep
->start
++; /* Skip opening quote. */
428 if (ep
->end
== NULL
) {
429 mandoc_msg(MANDOCERR_ARG_QUOTE
, ep
->parse
,
430 ep
->eqn
.ln
, ep
->eqn
.pos
, NULL
);
431 ep
->end
= strchr(ep
->start
, '\0');
434 ep
->end
= ep
->start
+ 1;
435 if (*ep
->start
!= '{' && *ep
->start
!= '}')
436 ep
->end
+= strcspn(ep
->end
, " ^~\"{}\t");
438 ep
->toksz
= ep
->end
- ep
->start
;
439 if (quoted
&& *ep
->end
!= '\0')
440 ep
->end
++; /* Skip closing quote. */
441 while (*ep
->end
!= '\0' && strchr(" \t^~", *ep
->end
) != NULL
)
443 if (quoted
) /* Cannot return, may have to strndup. */
445 if (mode
== MODE_NOSUB
)
447 if ((def
= eqn_def_find(ep
)) == NULL
)
449 if (++lim
> EQN_NEST_MAX
) {
450 mandoc_msg(MANDOCERR_ROFFLOOP
, ep
->parse
,
451 ep
->eqn
.ln
, ep
->eqn
.pos
, NULL
);
455 /* Replace a defined name with its string value. */
456 if ((diff
= def
->valsz
- ep
->toksz
) > 0) {
457 start
= ep
->start
- ep
->data
;
459 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ 1);
460 ep
->start
= ep
->data
+ start
;
463 memmove(ep
->start
+ def
->valsz
, ep
->start
+ ep
->toksz
,
464 strlen(ep
->start
+ ep
->toksz
) + 1);
465 memcpy(ep
->start
, def
->val
, def
->valsz
);
466 last_len
= ep
->start
- ep
->data
+ def
->valsz
;
468 if (mode
!= MODE_TOK
)
469 return quoted
? EQN_TOK_QUOTED
: EQN_TOK__MAX
;
471 ep
->start
= mandoc_strndup(ep
->start
, ep
->toksz
);
472 return EQN_TOK_QUOTED
;
474 for (tok
= 0; tok
< EQN_TOK__MAX
; tok
++)
475 if (STRNEQ(ep
->start
, ep
->toksz
,
476 eqn_toks
[tok
], strlen(eqn_toks
[tok
])))
479 for (i
= 0; i
< EQNSYM__MAX
; i
++) {
480 if (STRNEQ(ep
->start
, ep
->toksz
,
481 eqnsyms
[i
].str
, strlen(eqnsyms
[i
].str
))) {
482 mandoc_asprintf(&ep
->start
,
483 "\\[%s]", eqnsyms
[i
].sym
);
487 ep
->start
= mandoc_strndup(ep
->start
, ep
->toksz
);
488 for (i
= 0; i
< (int)(sizeof(eqn_func
)/sizeof(*eqn_func
)); i
++)
489 if (STRNEQ(ep
->start
, ep
->toksz
,
490 eqn_func
[i
], strlen(eqn_func
[i
])))
496 eqn_box_free(struct eqn_box
*bp
)
500 eqn_box_free(bp
->first
);
502 eqn_box_free(bp
->next
);
513 * Allocate a box as the last child of the parent node.
515 static struct eqn_box
*
516 eqn_box_alloc(struct eqn_node
*ep
, struct eqn_box
*parent
)
520 bp
= mandoc_calloc(1, sizeof(struct eqn_box
));
523 bp
->expectargs
= UINT_MAX
;
524 bp
->font
= bp
->parent
->font
;
525 bp
->size
= ep
->gsize
;
527 if (NULL
!= parent
->first
) {
528 parent
->last
->next
= bp
;
529 bp
->prev
= parent
->last
;
538 * Reparent the current last node (of the current parent) under a new
539 * EQN_SUBEXPR as the first element.
540 * Then return the new parent.
541 * The new EQN_SUBEXPR will have a two-child limit.
543 static struct eqn_box
*
544 eqn_box_makebinary(struct eqn_node
*ep
,
545 enum eqn_post pos
, 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
);
558 newb
->type
= EQN_SUBEXPR
;
559 newb
->expectargs
= 2;
561 newb
->first
= newb
->last
= b
;
562 newb
->first
->next
= NULL
;
568 * Parse the "delim" control statement.
571 eqn_delim(struct eqn_node
*ep
)
573 if (ep
->end
[0] == '\0' || ep
->end
[1] == '\0') {
574 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
575 ep
->eqn
.ln
, ep
->eqn
.pos
, "delim");
576 if (ep
->end
[0] != '\0')
578 } else if (strncmp(ep
->end
, "off", 3) == 0) {
581 } else if (strncmp(ep
->end
, "on", 2) == 0) {
582 if (ep
->odelim
&& ep
->cdelim
)
586 ep
->odelim
= *ep
->end
++;
587 ep
->cdelim
= *ep
->end
++;
593 * Undefine a previously-defined string.
596 eqn_undef(struct eqn_node
*ep
)
600 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
) {
601 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
602 ep
->eqn
.ln
, ep
->eqn
.pos
, "undef");
605 if ((def
= eqn_def_find(ep
)) == NULL
)
609 def
->key
= def
->val
= NULL
;
610 def
->keysz
= def
->valsz
= 0;
614 eqn_def(struct eqn_node
*ep
)
619 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
) {
620 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
621 ep
->eqn
.ln
, ep
->eqn
.pos
, "define");
626 * Search for a key that already exists.
627 * Create a new key if none is found.
629 if ((def
= eqn_def_find(ep
)) == NULL
) {
630 /* Find holes in string array. */
631 for (i
= 0; i
< (int)ep
->defsz
; i
++)
632 if (0 == ep
->defs
[i
].keysz
)
635 if (i
== (int)ep
->defsz
) {
637 ep
->defs
= mandoc_reallocarray(ep
->defs
,
638 ep
->defsz
, sizeof(struct eqn_def
));
639 ep
->defs
[i
].key
= ep
->defs
[i
].val
= NULL
;
644 def
->key
= mandoc_strndup(ep
->start
, ep
->toksz
);
645 def
->keysz
= ep
->toksz
;
648 if (eqn_next(ep
, MODE_QUOTED
) == EQN_TOK_EOF
) {
649 mandoc_vmsg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
650 ep
->eqn
.ln
, ep
->eqn
.pos
, "define %s", def
->key
);
653 def
->key
= def
->val
= NULL
;
654 def
->keysz
= def
->valsz
= 0;
658 def
->val
= mandoc_strndup(ep
->start
, ep
->toksz
);
659 def
->valsz
= ep
->toksz
;
663 * Recursively parse an eqn(7) expression.
666 eqn_parse(struct eqn_node
*ep
, struct eqn_box
*parent
)
668 struct eqn_box
*cur
, *nbox
, *split
;
669 const char *cp
, *cpn
;
673 enum { CCL_LET
, CCL_DIG
, CCL_PUN
} ccl
, ccln
;
676 assert(parent
!= NULL
);
680 * Do not add it to the high-level syntax tree.
683 if (ep
->data
== NULL
)
686 ep
->start
= ep
->end
= ep
->data
+ strspn(ep
->data
, " ^~");
689 tok
= eqn_next(ep
, MODE_TOK
);
694 case EQN_TOK_NDEFINE
:
698 case EQN_TOK_TDEFINE
:
699 if (eqn_next(ep
, MODE_NOSUB
) == EQN_TOK_EOF
||
700 eqn_next(ep
, MODE_QUOTED
) == EQN_TOK_EOF
)
701 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
702 ep
->eqn
.ln
, ep
->eqn
.pos
, "tdefine");
708 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
)
709 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
710 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
724 if (parent
->last
== NULL
) {
725 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
726 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
727 cur
= eqn_box_alloc(ep
, parent
);
728 cur
->type
= EQN_TEXT
;
729 cur
->text
= mandoc_strdup("");
731 parent
= eqn_box_makebinary(ep
, EQNPOS_NONE
, parent
);
732 parent
->type
= EQN_LISTONE
;
733 parent
->expectargs
= 1;
734 parent
->font
= EQNFONT_ROMAN
;
737 parent
->top
= mandoc_strdup("\\[ad]");
740 parent
->top
= mandoc_strdup("\\[->]");
743 parent
->top
= mandoc_strdup("\\[<>]");
746 parent
->top
= mandoc_strdup("\\[a~]");
749 parent
->bottom
= mandoc_strdup("\\[ul]");
752 parent
->top
= mandoc_strdup("\\[rl]");
755 parent
->top
= mandoc_strdup("\\[a.]");
758 parent
->top
= mandoc_strdup("\\[ha]");
763 parent
= parent
->parent
;
769 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
)
770 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
771 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
777 while (parent
->args
== parent
->expectargs
)
778 parent
= parent
->parent
;
780 * These values apply to the next word or sequence of
781 * words; thus, we mark that we'll have a child with
782 * exactly one of those.
784 parent
= eqn_box_alloc(ep
, parent
);
785 parent
->type
= EQN_LISTONE
;
786 parent
->expectargs
= 1;
789 parent
->font
= EQNFONT_FAT
;
792 parent
->font
= EQNFONT_ROMAN
;
795 parent
->font
= EQNFONT_ITALIC
;
798 parent
->font
= EQNFONT_BOLD
;
806 /* Accept two values: integral size and a single. */
807 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
808 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
809 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
812 size
= mandoc_strntoi(ep
->start
, ep
->toksz
, 10);
814 mandoc_msg(MANDOCERR_IT_NONUM
, ep
->parse
,
815 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
818 if (EQN_TOK_GSIZE
== tok
) {
822 parent
= eqn_box_alloc(ep
, parent
);
823 parent
->type
= EQN_LISTONE
;
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
->parse
,
838 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
839 cur
= eqn_box_alloc(ep
, parent
);
840 cur
->type
= EQN_TEXT
;
841 cur
->text
= mandoc_strdup("");
843 /* Handle the "subsup" and "fromto" positions. */
844 if (EQN_TOK_SUP
== tok
&& parent
->pos
== EQNPOS_SUB
) {
845 parent
->expectargs
= 3;
846 parent
->pos
= EQNPOS_SUBSUP
;
849 if (EQN_TOK_TO
== tok
&& parent
->pos
== EQNPOS_FROM
) {
850 parent
->expectargs
= 3;
851 parent
->pos
= EQNPOS_FROMTO
;
870 parent
= eqn_box_makebinary(ep
, pos
, parent
);
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
->parse
,
893 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
894 cur
= eqn_box_alloc(ep
, parent
);
895 cur
->type
= EQN_TEXT
;
896 cur
->text
= mandoc_strdup("");
898 while (EQN_SUBEXPR
== parent
->type
)
899 parent
= parent
->parent
;
900 parent
= eqn_box_makebinary(ep
, EQNPOS_OVER
, parent
);
903 case EQN_TOK_BRACE_CLOSE
:
905 * Close out the existing brace.
906 * FIXME: this is a shitty sentinel: we should really
907 * have a native EQN_BRACE type or whatnot.
909 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
910 if (cur
->type
== EQN_LIST
&&
911 (tok
== EQN_TOK_BRACE_CLOSE
||
915 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ep
->parse
,
916 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
920 if (EQN_TOK_RIGHT
== tok
) {
921 if (eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
922 mandoc_msg(MANDOCERR_REQ_EMPTY
,
923 ep
->parse
, ep
->eqn
.ln
,
924 ep
->eqn
.pos
, eqn_toks
[tok
]);
927 /* Handling depends on right/left. */
928 if (STRNEQ(ep
->start
, ep
->toksz
, "ceiling", 7))
929 parent
->right
= mandoc_strdup("\\[rc]");
930 else if (STRNEQ(ep
->start
, ep
->toksz
, "floor", 5))
931 parent
->right
= mandoc_strdup("\\[rf]");
934 mandoc_strndup(ep
->start
, ep
->toksz
);
936 parent
= parent
->parent
;
937 if (tok
== EQN_TOK_BRACE_CLOSE
&&
938 (parent
->type
== EQN_PILE
||
939 parent
->type
== EQN_MATRIX
))
940 parent
= parent
->parent
;
941 /* Close out any "singleton" lists. */
942 while (parent
->type
== EQN_LISTONE
&&
943 parent
->args
== parent
->expectargs
)
944 parent
= parent
->parent
;
946 case EQN_TOK_BRACE_OPEN
:
949 * If we already have something in the stack and we're
950 * in an expression, then rewind til we're not any more
951 * (just like with the text node).
953 while (parent
->args
== parent
->expectargs
)
954 parent
= parent
->parent
;
955 if (EQN_TOK_LEFT
== tok
&&
956 eqn_next(ep
, MODE_SUB
) == EQN_TOK_EOF
) {
957 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
958 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
961 parent
= eqn_box_alloc(ep
, parent
);
962 parent
->type
= EQN_LIST
;
963 if (EQN_TOK_LEFT
== tok
) {
964 if (STRNEQ(ep
->start
, ep
->toksz
, "ceiling", 7))
965 parent
->left
= mandoc_strdup("\\[lc]");
966 else if (STRNEQ(ep
->start
, ep
->toksz
, "floor", 5))
967 parent
->left
= mandoc_strdup("\\[lf]");
970 mandoc_strndup(ep
->start
, ep
->toksz
);
980 while (parent
->args
== parent
->expectargs
)
981 parent
= parent
->parent
;
982 parent
= eqn_box_alloc(ep
, parent
);
983 parent
->type
= EQN_PILE
;
984 parent
->expectargs
= 1;
987 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
988 if (cur
->type
== EQN_PILE
)
991 mandoc_msg(MANDOCERR_IT_STRAY
, ep
->parse
,
992 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
995 parent
= eqn_box_alloc(ep
, cur
);
996 parent
->type
= EQN_LIST
;
999 while (parent
->args
== parent
->expectargs
)
1000 parent
= parent
->parent
;
1001 parent
= eqn_box_alloc(ep
, parent
);
1002 parent
->type
= EQN_MATRIX
;
1003 parent
->expectargs
= 1;
1008 * TODO: make sure we're not in an open subexpression.
1013 case EQN_TOK_QUOTED
:
1018 * If we already have something in the stack and we're
1019 * in an expression, then rewind til we're not any more.
1021 while (parent
->args
== parent
->expectargs
)
1022 parent
= parent
->parent
;
1023 cur
= eqn_box_alloc(ep
, parent
);
1024 cur
->type
= EQN_TEXT
;
1028 cur
->font
= EQNFONT_ROMAN
;
1030 case EQN_TOK_QUOTED
:
1031 if (cur
->font
== EQNFONT_NONE
)
1032 cur
->font
= EQNFONT_ITALIC
;
1037 if (cur
->font
!= EQNFONT_NONE
|| *p
== '\0')
1043 /* Advance to next character. */
1046 ccln
= isalpha((unsigned char)*cpn
) ? CCL_LET
:
1047 isdigit((unsigned char)*cpn
) ||
1048 (*cpn
== '.' && (ccl
== CCL_DIG
||
1049 isdigit((unsigned char)cpn
[1]))) ?
1051 /* No boundary before first character. */
1054 cur
->font
= ccl
== CCL_LET
?
1055 EQNFONT_ITALIC
: EQNFONT_ROMAN
;
1057 mandoc_escape(&cpn
, NULL
, NULL
);
1058 /* No boundary after last character. */
1063 /* Boundary found, split the text. */
1064 if (parent
->args
== parent
->expectargs
) {
1065 /* Remove the text from the tree. */
1066 if (cur
->prev
== NULL
)
1067 parent
->first
= cur
->next
;
1069 cur
->prev
->next
= NULL
;
1070 parent
->last
= cur
->prev
;
1072 /* Set up a list instead. */
1073 split
= eqn_box_alloc(ep
, parent
);
1074 split
->type
= EQN_LIST
;
1075 /* Insert the word into the list. */
1076 split
->first
= split
->last
= cur
;
1077 cur
->parent
= split
;
1081 /* Append a new text box. */
1082 nbox
= eqn_box_alloc(ep
, parent
);
1083 nbox
->type
= EQN_TEXT
;
1084 nbox
->text
= mandoc_strdup(cpn
);
1085 /* Truncate the old box. */
1086 p
= mandoc_strndup(cur
->text
,
1090 /* Setup to process the new box. */
1097 parent
= split
->parent
;
1101 * Post-process list status.
1103 while (parent
->type
== EQN_LISTONE
&&
1104 parent
->args
== parent
->expectargs
)
1105 parent
= parent
->parent
;
1114 eqn_end(struct eqn_node
**epp
)
1116 struct eqn_node
*ep
;
1121 ep
->eqn
.root
= mandoc_calloc(1, sizeof(struct eqn_box
));
1122 ep
->eqn
.root
->expectargs
= UINT_MAX
;
1123 return eqn_parse(ep
, ep
->eqn
.root
);
1127 eqn_free(struct eqn_node
*p
)
1131 eqn_box_free(p
->eqn
.root
);
1133 for (i
= 0; i
< (int)p
->defsz
; i
++) {
1134 free(p
->defs
[i
].key
);
1135 free(p
->defs
[i
].val
);