teapot-spreadsheet/src/common/token.c

513 lines
16 KiB
C

#include <assert.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "default.h"
#include "main.h"
#include "style.h"
#include "token.h"
#include "utf8.h"
const char *Type_Name[] =
{ [EMPTY] = "EMPTY", [STRING] = "STRING", [FLOAT] = "FLOAT", [INT] = "INT",
[OPERATOR] = "OPERATOR", [LIDENT] = "LABEL", [FIDENT] = "FUNCTION",
[LOCATION] = "LOCATION", [FUNCALL] = "FUNCTION-CALL", [EEK] = "ERROR",
[BOOL] = "BOOL", [STYLE] = "STYLE"
};
const char *Op_Name[] =
{ [PLUS] = "+", [MINUS] = "-", [MUL] = "*", [DIV] = "/",
[OP] = "(", [CP] = ")", [COMMA] = ",",
[LT] = "<", [LE] = "<=", [GE] = ">=", [GT] = ">",
[ISEQUAL] = "==", [ABOUTEQ] = "~=", [NE] = "!=",
[POW] = "^", [MOD] = "%", [LAND] = "and", [LOR] = "or"
};
/* loc_in_box -- returns true if test is in the box determined by b and c */
bool loc_in_box(const Location test,
const Location b, const Location c)
{
for (Dimensions dim = X; dim < HYPER; ++dim)
{
if (test[dim] < b[dim] && test[dim] < c[dim]) return false;
if (test[dim] > b[dim] && test[dim] > c[dim]) return false;
}
return true;
}
/* cleartoken - Initialize all of the memory of a token */ /*{{{*/
void cleartoken(Token* tok)
{
tok->type = EMPTY;
clearstyle(&(tok->u.style));
tok->u.flt = 0.0;
tok->u.location[0] = 0;
tok->u.location[1] = 0;
tok->u.location[2] = 0;
}
/* tok_matches - return true if l and r appear to be the same token */ /*{{{*/
bool tok_matches(const Token* l, const Token *r)
{
if (l->type != r->type) return false;
switch (l->type) {
case EMPTY: return true;
case STRING: return l->u.string == r->u.string;
case FLOAT: return l->u.flt == r->u.flt;
case INT: return l->u.integer == r->u.integer;
case OPERATOR: return l->u.op == r->u.op;
case LIDENT: return l->u.lident == r->u.lident;
case FIDENT: return l->u.fident == r->u.fident;
case LOCATION:
return l->u.location[X] == r->u.location[X]
&& l->u.location[Y] == r->u.location[Y]
&& l->u.location[Z] == r->u.location[Z];
case EEK: return l->u.err == r->u.err;
case FUNCALL:
return l->u.funcall.fident == r->u.funcall.fident
&& l->u.funcall.argc == r->u.funcall.argc
&& l->u.funcall.argv == r->u.funcall.argv;
case BOOL:
return l->u.bl == r->u.bl;
case STYLE:
return style_equal(l->u.style, r->u.style);
}
assert(0);
return false;
}
/* duperror - Sets tok to an error and strdups the message into place */
Token duperror(Token* tok, const char* erro)
{
tok->type = EEK;
tok->u.err = strdup(erro);
return *tok;
}
/* print_fident -- print a field identifier to a dest and return the number
of characters written
*/ /*{{{*/
static size_t print_fident(char* dest, size_t space, FunctionIdentifier id)
{
size_t identlen = strlen(tfunc[id].name);
if ((identlen+1) < space) strcpy(dest, tfunc[id].name);
else {
(void)strncpy(dest, tfunc[id].name, space);
dest[space-1] = '\0';
}
return identlen;
}
/*}}}*/
/* print_string -- print a string value to a dest and return the number of
characters written
*/ /*{{{*/
static size_t print_string(char* dest, size_t space,
const char *str, StringFormat sf)
{
size_t cur = 0;
if (sf == QUOTE_STRING && cur < space) dest[cur++] = '"';
for ( ; cur < space && *str != '\0'; ++str)
{
if (sf == QUOTE_STRING && (*str == '"' || *str=='\\')) dest[cur++] = '\\';
if (cur < space) dest[cur++] = *str;
}
if (sf == QUOTE_STRING && cur < space) dest[cur++] = '"';
return cur;
}
/*}}}*/
static size_t print_chars(char* d, size_t l, const char *s)
{
return print_string(d, l, s, DIRECT_STRING);
}
/* ptokatprec -- like printtok but with an ambient precedence */
static size_t ptokatprec(char *dest, size_t size, size_t field_width,
StringFormat sf, FloatFormat ff, PrecisionLevel digits,
ErrorFormat ef, const Token *tok,
FunctionPrecedence atprec)
{
if (debug_level > 2) {
printf("..Entering printtok; bufsize %zu, field_width %zu, qs %d, ff %s,"
"prec %d, verr %d\n",
size, field_width, sf, FloatFormat_Name[ff], digits, ef);
}
if (size > 0 ) *dest = '\0';
if (tok == NULLTOKEN || tok->type == EMPTY) return 0;
size_t cur = 0;
switch (tok->type)
{
case STRING: cur += print_string(dest, size, tok->u.string, sf); break;
case INT: cur += (size_t)snprintf(dest, size, INT_T_FMT, tok->u.integer); break;
case FLOAT: /*{{{*/
{
char buf[1100], buf2[1100];
if (digits == 0) digits = def_precision;
else if (digits < 0) digits += FLT_T_DIG + 1;
assert(digits > 0);
if ((size_t)digits > sizeof(buf) - FLT_T_DIG - 1)
digits = (PrecisionLevel)(sizeof(buf) - FLT_T_DIG - 1);
int len = 0, len2;
char *use = NULL;
switch (ff) {
case FLT_DECIMAL:
len = sprintf(buf, FLT_T_STD_FMT, digits, tok->u.flt);
use = buf;
break;
case FLT_SCIENTIFIC:
len = sprintf(buf, FLT_T_SCI_FMT, digits, tok->u.flt);
use = buf;
break;
case FLT_COMPACT:
len = sprintf(buf, FLT_T_CPT_FMT, digits, tok->u.flt);
/* Unfortunately, %g is so clever that sometimes it omits the
decimal; it also has a penchant for not switching to scientific
notation until the magnitude gets very large. So we try to
counteract these bad tendencies here. If there is no decimal,
we take the shorter of %.1f and %e with the same number of digits.
*/
use = buf;
if (strchr(buf, '.') == NULL) {
len = sprintf(buf, FLT_T_STD_FMT, 1, tok->u.flt);
len2 = sprintf(buf2, FLT_T_SCI_FMT, digits, tok->u.flt);
/* remove trailing 0s from sci format to be fair */
long epos = strchr(buf2, 'e') - buf2;
long tpos = epos;
while (tpos > 1 && buf2[tpos-1] == '0' && buf2[tpos-2] != '.') --tpos;
if (tpos < epos) {
memmove(buf2+tpos, buf2+epos, len2-epos+1);
len2 -= epos - tpos;
}
if (len2 < len) { use = buf2; len = len2; }
}
break;
case FLT_HEXACT:
len = sprintf(buf, FLT_T_HEX_FMT, tok->u.flt);
use = buf;
break;
default: assert(0);
}
assert(len < sizeof(buf));
char *p = use + len;
while (*--p == ' ') { *p = '\0'; --len; }
(void)strncpy(dest, use, size);
cur += (size_t)len;
break;
}
/*}}}*/
case OPERATOR: /*{{{*/
{
static const char *ops[]={ "+", "-", "*", "/", "(", ")", ",", "<", "<=", ">=", ">", "==", "~=", "!=", "^", "%" };
if (size >1)
{
dest[cur++]=*ops[tok->u.op];
if (*(ops[tok->u.op]+1) && size > cur) dest[cur++]=*(ops[tok->u.op]+1);
}
break;
}
/*}}}*/
case LIDENT:
cur += print_chars(dest, size, tok->u.lident); break;
case FIDENT: cur += print_fident(dest, size, tok->u.fident); break;
case LOCATION: /*{{{*/
{
char buf[128];
sprintf(buf, "&(%d,%d,%d)",
tok->u.location[X], tok->u.location[Y], tok->u.location[Z]);
cur += print_chars(dest, size, buf);
break;
}
/*}}}*/
case FUNCALL: /*{{{*/
{
FunctionIdentifier fid = tok->u.funcall.fident;
if (tok->u.funcall.argc <= 0)
{
cur += print_fident(dest+cur, size-cur-1, fid);
if (tok->u.funcall.argc == 0)
{
if (cur < size - 2) { dest[cur++] = '('; dest[cur++] = ')'; }
else cur += 2;
}
break;
}
FunctionPrecedence fp = tfunc[fid].precedence;
switch (fp)
{
case PREFIX_FUNC: {
cur += print_fident(dest+cur, size-cur-1, fid);
if (cur < size) dest[cur++] = '(';
for (int ai = 0; ai < tok->u.funcall.argc && cur < size-1; ++ai)
{
if (ai > 0 && cur < size) dest[cur++] = ',';
/* The commas eliminate the need for precedence worries in arguments*/
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
digits, ef, tok->u.funcall.argv + ai,
NO_PRECEDENCE);
}
if (cur < size) dest[cur++] = ')';
break;
}
case PREFIX_NEG: {
assert(tok->u.funcall.argc == 1);
assert(tfunc[fid].display_symbol != NULL);
if (fp < atprec && cur < size) dest[cur++] = '(';
cur += print_chars(dest+cur, size-cur-1, tfunc[fid].display_symbol);
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
digits, ef, tok->u.funcall.argv, fp);
if (fp < atprec && cur < size) dest[cur++] = ')';
break;
}
default: /* infix argument */
assert(tok->u.funcall.argc > 1);
if (fp < atprec && cur < size) dest[cur++] = '(';
/* Check for the special relation sequence notation */
bool specialrel = true;
if (fid != FUNC_AND || tok->u.funcall.argc < 2) specialrel = false;
else {
for (int ai = 0; ai < tok->u.funcall.argc; ++ai) {
if (tok->u.funcall.argv[ai].type != FUNCALL
|| tok->u.funcall.argv[ai].u.funcall.argc != 2
|| !IS_RELATION_FUNC(tok->u.funcall.argv[ai].u.funcall.fident))
{
specialrel = false;
break;
}
if (ai > 0
&& !tok_matches(tok->u.funcall.argv[ai].u.funcall.argv,
tok->u.funcall.argv[ai-1].u.funcall.argv+1))
{
specialrel = false;
break;
}
}
}
if (specialrel) {
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
digits, ef, tok->u.funcall.argv, INFIX_BOOL);
for (int ai = 1; ai < tok->u.funcall.argc && cur < size-1; ++ ai) {
dest[cur++] = ' ';
const char *use =
tfunc[tok->u.funcall.argv[ai].u.funcall.fident].name;
cur += print_chars(dest+cur, size-cur-1, use);
if (cur < size) dest[cur++] = ' ';
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
digits, ef,
tok->u.funcall.argv[ai].u.funcall.argv + 1,
INFIX_REL);
}
} else {
for (int ai = 0; ai < tok->u.funcall.argc && cur < size-1; ++ai)
{
bool parenarg = false;
if (ai > 0) {
if (fp < INFIX_MUL && cur < size) dest[cur++] = ' ';
const char *use = tfunc[fid].display_symbol;
if (use == NULL) use = tfunc[fid].name;
cur += print_chars(dest+cur, size-cur-1, use);
if (fp < INFIX_MUL && cur < size) dest[cur++] = ' ';
} else if (fp > PREFIX_NEG) {
char powbuf[4096];
size_t arglen = ptokatprec(powbuf, sizeof(powbuf), 0, sf, ff,
digits, ef, tok->u.funcall.argv, fp);
if (arglen >= sizeof(powbuf)-1) assert(0);
if (powbuf[0] == '-') {
parenarg = true;
if (cur < size) dest[cur++] = '(';
}
}
cur += ptokatprec(dest+cur, size-cur-1, field_width-cur, sf, ff,
digits, ef, tok->u.funcall.argv + ai, fp);
if (parenarg && cur < size) dest[cur++] = ')';
}
}
if (fp < atprec && cur < size) dest[cur++] = ')';
break;
}
break;
}
case BOOL:
cur += print_chars(dest, size, (tok->u.bl) ? "true" : "false"); break;
case STYLE: /*{{{*/
{
const char *emptyrep = "background(0)";
size_t nfields = 0;
if (tok->u.style.precision != NO_PRECISION) ++nfields;
for (ColorAspect ca = FOREGROUND; ca < NUM_COLOR_ASPECTS; ++ca)
if (tok->u.style.aspect[ca] != NO_COLOR_SET) ++nfields;
if (tok->u.style.adjust != AUTOADJUST) ++nfields;
if (tok->u.style.fform != FLT_NO_FORMAT) ++nfields;
if (tok->u.style.shadowed_set) ++nfields;
if (tok->u.style.transparent_set) ++nfields;
if (tok->u.style.bold_set) ++nfields;
if (tok->u.style.underline_set) ++nfields;
if (nfields == 0) cur += print_chars(dest, size, emptyrep);
else {
if (nfields > 1) cur += print_chars(dest, size, "+(");
size_t shownfields = 0;
if (tok->u.style.precision != NO_PRECISION) {
cur += print_chars(dest+cur, size-cur-1, "precision(");
cur += (size_t)snprintf(dest+cur, size-cur-1, PRECISION_FORMAT,
tok->u.style.precision);
if (cur < size) dest[cur++] = ')';
++shownfields;
}
for (ColorAspect ca = FOREGROUND; ca < NUM_COLOR_ASPECTS; ++ca)
if (tok->u.style.aspect[ca] != NO_COLOR_SET) {
if (shownfields > 0 && cur < size) dest[cur++] = ',';
cur += print_chars(dest+cur, size-cur-1, ColorAspect_Name[ca]);
if (cur < size) dest[cur++] = '(';
cur += (size_t)snprintf(dest+cur, size-cur-1, COLOR_FORMAT,
tok->u.style.aspect[ca]);
if (cur < size) dest[cur++] = ')';
++shownfields;
}
if (tok->u.style.adjust != AUTOADJUST) {
if (shownfields > 0 && cur < size) dest[cur++] = ',';
cur += print_chars(dest+cur, size-cur-1, "justify(");
cur += print_chars(dest+cur, size-cur-1,
Adjust_Name[tok->u.style.adjust]);
if (cur < size) dest[cur++] = ')';
++shownfields;
}
if (tok->u.style.fform != FLT_NO_FORMAT) {
if (shownfields > 0 && cur < size) dest[cur++] = ',';
cur += print_chars(dest+cur, size-cur-1, "floatfmt(");
cur += print_chars(dest+cur, size-cur-1,
FloatFormat_Name[tok->u.style.fform]);
if (cur < size) dest[cur++] = ')';
++shownfields;
}
if (tok->u.style.shadowed_set) {
if (shownfields > 0 && cur < size) dest[cur++] = ',';
cur += print_chars(dest+cur, size-cur-1, "shadowed(");
cur += print_chars(dest+cur, size-cur-1,
(tok->u.style.shadowed) ? "" : "false");
if (cur < size) dest[cur++] = ')';
++shownfields;
}
if (tok->u.style.transparent_set) {
if (shownfields > 0 && cur < size) dest[cur++] = ',';
cur += print_chars(dest+cur, size-cur-1, "transparent(");
cur += print_chars(dest+cur, size-cur-1,
(tok->u.style.transparent) ? "" : "false");
if (cur < size) dest[cur++] = ')';
++shownfields;
}
if (tok->u.style.bold_set) {
if (shownfields > 0 && cur < size) dest[cur++] = ',';
cur += print_chars(dest+cur, size-cur-1, "bold(");
cur += print_chars(dest+cur, size-cur-1,
(tok->u.style.bold) ? "" : "false");
if (cur < size) dest[cur++] = ')';
++shownfields;
}
if (tok->u.style.underline_set) {
if (shownfields > 0 && cur < size) dest[cur++] = ',';
cur += print_chars(dest+cur, size-cur-1, "underline(");
cur += print_chars(dest+cur, size-cur-1,
(tok->u.style.underline) ? "" : "false");
if (cur < size) dest[cur++] = ')';
++shownfields;
}
if (nfields > 1 && cur < size) dest[cur++] = ')';
}
break;
} /*}}}*/
case EEK: /*{{{*/
{
if (ef == RESTORE_ERROR) {
strncpy(dest + cur, "error(", size-cur-1);
cur += 6;
if (cur < size - 1)
cur += print_string(dest+cur, size-cur-1, tok->u.err, QUOTE_STRING);
if (cur < size - 1)
dest[cur++] = ')';
break;
}
(void)strncpy(dest+cur, _("ERROR"), size-cur-1);
cur += strlen(_("ERROR"));
if (ef == VERBOSE_ERROR)
{
(void)strncpy(dest+cur, ": ", size-cur-1);
cur += 2;
size_t errlen = strlen(tok->u.err);
if ((cur+errlen+1) < size) strcpy(dest+cur, tok->u.err);
else (void)strncpy(dest+cur, tok->u.err, size-cur-1);
cur += errlen;
}
break;
}
/*}}}*/
/* default */ /*{{{*/
default: assert(0);
/*}}}*/
}
if (cur<size) dest[cur] = 0;
else
{
dest[size-1] = 0;
cur = size;
}
size_t mlen = mbslen(dest);
if (field_width && mlen > field_width) {
if (field_width < 6) {
for (cur = 0; cur < field_width; ++cur) dest[cur] = '#';
dest[cur] = 0;
} else {
char *ov = mbspos(dest+cur, -5);
cur = (size_t)(ov - dest);
cur += print_chars(dest+cur, size-cur-1, "...##");
}
}
return cur;
}
/*}}}*/
/* printtok -- print a single token, passed by address, although not changed */ /*{{{*/
size_t printtok(char* dest, size_t size, size_t field_width,
StringFormat sf, FloatFormat ff,
PrecisionLevel digits, ErrorFormat ef, const Token *tok)
{
return ptokatprec(dest, size, field_width, sf, ff, digits, ef, tok,
NO_PRECEDENCE);
}
/*}}}*/
static char dbgpbuf[4096];
/* dbgprint -- simple on the fly print for in the debugger */
const char *dbgprint(const Token* tok) {
size_t used = printtok(dbgpbuf, sizeof(dbgpbuf), 0, QUOTE_STRING, FLT_COMPACT,
-1, VERBOSE_ERROR, tok);
if (used > sizeof(dbgpbuf) - 2) {
printf(_("Warning: buffer overflow in dbgprint"));
}
return dbgpbuf;
}
/* print -- print token sequence */ /*{{{*/
void print(char *s, size_t size, size_t chars, StringFormat sf, FloatFormat ff,
PrecisionLevel digits, Token **n)
{
size_t cur;
cur=0;
if (n != EMPTY_TVEC)
for (; cur<size-1 && (*n) != NULLTOKEN; ++n)
cur += printtok(s+cur, size-cur, 0, sf, ff, digits, TRUNCATED_ERROR, *n);
if (cur<size) s[cur] = 0;
else s[size-1] = 0;
if (chars && mbslen(s) > chars) {
for (cur=0; cur < chars; ++cur) s[cur] = '#';
s[cur] = 0;
}
}
/*}}}*/