-/* $Id: eqn.c,v 1.53 2014/10/12 20:08:58 schwarze Exp $ */
+/* $Id: eqn.c,v 1.58 2015/03/04 12:19:49 schwarze Exp $ */
/*
* Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2014, 2015 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
#include "libmandoc.h"
#include "libroff.h"
-#define EQN_MSG(t, x) \
- mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL)
#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,
EQNSYM_equiv,
EQNSYM_lessequal,
EQNSYM_moreequal,
+ EQNSYM_minus,
EQNSYM__MAX
};
{ "cdot", "pc" }, /* EQNSYM_cdot */
{ "nothing", "&" }, /* EQNSYM_nothing */
{ "approx", "~~" }, /* EQNSYM_approx */
- { "prime", "aq" }, /* EQNSYM_prime */
+ { "prime", "fm" }, /* EQNSYM_prime */
{ "half", "12" }, /* EQNSYM_half */
{ "partial", "pd" }, /* EQNSYM_partial */
{ "inf", "if" }, /* EQNSYM_inf */
{ "==", "==" }, /* EQNSYM_equiv */
{ "<=", "<=" }, /* EQNSYM_lessequal */
{ ">=", ">=" }, /* EQNSYM_moreequal */
+ { "-", "mi" }, /* EQNSYM_minus */
};
+static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *);
+static void eqn_box_free(struct eqn_box *);
+static struct eqn_box *eqn_box_makebinary(struct eqn_node *,
+ enum eqn_post, struct eqn_box *);
+static void eqn_def(struct eqn_node *);
+static struct eqn_def *eqn_def_find(struct eqn_node *, const char *, size_t);
+static void eqn_delim(struct eqn_node *);
+static const char *eqn_next(struct eqn_node *, char, size_t *, int);
+static const char *eqn_nextrawtok(struct eqn_node *, size_t *);
+static const char *eqn_nexttok(struct eqn_node *, size_t *);
+static enum rofferr eqn_parse(struct eqn_node *, struct eqn_box *);
+static enum eqn_tok eqn_tok_parse(struct eqn_node *, char **);
+static void eqn_undef(struct eqn_node *);
+
+
enum rofferr
eqn_read(struct eqn_node **epp, int ln,
const char *p, int pos, int *offs)
}
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;
/* Prevent self-definitions. */
if (lim >= EQN_NEST_MAX) {
- EQN_MSG(MANDOCERR_ROFFLOOP, ep);
+ mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, NULL);
return(NULL);
}
ep->cur++;
} else {
if (q)
- EQN_MSG(MANDOCERR_ARG_QUOTE, ep);
+ mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, NULL);
next = strchr(start, '\0');
*sz = (size_t)(next - start);
ep->cur += *sz;
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.
*/
-static int
+static void
eqn_undef(struct eqn_node *ep)
{
const char *start;
struct eqn_def *def;
size_t sz;
- if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
- EQN_MSG(MANDOCERR_EQNEOF, ep);
- return(0);
- } else if (NULL != (def = eqn_def_find(ep, start, sz)))
- def->keysz = 0;
-
- return(1);
+ if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, "undef");
+ return;
+ }
+ if ((def = eqn_def_find(ep, start, sz)) == NULL)
+ return;
+ free(def->key);
+ free(def->val);
+ def->key = def->val = NULL;
+ def->keysz = def->valsz = 0;
}
-static int
+static void
eqn_def(struct eqn_node *ep)
{
const char *start;
struct eqn_def *def;
int i;
- if (NULL == (start = eqn_nextrawtok(ep, &sz))) {
- EQN_MSG(MANDOCERR_EQNEOF, ep);
- return(0);
+ if ((start = eqn_nextrawtok(ep, &sz)) == NULL) {
+ mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, "define");
+ return;
}
/*
ep->defs[i].key = ep->defs[i].val = NULL;
}
- ep->defs[i].keysz = sz;
- ep->defs[i].key = mandoc_realloc(
- ep->defs[i].key, sz + 1);
-
- memcpy(ep->defs[i].key, start, sz);
- ep->defs[i].key[(int)sz] = '\0';
- def = &ep->defs[i];
+ def = ep->defs + i;
+ free(def->key);
+ def->key = mandoc_strndup(start, sz);
+ def->keysz = sz;
}
start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0);
-
- if (NULL == start) {
- EQN_MSG(MANDOCERR_EQNEOF, ep);
- return(-1);
+ if (start == NULL) {
+ mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse,
+ ep->eqn.ln, ep->eqn.pos, "define %s", def->key);
+ free(def->key);
+ free(def->val);
+ def->key = def->val = NULL;
+ def->keysz = def->valsz = 0;
+ return;
}
-
+ free(def->val);
+ def->val = mandoc_strndup(start, sz);
def->valsz = sz;
- def->val = mandoc_realloc(def->val, sz + 1);
- memcpy(def->val, start, sz);
- def->val[(int)sz] = '\0';
- return(1);
}
/*
* Recursively parse an eqn(7) expression.
*/
-static int
+static enum rofferr
eqn_parse(struct eqn_node *ep, struct eqn_box *parent)
{
+ char sym[64];
+ struct eqn_box *cur;
+ const char *start;
char *p;
+ size_t i, sz;
enum eqn_tok tok, subtok;
enum eqn_post pos;
- struct eqn_box *cur;
- int rc, size;
- size_t i, sz;
- char sym[64];
- const char *start;
+ int size;
- assert(NULL != parent);
+ assert(parent != NULL);
+
+ /*
+ * Empty equation.
+ * Do not add it to the high-level syntax tree.
+ */
+
+ if (ep->data == NULL)
+ return(ROFF_IGN);
next_tok:
tok = eqn_tok_parse(ep, &p);
this_tok:
switch (tok) {
case (EQN_TOK_UNDEF):
- if ((rc = eqn_undef(ep)) <= 0)
- return(rc);
+ eqn_undef(ep);
break;
case (EQN_TOK_NDEFINE):
case (EQN_TOK_DEFINE):
- if ((rc = eqn_def(ep)) <= 0)
- return(rc);
+ eqn_def(ep);
break;
case (EQN_TOK_TDEFINE):
- if (NULL == eqn_nextrawtok(ep, NULL))
- EQN_MSG(MANDOCERR_EQNEOF, ep);
- else if (NULL == eqn_next(ep,
- ep->data[(int)ep->cur], NULL, 0))
- EQN_MSG(MANDOCERR_EQNEOF, ep);
+ 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):
+ eqn_delim(ep);
+ break;
case (EQN_TOK_GFONT):
if (eqn_nextrawtok(ep, NULL) == NULL)
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse,
* End of file!
* TODO: make sure we're not in an open subexpression.
*/
- return(0);
+ return(ROFF_EQN);
default:
assert(tok == EQN_TOK__MAX);
assert(NULL != p);
ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box));
ep->eqn.root->expectargs = UINT_MAX;
- return(0 == eqn_parse(ep, ep->eqn.root) ? ROFF_EQN : ROFF_IGN);
+ return(eqn_parse(ep, ep->eqn.root));
}
void
free(p->defs[i].val);
}
- free(p->eqn.name);
free(p->data);
free(p->defs);
free(p);