]>
git.cameronkatri.com Git - mandoc.git/blob - eqn.c
1 /* $Id: eqn.c,v 1.65 2017/06/21 18:38:26 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>
30 #include "mandoc_aux.h"
31 #include "libmandoc.h"
34 #define EQN_NEST_MAX 128 /* maximum nesting of defines */
35 #define STRNEQ(p1, sz1, p2, sz2) \
36 ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
89 static const char *eqn_toks
[EQN_TOK__MAX
] = {
90 "dyad", /* EQN_TOK_DYAD */
91 "vec", /* EQN_TOK_VEC */
92 "under", /* EQN_TOK_UNDER */
93 "bar", /* EQN_TOK_BAR */
94 "tilde", /* EQN_TOK_TILDE */
95 "hat", /* EQN_TOK_HAT */
96 "dot", /* EQN_TOK_DOT */
97 "dotdot", /* EQN_TOK_DOTDOT */
98 "fwd", /* EQN_TOK_FWD * */
99 "back", /* EQN_TOK_BACK */
100 "down", /* EQN_TOK_DOWN */
101 "up", /* EQN_TOK_UP */
102 "fat", /* EQN_TOK_FAT */
103 "roman", /* EQN_TOK_ROMAN */
104 "italic", /* EQN_TOK_ITALIC */
105 "bold", /* EQN_TOK_BOLD */
106 "size", /* EQN_TOK_SIZE */
107 "sub", /* EQN_TOK_SUB */
108 "sup", /* EQN_TOK_SUP */
109 "sqrt", /* EQN_TOK_SQRT */
110 "over", /* EQN_TOK_OVER */
111 "from", /* EQN_TOK_FROM */
112 "to", /* EQN_TOK_TO */
113 "{", /* EQN_TOK_BRACE_OPEN */
114 "}", /* EQN_TOK_BRACE_CLOSE */
115 "gsize", /* EQN_TOK_GSIZE */
116 "gfont", /* EQN_TOK_GFONT */
117 "mark", /* EQN_TOK_MARK */
118 "lineup", /* EQN_TOK_LINEUP */
119 "left", /* EQN_TOK_LEFT */
120 "right", /* EQN_TOK_RIGHT */
121 "pile", /* EQN_TOK_PILE */
122 "lpile", /* EQN_TOK_LPILE */
123 "rpile", /* EQN_TOK_RPILE */
124 "cpile", /* EQN_TOK_CPILE */
125 "matrix", /* EQN_TOK_MATRIX */
126 "ccol", /* EQN_TOK_CCOL */
127 "lcol", /* EQN_TOK_LCOL */
128 "rcol", /* EQN_TOK_RCOL */
129 "delim", /* EQN_TOK_DELIM */
130 "define", /* EQN_TOK_DEFINE */
131 "tdefine", /* EQN_TOK_TDEFINE */
132 "ndefine", /* EQN_TOK_NDEFINE */
133 "undef", /* EQN_TOK_UNDEF */
134 "above", /* EQN_TOK_ABOVE */
137 static const char *const eqn_func
[] = {
138 "acos", "acsc", "and", "arc", "asec", "asin", "atan",
139 "cos", "cosh", "coth", "csc", "det", "exp", "for",
140 "if", "lim", "ln", "log", "max", "min",
141 "sec", "sin", "sinh", "tan", "tanh", "Im", "Re",
213 static const struct eqnsym eqnsyms
[EQNSYM__MAX
] = {
214 { "alpha", "*a" }, /* EQNSYM_alpha */
215 { "beta", "*b" }, /* EQNSYM_beta */
216 { "chi", "*x" }, /* EQNSYM_chi */
217 { "delta", "*d" }, /* EQNSYM_delta */
218 { "epsilon", "*e" }, /* EQNSYM_epsilon */
219 { "eta", "*y" }, /* EQNSYM_eta */
220 { "gamma", "*g" }, /* EQNSYM_gamma */
221 { "iota", "*i" }, /* EQNSYM_iota */
222 { "kappa", "*k" }, /* EQNSYM_kappa */
223 { "lambda", "*l" }, /* EQNSYM_lambda */
224 { "mu", "*m" }, /* EQNSYM_mu */
225 { "nu", "*n" }, /* EQNSYM_nu */
226 { "omega", "*w" }, /* EQNSYM_omega */
227 { "omicron", "*o" }, /* EQNSYM_omicron */
228 { "phi", "*f" }, /* EQNSYM_phi */
229 { "pi", "*p" }, /* EQNSYM_pi */
230 { "psi", "*q" }, /* EQNSYM_psi */
231 { "rho", "*r" }, /* EQNSYM_rho */
232 { "sigma", "*s" }, /* EQNSYM_sigma */
233 { "tau", "*t" }, /* EQNSYM_tau */
234 { "theta", "*h" }, /* EQNSYM_theta */
235 { "upsilon", "*u" }, /* EQNSYM_upsilon */
236 { "xi", "*c" }, /* EQNSYM_xi */
237 { "zeta", "*z" }, /* EQNSYM_zeta */
238 { "DELTA", "*D" }, /* EQNSYM_DELTA */
239 { "GAMMA", "*G" }, /* EQNSYM_GAMMA */
240 { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */
241 { "OMEGA", "*W" }, /* EQNSYM_OMEGA */
242 { "PHI", "*F" }, /* EQNSYM_PHI */
243 { "PI", "*P" }, /* EQNSYM_PI */
244 { "PSI", "*Q" }, /* EQNSYM_PSI */
245 { "SIGMA", "*S" }, /* EQNSYM_SIGMA */
246 { "THETA", "*H" }, /* EQNSYM_THETA */
247 { "UPSILON", "*U" }, /* EQNSYM_UPSILON */
248 { "XI", "*C" }, /* EQNSYM_XI */
249 { "inter", "ca" }, /* EQNSYM_inter */
250 { "union", "cu" }, /* EQNSYM_union */
251 { "prod", "product" }, /* EQNSYM_prod */
252 { "int", "integral" }, /* EQNSYM_int */
253 { "sum", "sum" }, /* EQNSYM_sum */
254 { "grad", "gr" }, /* EQNSYM_grad */
255 { "del", "gr" }, /* EQNSYM_del */
256 { "times", "mu" }, /* EQNSYM_times */
257 { "cdot", "pc" }, /* EQNSYM_cdot */
258 { "nothing", "&" }, /* EQNSYM_nothing */
259 { "approx", "~~" }, /* EQNSYM_approx */
260 { "prime", "fm" }, /* EQNSYM_prime */
261 { "half", "12" }, /* EQNSYM_half */
262 { "partial", "pd" }, /* EQNSYM_partial */
263 { "inf", "if" }, /* EQNSYM_inf */
264 { ">>", ">>" }, /* EQNSYM_muchgreat */
265 { "<<", "<<" }, /* EQNSYM_muchless */
266 { "<-", "<-" }, /* EQNSYM_larrow */
267 { "->", "->" }, /* EQNSYM_rarrow */
268 { "+-", "+-" }, /* EQNSYM_pm */
269 { "!=", "!=" }, /* EQNSYM_nequal */
270 { "==", "==" }, /* EQNSYM_equiv */
271 { "<=", "<=" }, /* EQNSYM_lessequal */
272 { ">=", ">=" }, /* EQNSYM_moreequal */
273 { "-", "mi" }, /* EQNSYM_minus */
276 static struct eqn_box
*eqn_box_alloc(struct eqn_node
*, struct eqn_box
*);
277 static void eqn_box_free(struct eqn_box
*);
278 static struct eqn_box
*eqn_box_makebinary(struct eqn_node
*,
279 enum eqn_post
, struct eqn_box
*);
280 static void eqn_def(struct eqn_node
*);
281 static struct eqn_def
*eqn_def_find(struct eqn_node
*, const char *, size_t);
282 static void eqn_delim(struct eqn_node
*);
283 static const char *eqn_next(struct eqn_node
*, char, size_t *, int);
284 static const char *eqn_nextrawtok(struct eqn_node
*, size_t *);
285 static const char *eqn_nexttok(struct eqn_node
*, size_t *);
286 static enum rofferr
eqn_parse(struct eqn_node
*, struct eqn_box
*);
287 static enum eqn_tok
eqn_tok_parse(struct eqn_node
*, char **);
288 static void eqn_undef(struct eqn_node
*);
292 eqn_read(struct eqn_node
**epp
, int ln
,
293 const char *p
, int pos
, int *offs
)
302 * If we're the terminating mark, unset our equation status and
303 * validate the full equation.
306 if (0 == strncmp(p
, ".EN", 3)) {
309 while (' ' == *p
|| '\t' == *p
)
313 mandoc_vmsg(MANDOCERR_ARG_SKIP
, ep
->parse
,
314 ln
, pos
, "EN %s", p
);
319 * Build up the full string, replacing all newlines with regular
323 sz
= strlen(p
+ pos
) + 1;
324 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ sz
+ 1);
326 /* First invocation: nil terminate the string. */
332 strlcat(ep
->data
, p
+ pos
, ep
->sz
+ 1);
333 strlcat(ep
->data
, " ", ep
->sz
+ 1);
338 eqn_alloc(int pos
, int line
, struct mparse
*parse
)
342 p
= mandoc_calloc(1, sizeof(struct eqn_node
));
347 p
->gsize
= EQN_DEFSIZE
;
353 * Find the key "key" of the give size within our eqn-defined values.
355 static struct eqn_def
*
356 eqn_def_find(struct eqn_node
*ep
, const char *key
, size_t sz
)
360 for (i
= 0; i
< (int)ep
->defsz
; i
++)
361 if (ep
->defs
[i
].keysz
&& STRNEQ(ep
->defs
[i
].key
,
362 ep
->defs
[i
].keysz
, key
, sz
))
369 * Get the next token from the input stream using the given quote
371 * Optionally make any replacements.
374 eqn_next(struct eqn_node
*ep
, char quote
, size_t *sz
, int repl
)
376 static size_t last_len
;
387 if (ep
->cur
>= last_len
)
391 /* Prevent self-definitions. */
393 if (lim
>= EQN_NEST_MAX
) {
394 mandoc_msg(MANDOCERR_ROFFLOOP
, ep
->parse
,
395 ep
->eqn
.ln
, ep
->eqn
.pos
, NULL
);
400 start
= &ep
->data
[(int)ep
->cur
];
406 if (quote
== *start
) {
411 start
= &ep
->data
[(int)ep
->cur
];
414 if ('{' == *start
|| '}' == *start
)
417 ssz
= strcspn(start
+ 1, " ^~\"{}\t") + 1;
418 next
= start
+ (int)ssz
;
422 next
= strchr(start
, quote
);
425 *sz
= (size_t)(next
- start
);
429 while (' ' == ep
->data
[(int)ep
->cur
] ||
430 '\t' == ep
->data
[(int)ep
->cur
] ||
431 '^' == ep
->data
[(int)ep
->cur
] ||
432 '~' == ep
->data
[(int)ep
->cur
])
436 mandoc_msg(MANDOCERR_ARG_QUOTE
, ep
->parse
,
437 ep
->eqn
.ln
, ep
->eqn
.pos
, NULL
);
438 next
= strchr(start
, '\0');
439 *sz
= (size_t)(next
- start
);
443 /* Quotes aren't expanded for values. */
448 if (NULL
!= (def
= eqn_def_find(ep
, start
, *sz
))) {
449 diff
= def
->valsz
- *sz
;
451 if (def
->valsz
> *sz
) {
453 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ 1);
454 ep
->data
[ep
->sz
] = '\0';
455 start
= &ep
->data
[(int)ep
->rew
];
458 diff
= def
->valsz
- *sz
;
459 memmove(start
+ *sz
+ diff
, start
+ *sz
,
460 (strlen(start
) - *sz
) + 1);
461 memcpy(start
, def
->val
, def
->valsz
);
462 last_len
= start
- ep
->data
+ def
->valsz
;
471 * Get the next delimited token using the default current quote
475 eqn_nexttok(struct eqn_node
*ep
, size_t *sz
)
478 return eqn_next(ep
, '"', sz
, 1);
482 * Get next token without replacement.
485 eqn_nextrawtok(struct eqn_node
*ep
, size_t *sz
)
488 return eqn_next(ep
, '"', sz
, 0);
492 * Parse a token from the stream of text.
493 * A token consists of one of the recognised eqn(7) strings.
494 * Strings are separated by delimiting marks.
495 * This returns EQN_TOK_EOF when there are no more tokens.
496 * If the token is an unrecognised string literal, then it returns
497 * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated
499 * This must be later freed with free(3).
502 eqn_tok_parse(struct eqn_node
*ep
, char **p
)
511 quoted
= ep
->data
[ep
->cur
] == '"';
513 if ((start
= eqn_nexttok(ep
, &sz
)) == NULL
)
518 *p
= mandoc_strndup(start
, sz
);
522 for (i
= 0; i
< EQN_TOK__MAX
; i
++)
523 if (STRNEQ(start
, sz
, eqn_toks
[i
], strlen(eqn_toks
[i
])))
526 for (i
= 0; i
< EQNSYM__MAX
; i
++) {
527 if (STRNEQ(start
, sz
,
528 eqnsyms
[i
].str
, strlen(eqnsyms
[i
].str
))) {
529 mandoc_asprintf(p
, "\\[%s]", eqnsyms
[i
].sym
);
535 *p
= mandoc_strndup(start
, sz
);
537 for (i
= 0; i
< sizeof(eqn_func
)/sizeof(*eqn_func
); i
++)
538 if (STRNEQ(start
, sz
, eqn_func
[i
], strlen(eqn_func
[i
])))
545 eqn_box_free(struct eqn_box
*bp
)
549 eqn_box_free(bp
->first
);
551 eqn_box_free(bp
->next
);
562 * Allocate a box as the last child of the parent node.
564 static struct eqn_box
*
565 eqn_box_alloc(struct eqn_node
*ep
, struct eqn_box
*parent
)
569 bp
= mandoc_calloc(1, sizeof(struct eqn_box
));
572 bp
->expectargs
= UINT_MAX
;
573 bp
->size
= ep
->gsize
;
575 if (NULL
!= parent
->first
) {
576 parent
->last
->next
= bp
;
577 bp
->prev
= parent
->last
;
586 * Reparent the current last node (of the current parent) under a new
587 * EQN_SUBEXPR as the first element.
588 * Then return the new parent.
589 * The new EQN_SUBEXPR will have a two-child limit.
591 static struct eqn_box
*
592 eqn_box_makebinary(struct eqn_node
*ep
,
593 enum eqn_post pos
, struct eqn_box
*parent
)
595 struct eqn_box
*b
, *newb
;
597 assert(NULL
!= parent
->last
);
599 if (parent
->last
== parent
->first
)
600 parent
->first
= NULL
;
602 parent
->last
= b
->prev
;
604 newb
= eqn_box_alloc(ep
, parent
);
606 newb
->type
= EQN_SUBEXPR
;
607 newb
->expectargs
= 2;
609 newb
->first
= newb
->last
= b
;
610 newb
->first
->next
= NULL
;
616 * Parse the "delim" control statement.
619 eqn_delim(struct eqn_node
*ep
)
624 if ((start
= eqn_nextrawtok(ep
, &sz
)) == NULL
)
625 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
626 ep
->eqn
.ln
, ep
->eqn
.pos
, "delim");
627 else if (strncmp(start
, "off", 3) == 0)
629 else if (strncmp(start
, "on", 2) == 0) {
630 if (ep
->odelim
&& ep
->cdelim
)
632 } else if (start
[1] != '\0') {
633 ep
->odelim
= start
[0];
634 ep
->cdelim
= start
[1];
640 * Undefine a previously-defined string.
643 eqn_undef(struct eqn_node
*ep
)
649 if ((start
= eqn_nextrawtok(ep
, &sz
)) == NULL
) {
650 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
651 ep
->eqn
.ln
, ep
->eqn
.pos
, "undef");
654 if ((def
= eqn_def_find(ep
, start
, sz
)) == NULL
)
658 def
->key
= def
->val
= NULL
;
659 def
->keysz
= def
->valsz
= 0;
663 eqn_def(struct eqn_node
*ep
)
670 if ((start
= eqn_nextrawtok(ep
, &sz
)) == NULL
) {
671 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
672 ep
->eqn
.ln
, ep
->eqn
.pos
, "define");
677 * Search for a key that already exists.
678 * Create a new key if none is found.
680 if (NULL
== (def
= eqn_def_find(ep
, start
, sz
))) {
681 /* Find holes in string array. */
682 for (i
= 0; i
< (int)ep
->defsz
; i
++)
683 if (0 == ep
->defs
[i
].keysz
)
686 if (i
== (int)ep
->defsz
) {
688 ep
->defs
= mandoc_reallocarray(ep
->defs
,
689 ep
->defsz
, sizeof(struct eqn_def
));
690 ep
->defs
[i
].key
= ep
->defs
[i
].val
= NULL
;
695 def
->key
= mandoc_strndup(start
, sz
);
699 start
= eqn_next(ep
, ep
->data
[(int)ep
->cur
], &sz
, 0);
701 mandoc_vmsg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
702 ep
->eqn
.ln
, ep
->eqn
.pos
, "define %s", def
->key
);
705 def
->key
= def
->val
= NULL
;
706 def
->keysz
= def
->valsz
= 0;
710 def
->val
= mandoc_strndup(start
, sz
);
715 * Recursively parse an eqn(7) expression.
718 eqn_parse(struct eqn_node
*ep
, struct eqn_box
*parent
)
725 enum eqn_tok tok
, subtok
;
729 assert(parent
!= NULL
);
733 * Do not add it to the high-level syntax tree.
736 if (ep
->data
== NULL
)
740 tok
= eqn_tok_parse(ep
, &p
);
747 case EQN_TOK_NDEFINE
:
751 case EQN_TOK_TDEFINE
:
752 if (eqn_nextrawtok(ep
, NULL
) == NULL
||
753 eqn_next(ep
, ep
->data
[(int)ep
->cur
], NULL
, 0) == NULL
)
754 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
755 ep
->eqn
.ln
, ep
->eqn
.pos
, "tdefine");
761 if (eqn_nextrawtok(ep
, NULL
) == NULL
)
762 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
763 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
777 if (parent
->last
== NULL
) {
778 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
779 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
780 cur
= eqn_box_alloc(ep
, parent
);
781 cur
->type
= EQN_TEXT
;
782 cur
->text
= mandoc_strdup("");
784 parent
= eqn_box_makebinary(ep
, EQNPOS_NONE
, parent
);
785 parent
->type
= EQN_LISTONE
;
786 parent
->expectargs
= 1;
789 strlcpy(sym
, "\\[ad]", sizeof(sym
));
792 strlcpy(sym
, "\\[->]", sizeof(sym
));
795 strlcpy(sym
, "\\[<>]", sizeof(sym
));
798 strlcpy(sym
, "\\[a~]", sizeof(sym
));
801 strlcpy(sym
, "\\[ul]", sizeof(sym
));
804 strlcpy(sym
, "\\[rl]", sizeof(sym
));
807 strlcpy(sym
, "\\[a.]", sizeof(sym
));
810 strlcpy(sym
, "\\[ha]", sizeof(sym
));
824 parent
->top
= mandoc_strdup(sym
);
827 parent
->bottom
= mandoc_strdup(sym
);
832 parent
= parent
->parent
;
838 subtok
= eqn_tok_parse(ep
, NULL
);
839 if (subtok
!= EQN_TOK__MAX
) {
840 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
841 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
850 while (parent
->args
== parent
->expectargs
)
851 parent
= parent
->parent
;
853 * These values apply to the next word or sequence of
854 * words; thus, we mark that we'll have a child with
855 * exactly one of those.
857 parent
= eqn_box_alloc(ep
, parent
);
858 parent
->type
= EQN_LISTONE
;
859 parent
->expectargs
= 1;
862 parent
->font
= EQNFONT_FAT
;
865 parent
->font
= EQNFONT_ROMAN
;
868 parent
->font
= EQNFONT_ITALIC
;
871 parent
->font
= EQNFONT_BOLD
;
879 /* Accept two values: integral size and a single. */
880 if (NULL
== (start
= eqn_nexttok(ep
, &sz
))) {
881 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
882 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
885 size
= mandoc_strntoi(start
, sz
, 10);
887 mandoc_msg(MANDOCERR_IT_NONUM
, ep
->parse
,
888 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
891 if (EQN_TOK_GSIZE
== tok
) {
895 parent
= eqn_box_alloc(ep
, parent
);
896 parent
->type
= EQN_LISTONE
;
897 parent
->expectargs
= 1;
905 * We have a left-right-associative expression.
906 * Repivot under a positional node, open a child scope
907 * and keep on reading.
909 if (parent
->last
== NULL
) {
910 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
911 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
912 cur
= eqn_box_alloc(ep
, parent
);
913 cur
->type
= EQN_TEXT
;
914 cur
->text
= mandoc_strdup("");
916 /* Handle the "subsup" and "fromto" positions. */
917 if (EQN_TOK_SUP
== tok
&& parent
->pos
== EQNPOS_SUB
) {
918 parent
->expectargs
= 3;
919 parent
->pos
= EQNPOS_SUBSUP
;
922 if (EQN_TOK_TO
== tok
&& parent
->pos
== EQNPOS_FROM
) {
923 parent
->expectargs
= 3;
924 parent
->pos
= EQNPOS_FROMTO
;
943 parent
= eqn_box_makebinary(ep
, pos
, parent
);
946 while (parent
->args
== parent
->expectargs
)
947 parent
= parent
->parent
;
949 * Accept a left-right-associative set of arguments just
950 * like sub and sup and friends but without rebalancing
953 parent
= eqn_box_alloc(ep
, parent
);
954 parent
->type
= EQN_SUBEXPR
;
955 parent
->pos
= EQNPOS_SQRT
;
956 parent
->expectargs
= 1;
960 * We have a right-left-associative fraction.
961 * Close out anything that's currently open, then
962 * rebalance and continue reading.
964 if (parent
->last
== NULL
) {
965 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
966 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
967 cur
= eqn_box_alloc(ep
, parent
);
968 cur
->type
= EQN_TEXT
;
969 cur
->text
= mandoc_strdup("");
971 while (EQN_SUBEXPR
== parent
->type
)
972 parent
= parent
->parent
;
973 parent
= eqn_box_makebinary(ep
, EQNPOS_OVER
, parent
);
976 case EQN_TOK_BRACE_CLOSE
:
978 * Close out the existing brace.
979 * FIXME: this is a shitty sentinel: we should really
980 * have a native EQN_BRACE type or whatnot.
982 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
983 if (cur
->type
== EQN_LIST
&&
984 (tok
== EQN_TOK_BRACE_CLOSE
||
988 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ep
->parse
,
989 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
993 if (EQN_TOK_RIGHT
== tok
) {
994 if (NULL
== (start
= eqn_nexttok(ep
, &sz
))) {
995 mandoc_msg(MANDOCERR_REQ_EMPTY
,
996 ep
->parse
, ep
->eqn
.ln
,
997 ep
->eqn
.pos
, eqn_toks
[tok
]);
1000 /* Handling depends on right/left. */
1001 if (STRNEQ(start
, sz
, "ceiling", 7)) {
1002 strlcpy(sym
, "\\[rc]", sizeof(sym
));
1003 parent
->right
= mandoc_strdup(sym
);
1004 } else if (STRNEQ(start
, sz
, "floor", 5)) {
1005 strlcpy(sym
, "\\[rf]", sizeof(sym
));
1006 parent
->right
= mandoc_strdup(sym
);
1008 parent
->right
= mandoc_strndup(start
, sz
);
1010 parent
= parent
->parent
;
1011 if (tok
== EQN_TOK_BRACE_CLOSE
&&
1012 (parent
->type
== EQN_PILE
||
1013 parent
->type
== EQN_MATRIX
))
1014 parent
= parent
->parent
;
1015 /* Close out any "singleton" lists. */
1016 while (parent
->type
== EQN_LISTONE
&&
1017 parent
->args
== parent
->expectargs
)
1018 parent
= parent
->parent
;
1020 case EQN_TOK_BRACE_OPEN
:
1023 * If we already have something in the stack and we're
1024 * in an expression, then rewind til we're not any more
1025 * (just like with the text node).
1027 while (parent
->args
== parent
->expectargs
)
1028 parent
= parent
->parent
;
1029 if (EQN_TOK_LEFT
== tok
&&
1030 (start
= eqn_nexttok(ep
, &sz
)) == NULL
) {
1031 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
1032 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
1035 parent
= eqn_box_alloc(ep
, parent
);
1036 parent
->type
= EQN_LIST
;
1037 if (EQN_TOK_LEFT
== tok
) {
1038 if (STRNEQ(start
, sz
, "ceiling", 7)) {
1039 strlcpy(sym
, "\\[lc]", sizeof(sym
));
1040 parent
->left
= mandoc_strdup(sym
);
1041 } else if (STRNEQ(start
, sz
, "floor", 5)) {
1042 strlcpy(sym
, "\\[lf]", sizeof(sym
));
1043 parent
->left
= mandoc_strdup(sym
);
1045 parent
->left
= mandoc_strndup(start
, sz
);
1055 while (parent
->args
== parent
->expectargs
)
1056 parent
= parent
->parent
;
1057 parent
= eqn_box_alloc(ep
, parent
);
1058 parent
->type
= EQN_PILE
;
1059 parent
->expectargs
= 1;
1062 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
1063 if (cur
->type
== EQN_PILE
)
1066 mandoc_msg(MANDOCERR_IT_STRAY
, ep
->parse
,
1067 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
1070 parent
= eqn_box_alloc(ep
, cur
);
1071 parent
->type
= EQN_LIST
;
1073 case EQN_TOK_MATRIX
:
1074 while (parent
->args
== parent
->expectargs
)
1075 parent
= parent
->parent
;
1076 parent
= eqn_box_alloc(ep
, parent
);
1077 parent
->type
= EQN_MATRIX
;
1078 parent
->expectargs
= 1;
1083 * TODO: make sure we're not in an open subexpression.
1090 * If we already have something in the stack and we're
1091 * in an expression, then rewind til we're not any more.
1093 while (parent
->args
== parent
->expectargs
)
1094 parent
= parent
->parent
;
1095 if (tok
== EQN_TOK_FUNC
) {
1096 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
1097 if (cur
->font
!= EQNFONT_NONE
)
1099 if (cur
== NULL
|| cur
->font
!= EQNFONT_ROMAN
) {
1100 parent
= eqn_box_alloc(ep
, parent
);
1101 parent
->type
= EQN_LISTONE
;
1102 parent
->font
= EQNFONT_ROMAN
;
1103 parent
->expectargs
= 1;
1106 cur
= eqn_box_alloc(ep
, parent
);
1107 cur
->type
= EQN_TEXT
;
1111 * Post-process list status.
1113 while (parent
->type
== EQN_LISTONE
&&
1114 parent
->args
== parent
->expectargs
)
1115 parent
= parent
->parent
;
1124 eqn_end(struct eqn_node
**epp
)
1126 struct eqn_node
*ep
;
1131 ep
->eqn
.root
= mandoc_calloc(1, sizeof(struct eqn_box
));
1132 ep
->eqn
.root
->expectargs
= UINT_MAX
;
1133 return eqn_parse(ep
, ep
->eqn
.root
);
1137 eqn_free(struct eqn_node
*p
)
1141 eqn_box_free(p
->eqn
.root
);
1143 for (i
= 0; i
< (int)p
->defsz
; i
++) {
1144 free(p
->defs
[i
].key
);
1145 free(p
->defs
[i
].val
);