version 1.71, 2017/06/26 20:09:04 |
version 1.82, 2018/12/14 05:18:02 |
|
|
/* $Id$ */ |
/* $Id$ */ |
/* |
/* |
* Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> |
* Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> |
* Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> |
* Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
|
|
#include <string.h> |
#include <string.h> |
#include <time.h> |
#include <time.h> |
|
|
#include "mandoc.h" |
|
#include "mandoc_aux.h" |
#include "mandoc_aux.h" |
|
#include "mandoc.h" |
|
#include "roff.h" |
|
#include "eqn.h" |
#include "libmandoc.h" |
#include "libmandoc.h" |
#include "libroff.h" |
#include "eqn_parse.h" |
|
|
#define EQN_NEST_MAX 128 /* maximum nesting of defines */ |
#define EQN_NEST_MAX 128 /* maximum nesting of defines */ |
#define STRNEQ(p1, sz1, p2, sz2) \ |
#define STRNEQ(p1, sz1, p2, sz2) \ |
Line 283 enum parse_mode { |
|
Line 285 enum parse_mode { |
|
MODE_TOK |
MODE_TOK |
}; |
}; |
|
|
|
struct eqn_def { |
|
char *key; |
|
size_t keysz; |
|
char *val; |
|
size_t valsz; |
|
}; |
|
|
static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *); |
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 *, |
static struct eqn_box *eqn_box_makebinary(struct eqn_node *, |
enum eqn_post, struct eqn_box *); |
struct eqn_box *); |
static void eqn_def(struct eqn_node *); |
static void eqn_def(struct eqn_node *); |
static struct eqn_def *eqn_def_find(struct eqn_node *); |
static struct eqn_def *eqn_def_find(struct eqn_node *); |
static void eqn_delim(struct eqn_node *); |
static void eqn_delim(struct eqn_node *); |
static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode); |
static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode); |
static enum rofferr eqn_parse(struct eqn_node *, struct eqn_box *); |
|
static void eqn_undef(struct eqn_node *); |
static void eqn_undef(struct eqn_node *); |
|
|
|
|
enum rofferr |
struct eqn_node * |
eqn_read(struct eqn_node **epp, int ln, |
eqn_alloc(struct mparse *parse) |
const char *p, int pos, int *offs) |
|
{ |
{ |
size_t sz; |
struct eqn_node *ep; |
struct eqn_node *ep; |
|
enum rofferr er; |
|
|
|
ep = *epp; |
ep = mandoc_calloc(1, sizeof(*ep)); |
|
ep->parse = parse; |
|
ep->gsize = EQN_DEFSIZE; |
|
return ep; |
|
} |
|
|
/* |
void |
* If we're the terminating mark, unset our equation status and |
eqn_reset(struct eqn_node *ep) |
* validate the full equation. |
{ |
*/ |
free(ep->data); |
|
ep->data = ep->start = ep->end = NULL; |
if (0 == strncmp(p, ".EN", 3)) { |
ep->sz = ep->toksz = 0; |
er = eqn_end(epp); |
|
p += 3; |
|
while (' ' == *p || '\t' == *p) |
|
p++; |
|
if ('\0' == *p) |
|
return er; |
|
mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse, |
|
ln, pos, "EN %s", p); |
|
return er; |
|
} |
|
|
|
/* |
|
* Build up the full string, replacing all newlines with regular |
|
* whitespace. |
|
*/ |
|
|
|
sz = strlen(p + pos) + 1; |
|
ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1); |
|
|
|
/* First invocation: nil terminate the string. */ |
|
|
|
if (0 == ep->sz) |
|
*ep->data = '\0'; |
|
|
|
ep->sz += sz; |
|
strlcat(ep->data, p + pos, ep->sz + 1); |
|
strlcat(ep->data, " ", ep->sz + 1); |
|
return ROFF_IGN; |
|
} |
} |
|
|
struct eqn_node * |
void |
eqn_alloc(int pos, int line, struct mparse *parse) |
eqn_read(struct eqn_node *ep, const char *p) |
{ |
{ |
struct eqn_node *p; |
char *cp; |
|
|
p = mandoc_calloc(1, sizeof(struct eqn_node)); |
if (ep->data == NULL) { |
|
ep->sz = strlen(p); |
p->parse = parse; |
ep->data = mandoc_strdup(p); |
p->eqn.ln = line; |
} else { |
p->eqn.pos = pos; |
ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p); |
p->gsize = EQN_DEFSIZE; |
free(ep->data); |
|
ep->data = cp; |
return p; |
} |
|
ep->sz += 1; |
} |
} |
|
|
/* |
/* |
Line 426 eqn_next(struct eqn_node *ep, enum parse_mode mode) |
|
Line 407 eqn_next(struct eqn_node *ep, enum parse_mode mode) |
|
ep->end = strchr(ep->start + 1, *ep->start); |
ep->end = strchr(ep->start + 1, *ep->start); |
ep->start++; /* Skip opening quote. */ |
ep->start++; /* Skip opening quote. */ |
if (ep->end == NULL) { |
if (ep->end == NULL) { |
mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse, |
mandoc_msg(MANDOCERR_ARG_QUOTE, |
ep->eqn.ln, ep->eqn.pos, NULL); |
ep->node->line, ep->node->pos, NULL); |
ep->end = strchr(ep->start, '\0'); |
ep->end = strchr(ep->start, '\0'); |
} |
} |
} else { |
} else { |
Line 447 eqn_next(struct eqn_node *ep, enum parse_mode mode) |
|
Line 428 eqn_next(struct eqn_node *ep, enum parse_mode mode) |
|
if ((def = eqn_def_find(ep)) == NULL) |
if ((def = eqn_def_find(ep)) == NULL) |
break; |
break; |
if (++lim > EQN_NEST_MAX) { |
if (++lim > EQN_NEST_MAX) { |
mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse, |
mandoc_msg(MANDOCERR_ROFFLOOP, |
ep->eqn.ln, ep->eqn.pos, NULL); |
ep->node->line, ep->node->pos, NULL); |
return EQN_TOK_EOF; |
return EQN_TOK_EOF; |
} |
} |
|
|
Line 492 eqn_next(struct eqn_node *ep, enum parse_mode mode) |
|
Line 473 eqn_next(struct eqn_node *ep, enum parse_mode mode) |
|
return EQN_TOK__MAX; |
return EQN_TOK__MAX; |
} |
} |
|
|
static void |
void |
eqn_box_free(struct eqn_box *bp) |
eqn_box_free(struct eqn_box *bp) |
{ |
{ |
|
if (bp == NULL) |
|
return; |
|
|
if (bp->first) |
if (bp->first) |
eqn_box_free(bp->first); |
eqn_box_free(bp->first); |
Line 509 eqn_box_free(struct eqn_box *bp) |
|
Line 492 eqn_box_free(struct eqn_box *bp) |
|
free(bp); |
free(bp); |
} |
} |
|
|
|
struct eqn_box * |
|
eqn_box_new(void) |
|
{ |
|
struct eqn_box *bp; |
|
|
|
bp = mandoc_calloc(1, sizeof(*bp)); |
|
bp->expectargs = UINT_MAX; |
|
return bp; |
|
} |
|
|
/* |
/* |
* Allocate a box as the last child of the parent node. |
* Allocate a box as the last child of the parent node. |
*/ |
*/ |
Line 517 eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par |
|
Line 510 eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par |
|
{ |
{ |
struct eqn_box *bp; |
struct eqn_box *bp; |
|
|
bp = mandoc_calloc(1, sizeof(struct eqn_box)); |
bp = eqn_box_new(); |
bp->parent = parent; |
bp->parent = parent; |
bp->parent->args++; |
bp->parent->args++; |
bp->expectargs = UINT_MAX; |
|
bp->font = bp->parent->font; |
bp->font = bp->parent->font; |
bp->size = ep->gsize; |
bp->size = ep->gsize; |
|
|
Line 541 eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par |
|
Line 533 eqn_box_alloc(struct eqn_node *ep, struct eqn_box *par |
|
* The new EQN_SUBEXPR will have a two-child limit. |
* The new EQN_SUBEXPR will have a two-child limit. |
*/ |
*/ |
static struct eqn_box * |
static struct eqn_box * |
eqn_box_makebinary(struct eqn_node *ep, |
eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent) |
enum eqn_post pos, struct eqn_box *parent) |
|
{ |
{ |
struct eqn_box *b, *newb; |
struct eqn_box *b, *newb; |
|
|
Line 554 eqn_box_makebinary(struct eqn_node *ep, |
|
Line 545 eqn_box_makebinary(struct eqn_node *ep, |
|
parent->last = b->prev; |
parent->last = b->prev; |
b->prev = NULL; |
b->prev = NULL; |
newb = eqn_box_alloc(ep, parent); |
newb = eqn_box_alloc(ep, parent); |
newb->pos = pos; |
|
newb->type = EQN_SUBEXPR; |
newb->type = EQN_SUBEXPR; |
newb->expectargs = 2; |
newb->expectargs = 2; |
newb->args = 1; |
newb->args = 1; |
|
|
eqn_delim(struct eqn_node *ep) |
eqn_delim(struct eqn_node *ep) |
{ |
{ |
if (ep->end[0] == '\0' || ep->end[1] == '\0') { |
if (ep->end[0] == '\0' || ep->end[1] == '\0') { |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, |
ep->eqn.ln, ep->eqn.pos, "delim"); |
ep->node->line, ep->node->pos, "delim"); |
if (ep->end[0] != '\0') |
if (ep->end[0] != '\0') |
ep->end++; |
ep->end++; |
} else if (strncmp(ep->end, "off", 3) == 0) { |
} else if (strncmp(ep->end, "off", 3) == 0) { |
Line 598 eqn_undef(struct eqn_node *ep) |
|
Line 588 eqn_undef(struct eqn_node *ep) |
|
struct eqn_def *def; |
struct eqn_def *def; |
|
|
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { |
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, |
ep->eqn.ln, ep->eqn.pos, "undef"); |
ep->node->line, ep->node->pos, "undef"); |
return; |
return; |
} |
} |
if ((def = eqn_def_find(ep)) == NULL) |
if ((def = eqn_def_find(ep)) == NULL) |
Line 617 eqn_def(struct eqn_node *ep) |
|
Line 607 eqn_def(struct eqn_node *ep) |
|
int i; |
int i; |
|
|
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { |
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, |
ep->eqn.ln, ep->eqn.pos, "define"); |
ep->node->line, ep->node->pos, "define"); |
return; |
return; |
} |
} |
|
|
Line 646 eqn_def(struct eqn_node *ep) |
|
Line 636 eqn_def(struct eqn_node *ep) |
|
} |
} |
|
|
if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) { |
if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) { |
mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, |
ep->eqn.ln, ep->eqn.pos, "define %s", def->key); |
ep->node->line, ep->node->pos, "define %s", def->key); |
free(def->key); |
free(def->key); |
free(def->val); |
free(def->val); |
def->key = def->val = NULL; |
def->key = def->val = NULL; |
Line 659 eqn_def(struct eqn_node *ep) |
|
Line 649 eqn_def(struct eqn_node *ep) |
|
def->valsz = ep->toksz; |
def->valsz = ep->toksz; |
} |
} |
|
|
/* |
void |
* Recursively parse an eqn(7) expression. |
eqn_parse(struct eqn_node *ep) |
*/ |
|
static enum rofferr |
|
eqn_parse(struct eqn_node *ep, struct eqn_box *parent) |
|
{ |
{ |
struct eqn_box *cur, *nbox, *split; |
struct eqn_box *cur, *nbox, *parent, *split; |
const char *cp, *cpn; |
const char *cp, *cpn; |
char *p; |
char *p; |
enum eqn_tok tok; |
enum eqn_tok tok; |
enum eqn_post pos; |
|
enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln; |
enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln; |
int size; |
int size; |
|
|
|
parent = ep->node->eqn; |
assert(parent != NULL); |
assert(parent != NULL); |
|
|
/* |
/* |
Line 681 eqn_parse(struct eqn_node *ep, struct eqn_box *parent) |
|
Line 668 eqn_parse(struct eqn_node *ep, struct eqn_box *parent) |
|
*/ |
*/ |
|
|
if (ep->data == NULL) |
if (ep->data == NULL) |
return ROFF_IGN; |
return; |
|
|
ep->start = ep->end = ep->data; |
ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); |
|
|
next_tok: |
next_tok: |
tok = eqn_next(ep, MODE_TOK); |
tok = eqn_next(ep, MODE_TOK); |
|
|
case EQN_TOK_TDEFINE: |
case EQN_TOK_TDEFINE: |
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF || |
if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF || |
eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) |
eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, |
ep->eqn.ln, ep->eqn.pos, "tdefine"); |
ep->node->line, ep->node->pos, "tdefine"); |
break; |
break; |
case EQN_TOK_DELIM: |
case EQN_TOK_DELIM: |
eqn_delim(ep); |
eqn_delim(ep); |
break; |
break; |
case EQN_TOK_GFONT: |
case EQN_TOK_GFONT: |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
break; |
break; |
case EQN_TOK_MARK: |
case EQN_TOK_MARK: |
case EQN_TOK_LINEUP: |
case EQN_TOK_LINEUP: |
|
|
case EQN_TOK_DOT: |
case EQN_TOK_DOT: |
case EQN_TOK_DOTDOT: |
case EQN_TOK_DOTDOT: |
if (parent->last == NULL) { |
if (parent->last == NULL) { |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
cur = eqn_box_alloc(ep, parent); |
cur = eqn_box_alloc(ep, parent); |
cur->type = EQN_TEXT; |
cur->type = EQN_TEXT; |
cur->text = mandoc_strdup(""); |
cur->text = mandoc_strdup(""); |
} |
} |
parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent); |
parent = eqn_box_makebinary(ep, parent); |
parent->type = EQN_LISTONE; |
parent->type = EQN_LIST; |
parent->expectargs = 1; |
parent->expectargs = 1; |
parent->font = EQNFONT_ROMAN; |
parent->font = EQNFONT_ROMAN; |
switch (tok) { |
switch (tok) { |
|
|
parent->bottom = mandoc_strdup("\\[ul]"); |
parent->bottom = mandoc_strdup("\\[ul]"); |
break; |
break; |
case EQN_TOK_BAR: |
case EQN_TOK_BAR: |
parent->top = mandoc_strdup("\\[rl]"); |
parent->top = mandoc_strdup("\\[rn]"); |
break; |
break; |
case EQN_TOK_DOT: |
case EQN_TOK_DOT: |
parent->top = mandoc_strdup("\\[a.]"); |
parent->top = mandoc_strdup("\\[a.]"); |
|
|
case EQN_TOK_DOWN: |
case EQN_TOK_DOWN: |
case EQN_TOK_UP: |
case EQN_TOK_UP: |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
break; |
break; |
case EQN_TOK_FAT: |
case EQN_TOK_FAT: |
case EQN_TOK_ROMAN: |
case EQN_TOK_ROMAN: |
|
|
* exactly one of those. |
* exactly one of those. |
*/ |
*/ |
parent = eqn_box_alloc(ep, parent); |
parent = eqn_box_alloc(ep, parent); |
parent->type = EQN_LISTONE; |
parent->type = EQN_LIST; |
parent->expectargs = 1; |
parent->expectargs = 1; |
switch (tok) { |
switch (tok) { |
case EQN_TOK_FAT: |
case EQN_TOK_FAT: |
|
|
case EQN_TOK_GSIZE: |
case EQN_TOK_GSIZE: |
/* Accept two values: integral size and a single. */ |
/* Accept two values: integral size and a single. */ |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
break; |
break; |
} |
} |
size = mandoc_strntoi(ep->start, ep->toksz, 10); |
size = mandoc_strntoi(ep->start, ep->toksz, 10); |
if (-1 == size) { |
if (-1 == size) { |
mandoc_msg(MANDOCERR_IT_NONUM, ep->parse, |
mandoc_msg(MANDOCERR_IT_NONUM, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
break; |
break; |
} |
} |
if (EQN_TOK_GSIZE == tok) { |
if (EQN_TOK_GSIZE == tok) { |
ep->gsize = size; |
ep->gsize = size; |
break; |
break; |
} |
} |
|
while (parent->args == parent->expectargs) |
|
parent = parent->parent; |
parent = eqn_box_alloc(ep, parent); |
parent = eqn_box_alloc(ep, parent); |
parent->type = EQN_LISTONE; |
parent->type = EQN_LIST; |
parent->expectargs = 1; |
parent->expectargs = 1; |
parent->size = size; |
parent->size = size; |
break; |
break; |
|
|
* and keep on reading. |
* and keep on reading. |
*/ |
*/ |
if (parent->last == NULL) { |
if (parent->last == NULL) { |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
cur = eqn_box_alloc(ep, parent); |
cur = eqn_box_alloc(ep, parent); |
cur->type = EQN_TEXT; |
cur->type = EQN_TEXT; |
cur->text = mandoc_strdup(""); |
cur->text = mandoc_strdup(""); |
} |
} |
/* Handle the "subsup" and "fromto" positions. */ |
while (parent->expectargs == 1 && parent->args == 1) |
if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) { |
parent = parent->parent; |
|
if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) { |
|
for (cur = parent; cur != NULL; cur = cur->parent) |
|
if (cur->pos == EQNPOS_SUB || |
|
cur->pos == EQNPOS_SUP || |
|
cur->pos == EQNPOS_SUBSUP || |
|
cur->pos == EQNPOS_SQRT || |
|
cur->pos == EQNPOS_OVER) |
|
break; |
|
if (cur != NULL) |
|
parent = cur->parent; |
|
} |
|
if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) { |
parent->expectargs = 3; |
parent->expectargs = 3; |
parent->pos = EQNPOS_SUBSUP; |
parent->pos = EQNPOS_SUBSUP; |
break; |
break; |
} |
} |
if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) { |
if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) { |
parent->expectargs = 3; |
parent->expectargs = 3; |
parent->pos = EQNPOS_FROMTO; |
parent->pos = EQNPOS_FROMTO; |
break; |
break; |
} |
} |
|
parent = eqn_box_makebinary(ep, parent); |
switch (tok) { |
switch (tok) { |
case EQN_TOK_FROM: |
case EQN_TOK_FROM: |
pos = EQNPOS_FROM; |
parent->pos = EQNPOS_FROM; |
break; |
break; |
case EQN_TOK_TO: |
case EQN_TOK_TO: |
pos = EQNPOS_TO; |
parent->pos = EQNPOS_TO; |
break; |
break; |
case EQN_TOK_SUP: |
case EQN_TOK_SUP: |
pos = EQNPOS_SUP; |
parent->pos = EQNPOS_SUP; |
break; |
break; |
case EQN_TOK_SUB: |
case EQN_TOK_SUB: |
pos = EQNPOS_SUB; |
parent->pos = EQNPOS_SUB; |
break; |
break; |
default: |
default: |
abort(); |
abort(); |
} |
} |
parent = eqn_box_makebinary(ep, pos, parent); |
|
break; |
break; |
case EQN_TOK_SQRT: |
case EQN_TOK_SQRT: |
while (parent->args == parent->expectargs) |
while (parent->args == parent->expectargs) |
|
|
* rebalance and continue reading. |
* rebalance and continue reading. |
*/ |
*/ |
if (parent->last == NULL) { |
if (parent->last == NULL) { |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, |
mandoc_msg(MANDOCERR_EQN_NOBOX, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
cur = eqn_box_alloc(ep, parent); |
cur = eqn_box_alloc(ep, parent); |
cur->type = EQN_TEXT; |
cur->type = EQN_TEXT; |
cur->text = mandoc_strdup(""); |
cur->text = mandoc_strdup(""); |
} |
} |
|
while (parent->args == parent->expectargs) |
|
parent = parent->parent; |
while (EQN_SUBEXPR == parent->type) |
while (EQN_SUBEXPR == parent->type) |
parent = parent->parent; |
parent = parent->parent; |
parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent); |
parent = eqn_box_makebinary(ep, parent); |
|
parent->pos = EQNPOS_OVER; |
break; |
break; |
case EQN_TOK_RIGHT: |
case EQN_TOK_RIGHT: |
case EQN_TOK_BRACE_CLOSE: |
case EQN_TOK_BRACE_CLOSE: |
|
|
*/ |
*/ |
for (cur = parent; cur != NULL; cur = cur->parent) |
for (cur = parent; cur != NULL; cur = cur->parent) |
if (cur->type == EQN_LIST && |
if (cur->type == EQN_LIST && |
|
cur->expectargs > 1 && |
(tok == EQN_TOK_BRACE_CLOSE || |
(tok == EQN_TOK_BRACE_CLOSE || |
cur->left != NULL)) |
cur->left != NULL)) |
break; |
break; |
if (cur == NULL) { |
if (cur == NULL) { |
mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse, |
mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
break; |
break; |
} |
} |
parent = cur; |
parent = cur; |
if (EQN_TOK_RIGHT == tok) { |
if (EQN_TOK_RIGHT == tok) { |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
mandoc_msg(MANDOCERR_REQ_EMPTY, |
mandoc_msg(MANDOCERR_REQ_EMPTY, |
ep->parse, ep->eqn.ln, |
ep->node->line, ep->node->pos, |
ep->eqn.pos, eqn_toks[tok]); |
"%s", eqn_toks[tok]); |
break; |
break; |
} |
} |
/* Handling depends on right/left. */ |
/* Handling depends on right/left. */ |
|
|
parent->type == EQN_MATRIX)) |
parent->type == EQN_MATRIX)) |
parent = parent->parent; |
parent = parent->parent; |
/* Close out any "singleton" lists. */ |
/* Close out any "singleton" lists. */ |
while (parent->type == EQN_LISTONE && |
while (parent->type == EQN_LIST && |
parent->args == parent->expectargs) |
parent->expectargs == 1 && |
|
parent->args == 1) |
parent = parent->parent; |
parent = parent->parent; |
break; |
break; |
case EQN_TOK_BRACE_OPEN: |
case EQN_TOK_BRACE_OPEN: |
|
|
parent = parent->parent; |
parent = parent->parent; |
if (EQN_TOK_LEFT == tok && |
if (EQN_TOK_LEFT == tok && |
eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, |
mandoc_msg(MANDOCERR_REQ_EMPTY, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
break; |
break; |
} |
} |
parent = eqn_box_alloc(ep, parent); |
parent = eqn_box_alloc(ep, parent); |
|
|
if (cur->type == EQN_PILE) |
if (cur->type == EQN_PILE) |
break; |
break; |
if (cur == NULL) { |
if (cur == NULL) { |
mandoc_msg(MANDOCERR_IT_STRAY, ep->parse, |
mandoc_msg(MANDOCERR_IT_STRAY, ep->node->line, |
ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); |
ep->node->pos, "%s", eqn_toks[tok]); |
break; |
break; |
} |
} |
parent = eqn_box_alloc(ep, cur); |
parent = eqn_box_alloc(ep, cur); |
|
|
parent->expectargs = 1; |
parent->expectargs = 1; |
break; |
break; |
case EQN_TOK_EOF: |
case EQN_TOK_EOF: |
/* |
return; |
* End of file! |
|
* TODO: make sure we're not in an open subexpression. |
|
*/ |
|
return ROFF_EQN; |
|
case EQN_TOK__MAX: |
case EQN_TOK__MAX: |
case EQN_TOK_FUNC: |
case EQN_TOK_FUNC: |
case EQN_TOK_QUOTED: |
case EQN_TOK_QUOTED: |
|
|
/* No boundary after last character. */ |
/* No boundary after last character. */ |
if (*cpn == '\0') |
if (*cpn == '\0') |
break; |
break; |
if (ccln == ccl) |
if (ccln == ccl && *cp != ',' && *cpn != ',') |
continue; |
continue; |
/* Boundary found, split the text. */ |
/* Boundary found, split the text. */ |
if (parent->args == parent->expectargs) { |
if (parent->args == parent->expectargs) { |
|
|
parent = split->parent; |
parent = split->parent; |
break; |
break; |
} |
} |
/* |
|
* Post-process list status. |
|
*/ |
|
while (parent->type == EQN_LISTONE && |
|
parent->args == parent->expectargs) |
|
parent = parent->parent; |
|
break; |
break; |
default: |
default: |
abort(); |
abort(); |
|
|
goto next_tok; |
goto next_tok; |
} |
} |
|
|
enum rofferr |
|
eqn_end(struct eqn_node **epp) |
|
{ |
|
struct eqn_node *ep; |
|
|
|
ep = *epp; |
|
*epp = NULL; |
|
|
|
ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box)); |
|
ep->eqn.root->expectargs = UINT_MAX; |
|
return eqn_parse(ep, ep->eqn.root); |
|
} |
|
|
|
void |
void |
eqn_free(struct eqn_node *p) |
eqn_free(struct eqn_node *p) |
{ |
{ |
int i; |
int i; |
|
|
eqn_box_free(p->eqn.root); |
if (p == NULL) |
|
return; |
|
|
for (i = 0; i < (int)p->defsz; i++) { |
for (i = 0; i < (int)p->defsz; i++) { |
free(p->defs[i].key); |
free(p->defs[i].key); |