]>
git.cameronkatri.com Git - mandoc.git/blob - eqn.c
1 /* $Id: eqn.c,v 1.53 2014/10/12 20:08:58 schwarze Exp $ */
3 * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2014 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"
31 #include "libmandoc.h"
34 #define EQN_MSG(t, x) \
35 mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL)
36 #define EQN_NEST_MAX 128 /* maximum nesting of defines */
37 #define STRNEQ(p1, sz1, p2, sz2) \
38 ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
39 #define EQNSTREQ(x, p, sz) \
40 STRNEQ((x)->name, (x)->sz, (p), (sz))
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 NULL
, /* EQN_TOK_EOF */
138 "above", /* EQN_TOK_ABOVE */
209 static const struct eqnsym eqnsyms
[EQNSYM__MAX
] = {
210 { "alpha", "*a" }, /* EQNSYM_alpha */
211 { "beta", "*b" }, /* EQNSYM_beta */
212 { "chi", "*x" }, /* EQNSYM_chi */
213 { "delta", "*d" }, /* EQNSYM_delta */
214 { "epsilon", "*e" }, /* EQNSYM_epsilon */
215 { "eta", "*y" }, /* EQNSYM_eta */
216 { "gamma", "*g" }, /* EQNSYM_gamma */
217 { "iota", "*i" }, /* EQNSYM_iota */
218 { "kappa", "*k" }, /* EQNSYM_kappa */
219 { "lambda", "*l" }, /* EQNSYM_lambda */
220 { "mu", "*m" }, /* EQNSYM_mu */
221 { "nu", "*n" }, /* EQNSYM_nu */
222 { "omega", "*w" }, /* EQNSYM_omega */
223 { "omicron", "*o" }, /* EQNSYM_omicron */
224 { "phi", "*f" }, /* EQNSYM_phi */
225 { "pi", "*p" }, /* EQNSYM_pi */
226 { "psi", "*q" }, /* EQNSYM_psi */
227 { "rho", "*r" }, /* EQNSYM_rho */
228 { "sigma", "*s" }, /* EQNSYM_sigma */
229 { "tau", "*t" }, /* EQNSYM_tau */
230 { "theta", "*h" }, /* EQNSYM_theta */
231 { "upsilon", "*u" }, /* EQNSYM_upsilon */
232 { "xi", "*c" }, /* EQNSYM_xi */
233 { "zeta", "*z" }, /* EQNSYM_zeta */
234 { "DELTA", "*D" }, /* EQNSYM_DELTA */
235 { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
236 { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
237 { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
238 { "PHI", "*F" }, /* EQNSYM_PHI */
239 { "PI", "*P" }, /* EQNSYM_PI */
240 { "PSI", "*Q" }, /* EQNSYM_PSI */
241 { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
242 { "THETA", "*H" }, /* EQNSYM_THETA */
243 { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
244 { "XI", "*C" }, /* EQNSYM_XI */
245 { "inter", "ca" }, /* EQNSYM_inter */
246 { "union", "cu" }, /* EQNSYM_union */
247 { "prod", "product" }, /* EQNSYM_prod */
248 { "int", "integral" }, /* EQNSYM_int */
249 { "sum", "sum" }, /* EQNSYM_sum */
250 { "grad", "gr" }, /* EQNSYM_grad */
251 { "del", "gr" }, /* EQNSYM_del */
252 { "times", "mu" }, /* EQNSYM_times */
253 { "cdot", "pc" }, /* EQNSYM_cdot */
254 { "nothing", "&" }, /* EQNSYM_nothing */
255 { "approx", "~~" }, /* EQNSYM_approx */
256 { "prime", "aq" }, /* EQNSYM_prime */
257 { "half", "12" }, /* EQNSYM_half */
258 { "partial", "pd" }, /* EQNSYM_partial */
259 { "inf", "if" }, /* EQNSYM_inf */
260 { ">>", ">>" }, /* EQNSYM_muchgreat */
261 { "<<", "<<" }, /* EQNSYM_muchless */
262 { "<-", "<-" }, /* EQNSYM_larrow */
263 { "->", "->" }, /* EQNSYM_rarrow */
264 { "+-", "+-" }, /* EQNSYM_pm */
265 { "!=", "!=" }, /* EQNSYM_nequal */
266 { "==", "==" }, /* EQNSYM_equiv */
267 { "<=", "<=" }, /* EQNSYM_lessequal */
268 { ">=", ">=" }, /* EQNSYM_moreequal */
272 eqn_read(struct eqn_node
**epp
, int ln
,
273 const char *p
, int pos
, int *offs
)
282 * If we're the terminating mark, unset our equation status and
283 * validate the full equation.
286 if (0 == strncmp(p
, ".EN", 3)) {
289 while (' ' == *p
|| '\t' == *p
)
293 mandoc_vmsg(MANDOCERR_ARG_SKIP
, ep
->parse
,
294 ln
, pos
, "EN %s", p
);
299 * Build up the full string, replacing all newlines with regular
303 sz
= strlen(p
+ pos
) + 1;
304 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ sz
+ 1);
306 /* First invocation: nil terminate the string. */
312 strlcat(ep
->data
, p
+ pos
, ep
->sz
+ 1);
313 strlcat(ep
->data
, " ", ep
->sz
+ 1);
318 eqn_alloc(const char *name
, int pos
, int line
, struct mparse
*parse
)
324 p
= mandoc_calloc(1, sizeof(struct eqn_node
));
326 if (name
&& '\0' != *name
) {
331 end
= name
+ (int)sz
;
332 } while (' ' == *end
|| '\t' == *end
);
333 p
->eqn
.name
= mandoc_strndup(name
, sz
+ 1);
339 p
->gsize
= EQN_DEFSIZE
;
345 * Find the key "key" of the give size within our eqn-defined values.
347 static struct eqn_def
*
348 eqn_def_find(struct eqn_node
*ep
, const char *key
, size_t sz
)
352 for (i
= 0; i
< (int)ep
->defsz
; i
++)
353 if (ep
->defs
[i
].keysz
&& STRNEQ(ep
->defs
[i
].key
,
354 ep
->defs
[i
].keysz
, key
, sz
))
355 return(&ep
->defs
[i
]);
361 * Get the next token from the input stream using the given quote
363 * Optionally make any replacements.
366 eqn_next(struct eqn_node
*ep
, char quote
, size_t *sz
, int repl
)
379 /* Prevent self-definitions. */
381 if (lim
>= EQN_NEST_MAX
) {
382 EQN_MSG(MANDOCERR_ROFFLOOP
, ep
);
387 start
= &ep
->data
[(int)ep
->cur
];
393 if (quote
== *start
) {
398 start
= &ep
->data
[(int)ep
->cur
];
401 if ('{' == *start
|| '}' == *start
)
404 ssz
= strcspn(start
+ 1, " ^~\"{}\t") + 1;
405 next
= start
+ (int)ssz
;
409 next
= strchr(start
, quote
);
412 *sz
= (size_t)(next
- start
);
416 while (' ' == ep
->data
[(int)ep
->cur
] ||
417 '\t' == ep
->data
[(int)ep
->cur
] ||
418 '^' == ep
->data
[(int)ep
->cur
] ||
419 '~' == ep
->data
[(int)ep
->cur
])
423 EQN_MSG(MANDOCERR_ARG_QUOTE
, ep
);
424 next
= strchr(start
, '\0');
425 *sz
= (size_t)(next
- start
);
429 /* Quotes aren't expanded for values. */
434 if (NULL
!= (def
= eqn_def_find(ep
, start
, *sz
))) {
435 diff
= def
->valsz
- *sz
;
437 if (def
->valsz
> *sz
) {
439 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ 1);
440 ep
->data
[ep
->sz
] = '\0';
441 start
= &ep
->data
[(int)ep
->rew
];
444 diff
= def
->valsz
- *sz
;
445 memmove(start
+ *sz
+ diff
, start
+ *sz
,
446 (strlen(start
) - *sz
) + 1);
447 memcpy(start
, def
->val
, def
->valsz
);
455 * Get the next delimited token using the default current quote
459 eqn_nexttok(struct eqn_node
*ep
, size_t *sz
)
462 return(eqn_next(ep
, '"', sz
, 1));
466 * Get next token without replacement.
469 eqn_nextrawtok(struct eqn_node
*ep
, size_t *sz
)
472 return(eqn_next(ep
, '"', sz
, 0));
476 * Parse a token from the stream of text.
477 * A token consists of one of the recognised eqn(7) strings.
478 * Strings are separated by delimiting marks.
479 * This returns EQN_TOK_EOF when there are no more tokens.
480 * If the token is an unrecognised string literal, then it returns
481 * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated
483 * This must be later freed with free(3).
486 eqn_tok_parse(struct eqn_node
*ep
, char **p
)
495 quoted
= ep
->data
[ep
->cur
] == '"';
497 if (NULL
== (start
= eqn_nexttok(ep
, &sz
)))
502 *p
= mandoc_strndup(start
, sz
);
503 return(EQN_TOK__MAX
);
506 for (i
= 0; i
< EQN_TOK__MAX
; i
++) {
507 if (NULL
== eqn_toks
[i
])
509 if (STRNEQ(start
, sz
, eqn_toks
[i
], strlen(eqn_toks
[i
])))
513 if (i
== EQN_TOK__MAX
&& NULL
!= p
)
514 *p
= mandoc_strndup(start
, sz
);
520 eqn_box_free(struct eqn_box
*bp
)
524 eqn_box_free(bp
->first
);
526 eqn_box_free(bp
->next
);
537 * Allocate a box as the last child of the parent node.
539 static struct eqn_box
*
540 eqn_box_alloc(struct eqn_node
*ep
, struct eqn_box
*parent
)
544 bp
= mandoc_calloc(1, sizeof(struct eqn_box
));
547 bp
->expectargs
= UINT_MAX
;
548 bp
->size
= ep
->gsize
;
550 if (NULL
!= parent
->first
) {
551 parent
->last
->next
= bp
;
552 bp
->prev
= parent
->last
;
561 * Reparent the current last node (of the current parent) under a new
562 * EQN_SUBEXPR as the first element.
563 * Then return the new parent.
564 * The new EQN_SUBEXPR will have a two-child limit.
566 static struct eqn_box
*
567 eqn_box_makebinary(struct eqn_node
*ep
,
568 enum eqn_post pos
, struct eqn_box
*parent
)
570 struct eqn_box
*b
, *newb
;
572 assert(NULL
!= parent
->last
);
574 if (parent
->last
== parent
->first
)
575 parent
->first
= NULL
;
577 parent
->last
= b
->prev
;
579 newb
= eqn_box_alloc(ep
, parent
);
581 newb
->type
= EQN_SUBEXPR
;
582 newb
->expectargs
= 2;
584 newb
->first
= newb
->last
= b
;
585 newb
->first
->next
= NULL
;
591 * Undefine a previously-defined string.
594 eqn_undef(struct eqn_node
*ep
)
600 if (NULL
== (start
= eqn_nextrawtok(ep
, &sz
))) {
601 EQN_MSG(MANDOCERR_EQNEOF
, ep
);
603 } else if (NULL
!= (def
= eqn_def_find(ep
, start
, sz
)))
610 eqn_def(struct eqn_node
*ep
)
617 if (NULL
== (start
= eqn_nextrawtok(ep
, &sz
))) {
618 EQN_MSG(MANDOCERR_EQNEOF
, ep
);
623 * Search for a key that already exists.
624 * Create a new key if none is found.
626 if (NULL
== (def
= eqn_def_find(ep
, start
, sz
))) {
627 /* Find holes in string array. */
628 for (i
= 0; i
< (int)ep
->defsz
; i
++)
629 if (0 == ep
->defs
[i
].keysz
)
632 if (i
== (int)ep
->defsz
) {
634 ep
->defs
= mandoc_reallocarray(ep
->defs
,
635 ep
->defsz
, sizeof(struct eqn_def
));
636 ep
->defs
[i
].key
= ep
->defs
[i
].val
= NULL
;
639 ep
->defs
[i
].keysz
= sz
;
640 ep
->defs
[i
].key
= mandoc_realloc(
641 ep
->defs
[i
].key
, sz
+ 1);
643 memcpy(ep
->defs
[i
].key
, start
, sz
);
644 ep
->defs
[i
].key
[(int)sz
] = '\0';
648 start
= eqn_next(ep
, ep
->data
[(int)ep
->cur
], &sz
, 0);
651 EQN_MSG(MANDOCERR_EQNEOF
, ep
);
656 def
->val
= mandoc_realloc(def
->val
, sz
+ 1);
657 memcpy(def
->val
, start
, sz
);
658 def
->val
[(int)sz
] = '\0';
663 * Recursively parse an eqn(7) expression.
666 eqn_parse(struct eqn_node
*ep
, struct eqn_box
*parent
)
669 enum eqn_tok tok
, subtok
;
677 assert(NULL
!= parent
);
680 tok
= eqn_tok_parse(ep
, &p
);
684 case (EQN_TOK_UNDEF
):
685 if ((rc
= eqn_undef(ep
)) <= 0)
688 case (EQN_TOK_NDEFINE
):
689 case (EQN_TOK_DEFINE
):
690 if ((rc
= eqn_def(ep
)) <= 0)
693 case (EQN_TOK_TDEFINE
):
694 if (NULL
== eqn_nextrawtok(ep
, NULL
))
695 EQN_MSG(MANDOCERR_EQNEOF
, ep
);
696 else if (NULL
== eqn_next(ep
,
697 ep
->data
[(int)ep
->cur
], NULL
, 0))
698 EQN_MSG(MANDOCERR_EQNEOF
, ep
);
700 case (EQN_TOK_DELIM
):
701 case (EQN_TOK_GFONT
):
702 if (eqn_nextrawtok(ep
, NULL
) == NULL
)
703 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
704 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
707 case (EQN_TOK_LINEUP
):
712 case (EQN_TOK_UNDER
):
714 case (EQN_TOK_TILDE
):
717 case (EQN_TOK_DOTDOT
):
718 if (parent
->last
== NULL
) {
719 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
720 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
721 cur
= eqn_box_alloc(ep
, parent
);
722 cur
->type
= EQN_TEXT
;
723 cur
->text
= mandoc_strdup("");
725 parent
= eqn_box_makebinary(ep
, EQNPOS_NONE
, parent
);
726 parent
->type
= EQN_LISTONE
;
727 parent
->expectargs
= 1;
729 case (EQN_TOK_DOTDOT
):
730 strlcpy(sym
, "\\[ad]", sizeof(sym
));
733 strlcpy(sym
, "\\[->]", sizeof(sym
));
736 strlcpy(sym
, "\\[<>]", sizeof(sym
));
738 case (EQN_TOK_TILDE
):
739 strlcpy(sym
, "\\[a~]", sizeof(sym
));
741 case (EQN_TOK_UNDER
):
742 strlcpy(sym
, "\\[ul]", sizeof(sym
));
745 strlcpy(sym
, "\\[rl]", sizeof(sym
));
748 strlcpy(sym
, "\\[a.]", sizeof(sym
));
751 strlcpy(sym
, "\\[ha]", sizeof(sym
));
758 case (EQN_TOK_DOTDOT
):
761 case (EQN_TOK_TILDE
):
765 parent
->top
= mandoc_strdup(sym
);
767 case (EQN_TOK_UNDER
):
768 parent
->bottom
= mandoc_strdup(sym
);
773 parent
= parent
->parent
;
779 subtok
= eqn_tok_parse(ep
, NULL
);
780 if (subtok
!= EQN_TOK__MAX
) {
781 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
782 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
788 case (EQN_TOK_ROMAN
):
789 case (EQN_TOK_ITALIC
):
791 while (parent
->args
== parent
->expectargs
)
792 parent
= parent
->parent
;
794 * These values apply to the next word or sequence of
795 * words; thus, we mark that we'll have a child with
796 * exactly one of those.
798 parent
= eqn_box_alloc(ep
, parent
);
799 parent
->type
= EQN_LISTONE
;
800 parent
->expectargs
= 1;
803 parent
->font
= EQNFONT_FAT
;
805 case (EQN_TOK_ROMAN
):
806 parent
->font
= EQNFONT_ROMAN
;
808 case (EQN_TOK_ITALIC
):
809 parent
->font
= EQNFONT_ITALIC
;
812 parent
->font
= EQNFONT_BOLD
;
819 case (EQN_TOK_GSIZE
):
820 /* Accept two values: integral size and a single. */
821 if (NULL
== (start
= eqn_nexttok(ep
, &sz
))) {
822 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
823 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
826 size
= mandoc_strntoi(start
, sz
, 10);
828 mandoc_msg(MANDOCERR_IT_NONUM
, ep
->parse
,
829 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
832 if (EQN_TOK_GSIZE
== tok
) {
836 parent
= eqn_box_alloc(ep
, parent
);
837 parent
->type
= EQN_LISTONE
;
838 parent
->expectargs
= 1;
846 * We have a left-right-associative expression.
847 * Repivot under a positional node, open a child scope
848 * and keep on reading.
850 if (parent
->last
== NULL
) {
851 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
852 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
853 cur
= eqn_box_alloc(ep
, parent
);
854 cur
->type
= EQN_TEXT
;
855 cur
->text
= mandoc_strdup("");
857 /* Handle the "subsup" and "fromto" positions. */
858 if (EQN_TOK_SUP
== tok
&& parent
->pos
== EQNPOS_SUB
) {
859 parent
->expectargs
= 3;
860 parent
->pos
= EQNPOS_SUBSUP
;
863 if (EQN_TOK_TO
== tok
&& parent
->pos
== EQNPOS_FROM
) {
864 parent
->expectargs
= 3;
865 parent
->pos
= EQNPOS_FROMTO
;
884 parent
= eqn_box_makebinary(ep
, pos
, parent
);
887 while (parent
->args
== parent
->expectargs
)
888 parent
= parent
->parent
;
890 * Accept a left-right-associative set of arguments just
891 * like sub and sup and friends but without rebalancing
894 parent
= eqn_box_alloc(ep
, parent
);
895 parent
->type
= EQN_SUBEXPR
;
896 parent
->pos
= EQNPOS_SQRT
;
897 parent
->expectargs
= 1;
901 * We have a right-left-associative fraction.
902 * Close out anything that's currently open, then
903 * rebalance and continue reading.
905 if (parent
->last
== NULL
) {
906 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
907 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
908 cur
= eqn_box_alloc(ep
, parent
);
909 cur
->type
= EQN_TEXT
;
910 cur
->text
= mandoc_strdup("");
912 while (EQN_SUBEXPR
== parent
->type
)
913 parent
= parent
->parent
;
914 parent
= eqn_box_makebinary(ep
, EQNPOS_OVER
, parent
);
916 case (EQN_TOK_RIGHT
):
917 case (EQN_TOK_BRACE_CLOSE
):
919 * Close out the existing brace.
920 * FIXME: this is a shitty sentinel: we should really
921 * have a native EQN_BRACE type or whatnot.
923 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
924 if (cur
->type
== EQN_LIST
&&
925 (tok
== EQN_TOK_BRACE_CLOSE
||
929 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ep
->parse
,
930 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
934 if (EQN_TOK_RIGHT
== tok
) {
935 if (NULL
== (start
= eqn_nexttok(ep
, &sz
))) {
936 mandoc_msg(MANDOCERR_REQ_EMPTY
,
937 ep
->parse
, ep
->eqn
.ln
,
938 ep
->eqn
.pos
, eqn_toks
[tok
]);
941 /* Handling depends on right/left. */
942 if (STRNEQ(start
, sz
, "ceiling", 7)) {
943 strlcpy(sym
, "\\[rc]", sizeof(sym
));
944 parent
->right
= mandoc_strdup(sym
);
945 } else if (STRNEQ(start
, sz
, "floor", 5)) {
946 strlcpy(sym
, "\\[rf]", sizeof(sym
));
947 parent
->right
= mandoc_strdup(sym
);
949 parent
->right
= mandoc_strndup(start
, sz
);
951 parent
= parent
->parent
;
952 if (EQN_TOK_BRACE_CLOSE
== tok
&& parent
&&
953 (parent
->type
== EQN_PILE
||
954 parent
->type
== EQN_MATRIX
))
955 parent
= parent
->parent
;
956 /* Close out any "singleton" lists. */
957 while (parent
->type
== EQN_LISTONE
&&
958 parent
->args
== parent
->expectargs
)
959 parent
= parent
->parent
;
961 case (EQN_TOK_BRACE_OPEN
):
964 * If we already have something in the stack and we're
965 * in an expression, then rewind til we're not any more
966 * (just like with the text node).
968 while (parent
->args
== parent
->expectargs
)
969 parent
= parent
->parent
;
970 if (EQN_TOK_LEFT
== tok
&&
971 (start
= eqn_nexttok(ep
, &sz
)) == NULL
) {
972 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
973 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
976 parent
= eqn_box_alloc(ep
, parent
);
977 parent
->type
= EQN_LIST
;
978 if (EQN_TOK_LEFT
== tok
) {
979 if (STRNEQ(start
, sz
, "ceiling", 7)) {
980 strlcpy(sym
, "\\[lc]", sizeof(sym
));
981 parent
->left
= mandoc_strdup(sym
);
982 } else if (STRNEQ(start
, sz
, "floor", 5)) {
983 strlcpy(sym
, "\\[lf]", sizeof(sym
));
984 parent
->left
= mandoc_strdup(sym
);
986 parent
->left
= mandoc_strndup(start
, sz
);
990 case (EQN_TOK_LPILE
):
991 case (EQN_TOK_RPILE
):
992 case (EQN_TOK_CPILE
):
996 while (parent
->args
== parent
->expectargs
)
997 parent
= parent
->parent
;
998 parent
= eqn_box_alloc(ep
, parent
);
999 parent
->type
= EQN_PILE
;
1000 parent
->expectargs
= 1;
1002 case (EQN_TOK_ABOVE
):
1003 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
1004 if (cur
->type
== EQN_PILE
)
1007 mandoc_msg(MANDOCERR_IT_STRAY
, ep
->parse
,
1008 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
1011 parent
= eqn_box_alloc(ep
, cur
);
1012 parent
->type
= EQN_LIST
;
1014 case (EQN_TOK_MATRIX
):
1015 while (parent
->args
== parent
->expectargs
)
1016 parent
= parent
->parent
;
1017 parent
= eqn_box_alloc(ep
, parent
);
1018 parent
->type
= EQN_MATRIX
;
1019 parent
->expectargs
= 1;
1024 * TODO: make sure we're not in an open subexpression.
1028 assert(tok
== EQN_TOK__MAX
);
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
;
1038 for (i
= 0; i
< EQNSYM__MAX
; i
++)
1039 if (0 == strcmp(eqnsyms
[i
].str
, p
)) {
1040 (void)snprintf(sym
, sizeof(sym
),
1041 "\\[%s]", eqnsyms
[i
].sym
);
1042 cur
->text
= mandoc_strdup(sym
);
1047 if (i
== EQNSYM__MAX
)
1050 * Post-process list status.
1052 while (parent
->type
== EQN_LISTONE
&&
1053 parent
->args
== parent
->expectargs
)
1054 parent
= parent
->parent
;
1061 eqn_end(struct eqn_node
**epp
)
1063 struct eqn_node
*ep
;
1068 ep
->eqn
.root
= mandoc_calloc(1, sizeof(struct eqn_box
));
1069 ep
->eqn
.root
->expectargs
= UINT_MAX
;
1070 return(0 == eqn_parse(ep
, ep
->eqn
.root
) ? ROFF_EQN
: ROFF_IGN
);
1074 eqn_free(struct eqn_node
*p
)
1078 eqn_box_free(p
->eqn
.root
);
1080 for (i
= 0; i
< (int)p
->defsz
; i
++) {
1081 free(p
->defs
[i
].key
);
1082 free(p
->defs
[i
].val
);