]>
git.cameronkatri.com Git - mandoc.git/blob - eqn.c
1 /* $Id: eqn.c,v 1.69 2017/06/23 21:04:57 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 */
279 static struct eqn_box
*eqn_box_alloc(struct eqn_node
*, struct eqn_box
*);
280 static void eqn_box_free(struct eqn_box
*);
281 static struct eqn_box
*eqn_box_makebinary(struct eqn_node
*,
282 enum eqn_post
, struct eqn_box
*);
283 static void eqn_def(struct eqn_node
*);
284 static struct eqn_def
*eqn_def_find(struct eqn_node
*, const char *, size_t);
285 static void eqn_delim(struct eqn_node
*);
286 static const char *eqn_next(struct eqn_node
*, char, size_t *, int);
287 static const char *eqn_nextrawtok(struct eqn_node
*, size_t *);
288 static const char *eqn_nexttok(struct eqn_node
*, size_t *);
289 static enum rofferr
eqn_parse(struct eqn_node
*, struct eqn_box
*);
290 static enum eqn_tok
eqn_tok_parse(struct eqn_node
*, char **);
291 static void eqn_undef(struct eqn_node
*);
295 eqn_read(struct eqn_node
**epp
, int ln
,
296 const char *p
, int pos
, int *offs
)
305 * If we're the terminating mark, unset our equation status and
306 * validate the full equation.
309 if (0 == strncmp(p
, ".EN", 3)) {
312 while (' ' == *p
|| '\t' == *p
)
316 mandoc_vmsg(MANDOCERR_ARG_SKIP
, ep
->parse
,
317 ln
, pos
, "EN %s", p
);
322 * Build up the full string, replacing all newlines with regular
326 sz
= strlen(p
+ pos
) + 1;
327 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ sz
+ 1);
329 /* First invocation: nil terminate the string. */
335 strlcat(ep
->data
, p
+ pos
, ep
->sz
+ 1);
336 strlcat(ep
->data
, " ", ep
->sz
+ 1);
341 eqn_alloc(int pos
, int line
, struct mparse
*parse
)
345 p
= mandoc_calloc(1, sizeof(struct eqn_node
));
350 p
->gsize
= EQN_DEFSIZE
;
356 * Find the key "key" of the give size within our eqn-defined values.
358 static struct eqn_def
*
359 eqn_def_find(struct eqn_node
*ep
, const char *key
, size_t sz
)
363 for (i
= 0; i
< (int)ep
->defsz
; i
++)
364 if (ep
->defs
[i
].keysz
&& STRNEQ(ep
->defs
[i
].key
,
365 ep
->defs
[i
].keysz
, key
, sz
))
372 * Get the next token from the input stream using the given quote
374 * Optionally make any replacements.
377 eqn_next(struct eqn_node
*ep
, char quote
, size_t *sz
, int repl
)
379 static size_t last_len
;
390 if (ep
->cur
>= last_len
)
394 /* Prevent self-definitions. */
396 if (lim
>= EQN_NEST_MAX
) {
397 mandoc_msg(MANDOCERR_ROFFLOOP
, ep
->parse
,
398 ep
->eqn
.ln
, ep
->eqn
.pos
, NULL
);
403 start
= &ep
->data
[(int)ep
->cur
];
409 if (quote
== *start
) {
414 start
= &ep
->data
[(int)ep
->cur
];
417 if ('{' == *start
|| '}' == *start
)
420 ssz
= strcspn(start
+ 1, " ^~\"{}\t") + 1;
421 next
= start
+ (int)ssz
;
425 next
= strchr(start
, quote
);
428 *sz
= (size_t)(next
- start
);
432 while (' ' == ep
->data
[(int)ep
->cur
] ||
433 '\t' == ep
->data
[(int)ep
->cur
] ||
434 '^' == ep
->data
[(int)ep
->cur
] ||
435 '~' == ep
->data
[(int)ep
->cur
])
439 mandoc_msg(MANDOCERR_ARG_QUOTE
, ep
->parse
,
440 ep
->eqn
.ln
, ep
->eqn
.pos
, NULL
);
441 next
= strchr(start
, '\0');
442 *sz
= (size_t)(next
- start
);
446 /* Quotes aren't expanded for values. */
451 if (NULL
!= (def
= eqn_def_find(ep
, start
, *sz
))) {
452 diff
= def
->valsz
- *sz
;
454 if (def
->valsz
> *sz
) {
456 ep
->data
= mandoc_realloc(ep
->data
, ep
->sz
+ 1);
457 ep
->data
[ep
->sz
] = '\0';
458 start
= &ep
->data
[(int)ep
->rew
];
461 diff
= def
->valsz
- *sz
;
462 memmove(start
+ *sz
+ diff
, start
+ *sz
,
463 (strlen(start
) - *sz
) + 1);
464 memcpy(start
, def
->val
, def
->valsz
);
465 last_len
= start
- ep
->data
+ def
->valsz
;
474 * Get the next delimited token using the default current quote
478 eqn_nexttok(struct eqn_node
*ep
, size_t *sz
)
481 return eqn_next(ep
, '"', sz
, 1);
485 * Get next token without replacement.
488 eqn_nextrawtok(struct eqn_node
*ep
, size_t *sz
)
491 return eqn_next(ep
, '"', sz
, 0);
495 * Parse a token from the stream of text.
496 * A token consists of one of the recognised eqn(7) strings.
497 * Strings are separated by delimiting marks.
498 * This returns EQN_TOK_EOF when there are no more tokens.
499 * If the token is an unrecognised string literal, then it returns
500 * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated
502 * This must be later freed with free(3).
505 eqn_tok_parse(struct eqn_node
*ep
, char **p
)
514 quoted
= ep
->data
[ep
->cur
] == '"';
516 if ((start
= eqn_nexttok(ep
, &sz
)) == NULL
)
521 *p
= mandoc_strndup(start
, sz
);
522 return EQN_TOK_QUOTED
;
525 for (i
= 0; i
< EQN_TOK__MAX
; i
++)
526 if (STRNEQ(start
, sz
, eqn_toks
[i
], strlen(eqn_toks
[i
])))
529 for (i
= 0; i
< EQNSYM__MAX
; i
++) {
530 if (STRNEQ(start
, sz
,
531 eqnsyms
[i
].str
, strlen(eqnsyms
[i
].str
))) {
532 mandoc_asprintf(p
, "\\[%s]", eqnsyms
[i
].sym
);
538 *p
= mandoc_strndup(start
, sz
);
540 for (i
= 0; i
< sizeof(eqn_func
)/sizeof(*eqn_func
); i
++)
541 if (STRNEQ(start
, sz
, eqn_func
[i
], strlen(eqn_func
[i
])))
548 eqn_box_free(struct eqn_box
*bp
)
552 eqn_box_free(bp
->first
);
554 eqn_box_free(bp
->next
);
565 * Allocate a box as the last child of the parent node.
567 static struct eqn_box
*
568 eqn_box_alloc(struct eqn_node
*ep
, struct eqn_box
*parent
)
572 bp
= mandoc_calloc(1, sizeof(struct eqn_box
));
575 bp
->expectargs
= UINT_MAX
;
576 bp
->font
= bp
->parent
->font
;
577 bp
->size
= ep
->gsize
;
579 if (NULL
!= parent
->first
) {
580 parent
->last
->next
= bp
;
581 bp
->prev
= parent
->last
;
590 * Reparent the current last node (of the current parent) under a new
591 * EQN_SUBEXPR as the first element.
592 * Then return the new parent.
593 * The new EQN_SUBEXPR will have a two-child limit.
595 static struct eqn_box
*
596 eqn_box_makebinary(struct eqn_node
*ep
,
597 enum eqn_post pos
, struct eqn_box
*parent
)
599 struct eqn_box
*b
, *newb
;
601 assert(NULL
!= parent
->last
);
603 if (parent
->last
== parent
->first
)
604 parent
->first
= NULL
;
606 parent
->last
= b
->prev
;
608 newb
= eqn_box_alloc(ep
, parent
);
610 newb
->type
= EQN_SUBEXPR
;
611 newb
->expectargs
= 2;
613 newb
->first
= newb
->last
= b
;
614 newb
->first
->next
= NULL
;
620 * Parse the "delim" control statement.
623 eqn_delim(struct eqn_node
*ep
)
628 if ((start
= eqn_nextrawtok(ep
, &sz
)) == NULL
)
629 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
630 ep
->eqn
.ln
, ep
->eqn
.pos
, "delim");
631 else if (strncmp(start
, "off", 3) == 0)
633 else if (strncmp(start
, "on", 2) == 0) {
634 if (ep
->odelim
&& ep
->cdelim
)
636 } else if (start
[1] != '\0') {
637 ep
->odelim
= start
[0];
638 ep
->cdelim
= start
[1];
644 * Undefine a previously-defined string.
647 eqn_undef(struct eqn_node
*ep
)
653 if ((start
= eqn_nextrawtok(ep
, &sz
)) == NULL
) {
654 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
655 ep
->eqn
.ln
, ep
->eqn
.pos
, "undef");
658 if ((def
= eqn_def_find(ep
, start
, sz
)) == NULL
)
662 def
->key
= def
->val
= NULL
;
663 def
->keysz
= def
->valsz
= 0;
667 eqn_def(struct eqn_node
*ep
)
674 if ((start
= eqn_nextrawtok(ep
, &sz
)) == NULL
) {
675 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
676 ep
->eqn
.ln
, ep
->eqn
.pos
, "define");
681 * Search for a key that already exists.
682 * Create a new key if none is found.
684 if (NULL
== (def
= eqn_def_find(ep
, start
, sz
))) {
685 /* Find holes in string array. */
686 for (i
= 0; i
< (int)ep
->defsz
; i
++)
687 if (0 == ep
->defs
[i
].keysz
)
690 if (i
== (int)ep
->defsz
) {
692 ep
->defs
= mandoc_reallocarray(ep
->defs
,
693 ep
->defsz
, sizeof(struct eqn_def
));
694 ep
->defs
[i
].key
= ep
->defs
[i
].val
= NULL
;
699 def
->key
= mandoc_strndup(start
, sz
);
703 start
= eqn_next(ep
, ep
->data
[(int)ep
->cur
], &sz
, 0);
705 mandoc_vmsg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
706 ep
->eqn
.ln
, ep
->eqn
.pos
, "define %s", def
->key
);
709 def
->key
= def
->val
= NULL
;
710 def
->keysz
= def
->valsz
= 0;
714 def
->val
= mandoc_strndup(start
, sz
);
719 * Recursively parse an eqn(7) expression.
722 eqn_parse(struct eqn_node
*ep
, struct eqn_box
*parent
)
725 struct eqn_box
*cur
, *nbox
;
726 const char *cp
, *cpn
, *start
;
729 enum eqn_tok tok
, subtok
;
731 enum { CCL_LET
, CCL_DIG
, CCL_PUN
} ccl
, ccln
;
734 assert(parent
!= NULL
);
738 * Do not add it to the high-level syntax tree.
741 if (ep
->data
== NULL
)
745 tok
= eqn_tok_parse(ep
, &p
);
752 case EQN_TOK_NDEFINE
:
756 case EQN_TOK_TDEFINE
:
757 if (eqn_nextrawtok(ep
, NULL
) == NULL
||
758 eqn_next(ep
, ep
->data
[(int)ep
->cur
], NULL
, 0) == NULL
)
759 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
760 ep
->eqn
.ln
, ep
->eqn
.pos
, "tdefine");
766 if (eqn_nextrawtok(ep
, NULL
) == NULL
)
767 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
768 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
782 if (parent
->last
== NULL
) {
783 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
784 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
785 cur
= eqn_box_alloc(ep
, parent
);
786 cur
->type
= EQN_TEXT
;
787 cur
->text
= mandoc_strdup("");
789 parent
= eqn_box_makebinary(ep
, EQNPOS_NONE
, parent
);
790 parent
->type
= EQN_LISTONE
;
791 parent
->expectargs
= 1;
792 parent
->font
= EQNFONT_ROMAN
;
795 strlcpy(sym
, "\\[ad]", sizeof(sym
));
798 strlcpy(sym
, "\\[->]", sizeof(sym
));
801 strlcpy(sym
, "\\[<>]", sizeof(sym
));
804 strlcpy(sym
, "\\[a~]", sizeof(sym
));
807 strlcpy(sym
, "\\[ul]", sizeof(sym
));
810 strlcpy(sym
, "\\[rl]", sizeof(sym
));
813 strlcpy(sym
, "\\[a.]", sizeof(sym
));
816 strlcpy(sym
, "\\[ha]", sizeof(sym
));
830 parent
->top
= mandoc_strdup(sym
);
833 parent
->bottom
= mandoc_strdup(sym
);
838 parent
= parent
->parent
;
844 subtok
= eqn_tok_parse(ep
, NULL
);
845 if (subtok
!= EQN_TOK__MAX
) {
846 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
847 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
856 while (parent
->args
== parent
->expectargs
)
857 parent
= parent
->parent
;
859 * These values apply to the next word or sequence of
860 * words; thus, we mark that we'll have a child with
861 * exactly one of those.
863 parent
= eqn_box_alloc(ep
, parent
);
864 parent
->type
= EQN_LISTONE
;
865 parent
->expectargs
= 1;
868 parent
->font
= EQNFONT_FAT
;
871 parent
->font
= EQNFONT_ROMAN
;
874 parent
->font
= EQNFONT_ITALIC
;
877 parent
->font
= EQNFONT_BOLD
;
885 /* Accept two values: integral size and a single. */
886 if (NULL
== (start
= eqn_nexttok(ep
, &sz
))) {
887 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
888 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
891 size
= mandoc_strntoi(start
, sz
, 10);
893 mandoc_msg(MANDOCERR_IT_NONUM
, ep
->parse
,
894 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
897 if (EQN_TOK_GSIZE
== tok
) {
901 parent
= eqn_box_alloc(ep
, parent
);
902 parent
->type
= EQN_LISTONE
;
903 parent
->expectargs
= 1;
911 * We have a left-right-associative expression.
912 * Repivot under a positional node, open a child scope
913 * and keep on reading.
915 if (parent
->last
== NULL
) {
916 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
917 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
918 cur
= eqn_box_alloc(ep
, parent
);
919 cur
->type
= EQN_TEXT
;
920 cur
->text
= mandoc_strdup("");
922 /* Handle the "subsup" and "fromto" positions. */
923 if (EQN_TOK_SUP
== tok
&& parent
->pos
== EQNPOS_SUB
) {
924 parent
->expectargs
= 3;
925 parent
->pos
= EQNPOS_SUBSUP
;
928 if (EQN_TOK_TO
== tok
&& parent
->pos
== EQNPOS_FROM
) {
929 parent
->expectargs
= 3;
930 parent
->pos
= EQNPOS_FROMTO
;
949 parent
= eqn_box_makebinary(ep
, pos
, parent
);
952 while (parent
->args
== parent
->expectargs
)
953 parent
= parent
->parent
;
955 * Accept a left-right-associative set of arguments just
956 * like sub and sup and friends but without rebalancing
959 parent
= eqn_box_alloc(ep
, parent
);
960 parent
->type
= EQN_SUBEXPR
;
961 parent
->pos
= EQNPOS_SQRT
;
962 parent
->expectargs
= 1;
966 * We have a right-left-associative fraction.
967 * Close out anything that's currently open, then
968 * rebalance and continue reading.
970 if (parent
->last
== NULL
) {
971 mandoc_msg(MANDOCERR_EQN_NOBOX
, ep
->parse
,
972 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
973 cur
= eqn_box_alloc(ep
, parent
);
974 cur
->type
= EQN_TEXT
;
975 cur
->text
= mandoc_strdup("");
977 while (EQN_SUBEXPR
== parent
->type
)
978 parent
= parent
->parent
;
979 parent
= eqn_box_makebinary(ep
, EQNPOS_OVER
, parent
);
982 case EQN_TOK_BRACE_CLOSE
:
984 * Close out the existing brace.
985 * FIXME: this is a shitty sentinel: we should really
986 * have a native EQN_BRACE type or whatnot.
988 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
989 if (cur
->type
== EQN_LIST
&&
990 (tok
== EQN_TOK_BRACE_CLOSE
||
994 mandoc_msg(MANDOCERR_BLK_NOTOPEN
, ep
->parse
,
995 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
999 if (EQN_TOK_RIGHT
== tok
) {
1000 if (NULL
== (start
= eqn_nexttok(ep
, &sz
))) {
1001 mandoc_msg(MANDOCERR_REQ_EMPTY
,
1002 ep
->parse
, ep
->eqn
.ln
,
1003 ep
->eqn
.pos
, eqn_toks
[tok
]);
1006 /* Handling depends on right/left. */
1007 if (STRNEQ(start
, sz
, "ceiling", 7)) {
1008 strlcpy(sym
, "\\[rc]", sizeof(sym
));
1009 parent
->right
= mandoc_strdup(sym
);
1010 } else if (STRNEQ(start
, sz
, "floor", 5)) {
1011 strlcpy(sym
, "\\[rf]", sizeof(sym
));
1012 parent
->right
= mandoc_strdup(sym
);
1014 parent
->right
= mandoc_strndup(start
, sz
);
1016 parent
= parent
->parent
;
1017 if (tok
== EQN_TOK_BRACE_CLOSE
&&
1018 (parent
->type
== EQN_PILE
||
1019 parent
->type
== EQN_MATRIX
))
1020 parent
= parent
->parent
;
1021 /* Close out any "singleton" lists. */
1022 while (parent
->type
== EQN_LISTONE
&&
1023 parent
->args
== parent
->expectargs
)
1024 parent
= parent
->parent
;
1026 case EQN_TOK_BRACE_OPEN
:
1029 * If we already have something in the stack and we're
1030 * in an expression, then rewind til we're not any more
1031 * (just like with the text node).
1033 while (parent
->args
== parent
->expectargs
)
1034 parent
= parent
->parent
;
1035 if (EQN_TOK_LEFT
== tok
&&
1036 (start
= eqn_nexttok(ep
, &sz
)) == NULL
) {
1037 mandoc_msg(MANDOCERR_REQ_EMPTY
, ep
->parse
,
1038 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
1041 parent
= eqn_box_alloc(ep
, parent
);
1042 parent
->type
= EQN_LIST
;
1043 if (EQN_TOK_LEFT
== tok
) {
1044 if (STRNEQ(start
, sz
, "ceiling", 7)) {
1045 strlcpy(sym
, "\\[lc]", sizeof(sym
));
1046 parent
->left
= mandoc_strdup(sym
);
1047 } else if (STRNEQ(start
, sz
, "floor", 5)) {
1048 strlcpy(sym
, "\\[lf]", sizeof(sym
));
1049 parent
->left
= mandoc_strdup(sym
);
1051 parent
->left
= mandoc_strndup(start
, sz
);
1061 while (parent
->args
== parent
->expectargs
)
1062 parent
= parent
->parent
;
1063 parent
= eqn_box_alloc(ep
, parent
);
1064 parent
->type
= EQN_PILE
;
1065 parent
->expectargs
= 1;
1068 for (cur
= parent
; cur
!= NULL
; cur
= cur
->parent
)
1069 if (cur
->type
== EQN_PILE
)
1072 mandoc_msg(MANDOCERR_IT_STRAY
, ep
->parse
,
1073 ep
->eqn
.ln
, ep
->eqn
.pos
, eqn_toks
[tok
]);
1076 parent
= eqn_box_alloc(ep
, cur
);
1077 parent
->type
= EQN_LIST
;
1079 case EQN_TOK_MATRIX
:
1080 while (parent
->args
== parent
->expectargs
)
1081 parent
= parent
->parent
;
1082 parent
= eqn_box_alloc(ep
, parent
);
1083 parent
->type
= EQN_MATRIX
;
1084 parent
->expectargs
= 1;
1089 * TODO: make sure we're not in an open subexpression.
1094 case EQN_TOK_QUOTED
:
1098 * If we already have something in the stack and we're
1099 * in an expression, then rewind til we're not any more.
1101 while (parent
->args
== parent
->expectargs
)
1102 parent
= parent
->parent
;
1103 cur
= eqn_box_alloc(ep
, parent
);
1104 cur
->type
= EQN_TEXT
;
1108 cur
->font
= EQNFONT_ROMAN
;
1110 case EQN_TOK_QUOTED
:
1111 if (cur
->font
== EQNFONT_NONE
)
1112 cur
->font
= EQNFONT_ITALIC
;
1117 if (cur
->font
!= EQNFONT_NONE
|| *p
== '\0')
1122 /* Advance to next character. */
1125 ccln
= isalpha((unsigned char)*cpn
) ? CCL_LET
:
1126 isdigit((unsigned char)*cpn
) ||
1127 (*cpn
== '.' && (ccl
== CCL_DIG
||
1128 isdigit((unsigned char)cpn
[1]))) ?
1130 /* No boundary before first character. */
1133 cur
->font
= ccl
== CCL_LET
?
1134 EQNFONT_ITALIC
: EQNFONT_ROMAN
;
1136 mandoc_escape(&cpn
, NULL
, NULL
);
1137 /* No boundary after last character. */
1142 /* Boundary found, split the text. */
1143 if (parent
->args
== parent
->expectargs
) {
1144 /* Remove the text from the tree. */
1145 if (cur
->prev
== NULL
)
1146 parent
->first
= cur
->next
;
1148 cur
->prev
->next
= NULL
;
1149 parent
->last
= cur
->prev
;
1151 /* Set up a list instead. */
1152 nbox
= eqn_box_alloc(ep
, parent
);
1153 nbox
->type
= EQN_LIST
;
1154 /* Insert the word into the list. */
1155 nbox
->first
= nbox
->last
= cur
;
1160 /* Append a new text box. */
1161 nbox
= eqn_box_alloc(ep
, parent
);
1162 nbox
->type
= EQN_TEXT
;
1163 nbox
->text
= mandoc_strdup(cpn
);
1164 /* Truncate the old box. */
1165 p
= mandoc_strndup(cur
->text
,
1169 /* Setup to process the new box. */
1178 * Post-process list status.
1180 while (parent
->type
== EQN_LISTONE
&&
1181 parent
->args
== parent
->expectargs
)
1182 parent
= parent
->parent
;
1191 eqn_end(struct eqn_node
**epp
)
1193 struct eqn_node
*ep
;
1198 ep
->eqn
.root
= mandoc_calloc(1, sizeof(struct eqn_box
));
1199 ep
->eqn
.root
->expectargs
= UINT_MAX
;
1200 return eqn_parse(ep
, ep
->eqn
.root
);
1204 eqn_free(struct eqn_node
*p
)
1208 eqn_box_free(p
->eqn
.root
);
1210 for (i
= 0; i
< (int)p
->defsz
; i
++) {
1211 free(p
->defs
[i
].key
);
1212 free(p
->defs
[i
].val
);