-/* $Id: eqn.c,v 1.48 2014/10/10 08:44:24 kristaps Exp $ */
+/* $Id: eqn.c,v 1.56 2014/10/25 15:06:30 schwarze Exp $ */
/*
- * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#define EQN_NEST_MAX 128 /* maximum nesting of defines */
#define STRNEQ(p1, sz1, p2, sz2) \
((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1)))
-#define EQNSTREQ(x, p, sz) \
- STRNEQ((x)->name, (x)->sz, (p), (sz))
enum eqn_tok {
EQN_TOK_DYAD = 0,
}
struct eqn_node *
-eqn_alloc(const char *name, int pos, int line, struct mparse *parse)
+eqn_alloc(int pos, int line, struct mparse *parse)
{
struct eqn_node *p;
- size_t sz;
- const char *end;
p = mandoc_calloc(1, sizeof(struct eqn_node));
- if (name && '\0' != *name) {
- sz = strlen(name);
- assert(sz);
- do {
- sz--;
- end = name + (int)sz;
- } while (' ' == *end || '\t' == *end);
- p->eqn.name = mandoc_strndup(name, sz + 1);
- }
-
p->parse = parse;
p->eqn.ln = line;
p->eqn.pos = pos;
{
const char *start;
size_t i, sz;
+ int quoted;
if (NULL != p)
*p = NULL;
+ quoted = ep->data[ep->cur] == '"';
+
if (NULL == (start = eqn_nexttok(ep, &sz)))
return(EQN_TOK_EOF);
+ if (quoted) {
+ if (p != NULL)
+ *p = mandoc_strndup(start, sz);
+ return(EQN_TOK__MAX);
+ }
+
for (i = 0; i < EQN_TOK__MAX; i++) {
if (NULL == eqn_toks[i])
continue;
bp->expectargs = UINT_MAX;
bp->size = ep->gsize;
- assert(NULL != parent);
-
if (NULL != parent->first) {
- assert(NULL != parent->last);
parent->last->next = bp;
bp->prev = parent->last;
} else
* The new EQN_SUBEXPR will have a two-child limit.
*/
static struct eqn_box *
-eqn_box_makebinary(struct eqn_node *ep,
+eqn_box_makebinary(struct eqn_node *ep,
enum eqn_post pos, struct eqn_box *parent)
{
struct eqn_box *b, *newb;
return(newb);
}
+/*
+ * Parse the "delim" control statement.
+ */
+static void
+eqn_delim(struct eqn_node *ep)
+{
+ const char *start;
+ size_t sz;
+
+ if ((start = eqn_nextrawtok(ep, &sz)) == NULL)
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, "delim");
+ else if (strncmp(start, "off", 3) == 0)
+ ep->delim = 0;
+ else if (strncmp(start, "on", 2) == 0) {
+ if (ep->odelim && ep->cdelim)
+ ep->delim = 1;
+ } else if (start[1] != '\0') {
+ ep->odelim = start[0];
+ ep->cdelim = start[1];
+ ep->delim = 1;
+ }
+}
+
/*
* Undefine a previously-defined string.
*/
eqn_parse(struct eqn_node *ep, struct eqn_box *parent)
{
char *p;
- enum eqn_tok tok;
+ enum eqn_tok tok, subtok;
enum eqn_post pos;
struct eqn_box *cur;
int rc, size;
char sym[64];
const char *start;
- assert(NULL != parent);
-again:
-
- switch ((tok = eqn_tok_parse(ep, &p))) {
+ assert(parent != NULL);
+ if (ep->data == NULL)
+ return(-1);
+
+next_tok:
+ tok = eqn_tok_parse(ep, &p);
+
+this_tok:
+ switch (tok) {
case (EQN_TOK_UNDEF):
if ((rc = eqn_undef(ep)) <= 0)
return(rc);
case (EQN_TOK_TDEFINE):
if (NULL == eqn_nextrawtok(ep, NULL))
EQN_MSG(MANDOCERR_EQNEOF, ep);
- else if (NULL == eqn_next(ep,
+ else if (NULL == eqn_next(ep,
ep->data[(int)ep->cur], NULL, 0))
EQN_MSG(MANDOCERR_EQNEOF, ep);
break;
+ case (EQN_TOK_DELIM):
+ eqn_delim(ep);
+ break;
case (EQN_TOK_GFONT):
- if (NULL == eqn_nextrawtok(ep, NULL)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ 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_HAT):
case (EQN_TOK_DOT):
case (EQN_TOK_DOTDOT):
- if (NULL == parent->last) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
+ if (parent->last == NULL) {
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ cur = eqn_box_alloc(ep, parent);
+ cur->type = EQN_TEXT;
+ cur->text = mandoc_strdup("");
}
- parent = eqn_box_makebinary
- (ep, EQNPOS_NONE, parent);
+ parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent);
parent->type = EQN_LISTONE;
parent->expectargs = 1;
switch (tok) {
case (EQN_TOK_BACK):
case (EQN_TOK_DOWN):
case (EQN_TOK_UP):
- tok = eqn_tok_parse(ep, NULL);
- if (EQN_TOK__MAX != tok) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
+ subtok = eqn_tok_parse(ep, NULL);
+ if (subtok != EQN_TOK__MAX) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ tok = subtok;
+ goto this_tok;
}
break;
case (EQN_TOK_FAT):
case (EQN_TOK_ITALIC):
case (EQN_TOK_BOLD):
while (parent->args == parent->expectargs)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ parent = parent->parent;
/*
* These values apply to the next word or sequence of
* words; thus, we mark that we'll have a child with
case (EQN_TOK_GSIZE):
/* Accept two values: integral size and a single. */
if (NULL == (start = eqn_nexttok(ep, &sz))) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
}
size = mandoc_strntoi(start, sz, 10);
if (-1 == size) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
+ mandoc_msg(MANDOCERR_IT_NONUM, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
}
if (EQN_TOK_GSIZE == tok) {
ep->gsize = size;
* Repivot under a positional node, open a child scope
* and keep on reading.
*/
- if (NULL == parent->last) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
+ if (parent->last == NULL) {
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ cur = eqn_box_alloc(ep, parent);
+ cur->type = EQN_TEXT;
+ cur->text = mandoc_strdup("");
}
/* Handle the "subsup" and "fromto" positions. */
if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) {
break;
case (EQN_TOK_SQRT):
while (parent->args == parent->expectargs)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
- /*
+ parent = parent->parent;
+ /*
* Accept a left-right-associative set of arguments just
* like sub and sup and friends but without rebalancing
* under a pivot.
* Close out anything that's currently open, then
* rebalance and continue reading.
*/
- if (NULL == parent->last) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
+ if (parent->last == NULL) {
+ mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ cur = eqn_box_alloc(ep, parent);
+ cur->type = EQN_TEXT;
+ cur->text = mandoc_strdup("");
}
while (EQN_SUBEXPR == parent->type)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ parent = parent->parent;
parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent);
break;
case (EQN_TOK_RIGHT):
* FIXME: this is a shitty sentinel: we should really
* have a native EQN_BRACE type or whatnot.
*/
- while (parent->type != EQN_LIST)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ for (cur = parent; cur != NULL; cur = cur->parent)
+ if (cur->type == EQN_LIST &&
+ (tok == EQN_TOK_BRACE_CLOSE ||
+ cur->left != NULL))
+ break;
+ if (cur == NULL) {
+ mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
+ parent = cur;
if (EQN_TOK_RIGHT == tok) {
- if (NULL == parent->left) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
if (NULL == (start = eqn_nexttok(ep, &sz))) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
+ mandoc_msg(MANDOCERR_REQ_EMPTY,
+ ep->parse, ep->eqn.ln,
+ ep->eqn.pos, eqn_toks[tok]);
+ break;
}
/* Handling depends on right/left. */
if (STRNEQ(start, sz, "ceiling", 7)) {
} else if (STRNEQ(start, sz, "floor", 5)) {
strlcpy(sym, "\\[rf]", sizeof(sym));
parent->right = mandoc_strdup(sym);
- } else
+ } else
parent->right = mandoc_strndup(start, sz);
}
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
- if (EQN_TOK_BRACE_CLOSE == tok && parent &&
- (parent->type == EQN_PILE ||
- parent->type == EQN_MATRIX))
+ parent = parent->parent;
+ if (EQN_TOK_BRACE_CLOSE == tok && parent &&
+ (parent->type == EQN_PILE ||
+ parent->type == EQN_MATRIX))
parent = parent->parent;
/* Close out any "singleton" lists. */
- while (parent->type == EQN_LISTONE &&
- parent->args == parent->expectargs)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ while (parent->type == EQN_LISTONE &&
+ parent->args == parent->expectargs)
+ parent = parent->parent;
break;
case (EQN_TOK_BRACE_OPEN):
case (EQN_TOK_LEFT):
* (just like with the text node).
*/
while (parent->args == parent->expectargs)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ parent = parent->parent;
+ if (EQN_TOK_LEFT == tok &&
+ (start = eqn_nexttok(ep, &sz)) == NULL) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
parent = eqn_box_alloc(ep, parent);
parent->type = EQN_LIST;
if (EQN_TOK_LEFT == tok) {
- if (NULL == (start = eqn_nexttok(ep, &sz))) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
- /* Handling depends on right/left. */
if (STRNEQ(start, sz, "ceiling", 7)) {
strlcpy(sym, "\\[lc]", sizeof(sym));
parent->left = mandoc_strdup(sym);
} else if (STRNEQ(start, sz, "floor", 5)) {
strlcpy(sym, "\\[lf]", sizeof(sym));
parent->left = mandoc_strdup(sym);
- } else
+ } else
parent->left = mandoc_strndup(start, sz);
}
break;
case (EQN_TOK_LCOL):
case (EQN_TOK_RCOL):
while (parent->args == parent->expectargs)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
- if (EQN_TOK_BRACE_OPEN != eqn_tok_parse(ep, NULL)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ parent = parent->parent;
parent = eqn_box_alloc(ep, parent);
parent->type = EQN_PILE;
- parent = eqn_box_alloc(ep, parent);
- parent->type = EQN_LIST;
+ parent->expectargs = 1;
break;
case (EQN_TOK_ABOVE):
- while (parent->type != EQN_PILE)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
- parent = eqn_box_alloc(ep, parent);
+ for (cur = parent; cur != NULL; cur = cur->parent)
+ if (cur->type == EQN_PILE)
+ break;
+ if (cur == NULL) {
+ mandoc_msg(MANDOCERR_IT_STRAY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]);
+ break;
+ }
+ parent = eqn_box_alloc(ep, cur);
parent->type = EQN_LIST;
break;
case (EQN_TOK_MATRIX):
while (parent->args == parent->expectargs)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
- if (EQN_TOK_BRACE_OPEN != eqn_tok_parse(ep, NULL)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ parent = parent->parent;
parent = eqn_box_alloc(ep, parent);
parent->type = EQN_MATRIX;
- parent = eqn_box_alloc(ep, parent);
- parent->type = EQN_LIST;
+ parent->expectargs = 1;
break;
case (EQN_TOK_EOF):
/*
- * End of file!
+ * End of file!
* TODO: make sure we're not in an open subexpression.
*/
return(0);
* in an expression, then rewind til we're not any more.
*/
while (parent->args == parent->expectargs)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- free(p);
- return(-1);
- }
+ 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),
+ (void)snprintf(sym, sizeof(sym),
"\\[%s]", eqnsyms[i].sym);
cur->text = mandoc_strdup(sym);
free(p);
/*
* Post-process list status.
*/
- while (parent->type == EQN_LISTONE &&
- parent->args == parent->expectargs)
- if (NULL == (parent = parent->parent)) {
- EQN_MSG(MANDOCERR_EQNSYNT, ep);
- return(-1);
- }
+ while (parent->type == EQN_LISTONE &&
+ parent->args == parent->expectargs)
+ parent = parent->parent;
break;
}
- goto again;
+ goto next_tok;
}
enum rofferr
-eqn_end(struct eqn_node **epp)
+eqn_end(struct eqn_node **epp)
{
struct eqn_node *ep;
free(p->defs[i].val);
}
- free(p->eqn.name);
free(p->data);
free(p->defs);
free(p);