=================================================================== RCS file: /cvs/mandoc/eqn.c,v retrieving revision 1.61 retrieving revision 1.70 diff -u -p -r1.61 -r1.70 --- mandoc/eqn.c 2016/01/08 00:50:45 1.61 +++ mandoc/eqn.c 2017/06/26 11:04:47 1.70 @@ -1,7 +1,7 @@ -/* $Id: eqn.c,v 1.61 2016/01/08 00:50:45 schwarze Exp $ */ +/* $Id: eqn.c,v 1.70 2017/06/26 11:04:47 schwarze Exp $ */ /* * Copyright (c) 2011, 2014 Kristaps Dzonsons - * Copyright (c) 2014, 2015 Ingo Schwarze + * Copyright (c) 2014, 2015, 2017 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -80,9 +81,12 @@ enum eqn_tok { EQN_TOK_TDEFINE, EQN_TOK_NDEFINE, EQN_TOK_UNDEF, - EQN_TOK_EOF, EQN_TOK_ABOVE, - EQN_TOK__MAX + EQN_TOK__MAX, + EQN_TOK_FUNC, + EQN_TOK_QUOTED, + EQN_TOK_SYM, + EQN_TOK_EOF }; static const char *eqn_toks[EQN_TOK__MAX] = { @@ -130,10 +134,16 @@ static const char *eqn_toks[EQN_TOK__MAX] = { "tdefine", /* EQN_TOK_TDEFINE */ "ndefine", /* EQN_TOK_NDEFINE */ "undef", /* EQN_TOK_UNDEF */ - NULL, /* EQN_TOK_EOF */ "above", /* EQN_TOK_ABOVE */ }; +static const char *const eqn_func[] = { + "acos", "acsc", "and", "arc", "asec", "asin", "atan", + "cos", "cosh", "coth", "csc", "det", "exp", "for", + "if", "lim", "ln", "log", "max", "min", + "sec", "sin", "sinh", "tan", "tanh", "Im", "Re", +}; + enum eqn_symt { EQNSYM_alpha, EQNSYM_beta, @@ -366,15 +376,19 @@ eqn_def_find(struct eqn_node *ep, const char *key, siz static const char * eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl) { + static size_t last_len; + static int lim; + char *start, *next; - int q, diff, lim; + int q, diff; size_t ssz, dummy; struct eqn_def *def; if (NULL == sz) sz = &dummy; - lim = 0; + if (ep->cur >= last_len) + lim = 0; ep->rew = ep->cur; again: /* Prevent self-definitions. */ @@ -448,6 +462,7 @@ again: memmove(start + *sz + diff, start + *sz, (strlen(start) - *sz) + 1); memcpy(start, def->val, def->valsz); + last_len = start - ep->data + def->valsz; lim++; goto again; } @@ -493,31 +508,40 @@ eqn_tok_parse(struct eqn_node *ep, char **p) size_t i, sz; int quoted; - if (NULL != p) + if (p != NULL) *p = NULL; quoted = ep->data[ep->cur] == '"'; - if (NULL == (start = eqn_nexttok(ep, &sz))) + if ((start = eqn_nexttok(ep, &sz)) == NULL) return EQN_TOK_EOF; if (quoted) { if (p != NULL) *p = mandoc_strndup(start, sz); - return EQN_TOK__MAX; + return EQN_TOK_QUOTED; } - for (i = 0; i < EQN_TOK__MAX; i++) { - if (NULL == eqn_toks[i]) - continue; + for (i = 0; i < EQN_TOK__MAX; i++) if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i]))) - break; + return i; + + for (i = 0; i < EQNSYM__MAX; i++) { + if (STRNEQ(start, sz, + eqnsyms[i].str, strlen(eqnsyms[i].str))) { + mandoc_asprintf(p, "\\[%s]", eqnsyms[i].sym); + return EQN_TOK_SYM; + } } - if (i == EQN_TOK__MAX && NULL != p) + if (p != NULL) *p = mandoc_strndup(start, sz); - return i; + for (i = 0; i < sizeof(eqn_func)/sizeof(*eqn_func); i++) + if (STRNEQ(start, sz, eqn_func[i], strlen(eqn_func[i]))) + return EQN_TOK_FUNC; + + return EQN_TOK__MAX; } static void @@ -549,6 +573,7 @@ eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par bp->parent = parent; bp->parent->args++; bp->expectargs = UINT_MAX; + bp->font = bp->parent->font; bp->size = ep->gsize; if (NULL != parent->first) { @@ -697,12 +722,13 @@ static enum rofferr eqn_parse(struct eqn_node *ep, struct eqn_box *parent) { char sym[64]; - struct eqn_box *cur; - const char *start; + struct eqn_box *cur, *nbox, *split; + const char *cp, *cpn, *start; char *p; - size_t i, sz; + size_t sz; enum eqn_tok tok, subtok; enum eqn_post pos; + enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln; int size; assert(parent != NULL); @@ -720,39 +746,39 @@ next_tok: this_tok: switch (tok) { - case (EQN_TOK_UNDEF): + case EQN_TOK_UNDEF: eqn_undef(ep); break; - case (EQN_TOK_NDEFINE): - case (EQN_TOK_DEFINE): + case EQN_TOK_NDEFINE: + case EQN_TOK_DEFINE: eqn_def(ep); break; - case (EQN_TOK_TDEFINE): + case EQN_TOK_TDEFINE: if (eqn_nextrawtok(ep, NULL) == NULL || eqn_next(ep, ep->data[(int)ep->cur], NULL, 0) == NULL) mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, "tdefine"); break; - case (EQN_TOK_DELIM): + case EQN_TOK_DELIM: eqn_delim(ep); break; - case (EQN_TOK_GFONT): + case EQN_TOK_GFONT: if (eqn_nextrawtok(ep, NULL) == NULL) mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); break; - case (EQN_TOK_MARK): - case (EQN_TOK_LINEUP): + case EQN_TOK_MARK: + case EQN_TOK_LINEUP: /* Ignore these. */ break; - case (EQN_TOK_DYAD): - case (EQN_TOK_VEC): - case (EQN_TOK_UNDER): - case (EQN_TOK_BAR): - case (EQN_TOK_TILDE): - case (EQN_TOK_HAT): - case (EQN_TOK_DOT): - case (EQN_TOK_DOTDOT): + case EQN_TOK_DYAD: + case EQN_TOK_VEC: + case EQN_TOK_UNDER: + case EQN_TOK_BAR: + case EQN_TOK_TILDE: + case EQN_TOK_HAT: + case EQN_TOK_DOT: + case EQN_TOK_DOTDOT: if (parent->last == NULL) { mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); @@ -763,29 +789,30 @@ this_tok: parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent); parent->type = EQN_LISTONE; parent->expectargs = 1; + parent->font = EQNFONT_ROMAN; switch (tok) { - case (EQN_TOK_DOTDOT): + case EQN_TOK_DOTDOT: strlcpy(sym, "\\[ad]", sizeof(sym)); break; - case (EQN_TOK_VEC): + case EQN_TOK_VEC: strlcpy(sym, "\\[->]", sizeof(sym)); break; - case (EQN_TOK_DYAD): + case EQN_TOK_DYAD: strlcpy(sym, "\\[<>]", sizeof(sym)); break; - case (EQN_TOK_TILDE): + case EQN_TOK_TILDE: strlcpy(sym, "\\[a~]", sizeof(sym)); break; - case (EQN_TOK_UNDER): + case EQN_TOK_UNDER: strlcpy(sym, "\\[ul]", sizeof(sym)); break; - case (EQN_TOK_BAR): + case EQN_TOK_BAR: strlcpy(sym, "\\[rl]", sizeof(sym)); break; - case (EQN_TOK_DOT): + case EQN_TOK_DOT: strlcpy(sym, "\\[a.]", sizeof(sym)); break; - case (EQN_TOK_HAT): + case EQN_TOK_HAT: strlcpy(sym, "\\[ha]", sizeof(sym)); break; default: @@ -793,16 +820,16 @@ this_tok: } switch (tok) { - case (EQN_TOK_DOTDOT): - case (EQN_TOK_VEC): - case (EQN_TOK_DYAD): - case (EQN_TOK_TILDE): - case (EQN_TOK_BAR): - case (EQN_TOK_DOT): - case (EQN_TOK_HAT): + case EQN_TOK_DOTDOT: + case EQN_TOK_VEC: + case EQN_TOK_DYAD: + case EQN_TOK_TILDE: + case EQN_TOK_BAR: + case EQN_TOK_DOT: + case EQN_TOK_HAT: parent->top = mandoc_strdup(sym); break; - case (EQN_TOK_UNDER): + case EQN_TOK_UNDER: parent->bottom = mandoc_strdup(sym); break; default: @@ -810,10 +837,10 @@ this_tok: } parent = parent->parent; break; - case (EQN_TOK_FWD): - case (EQN_TOK_BACK): - case (EQN_TOK_DOWN): - case (EQN_TOK_UP): + case EQN_TOK_FWD: + case EQN_TOK_BACK: + case EQN_TOK_DOWN: + case EQN_TOK_UP: subtok = eqn_tok_parse(ep, NULL); if (subtok != EQN_TOK__MAX) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, @@ -822,10 +849,10 @@ this_tok: goto this_tok; } break; - case (EQN_TOK_FAT): - case (EQN_TOK_ROMAN): - case (EQN_TOK_ITALIC): - case (EQN_TOK_BOLD): + case EQN_TOK_FAT: + case EQN_TOK_ROMAN: + case EQN_TOK_ITALIC: + case EQN_TOK_BOLD: while (parent->args == parent->expectargs) parent = parent->parent; /* @@ -837,24 +864,24 @@ this_tok: parent->type = EQN_LISTONE; parent->expectargs = 1; switch (tok) { - case (EQN_TOK_FAT): + case EQN_TOK_FAT: parent->font = EQNFONT_FAT; break; - case (EQN_TOK_ROMAN): + case EQN_TOK_ROMAN: parent->font = EQNFONT_ROMAN; break; - case (EQN_TOK_ITALIC): + case EQN_TOK_ITALIC: parent->font = EQNFONT_ITALIC; break; - case (EQN_TOK_BOLD): + case EQN_TOK_BOLD: parent->font = EQNFONT_BOLD; break; default: abort(); } break; - case (EQN_TOK_SIZE): - case (EQN_TOK_GSIZE): + case EQN_TOK_SIZE: + case EQN_TOK_GSIZE: /* Accept two values: integral size and a single. */ if (NULL == (start = eqn_nexttok(ep, &sz))) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, @@ -876,10 +903,10 @@ this_tok: parent->expectargs = 1; parent->size = size; break; - case (EQN_TOK_FROM): - case (EQN_TOK_TO): - case (EQN_TOK_SUB): - case (EQN_TOK_SUP): + case EQN_TOK_FROM: + case EQN_TOK_TO: + case EQN_TOK_SUB: + case EQN_TOK_SUP: /* * We have a left-right-associative expression. * Repivot under a positional node, open a child scope @@ -904,16 +931,16 @@ this_tok: break; } switch (tok) { - case (EQN_TOK_FROM): + case EQN_TOK_FROM: pos = EQNPOS_FROM; break; - case (EQN_TOK_TO): + case EQN_TOK_TO: pos = EQNPOS_TO; break; - case (EQN_TOK_SUP): + case EQN_TOK_SUP: pos = EQNPOS_SUP; break; - case (EQN_TOK_SUB): + case EQN_TOK_SUB: pos = EQNPOS_SUB; break; default: @@ -921,7 +948,7 @@ this_tok: } parent = eqn_box_makebinary(ep, pos, parent); break; - case (EQN_TOK_SQRT): + case EQN_TOK_SQRT: while (parent->args == parent->expectargs) parent = parent->parent; /* @@ -934,7 +961,7 @@ this_tok: parent->pos = EQNPOS_SQRT; parent->expectargs = 1; break; - case (EQN_TOK_OVER): + case EQN_TOK_OVER: /* * We have a right-left-associative fraction. * Close out anything that's currently open, then @@ -951,8 +978,8 @@ this_tok: parent = parent->parent; parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent); break; - case (EQN_TOK_RIGHT): - case (EQN_TOK_BRACE_CLOSE): + case EQN_TOK_RIGHT: + case EQN_TOK_BRACE_CLOSE: /* * Close out the existing brace. * FIXME: this is a shitty sentinel: we should really @@ -996,8 +1023,8 @@ this_tok: parent->args == parent->expectargs) parent = parent->parent; break; - case (EQN_TOK_BRACE_OPEN): - case (EQN_TOK_LEFT): + case EQN_TOK_BRACE_OPEN: + case EQN_TOK_LEFT: /* * If we already have something in the stack and we're * in an expression, then rewind til we're not any more @@ -1024,20 +1051,20 @@ this_tok: parent->left = mandoc_strndup(start, sz); } break; - case (EQN_TOK_PILE): - case (EQN_TOK_LPILE): - case (EQN_TOK_RPILE): - case (EQN_TOK_CPILE): - case (EQN_TOK_CCOL): - case (EQN_TOK_LCOL): - case (EQN_TOK_RCOL): + case EQN_TOK_PILE: + case EQN_TOK_LPILE: + case EQN_TOK_RPILE: + case EQN_TOK_CPILE: + case EQN_TOK_CCOL: + case EQN_TOK_LCOL: + case EQN_TOK_RCOL: while (parent->args == parent->expectargs) parent = parent->parent; parent = eqn_box_alloc(ep, parent); parent->type = EQN_PILE; parent->expectargs = 1; break; - case (EQN_TOK_ABOVE): + case EQN_TOK_ABOVE: for (cur = parent; cur != NULL; cur = cur->parent) if (cur->type == EQN_PILE) break; @@ -1049,22 +1076,24 @@ this_tok: parent = eqn_box_alloc(ep, cur); parent->type = EQN_LIST; break; - case (EQN_TOK_MATRIX): + case EQN_TOK_MATRIX: while (parent->args == parent->expectargs) parent = parent->parent; parent = eqn_box_alloc(ep, parent); parent->type = EQN_MATRIX; parent->expectargs = 1; break; - case (EQN_TOK_EOF): + case EQN_TOK_EOF: /* * End of file! * TODO: make sure we're not in an open subexpression. */ return ROFF_EQN; - default: - assert(tok == EQN_TOK__MAX); - assert(NULL != p); + case EQN_TOK__MAX: + case EQN_TOK_FUNC: + case EQN_TOK_QUOTED: + case EQN_TOK_SYM: + assert(p != NULL); /* * If we already have something in the stack and we're * in an expression, then rewind til we're not any more. @@ -1073,17 +1102,81 @@ this_tok: parent = parent->parent; cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; - for (i = 0; i < EQNSYM__MAX; i++) - if (0 == strcmp(eqnsyms[i].str, p)) { - (void)snprintf(sym, sizeof(sym), - "\\[%s]", eqnsyms[i].sym); - cur->text = mandoc_strdup(sym); - free(p); + cur->text = p; + switch (tok) { + case EQN_TOK_FUNC: + cur->font = EQNFONT_ROMAN; + break; + case EQN_TOK_QUOTED: + if (cur->font == EQNFONT_NONE) + cur->font = EQNFONT_ITALIC; + break; + case EQN_TOK_SYM: + break; + default: + if (cur->font != EQNFONT_NONE || *p == '\0') break; + cpn = p - 1; + ccln = CCL_LET; + split = NULL; + for (;;) { + /* Advance to next character. */ + cp = cpn++; + ccl = ccln; + ccln = isalpha((unsigned char)*cpn) ? CCL_LET : + isdigit((unsigned char)*cpn) || + (*cpn == '.' && (ccl == CCL_DIG || + isdigit((unsigned char)cpn[1]))) ? + CCL_DIG : CCL_PUN; + /* No boundary before first character. */ + if (cp < p) + continue; + cur->font = ccl == CCL_LET ? + EQNFONT_ITALIC : EQNFONT_ROMAN; + if (*cp == '\\') + mandoc_escape(&cpn, NULL, NULL); + /* No boundary after last character. */ + if (*cpn == '\0') + break; + if (ccln == ccl) + continue; + /* Boundary found, split the text. */ + if (parent->args == parent->expectargs) { + /* Remove the text from the tree. */ + if (cur->prev == NULL) + parent->first = cur->next; + else + cur->prev->next = NULL; + parent->last = cur->prev; + parent->args--; + /* Set up a list instead. */ + split = eqn_box_alloc(ep, parent); + split->type = EQN_LIST; + /* Insert the word into the list. */ + split->first = split->last = cur; + cur->parent = split; + cur->prev = NULL; + parent = split; + } + /* Append a new text box. */ + nbox = eqn_box_alloc(ep, parent); + nbox->type = EQN_TEXT; + nbox->text = mandoc_strdup(cpn); + /* Truncate the old box. */ + p = mandoc_strndup(cur->text, + cpn - cur->text); + free(cur->text); + cur->text = p; + /* Setup to process the new box. */ + cur = nbox; + p = nbox->text; + cpn = p - 1; + ccln = CCL_LET; } - - if (i == EQNSYM__MAX) - cur->text = p; + if (split != NULL) + parent = split->parent; + break; + } /* * Post-process list status. */ @@ -1091,6 +1184,8 @@ this_tok: parent->args == parent->expectargs) parent = parent->parent; break; + default: + abort(); } goto next_tok; }