Streamline internals of teapot

The primary change is to add a “funcall” token, so that an entire expression
  can be encapsulated as a single token. This change is used to allow a cell to
  include simply a selection of appropriate semantic tokens. I.e., the content
  and iterative content are now each a single token like the value and the
  result value. Token vectors are used only as intermediate results in scanning
  and parsing.

  Not this means the cells are now in effect storing parse trees, so
  computation should be slightly faster and future extensions (like #56) should
  be facilitated.

  This commit also takes the opportunity while internals are being altered to
  add another token to a cell for future use for computed attributes, cf #22,
  and to change the internal numerical values from double and ints to long
  doubles and long longs. However, the change attempts to encapsulate that
  choice so it would be easy to change back or change to another representation.

  Note that these changes break savexdr(), as the internal binary format of
  a cell is now different. Rather than reimplement it, it is deprecated as
  the world does not need another binary spreadsheet format. Hence, the
  ascii format for teapot spreadsheets becomes the primary file format.
  Loading of old xdr files is still supported for backward compatibility.

  Closes #59.

  Also along the way, various other slight fixes and enhancements crept in,
  a partial but probably not exhaustive list of which follows:

  Fixes #31.

  Further revisions and improvements to documentation.

  Make the approximate comparison of floating point values scale more
  accurately with the size of the doubles being compared.

  Further extensions of absolute and relative cell addressing.

  Addition of (circle constant) tau function/constant.

  Modified string conversion to simply use internal printing routines, and
  to take "scientific" and "decimal" keywords.

  Allowed n() function to take a list of values, or just a single location
  defaulting to the current location.

  Added floor, ceil, trunc, and round functions, and allowed them to be
  keywords controlling the int() integer conversion function as well.

  Allowed substr() to drop its last argument to go to the end of the string.

  Provided an enum of built-in functions to preserve legacy function
  identifiers, allowing the large table inside func.c to be reorganized
  in a clearer fashion.

  Added additional annotation of properties of the built-in functions,
  including precedence.

  All operators are now also accessible as built-in functions.

  Made precedence of unary - lower than ^ to match python.

  Avoided inadvertently using FLTK @symbol abbreviations for formulas with
  "@" in them.
This commit is contained in:
Glen Whitney 2019-08-23 12:12:06 -07:00
parent a56efa0c91
commit 364ef2c0ea
22 changed files with 2611 additions and 1651 deletions

File diff suppressed because it is too large Load Diff

View File

@ -7,39 +7,52 @@
#include "eval.h" #include "eval.h"
#include "main.h" #include "main.h"
const char *ColorAspect_Name[] =
{ [FOREGROUND] = "Foreground", [BACKGROUND] = "Background"
};
const ColorNum DefaultCN[] = const ColorNum DefaultCN[] =
{ [FOREGROUND] = 0, [BACKGROUND] = 16, [NUM_COLOR_ASPECTS] = 255 }; { [FOREGROUND] = 0, [BACKGROUND] = 16, [NUM_COLOR_ASPECTS] = 255 };
const char *TokVariety_Name[] =
{ [BASE_CONT] = "Base Content", [ITER_CONT] = "Iterative Content",
[ATTR_REF] = "Attribute Reference", [CURR_VAL] = "Current Value",
[RES_VAL] = "Resultant Value", [CONTINGENT] = "Contingent Content"
};
/* initcellcontents - make a fresh cell into the "empty" one; don't worry /* initcellcontents - make a fresh cell into the "empty" one; don't worry
about freeing anything there, that will have been handled. */ about freeing anything there, that will have been handled. */
void initcellcontents(Cell *fresh) void initcellcontents(Cell *fresh)
{ {
(void)memset(fresh->contents, 0, sizeof(fresh->contents)); for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
fresh->label=(char*)0; fresh->tok[tv].type = EMPTY;
fresh->adjust=AUTOADJUST; fresh->label = NULL;
fresh->precision = -1; fresh->precision = -1;
fresh->shadowed=0; fresh->adjust = AUTOADJUST;
fresh->bold=0;
fresh->underline=0;
for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a) for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a)
fresh->aspect[a] = DefaultCN[a]; fresh->aspect[a] = DefaultCN[a];
fresh->updated = 0;
fresh->shadowed = 0;
fresh->scientific = DEF_SCIENTIFIC; fresh->scientific = DEF_SCIENTIFIC;
fresh->value.type=EMPTY;
fresh->resvalue.type=EMPTY;
fresh->locked = 0; fresh->locked = 0;
fresh->transparent = 0;
fresh->ignored = 0; fresh->ignored = 0;
fresh->clock_t0 = 0; fresh->clock_t0 = 0;
fresh->clock_t1 = 0; fresh->clock_t1 = 0;
fresh->clock_t2 = 0; fresh->clock_t2 = 0;
fresh->bold = 0;
fresh->underline = 0;
} }
/* getcont -- get contents */ /* getcont -- get contents */
Token **getcont(const Cell *cell, ContentVariety v) Token gettok(const Cell *cell, TokVariety v)
{ {
if (cell == NULLCELL) return EMPTY_TVEC; Token emp;
emp.type = EMPTY;
if (cell == NULLCELL) return emp;
if (v == CONTINGENT) if (v == CONTINGENT)
v = (cell->clock_t0 && cell->contents[ITERATIVE]) ? ITERATIVE : BASE; v = (cell->clock_t0 && cell->tok[ITER_CONT].type != EMPTY)
return cell->contents[v]; ? ITER_CONT : BASE_CONT;
return cell->tok[v];
} }
/* getadjust -- get cell adjustment */ /* getadjust -- get cell adjustment */
@ -47,7 +60,8 @@ Adjust getadjust(const Cell* cell)
{ {
if (cell == NULLCELL) return LEFT; if (cell == NULLCELL) return LEFT;
else if (cell->adjust == AUTOADJUST) else if (cell->adjust == AUTOADJUST)
return (cell->value.type == INT || cell->value.type == FLOAT ? RIGHT : LEFT); return (cell->tok[CURR_VAL].type == INT
|| cell->tok[CURR_VAL].type == FLOAT) ? RIGHT : LEFT;
else return cell->adjust; else return cell->adjust;
} }
@ -148,17 +162,13 @@ static void copytokens(Token*** totoks, Token** fromtoks)
/* freecellcontents -- free the resources of the cell at destruction time */ /* freecellcontents -- free the resources of the cell at destruction time */
void freecellcontents(Cell *faded) void freecellcontents(Cell *faded)
{ {
tvecfree(faded->contents[BASE]); for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
tvecfree(faded->contents[ITERATIVE]); tfree(&(faded->tok[tv]));
tfree(&(faded->value)); if (faded->label != NULL) free(faded->label);
tfree(&(faded->resvalue));
if (faded->label != (char*)0) {
free(faded->label);
}
} }
/* copycell - copies one Cell to another, handling any allocation issues */ /* copycell - copies one Cell to another, handling any allocation issues */
void copycell(Cell *to, const Cell *fromcell) void copycell(Cell *to, const Cell *fromcell, LabelHandling lh)
{ {
assert(to != NULLCELL); assert(to != NULLCELL);
if (to == fromcell) return; if (to == fromcell) return;
@ -166,16 +176,15 @@ void copycell(Cell *to, const Cell *fromcell)
freecellcontents(to); freecellcontents(to);
if (fromcell != NULLCELL) { if (fromcell != NULLCELL) {
memcpy(to, fromcell, sizeof(Cell)); memcpy(to, fromcell, sizeof(Cell));
copytokens(&(to->contents[BASE]), fromcell->contents[BASE]); for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
copytokens(&(to->contents[ITERATIVE]), fromcell->contents[ITERATIVE]); if (tv <= MAX_PERSIST_TV) to->tok[tv] = tcopy(fromcell->tok[tv]);
if (fromcell->label != (char*)0) { else to->tok[tv].type = EMPTY;
if (lh != PRESERVE_LABEL && fromcell->label != (char*)0) {
size_t len = strlen(fromcell->label); size_t len = strlen(fromcell->label);
to->label = strcpy(malloc(len+2), fromcell->label); to->label = strcpy(malloc(len+2), fromcell->label);
(to->label)[len] = '_'; (to->label)[len] = '_';
(to->label)[len+1] = '\0'; (to->label)[len+1] = '\0';
} }
to->value.type = EMPTY;
to->resvalue.type = EMPTY;
} else { } else {
initcellcontents(to); initcellcontents(to);
} }

View File

@ -9,23 +9,26 @@ extern "C" {
typedef enum { LEFT=0, RIGHT=1, CENTER=2, AUTOADJUST=3 } Adjust; typedef enum { LEFT=0, RIGHT=1, CENTER=2, AUTOADJUST=3 } Adjust;
typedef enum { BASE=0, ITERATIVE=1, CONTINGENT=2 } ContentVariety; typedef enum
{ BASE_CONT=0, ITER_CONT, ATTR_REF, MAX_PERSIST_TV = ATTR_REF,
CURR_VAL, RES_VAL,
CONTINGENT /* Must be last, marks end of many loops */
} TokVariety;
extern const char *TokVariety_Name[];
typedef unsigned char ColorNum; typedef unsigned char ColorNum;
#define MAX_MAX_COLORS UCHAR_MAX; #define MAX_MAX_COLORS UCHAR_MAX;
typedef enum { FOREGROUND = 0, BACKGROUND, NUM_COLOR_ASPECTS } ColorAspect; typedef enum { FOREGROUND = 0, BACKGROUND, NUM_COLOR_ASPECTS } ColorAspect;
extern const char *ColorAspect_Name[];
extern const ColorNum DefaultCN[]; extern const ColorNum DefaultCN[];
typedef struct typedef struct
{ {
Token **contents[CONTINGENT]; Token tok[CONTINGENT];
char *label; char *label;
Token value;
Token resvalue;
Adjust adjust;
int precision; int precision;
Adjust adjust;
ColorNum aspect[NUM_COLOR_ASPECTS]; ColorNum aspect[NUM_COLOR_ASPECTS];
unsigned int updated:1; unsigned int updated:1;
unsigned int shadowed:1; unsigned int shadowed:1;
@ -42,7 +45,9 @@ typedef struct
#define NULLCELL ((Cell*)0) #define NULLCELL ((Cell*)0)
Token **getcont(const Cell *cell, ContentVariety v); typedef enum {ALTER_LABEL, PRESERVE_LABEL} LabelHandling;
Token gettok(const Cell *cell, TokVariety v);
Adjust getadjust(const Cell *cell); Adjust getadjust(const Cell *cell);
bool shadowed(const Cell *cell); bool shadowed(const Cell *cell);
bool isbold(const Cell *cell); bool isbold(const Cell *cell);
@ -56,7 +61,7 @@ int getprecision(const Cell* cell);
const char *getlabel(const Cell *cell); const char *getlabel(const Cell *cell);
void initcellcontents(Cell *cell); void initcellcontents(Cell *cell);
void freecellcontents(Cell *cell); void freecellcontents(Cell *cell);
void copycell(Cell *to, const Cell *from); void copycell(Cell *to, const Cell *from, LabelHandling lh);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -17,6 +17,7 @@
#endif #endif
#include "csv.h" #include "csv.h"
#include "scanner.h"
static int semicol=0; static int semicol=0;
@ -77,15 +78,15 @@ void csv_separator(const char *s, const char **end)
} }
/*}}}*/ /*}}}*/
/* csv_long -- convert string [0[x]]12345 to long */ /*{{{*/ /* csv_long -- convert string [0[x]]12345 to long */ /*{{{*/
long csv_long(const char *s, const char **end) IntT csv_long(const char *s, const char **end)
{ {
long value; IntT value;
const char *t; const char *t;
assert(s!=(const char*)0); assert(s!=(const char*)0);
assert(end!=(const char**)0); assert(end!=(const char**)0);
if (*s=='\t') { *end=s; return 0L; }; if (*s=='\t') { *end=s; return 0L; };
value=strtol(s,(char**)end,0); value = STRTOINT(s, (char**)end, 0);
if (s!=*end) if (s!=*end)
{ {
t=*end; csv_separator(t,end); t=*end; csv_separator(t,end);
@ -95,15 +96,15 @@ long csv_long(const char *s, const char **end)
} }
/*}}}*/ /*}}}*/
/* csv_double -- convert string 123.4e5 to double */ /*{{{*/ /* csv_double -- convert string 123.4e5 to double */ /*{{{*/
double csv_double(const char *s, const char **end) FltT csv_double(const char *s, const char **end)
{ {
double value; FltT value;
const char *t; const char *t;
assert(s!=(const char*)0); assert(s!=(const char*)0);
assert(end!=(const char**)0); assert(end!=(const char**)0);
if (*s=='\t') { *end=s; return 0.0; }; if (*s=='\t') { *end=s; return 0.0; };
value=strtod(s,(char**)end); value = STRTOFLT(s,(char**)end);
if (s!=*end) if (s!=*end)
{ {
t=*end; csv_separator(t,end); t=*end; csv_separator(t,end);
@ -112,6 +113,7 @@ double csv_double(const char *s, const char **end)
*end=s; return 0.0; *end=s; return 0.0;
} }
/*}}}*/ /*}}}*/
/* csv_string -- convert almost any string to string */ /*{{{*/ /* csv_string -- convert almost any string to string */ /*{{{*/
char *csv_string(const char *s, const char **end) char *csv_string(const char *s, const char **end)
{ {

View File

@ -1,6 +1,8 @@
#ifndef CSV_H #ifndef CSV_H
#define CSV_H #define CSV_H
#include "scanner.h"
struct CSV_Date struct CSV_Date
{ {
int day; int day;
@ -11,7 +13,7 @@ struct CSV_Date
void csv_setopt(int sem); void csv_setopt(int sem);
void csv_separator(const char *s, const char **end); void csv_separator(const char *s, const char **end);
char *csv_string(const char *s, const char **end); char *csv_string(const char *s, const char **end);
double csv_double(const char *s, const char **end); FltT csv_double(const char *s, const char **end);
long csv_long(const char *s, const char **end); IntT csv_long(const char *s, const char **end);
#endif #endif

View File

@ -48,7 +48,7 @@ Token tcopy(Token n)
result.u.funcall.argv = malloc(n.u.funcall.argc*sizeof(Token)); result.u.funcall.argv = malloc(n.u.funcall.argc*sizeof(Token));
for (size_t ai = 0; ai < n.u.funcall.argc; ++ai) for (size_t ai = 0; ai < n.u.funcall.argc; ++ai)
result.u.funcall.argv[ai] = tcopy(n.u.funcall.argv[ai]); result.u.funcall.argv[ai] = tcopy(n.u.funcall.argv[ai]);
} } else result.u.funcall.argv = NULLTOKEN;
break; break;
case EEK: result.u.err = strdup(n.u.err); break; case EEK: result.u.err = strdup(n.u.err); break;
default: break; default: break;
@ -106,6 +106,7 @@ void tfree_protected(Token *n, const Token dontfree)
default: default:
break; break;
} }
n->type = EMPTY;
} }
/*}}}*/ /*}}}*/
@ -141,106 +142,96 @@ size_t tveclen(Token **tvec)
} }
/*}}}*/ /*}}}*/
/* checkflt -- check for error conditions in floating point result */
static Token checkflt(Token res, const char* context)
{
if (res.type != FLOAT) return res;
const char *msg = dblfinite(res.u.flt);
if (msg == NULL) return res;
res.type = EEK;
res.u.err = malloc(strlen(msg) + strlen(context) + 1);
(void)strcpy(res.u.err, context);
(void)strcat(res.u.err, msg);
return res;
}
/* operand_type_error -- return an error message about operands */
static Token operand_type_error(const char* context, Type lt, Type rt)
{
const char* templ = _("wrong operand types, %s and %s");
size_t conlen = strlen(context);
Token err;
err.type = EEK;
err.u.err = malloc(conlen + strlen(templ) + 2*MAX_TYPE_NAME_LENGTH + 1);
strcpy(err.u.err, context);
sprintf(err.u.err + conlen, templ, Type_Name[lt], Type_Name[rt]);
return err;
}
/* tadd -- + operator */ /*{{{*/ /* tadd -- + operator */ /*{{{*/
Token tadd(Token l, Token r) Token tadd(Token l, Token r)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
Token result; Token result;
const char *msg; const char *cntxt = "+: ";
char *buf;
int len;
/*}}}*/ /*}}}*/
if (l.type==EEK) if (l.type == EEK || r.type == EMPTY) return tcopy(l);
/* return left error */ /*{{{*/ if (r.type == EEK || l.type == EMPTY) return tcopy(r);
return tcopy(l); if (l.type == INT && r.type == INT)
/*}}}*/
else if (r.type==EEK)
/* return right error */ /*{{{*/
return tcopy(r);
/*}}}*/
else if (l.type==INT && r.type==INT)
/* result is int sum of two ints */ /*{{{*/ /* result is int sum of two ints */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer = l.u.integer+r.u.integer; result.u.integer = l.u.integer+r.u.integer;
return result;
} }
/*}}}*/ /*}}}*/
else if (l.type==STRING && r.type==STRING) if (l.type == STRING && r.type == STRING)
/* result is concatenated strings */ /*{{{*/ /* result is concatenated strings */ /*{{{*/
{ {
result.type = STRING; result.type = STRING;
result.u.string = malloc(strlen(l.u.string) + strlen(r.u.string) + 1); result.u.string = malloc(strlen(l.u.string) + strlen(r.u.string) + 1);
(void)strcpy(result.u.string, l.u.string); (void)strcpy(result.u.string, l.u.string);
(void)strcat(result.u.string, r.u.string); (void)strcat(result.u.string, r.u.string);
return result;
} }
/*}}}*/ /*}}}*/
else if (l.type==EMPTY && (r.type==INT || r.type==STRING || r.type==FLOAT || r.type==EMPTY)) if (l.type == INT && r.type == FLOAT)
/* return right argument */ /*{{{*/
return tcopy(r);
/*}}}*/
else if ((l.type==INT || l.type==STRING || l.type==FLOAT) && r.type==EMPTY)
/* return left argument */ /*{{{*/
return tcopy(l);
/*}}}*/
else if (l.type==INT && r.type==FLOAT)
/* result is float sum of int and float */ /*{{{*/ /* result is float sum of int and float */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=((double)l.u.integer)+r.u.flt; result.u.flt = ((FltT)l.u.integer) + r.u.flt;
return checkflt(result, cntxt);
} }
/*}}}*/ /*}}}*/
else if (l.type==FLOAT && r.type==INT) if (l.type == FLOAT && r.type == INT)
/* result is float sum of float and int */ /*{{{*/ /* result is float sum of float and int */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=l.u.flt+((double)r.u.integer); result.u.flt = l.u.flt + ((FltT)r.u.integer);
return checkflt(result, cntxt);
} }
/*}}}*/ /*}}}*/
else if (l.type==FLOAT && r.type==FLOAT) if (l.type == FLOAT && r.type == FLOAT)
/* result is float sum of float and float */ /*{{{*/ /* result is float sum of float and float */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt = l.u.flt + r.u.flt; result.u.flt = l.u.flt + r.u.flt;
return checkflt(result, cntxt);
} }
/*}}}*/ /*}}}*/
else if (l.type == LOCATION && r.type == LOCATION) if (l.type == LOCATION && r.type == LOCATION)
/* result is component-wise sum of locations */ /*{{{*/ /* result is component-wise sum of locations */ /*{{{*/
{ {
result.type = LOCATION; result.type = LOCATION;
for (len = 0; len < 3; ++len) for (size_t len = 0; len < 3; ++len)
result.u.location[len] = l.u.location[len] + r.u.location[len]; result.u.location[len] = l.u.location[len] + r.u.location[len];
}
/*}}}*/
else if (l.type==EMPTY && r.type==EMPTY)
/* result is emty */ /*{{{*/
{
result.type=EMPTY;
}
/*}}}*/
else
/* result is type error */ /*{{{*/
{
result.type=EEK;
len = strlen(_("wrong types for + operator"));
buf = malloc(len + 5 + 2*MAX_TYPE_NAME_LENGTH + 1);
strcpy(buf, _("wrong types for + operator"));
snprintf(buf + len, 128, ": %s + %s", Type_Name[l.type], Type_Name[r.type]);
result.u.err = buf;
}
/*}}}*/
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0)
/* result is error */ /*{{{*/
{
result.type=EEK;
result.u.err=malloc(strlen(msg)+4);
(void)strcpy(result.u.err,"+: ");
(void)strcat(result.u.err,msg);
}
/*}}}*/
return result; return result;
} }
/*}}}*/ /*}}}*/
return operand_type_error(cntxt, l.type, r.type);
}
/*}}}*/
/* tconcat -- concat operands as strings */ /*{{{*/ /* tconcat -- concat operands as strings */ /*{{{*/
Token tconcat(Token l, Token r) Token tconcat(Token l, Token r)
@ -280,19 +271,13 @@ Token tsub(Token l, Token r)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
Token result; Token result;
const char *msg; const char *cntxt = "-: ";
int len;
/*}}}*/ /*}}}*/
if (l.type==EEK) if (l.type == EEK || r.type == EMPTY) return tcopy(l);
/* return left error */ /*{{{*/ if (r.type == EEK) return tcopy(r);
return tcopy(l); if (l.type == EMPTY) return tneg(r);
/*}}}*/ if (l.type == INT && r.type == INT)
else if (r.type==EEK)
/* return right error */ /*{{{*/
return tcopy(r);
/*}}}*/
else if (l.type==INT && r.type==INT)
/* result is int difference between left int and right int */ /*{{{*/ /* result is int difference between left int and right int */ /*{{{*/
{ {
result.type = INT; result.type = INT;
@ -306,210 +291,153 @@ Token tsub(Token l, Token r)
result.u.flt=l.u.flt-r.u.flt; result.u.flt=l.u.flt-r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type==EMPTY)
/* return negated right argument */ /*{{{*/
return tneg(r);
/*}}}*/
else if ((l.type==INT || l.type==FLOAT) && r.type==EMPTY)
/* return left argument */ /*{{{*/
return tcopy(l);
/*}}}*/
else if (l.type==INT && r.type==FLOAT) else if (l.type==INT && r.type==FLOAT)
/* result is float difference of left integer and right float */ /*{{{*/ /* result is float difference of left integer and right float */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=((double)l.u.integer)-r.u.flt; result.u.flt = ((FltT)l.u.integer) - r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type==FLOAT && r.type==INT) else if (l.type==FLOAT && r.type==INT)
/* result is float difference between left float and right integer */ /*{{{*/ /* result is float difference between left float and right integer */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=l.u.flt-((double)r.u.integer); result.u.flt = l.u.flt - ((FltT)r.u.integer);
} }
/*}}}*/ /*}}}*/
else if (l.type == LOCATION && r.type == LOCATION) else if (l.type == LOCATION && r.type == LOCATION)
/* result is component-wise difference of locations */ /*{{{*/ /* result is component-wise difference of locations */ /*{{{*/
{ {
result.type = LOCATION; result.type = LOCATION;
for (len = 0; len < 3; ++len) for (size_t len = 0; len < 3; ++len)
result.u.location[len] = l.u.location[len] - r.u.location[len]; result.u.location[len] = l.u.location[len] - r.u.location[len];
} }
/*}}}*/ /*}}}*/
else else return operand_type_error(cntxt, l.type, r.type);
/* result is difference type error */ /*{{{*/ return checkflt(result, cntxt);
{
result.type=EEK;
result.u.err=strcpy(malloc(strlen(_("wrong types for - operator"))+1),_("wrong types for - operator"));
}
/*}}}*/
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0)
/* result is error */ /*{{{*/
{
result.type=EEK;
result.u.err=malloc(strlen(msg)+4);
(void)strcpy(result.u.err,"-: ");
(void)strcat(result.u.err,msg);
}
/*}}}*/
return result;
} }
/*}}}*/ /*}}}*/
/* tdiv -- / operator */ /*{{{*/ /* tdiv -- / operator */ /*{{{*/
Token tdiv(Token l, Token r) Token tdiv(Token l, Token r)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
Token result; Token result;
const char *msg; const char *cntxt = "/: ";
int len;
/*}}}*/ /*}}}*/
if (l.type==EEK) if (l.type == EEK) return tcopy(l);
/* return left error */ /*{{{*/ if (r.type == EEK) return tcopy(r);
return tcopy(l); if ((r.type == INT && r.u.integer == 0)|| (r.type == FLOAT && r.u.flt == 0.0)
/*}}}*/ || (r.type == EMPTY))
else if (r.type==EEK)
/* return right error */ /*{{{*/
return tcopy(r);
/*}}}*/
else if ((r.type==INT && r.u.integer==0) || (r.type==FLOAT && r.u.flt==0.0) || (r.type==EMPTY))
/* result is division by 0 error */ /*{{{*/ /* result is division by 0 error */ /*{{{*/
{ {
result.type=EEK; duperror(&result, _("division by 0"));
result.u.err=strcpy(malloc(strlen(_("division by 0"))+1),_("division by 0")); return result;
} }
/*}}}*/ /*}}}*/
else if (l.type==INT && r.type==INT) if (l.type == EMPTY && TOKISNUM(r))
if (r.type == INT)
{
result.type = INT; result.u.integer = 0; return result;
} else {
result.type = FLOAT; result.u.flt = 0.0; return result;
}
if (l.type==INT && r.type==INT)
/* result is quotient of left int and right int */ /*{{{*/ /* result is quotient of left int and right int */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer = l.u.integer/r.u.integer; result.u.integer = l.u.integer/r.u.integer;
return result;
} }
/*}}}*/ /*}}}*/
else if (l.type==FLOAT && r.type==FLOAT) if (l.type == FLOAT && r.type == FLOAT)
/* result is quotient of left float and right float */ /*{{{*/ /* result is quotient of left float and right float */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt = l.u.flt/r.u.flt; result.u.flt = l.u.flt/r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type==EMPTY && r.type==INT)
/* result is 0 */ /*{{{*/
{
result.type=INT;
result.u.integer=0;
}
/*}}}*/
else if (l.type==EMPTY && r.type==FLOAT)
/* result is 0.0 */ /*{{{*/
{
result.type=FLOAT;
result.u.flt=0.0;
}
/*}}}*/
else if (l.type == INT && r.type == FLOAT) else if (l.type == INT && r.type == FLOAT)
/* result is float quotient of left int and right float */ /*{{{*/ /* result is float quotient of left int and right float */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=((double)l.u.integer)/r.u.flt; result.u.flt = ((FltT)l.u.integer)/r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type == FLOAT && r.type == INT) else if (l.type == FLOAT && r.type == INT)
/* result is float quotient of left float and right int */ /*{{{*/ /* result is float quotient of left float and right int */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=l.u.flt/((double)r.u.integer); result.u.flt = l.u.flt/((FltT)r.u.integer);
} }
/*}}}*/ /*}}}*/
else if (l.type == LOCATION && r.type == INT) else if (l.type == LOCATION && r.type == INT)
/* result is divide each component of location by right */ /*{{{*/ /* result is divide each component of location by right */ /*{{{*/
{ {
result.type = LOCATION; result.type = LOCATION;
for (len = 0; len < 3; ++len) for (size_t len = 0; len < 3; ++len)
result.u.location[len] = l.u.location[len] / r.u.integer; result.u.location[len] = l.u.location[len] / r.u.integer;
} }
/*}}}*/ /*}}}*/
else else return operand_type_error(cntxt, l.type, r.type);
/* result is quotient type error */ /*{{{*/ return checkflt(result, cntxt);
{
result.type=EEK;
result.u.err=strcpy(malloc(strlen(_("wrong types for / operator"))+1),_("wrong types for / operator"));
}
/*}}}*/
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0)
/* result is error */ /*{{{*/
{
result.type=EEK;
result.u.err=malloc(strlen(msg)+4);
(void)strcpy(result.u.err,"/: ");
(void)strcat(result.u.err,msg);
}
/*}}}*/
return result;
} }
/*}}}*/ /*}}}*/
/* tmod -- % operator */ /*{{{*/ /* tmod -- % operator */ /*{{{*/
Token tmod(Token l, Token r) Token tmod(Token l, Token r)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
Token result; Token result;
const char *msg; const char *cntxt = "%: ";
int len;
/*}}}*/ /*}}}*/
if (l.type==EEK) /* return left error */ /*{{{*/ if (l.type == EEK) return tcopy(l);
return tcopy(l); if (r.type == EEK) return tcopy(r);
/*}}}*/ if ((r.type == INT && r.u.integer == 0) || (r.type == FLOAT && r.u.flt == 0.0)
else if (r.type==EEK) /* return right error */ /*{{{*/ || (r.type == EMPTY)) /* result is modulo 0 error */ /*{{{*/
return tcopy(r);
/*}}}*/
else if ((r.type==INT && r.u.integer==0) || (r.type==FLOAT && r.u.flt==0.0) || (r.type==EMPTY)) /* result is modulo 0 error */ /*{{{*/
{ {
result.type=EEK; duperror(&result, _("modulo 0"));
result.u.err=strcpy(malloc(strlen(_("modulo 0"))+1),_("modulo 0")); return result;
} }
/*}}}*/ if (l.type == EMPTY && TOKISNUM(r))
else if (l.type==INT && r.type==INT) /* result is remainder of left int and right int */ /*{{{*/ if (r.type == INT)
{
result.type = INT; result.u.integer = 0; return result;
} else {
result.type = FLOAT; result.u.flt = 0.0; return result;
}
if (l.type == INT && r.type == INT) /* result is remainder of left int and right int */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer = l.u.integer % r.u.integer; result.u.integer = l.u.integer % r.u.integer;
return result;
} }
/*}}}*/ /*}}}*/
else if (l.type==FLOAT && r.type==FLOAT) /* result is remainder of left float and right float */ /*{{{*/ if (l.type == FLOAT && r.type == FLOAT) /* result is remainder of left float and right float */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=fmod(l.u.flt,r.u.flt); result.u.flt = FMODFLT(l.u.flt, r.u.flt);
}
/*}}}*/
else if (l.type==EMPTY && r.type==INT) /* result is 0 */ /*{{{*/
{
result.type=INT;
result.u.integer=0;
}
/*}}}*/
else if (l.type==EMPTY && r.type==FLOAT) /* result is 0.0 */ /*{{{*/
{
result.type=FLOAT;
result.u.flt=0.0;
} }
/*}}}*/ /*}}}*/
else if (l.type == INT && r.type == FLOAT) /* result is float remainder of left int and right float */ /*{{{*/ else if (l.type == INT && r.type == FLOAT) /* result is float remainder of left int and right float */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=fmod((double)l.u.integer,r.u.flt); result.u.flt = FMODFLT((FltT)l.u.integer, r.u.flt);
} }
/*}}}*/ /*}}}*/
else if (l.type == FLOAT && r.type == INT) /* result is float remainder of left float and right int */ /*{{{*/ else if (l.type == FLOAT && r.type == INT) /* result is float remainder of left float and right int */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt=fmod(l.u.flt,(double)r.u.integer); result.u.flt = FMODFLT(l.u.flt, (FltT)r.u.integer);
} }
/*}}}*/ /*}}}*/
else if (l.type == LOCATION && r.type == LOCATION) else if (l.type == LOCATION && r.type == LOCATION)
/* result is component-wise mod of locations */ /*{{{*/ /* result is component-wise mod of locations */ /*{{{*/
{ {
result.type = LOCATION; result.type = LOCATION;
for (len = 0; len < 3; ++len) for (size_t len = 0; len < 3; ++len)
result.u.location[len] = l.u.location[len] % r.u.location[len]; result.u.location[len] = l.u.location[len] % r.u.location[len];
} }
/*}}}*/ /*}}}*/
@ -517,66 +445,62 @@ Token tmod(Token l, Token r)
/* result is mod each component of location by right */ /*{{{*/ /* result is mod each component of location by right */ /*{{{*/
{ {
result.type = LOCATION; result.type = LOCATION;
for (len = 0; len < 3; ++len) for (size_t len = 0; len < 3; ++len)
result.u.location[len] = l.u.location[len] % r.u.integer; result.u.location[len] = l.u.location[len] % r.u.integer;
} }
/*}}}*/ /*}}}*/
else /* result is remainder type error */ /*{{{*/ else return operand_type_error(cntxt, l.type, r.type);
{ return checkflt(result, cntxt);
result.type=EEK;
result.u.err=strcpy(malloc(strlen(_("wrong types for % operator"))+1),_("wrong types for % operator"));
}
/*}}}*/
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) /* result is error */ /*{{{*/
{
result.type=EEK;
result.u.err=malloc(strlen(msg)+4);
(void)strcpy(result.u.err,"%: ");
(void)strcat(result.u.err,msg);
}
/*}}}*/
return result;
} }
/*}}}*/ /*}}}*/
/* tmul -- * operator */ /*{{{*/ /* tmul -- * operator */ /*{{{*/
Token tmul(Token l, Token r) Token tmul(Token l, Token r)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
Token result; Token result;
const char *msg; const char *cntxt = "*: ";
int len;
/*}}}*/ /*}}}*/
if (l.type == EEK) result = tcopy(l); if (l.type == EEK) return tcopy(l);
else if (r.type == EEK) result = tcopy(r); if (r.type == EEK) return tcopy(r);
else if (l.type == INT && r.type == INT) if (l.type==EMPTY && r.type==EMPTY)
/* result is int product of left int and right int */ /*{{{*/ /* result is empty */ /*{{{*/
{ {
result = l; result.type = EMPTY;
result.u.integer = l.u.integer*r.u.integer; return result;
} }
/*}}}*/ /*}}}*/
else if (l.type == FLOAT && r.type == FLOAT) if ((l.type == EMPTY && r.type == INT) || (l.type == INT && r.type == EMPTY))
/* result is float product of left float and right float */ /*{{{*/
{
result.type = FLOAT;
result.u.flt = l.u.flt*r.u.flt;
}
/*}}}*/
else if ((l.type == EMPTY && r.type == INT)
|| (l.type == INT && r.type == EMPTY))
/* result is 0 */ /*{{{*/ /* result is 0 */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer = 0; result.u.integer = 0;
return result;
} }
/*}}}*/ /*}}}*/
else if ((l.type == EMPTY && r.type == FLOAT) if ((l.type == EMPTY && r.type == FLOAT)
|| (l.type == FLOAT && r.type == EMPTY)) || (l.type == FLOAT && r.type == EMPTY))
/* result is 0.0 */ /*{{{*/ /* result is 0.0 */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt = 0.0; result.u.flt = 0.0;
return result;
}
/*}}}*/
if (l.type == INT && r.type == INT)
/* result is int product of left int and right int */ /*{{{*/
{
result = l;
result.u.integer = l.u.integer * r.u.integer;
return result;
}
/*}}}*/
if (l.type == FLOAT && r.type == FLOAT)
/* result is float product of left float and right float */ /*{{{*/
{
result.type = FLOAT;
result.u.flt = l.u.flt * r.u.flt;
} }
/*}}}*/ /*}}}*/
else if ((l.type == INT && r.type == FLOAT) else if ((l.type == INT && r.type == FLOAT)
@ -584,21 +508,15 @@ Token tmul(Token l, Token r)
/* result is float product of int and float */ /*{{{*/ /* result is float product of int and float */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
if (l.type == INT) result.u.flt = ((double)l.u.integer) * r.u.flt; if (l.type == INT) result.u.flt = ((FltT)l.u.integer) * r.u.flt;
else result.u.flt = l.u.flt * ((double)r.u.integer); else result.u.flt = l.u.flt * ((FltT)r.u.integer);
}
/*}}}*/
else if (l.type==EMPTY && r.type==EMPTY)
/* result is empty */ /*{{{*/
{
result.type=EMPTY;
} }
/*}}}*/ /*}}}*/
else if (l.type == LOCATION && r.type == INT) else if (l.type == LOCATION && r.type == INT)
/* result is each component of location times right */ /*{{{*/ /* result is each component of location times right */ /*{{{*/
{ {
result.type = LOCATION; result.type = LOCATION;
for (len = 0; len < 3; ++len) for (size_t len = 0; len < 3; ++len)
result.u.location[len] = l.u.location[len] * r.u.integer; result.u.location[len] = l.u.location[len] * r.u.integer;
} }
/*}}}*/ /*}}}*/
@ -607,7 +525,7 @@ Token tmul(Token l, Token r)
{ {
result.type = INT; result.type = INT;
result.u.integer = 0; result.u.integer = 0;
for (len = 0; len < 3; ++len) for (size_t len = 0; len < 3; ++len)
result.u.integer += l.u.location[len] * r.u.location[len]; result.u.integer += l.u.location[len] * r.u.location[len];
} }
/*}}}*/ /*}}}*/
@ -644,25 +562,11 @@ Token tmul(Token l, Token r)
result.u.string[copies*len] = '\0'; result.u.string[copies*len] = '\0';
} }
} }
else else return operand_type_error(cntxt, l.type, r.type);
/* result is product type error */ /*{{{*/ return checkflt(result, cntxt);
{
result.type=EEK;
result.u.err=strcpy(malloc(strlen(_("wrong types for * operator"))+1),_("wrong types for * operator"));
}
/*}}}*/
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0)
/* result is error */ /*{{{*/
{
result.type=EEK;
result.u.err=malloc(strlen(msg)+4);
(void)strcpy(result.u.err,"*: ");
(void)strcat(result.u.err,msg);
}
/*}}}*/
return result;
} }
/*}}}*/ /*}}}*/
/* tneg -- monadic - operator */ /*{{{*/ /* tneg -- monadic - operator */ /*{{{*/
Token tneg(Token x) Token tneg(Token x)
{ {
@ -671,137 +575,107 @@ Token tneg(Token x)
int len; int len;
/*}}}*/ /*}}}*/
if (x.type==EEK) if (x.type == EEK) return tcopy(x);
/* return error */ /*{{{*/ if (x.type == EMPTY) return x;
return tcopy(x); if (x.type == INT)
/*}}}*/
else if (x.type==INT)
/* result is negated int argument */ /*{{{*/ /* result is negated int argument */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer = -x.u.integer; result.u.integer = -x.u.integer;
return result;
} }
/*}}}*/ /*}}}*/
else if (x.type==FLOAT) if (x.type == FLOAT)
/* result is negated float argument */ /*{{{*/ /* result is negated float argument */ /*{{{*/
{ {
result.type = FLOAT; result.type = FLOAT;
result.u.flt = -x.u.flt; result.u.flt = -x.u.flt;
}
/*}}}*/
else if (x.type==EMPTY)
/* result is argument itself */ /*{{{*/
{
result=tcopy(x);
}
/*}}}*/
else if (x.type == LOCATION)
/* result is component-wise negation of location */ /*{{{*/
{
result.type = LOCATION;
for (len = 0; len < 3; ++len)
result.u.location[len] = -x.u.location[len];
}
/*}}}*/
else
/* result is negation error */ /*{{{*/
{
result.type=EEK;
result.u.err=strcpy(malloc(strlen(_("wrong type for - operator"))+1),_("wrong type for - operator"));
}
/*}}}*/
return result; return result;
} }
/*}}}*/ /*}}}*/
if (x.type == LOCATION)
/* result is component-wise negation of location */ /*{{{*/
{
result.type = LOCATION;
for (size_t len = 0; len < 3; ++len)
result.u.location[len] = -x.u.location[len];
return result;
}
result.type = EEK;
const char* templ = "wrong type for - operator: %s";
result.u.err = malloc(strlen(templ) + MAX_TYPE_NAME_LENGTH + 1);
sprintf(result.u.err, templ, Type_Name[x.type]);
return result;
}
/*}}}*/
/* tpow -- ^ operator */ /*{{{*/ /* tpow -- ^ operator */ /*{{{*/
Token tpow(Token l, Token r) Token tpow(Token l, Token r)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
Token result; Token result;
const char *msg; const char *cntxt = "^: ";
/*}}}*/ /*}}}*/
if (l.type==EEK) /* return left error */ /*{{{*/ if (l.type == EEK) return tcopy(l);
return tcopy(l); if (r.type == EEK) return tcopy(r);
/*}}}*/ if (TOKISNUM(r) && TOKISNUM(l))
else if (r.type==EEK) /* return right error */ /*{{{*/
return tcopy(r);
/*}}}*/
else if ((l.type==INT || l.type==FLOAT || l.type==EMPTY) && (r.type==INT || r.type==FLOAT || l.type==EMPTY)) /* do the real work */ /*{{{*/
{ {
if ((l.type==INT || l.type==EMPTY) && ((r.type==INT && r.u.integer>=0) || r.type==EMPTY)) if ((l.type == INT || l.type == EMPTY)
&& ((r.type == INT && r.u.integer >= 0) || r.type == EMPTY))
/* int^int, return int or error if 0^0 */ /*{{{*/ /* int^int, return int or error if 0^0 */ /*{{{*/
{ {
long x,y; IntT x,y;
if (l.type == EMPTY) x=0; if (l.type == EMPTY) x=0;
else x = l.u.integer; else x = l.u.integer;
if (r.type == EMPTY) y=0; if (r.type == EMPTY) y=0;
else y = r.u.integer; else y = r.u.integer;
if (x==0 && y==0) if (x == 0 && y == 0) duperror(&result, _("0^0 is not defined"));
{
result.type=EEK;
result.u.err=strcpy(malloc(strlen(_("0^0 is not defined"))+1),_("0^0 is not defined"));
}
else else
{ {
long i; UIntT i;
result.type = INT; result.type = INT;
if (x == 0) result.u.integer = 0; if (x == 0) result.u.integer = 0;
else if (y == 0) result.u.integer = 1; else if (y == 0) result.u.integer = 1;
else for (result.u.integer = x,i=1; i < y; ++i) result.u.integer *= x; else for (result.u.integer = x,i=1; i < y; ++i) result.u.integer *= x;
} }
return result;
} }
/*}}}*/ /*}}}*/
else
/* float^float */ /*{{{*/ /* float^float */ /*{{{*/
{ FltT x=0.0, y=0.0;
double x=0.0,y=0.0;
switch (l.type) switch (l.type)
{ {
case INT: x=(double)l.u.integer; break; case INT: x = (FltT)l.u.integer; break;
case FLOAT: x = l.u.flt; break; case FLOAT: x = l.u.flt; break;
case EMPTY: x = 0.0; break; case EMPTY: x = 0.0; break;
default: assert(0); default: assert(0);
} }
switch (r.type) switch (r.type)
{ {
case INT: y=(double)r.u.integer; break; case INT: y = (FltT)r.u.integer; break;
case FLOAT: y = r.u.flt; break; case FLOAT: y = r.u.flt; break;
case EMPTY: y = 0.0; break; case EMPTY: y = 0.0; break;
default: assert(0); default: assert(0);
} }
result.type = FLOAT; result.type = FLOAT;
errno=0; /* there is no portable EOK :( */ errno=0; /* there is no portable EOK :( */
result.u.flt=pow(x,y); result.u.flt = POWFLT(x,y);
switch (errno) switch (errno)
{ {
case 0: result.type = FLOAT; break; case 0: result.type = FLOAT; break;
case ERANGE: case ERANGE:
case EDOM: result.type=EEK; result.u.err=strcpy(malloc(strlen(_("^ caused a domain error"))+1),_("^ caused a domain error")); break; case EDOM:
duperror(&result, _("^ caused a domain error"));
break;
default: assert(0); default: assert(0);
} }
return checkflt(result, cntxt);
} }
/*}}}*/ return operand_type_error(cntxt, l.type, r.type);
}
/*}}}*/
else /* result is type error */ /*{{{*/
{
result.type=EEK;
result.u.err=strcpy(malloc(strlen(_("wrong types for ^ operator"))+1),_("wrong types for ^ operator"));
}
/*}}}*/
if (result.type==FLOAT && (msg=dblfinite(result.u.flt))!=(const char*)0) /* result is error */ /*{{{*/
{
result.type=EEK;
result.u.err=malloc(strlen(msg)+4);
(void)strcpy(result.u.err,"^: ");
(void)strcat(result.u.err,msg);
}
/*}}}*/
return result;
} }
/*}}}*/ /*}}}*/
@ -882,13 +756,13 @@ Token tlt(Token l, Token r)
else if (l.type==FLOAT && r.type==INT) /* return left float < right float */ /*{{{*/ else if (l.type==FLOAT && r.type==INT) /* return left float < right float */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer=l.u.flt<((double)r.u.integer); result.u.integer = l.u.flt < ((FltT)r.u.integer);
} }
/*}}}*/ /*}}}*/
else if (l.type==INT && r.type==FLOAT) /* return left int < right float */ /*{{{*/ else if (l.type==INT && r.type==FLOAT) /* return left int < right float */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer=((double)l.u.integer)<r.u.flt; result.u.integer = ((FltT)l.u.integer) < r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type == LOCATION && r.type == LOCATION) else if (l.type == LOCATION && r.type == LOCATION)
@ -964,16 +838,16 @@ Token tle(Token l, Token r)
result.u.integer=l.u.flt<=r.u.flt; result.u.integer=l.u.flt<=r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type==FLOAT && r.type==INT) /* result is left float <= (double)right int */ /*{{{*/ else if (l.type==FLOAT && r.type==INT) /* result is left float <= (FltT)right int */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer=l.u.flt<=((double)r.u.integer); result.u.integer = l.u.flt <= ((FltT)r.u.integer);
} }
/*}}}*/ /*}}}*/
else if (l.type==INT && r.type==FLOAT) /* result is (double)left int <= right float */ /*{{{*/ else if (l.type==INT && r.type==FLOAT) /* result is (FltT)left int <= right float */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer=((double)l.u.integer)<=r.u.flt; result.u.integer = ((FltT)l.u.integer) <= r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type==EMPTY && r.type==EMPTY) /* result is 1 */ /*{{{*/ else if (l.type==EMPTY && r.type==EMPTY) /* result is 1 */ /*{{{*/
@ -996,6 +870,7 @@ Token tle(Token l, Token r)
return result; return result;
} }
/*}}}*/ /*}}}*/
/* tge -- >= operator */ /*{{{*/ /* tge -- >= operator */ /*{{{*/
Token tge(Token l, Token r) Token tge(Token l, Token r)
{ {
@ -1053,16 +928,16 @@ Token tge(Token l, Token r)
result.u.integer=l.u.flt>=r.u.flt; result.u.integer=l.u.flt>=r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type==FLOAT && r.type==INT) /* return left float >= (double) right int */ /*{{{*/ else if (l.type==FLOAT && r.type==INT) /* return left float >= (FltT) right int */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer=l.u.flt>=((double)r.u.integer); result.u.integer = l.u.flt >= ((FltT)r.u.integer);
} }
/*}}}*/ /*}}}*/
else if (l.type==INT && r.type==FLOAT) /* return (double) left int >= right float */ /*{{{*/ else if (l.type==INT && r.type==FLOAT) /* return (FltT) left int >= right float */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer=((double)l.u.integer)>=r.u.flt; result.u.integer = ((FltT)l.u.integer) >= r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type == LOCATION && r.type == LOCATION) else if (l.type == LOCATION && r.type == LOCATION)
@ -1136,16 +1011,16 @@ Token tgt(Token l, Token r)
result.u.integer=l.u.flt>r.u.flt; result.u.integer=l.u.flt>r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type==FLOAT && r.type==INT) /* result is left float > (double) right int */ /*{{{*/ else if (l.type==FLOAT && r.type==INT) /* result is left float > (FltT) right int */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer=l.u.flt>((double)r.u.integer); result.u.integer = l.u.flt > ((FltT)r.u.integer);
} }
/*}}}*/ /*}}}*/
else if (l.type==INT && r.type==FLOAT) /* result is left int > right float */ /*{{{*/ else if (l.type==INT && r.type==FLOAT) /* result is left int > right float */ /*{{{*/
{ {
result.type = INT; result.type = INT;
result.u.integer=((double)l.u.integer)>r.u.flt; result.u.integer = ((FltT)l.u.integer) > r.u.flt;
} }
/*}}}*/ /*}}}*/
else if (l.type == LOCATION && r.type == LOCATION) else if (l.type == LOCATION && r.type == LOCATION)
@ -1203,12 +1078,12 @@ Token teq(Token l, Token r)
if (l.type==FLOAT && r.type==INT) if (l.type==FLOAT && r.type==INT)
{ {
result.type = INT; result.type = INT;
result.u.integer=l.u.flt==((double)r.u.integer); result.u.integer = (l.u.flt == ((FltT)r.u.integer));
} }
else if (l.type==INT && r.type==FLOAT) else if (l.type==INT && r.type==FLOAT)
{ {
result.type = INT; result.type = INT;
result.u.integer=((double)l.u.integer)==r.u.flt; result.u.integer = (((FltT)l.u.integer) == r.u.flt);
} }
else if (l.type != r.type) else if (l.type != r.type)
{ {
@ -1249,6 +1124,22 @@ Token teq(Token l, Token r)
} }
/*}}}*/ /*}}}*/
static bool nearly_equal(FltT a1, FltT a2)
{
if (a1 == 0 && a2 == 0)
return true;
FltT A1 = ABSFLT(a1);
FltT A2 = ABSFLT(a2);
FltT max = A1 > A2 ? A1 : A2;
FltT eps = FLTEPS;
FltT diff = ABSFLT(a1-a2);
FltT thelog = LOG2FLT(max);
int expn = thelog;
FltT scale = POWFLT(2.0, expn);
return ABSFLT(a1 - a2) <= eps * scale;
}
/* tabouteq -- ~= operator */ /*{{{*/ /* tabouteq -- ~= operator */ /*{{{*/
Token tabouteq(Token l, Token r) Token tabouteq(Token l, Token r)
{ {
@ -1257,7 +1148,7 @@ Token tabouteq(Token l, Token r)
/*}}}*/ /*}}}*/
if (l.type==EEK) return tcopy(l); if (l.type==EEK) return tcopy(l);
else if (r.type==EEK) return tcopy(r); if (r.type==EEK) return tcopy(r);
if (l.type==EMPTY) if (l.type==EMPTY)
/* try to assign 0 element of r.type */ /*{{{*/ /* try to assign 0 element of r.type */ /*{{{*/
{ {
@ -1283,7 +1174,7 @@ Token tabouteq(Token l, Token r)
if (l.type==FLOAT && r.type==FLOAT) if (l.type==FLOAT && r.type==FLOAT)
{ {
result.type=INT; result.type=INT;
result.u.integer=(fabs(l.u.flt-r.u.flt)<=DBL_EPSILON); result.u.integer = (int)nearly_equal(l.u.flt, r.u.flt);
} }
else else
{ {
@ -1303,7 +1194,7 @@ Token tne(Token l, Token r)
/*}}}*/ /*}}}*/
if (l.type==EEK) return tcopy(l); if (l.type==EEK) return tcopy(l);
else if (r.type==EEK) return tcopy(r); if (r.type==EEK) return tcopy(r);
if (l.type==EMPTY) if (l.type==EMPTY)
/* try to assign 0 element of r.type */ /*{{{*/ /* try to assign 0 element of r.type */ /*{{{*/
{ {
@ -1333,12 +1224,12 @@ Token tne(Token l, Token r)
if (l.type==FLOAT && r.type==INT) if (l.type==FLOAT && r.type==INT)
{ {
result.type = INT; result.type = INT;
result.u.integer=l.u.flt!=((double)r.u.integer); result.u.integer = l.u.flt != ((FltT)r.u.integer);
} }
else if (l.type==INT && r.type==FLOAT) else if (l.type==INT && r.type==FLOAT)
{ {
result.type = INT; result.type = INT;
result.u.integer=((double)l.u.integer)!=r.u.flt; result.u.integer = ((FltT)l.u.integer)!=r.u.flt;
} }
else if (l.type != r.type) else if (l.type != r.type)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,56 @@
#ifndef FUNC_H #ifndef FUNC_H
#define FUNC_H #define FUNC_H
#include "scanner.h" #define MAX_FUNC_NAME_LENGTH 20
typedef enum
/* The purpose of this enum is to enforce backward compatibility
of numeric function identifiers which may be recorded in old
save files. Hence, do NOT change the order of the items in this
enum, merely add new identifiers at the end, just before
N_FUNCTION_IDENTIFIERS.
*/
{
NOT_A_FUNCTION = -1,
FIRST_FUNCTION = 0,
FUNC_AT_SYMBOL = 0, FUNC_AMPERSAND, FUNC_X, FUNC_Y, FUNC_Z, FUNC_EVAL,
FUNC_ERROR, FUNC_STRING, FUNC_SUM, FUNC_N, FUNC_INT, FUNC_FRAC, FUNC_LEN,
FUNC_MIN, FUNC_MAX, FUNC_ABS, FUNC_DOLLAR_SIGN, FUNC_FLOAT, FUNC_STRFTIME,
FUNC_CLOCK, FUNC_POLY, FUNC_E, FUNC_LOG, FUNC_SIN, FUNC_COS, FUNC_TAN,
FUNC_SINH, FUNC_COSH, FUNC_TANH, FUNC_ASIN, FUNC_ACOS, FUNC_ATAN,
FUNC_ARSINH, FUNC_ARCOSH, FUNC_ARTANH, FUNC_DEG2RAD, FUNC_RAD2DEG,
FUNC_RND, FUNC_SUBSTR, FUNC_STRPTIME, FUNC_TIME, FUNC_BITAND, FUNC_BITOR,
FUNC_R, FUNC_D, FUNC_CAP_X, FUNC_X_AMPERSAND, FUNC_NEGATE, FUNC_PLUS_SYMBOL,
FUNC_MINUS_SYMBOL, FUNC_ASTERISK, FUNC_SLASH,
FUNC_LESS_EQUAL, FUNC_GREATER_EQUAL, FUNC_LESS_THAN, FUNC_GREATER_THAN,
FUNC_EQUAL_EQUAL, FUNC_TILDE_EQUAL, FUNC_BANG_EQUAL, FUNC_CARET,
FUNC_PER_CENT, FUNC_CONCAT, FUNC_TAU, FUNC_SQRT, FUNC_FLOOR, FUNC_CEIL,
FUNC_TRUNC, FUNC_ROUND, FUNC_DECIMAL, FUNC_SCIENTIFIC,
N_FUNCTION_IDS
} FunctionIdentifier;
/* Forward declaration of Token, since this header is used in scanner.h */
typedef struct Token_struc Token;
typedef enum /* In increasing order of precedence */
{
INFIX_CONC, INFIX_REL, INFIX_PLUS, INFIX_MUL, PREFIX_NEG,
INFIX_POW, PREFIX_FUNC
} FunctionPrecedence;
typedef enum { FUNCT, MACRO } EvaluationStrategy;
typedef struct typedef struct
{ {
const char name[20]; const char name[MAX_FUNC_NAME_LENGTH + 1];
Token (*func)(int, const Token[]); Token (*func)(int, const Token*);
FunctionPrecedence precedence;
EvaluationStrategy eval_as;
const char* display_symbol;
} Tfunc; } Tfunc;
FunctionIdentifier identcode(const char *s, size_t len);
extern Tfunc tfunc[]; extern Tfunc tfunc[];
#endif #endif

View File

@ -43,6 +43,7 @@ extern char *strdup(const char* s);
#include "parser.h" #include "parser.h"
#include "sheet.h" #include "sheet.h"
#include "wk1.h" #include "wk1.h"
#include "xdr.h"
/*}}}*/ /*}}}*/
/* variables */ /*{{{*/ /* variables */ /*{{{*/
@ -157,7 +158,7 @@ int doanyway(Sheet *sheet, const char *msg)
/*}}}*/ /*}}}*/
/* do_edit -- set or modify cell contents */ /*{{{*/ /* do_edit -- set or modify cell contents */ /*{{{*/
static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv) static int do_edit(Sheet *cursheet, Key c, const char *expr, TokVariety tv)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
char buf[1024]; char buf[1024];
@ -165,6 +166,7 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv)
size_t x,offx; size_t x,offx;
Location scur; Location scur;
Token **t; Token **t;
Token newcont;
Cell *cell; Cell *cell;
/*}}}*/ /*}}}*/
@ -172,35 +174,46 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv)
if (locked(cell)) line_msg(_("Edit cell:"),_("Cell is locked")); if (locked(cell)) line_msg(_("Edit cell:"),_("Cell is locked"));
else else
{ {
newcont.type = EMPTY;
LOCATION_GETS(scur, cursheet->cur); LOCATION_GETS(scur, cursheet->cur);
if (expr) if (expr)
{ {
s = expr; s = expr;
t = scan(&s); t = scan(&s);
prompt = _("Cell contents:"); prompt = _("Cell contents:");
if (cv == ITERATIVE) prompt = _("Clocked cell contents"); if (tv == ITER_CONT) prompt = _("Clocked cell contents");
if (*s!='\0' && t==(Token**)0) line_msg(prompt, "XXX invalid expression"); if (*s != '\0')
if (t == EMPTY_TVEC)
line_msg(prompt, "XXX invalid expression");
else
{
newcont = eval_safe(t, LITERAL);
if (newcont.type = EEK)
line_msg(prompt, "XXX unparseable expression");
}
tvecfree(t);
} }
else else
{ {
Token cntt;
offx=0; offx=0;
if (c==K_NONE) if (c==K_NONE)
{ {
print(buf, sizeof(buf), 0, 1, getscientific(cell), -1, cntt = gettok(cell, tv);
getcont(cell, cv)); printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt);
s = buf+strlen(buf); s = buf+strlen(buf);
} }
else if (c==K_BACKSPACE) else if (c==K_BACKSPACE)
{ {
print(buf, sizeof(buf), 0, 1, getscientific(cell), -1, cntt = gettok(cell, tv);
getcont(cell, cv)); printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt);
if (strlen(buf)) *mbspos(buf+strlen(buf),-1)='\0'; if (strlen(buf)) *mbspos(buf+strlen(buf),-1)='\0';
s = buf+strlen(buf); s = buf+strlen(buf);
} }
else if (c==K_DC) else if (c==K_DC)
{ {
print(buf, sizeof(buf), 0, 1, getscientific(cell), -1, cntt = gettok(cell, tv);
getcont(cell, cv)); printtok(buf, sizeof(buf), 0, 1, getscientific(cell), -1, 0, &cntt);
memmove(buf,mbspos(buf,1),strlen(mbspos(buf,1))+1); memmove(buf,mbspos(buf,1),strlen(mbspos(buf,1))+1);
s = buf; s = buf;
} }
@ -222,22 +235,30 @@ static int do_edit(Sheet *cursheet, Key c, const char *expr, ContentVariety cv)
{ {
int r; int r;
tfree(&newcont);
newcont.type = EEK;
newcont.u.err = (char *)0;
x = mbslen(buf)-mbslen(s); x = mbslen(buf)-mbslen(s);
prompt = _("Cell contents:"); prompt = _("Cell contents:");
if (cv == ITERATIVE) prompt = _("Clocked cell contents:"); if (tv == ITER_CONT) prompt = _("Clocked cell contents:");
if ((r = line_edit(cursheet, buf, sizeof(buf), prompt, &x, &offx)) < 0) if ((r = line_edit(cursheet, buf, sizeof(buf), prompt, &x, &offx)) < 0)
return r; return r;
s = buf; s = buf;
if (buf[0] == '"' && buf[strlen(buf)-1] != '"' && strlen(buf)+1 < sizeof(buf)) { if (buf[0] == '"' && buf[strlen(buf)-1] != '"'
&& strlen(buf)+1 < sizeof(buf))
{
buf[strlen(buf)+1] = 0; buf[strlen(buf)+1] = 0;
buf[strlen(buf)] = '"'; buf[strlen(buf)] = '"';
} }
t = scan(&s); t = scan(&s);
} while (*s!='\0' && t==(Token**)0); if (t != EMPTY_TVEC) {
newcont = eval_safe(t, LITERAL);
}
tvecfree(t);
} while (*s != '\0' && newcont.type == EEK);
} }
if (t!=(Token**)0 && *t==(Token*)0) { free(t); t=(Token**)0; }
movetoloc(cursheet, scur); movetoloc(cursheet, scur);
putcont(cursheet, cursheet->cur, t, cv); puttok(cursheet, cursheet->cur, newcont, tv);
forceupdate(cursheet); forceupdate(cursheet);
} }
return 0; return 0;
@ -285,9 +306,11 @@ static int do_label(Sheet *sheet)
} }
} while (!ok); } while (!ok);
setlabel(sheet, sheet->cur, buf,1); setlabel(sheet, sheet->cur, buf,1);
if (buf[0]!='\0') if (buf[0] != '\0' && oldlabel[0] != '\0')
for (ALL_LOCS_IN_REGION(sheet,w)) for (ALL_LOCS_IN_REGION(sheet,w))
relabel(sheet, w, oldlabel, buf); relabel(sheet, w, oldlabel, buf);
cachelabels(sheet);
forceupdate(sheet);
} }
return -1; return -1;
} }
@ -1217,7 +1240,8 @@ void fillwith(Sheet *she)
Location dest; Location dest;
bool scan_labels = (src != NULLCELL && getlabel(src) != (const char*)0); bool scan_labels = (src != NULLCELL && getlabel(src) != (const char*)0);
/* the following is safe since we have handled copying a cell to itself */ /* the following is safe since we have handled copying a cell to itself */
for (ALL_LOCS_IN_REGION(she, dest)) copycelltosheet(src, she, dest); for (ALL_LOCS_IN_REGION(she, dest))
copycelltosheet(src, she, dest, ALTER_LABEL);
if (scan_labels) cachelabels(she); if (scan_labels) cachelabels(she);
forceupdate(she); forceupdate(she);
} }
@ -1508,7 +1532,7 @@ static int do_goto(Sheet *sheet, const char *expr)
LOCATION_GETS(upd_l, sheet->cur); LOCATION_GETS(upd_l, sheet->cur);
upd_sheet = sheet; upd_sheet = sheet;
value = eval(t, FULL); value = eval_safe(t, FULL);
tvecfree(t); tvecfree(t);
if (value.type==LOCATION && IN_OCTANT(value.u.location)) if (value.type==LOCATION && IN_OCTANT(value.u.location))
movetoloc(sheet, value.u.location); movetoloc(sheet, value.u.location);
@ -1851,9 +1875,10 @@ int main(int argc, char *argv[])
char *end; char *end;
n=strtol(optarg,&end,0); n=strtol(optarg,&end,0);
if (*end || n<0 || n>DBL_DIG) if (*end || n < 0 || n > LDBL_DIG)
{ {
fprintf(stderr,_("teapot: precision must be between 0 and %d.\n"),DBL_DIG); fprintf(stderr,
_("teapot: precision must be between 0 and %d.\n"), LDBL_DIG);
exit(1); exit(1);
} }
def_precision=n; def_precision=n;

View File

@ -97,7 +97,7 @@ static void catchfpe(int n)
caughtfpe=1; caughtfpe=1;
} }
const char *dblfinite(double x) const char *dblfinite(FltT x)
{ {
/*struct sigaction act; /*struct sigaction act;
@ -118,18 +118,19 @@ const char *dblfinite(double x)
/* If one comparison was allowed, more won't hurt either. */ /* If one comparison was allowed, more won't hurt either. */
if (x<0.0) if (x<0.0)
{ {
if (x<-DBL_MAX) return _("Not a (finite) floating point number"); /* -infinite */ if (x < -FLTMX) return _("Not a (finite) floating point number"); /* -infinite */
else return (const char*)0; else return (const char*)0;
} }
else if (x>0.0) else if (x>0.0)
{ {
if (x>DBL_MAX) return _("Not a (finite) floating point number"); /* +infinite */ if (x > FLTMX) return _("Not a (finite) floating point number"); /* +infinite */
else return (const char*)0; else return (const char*)0;
} }
else return _("Not a (finite) floating point number"); /* NaN */ else return _("Not a (finite) floating point number"); /* NaN */
} }
} }
/*}}}*/ /*}}}*/
/* fputc_close -- error checking fputc which closes stream on error */ /*{{{*/ /* fputc_close -- error checking fputc which closes stream on error */ /*{{{*/
int fputc_close(char c, FILE *fp) int fputc_close(char c, FILE *fp)
{ {

View File

@ -11,7 +11,7 @@ extern "C" {
void posorder(int *x, int *y); void posorder(int *x, int *y);
long int posnumber(const char *s, const char **endptr); long int posnumber(const char *s, const char **endptr);
const char *dblfinite(double x); const char *dblfinite(FltT x);
int fputc_close(char c, FILE *fp); int fputc_close(char c, FILE *fp);
int fputs_close(const char *s, FILE *fp); int fputs_close(const char *s, FILE *fp);
void adjust(Adjust a, char *s, size_t n); void adjust(Adjust a, char *s, size_t n);

View File

@ -20,6 +20,7 @@ extern char *strdup(const char* s);
#include "eval.h" #include "eval.h"
#include "func.h"
#include "main.h" #include "main.h"
#include "misc.h" #include "misc.h"
#include "parser.h" #include "parser.h"
@ -34,6 +35,27 @@ extern char *strdup(const char* s);
static Token term(Token *n[], int *i, EvalMethod meth); static Token term(Token *n[], int *i, EvalMethod meth);
/*}}}*/ /*}}}*/
/* full_eval_funcall -- evaluate the args of a funcall token and then
call the function on them
*/
static Token full_eval_funcall(Token *t)
{
assert(t->type == FUNCALL);
if (t->u.funcall.argc < 1)
return tfuncall(t->u.funcall.fident, t->u.funcall.argc, 0);
Token *eval_argv = malloc(t->u.funcall.argc*sizeof(Token));
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai) {
eval_argv[ai] = evaltoken(t->u.funcall.argv[ai], FULL);
}
Token result = tfuncall(t->u.funcall.fident, t->u.funcall.argc, eval_argv);
/* To allow a function to return one of its arguments, we need
to be sure not to free that argument: */
for (size_t ai = 0; ai < t->u.funcall.argc; ++ai)
tfree_protected(&eval_argv[ai], result);
free(eval_argv);
return result;
}
/* primary -- parse and evaluate a primary term */ /*{{{*/ /* primary -- parse and evaluate a primary term */ /*{{{*/
static Token primary(Token *n[], int *i, EvalMethod meth) static Token primary(Token *n[], int *i, EvalMethod meth)
{ {
@ -87,19 +109,7 @@ static Token primary(Token *n[], int *i, EvalMethod meth)
duperror(&result, _(") expected")); duperror(&result, _(") expected"));
return result; return result;
/*}}}*/ /*}}}*/
case MINUS: /* return negated term */ /*{{{*/ /* Unary minus will be handled in powterm */
{
++(*i);
if (meth == FULL) return tneg(primary(n, i, meth));
Token arg = primary(n, i, meth);
if (arg.type == EEK) return arg;
result.type = FUNCALL;
result.u.funcall.fident = identcode("negate", 6);
result.u.funcall.argc = 1;
result.u.funcall.argv = malloc(sizeof(Token));
result.u.funcall.argv[0] = arg;
return result;
}
case CP: case CP:
duperror(&result, _("Extra umatched ')'")); duperror(&result, _("Extra umatched ')'"));
return result; return result;
@ -200,18 +210,16 @@ static Token primary(Token *n[], int *i, EvalMethod meth)
if (argc > 0) if (argc > 0)
{ {
result.u.funcall.argv = malloc(argc*sizeof(Token)); result.u.funcall.argv = malloc(argc*sizeof(Token));
for (size_t ai; ai < argc; ++ai) for (size_t ai = 0; ai < argc; ++ai)
result.u.funcall.argv[ai] = argv[ai]; result.u.funcall.argv[ai] = argv[ai];
} } else result.u.funcall.argv = NULLTOKEN;
return result; return result;
} }
/*}}}*/ /*}}}*/
/* FUNCALL */ /*{{{*/ /* FUNCALL */ /*{{{*/
case FUNCALL: case FUNCALL:
if (meth == FULL) if (meth == FULL) result = full_eval_funcall(n[*i]);
result = tfuncall(n[*i]->u.funcall.fident, n[*i]->u.funcall.argc,
n[*i]->u.funcall.argv);
else result = tcopy(*n[*i]); else result = tcopy(*n[*i]);
++(*i); ++(*i);
return result; return result;
@ -230,9 +238,45 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
Token l; Token l;
size_t npows = 0; size_t npows = 0;
if (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op == MINUS)
{
/* A - symbol here is a pain. If it is being used as a function symbol, it
is higher precedence than exponentiation. If it is unary negation,
then it's lower precedence and we have to grab a powterm to the right,
and negate it. As far as I can tell the only way to tell is to
look ahead a term to see if there's a comma...
*/
bool unaryneg = true;
int j = *i + 1;
if (n[j] == NULLTOKEN)
return duperror(&l, _("A bare - is not a valid expression"));
if (n[j]->type == OPERATOR && n[j]->u.op == OP)
{
++j;
Token dummy = term(n, &j, meth);
if (n[j] != NULLTOKEN && n[j]->type == OPERATOR && n[j]->u.op == COMMA)
unaryneg = false;
tfree(&dummy);
}
if (unaryneg)
{
++(*i);
l = powterm(n, i, meth);
if (meth == FULL) return tneg(l);
if (l.type == EEK) return l;
if (TOKISNUM(l)) return tneg(l);
Token result;
result.type = FUNCALL;
result.u.funcall.fident = FUNC_NEGATE;
result.u.funcall.argc = 1;
result.u.funcall.argv = malloc(sizeof(Token));
result.u.funcall.argv[0] = l;
return result;
}
}
l = primary(n, i, meth); l = primary(n, i, meth);
if (l.type == EEK) return l; if (l.type == EEK) return l;
while (n[*i] != (Token*)0 && n[*i]->type == OPERATOR && n[*i]->u.op == POW) while (n[*i] != NULLTOKEN && n[*i]->type == OPERATOR && n[*i]->u.op == POW)
{ {
Token r; Token r;
@ -255,7 +299,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
{ {
Token tmp = l; Token tmp = l;
l.type = FUNCALL; l.type = FUNCALL;
l.u.funcall.fident = identcode("^", 1); l.u.funcall.fident = FUNC_CARET;
l.u.funcall.argc = 1; l.u.funcall.argc = 1;
l.u.funcall.argv = malloc(MAXARGC * sizeof(Token)); l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
l.u.funcall.argv[0] = tmp; l.u.funcall.argv[0] = tmp;
@ -278,7 +322,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
/* piterm -- parse and evaluate a product/division/modulo term */ /*{{{*/ /* piterm -- parse and evaluate a product/division/modulo term */ /*{{{*/
static Token piterm(Token *n[], int *i, EvalMethod meth) static Token piterm(Token *n[], int *i, EvalMethod meth)
{ {
int mulident = identcode("*", 1); FunctionIdentifier mulident = FUNC_ASTERISK;
Token l; Token l;
Operator op = CP; Operator op = CP;
bool first_funcall = true; bool first_funcall = true;
@ -317,6 +361,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
{ {
first_funcall = false; first_funcall = false;
Token tmp = l; Token tmp = l;
l.type = FUNCALL;
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op])); l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
l.u.funcall.argc = 2; l.u.funcall.argc = 2;
if (op == MUL) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token)); if (op == MUL) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
@ -344,7 +389,7 @@ static Token powterm(Token *n[], int *i, EvalMethod meth)
/* factor -- parse and evaluate a factor of sums/differences */ /*{{{*/ /* factor -- parse and evaluate a factor of sums/differences */ /*{{{*/
static Token factor(Token *n[], int *i, EvalMethod meth) static Token factor(Token *n[], int *i, EvalMethod meth)
{ {
int plusident = identcode("+", 1); FunctionIdentifier plusident = FUNC_PLUS_SYMBOL;
Token l; Token l;
Operator op = CP; Operator op = CP;
bool first_funcall = true; bool first_funcall = true;
@ -376,6 +421,7 @@ static Token factor(Token *n[], int *i, EvalMethod meth)
{ {
first_funcall = false; first_funcall = false;
Token tmp = l; Token tmp = l;
l.type = FUNCALL;
l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op])); l.u.funcall.fident = identcode(Op_Name[op], strlen(Op_Name[op]));
l.u.funcall.argc = 2; l.u.funcall.argc = 2;
if (op == PLUS) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token)); if (op == PLUS) l.u.funcall.argv = malloc(MAXARGC * sizeof(Token));
@ -453,14 +499,15 @@ static Token term(Token *n[], int *i, EvalMethod meth)
} }
/*}}}*/ /*}}}*/
/* eval -- parse and evaluate token sequence */ /*{{{*/ /* eval -- parse and evaluate nonempty token sequence
if the sequence might be empty, use eval_safe. */ /*{{{*/
Token eval(Token **n, EvalMethod meth) Token eval(Token **n, EvalMethod meth)
{ {
Token l; Token l;
int i = 0; int i = 0;
bool first_funcall = true; bool first_funcall = true;
assert(upd_sheet != (Sheet*)0); assert(meth == LITERAL || upd_sheet != (Sheet*)0);
l = term(n, &i, meth); l = term(n, &i, meth);
if (l.type == EEK) return l; if (l.type == EEK) return l;
@ -485,10 +532,11 @@ Token eval(Token **n, EvalMethod meth)
{ {
first_funcall = false; first_funcall = false;
Token tmp = l; Token tmp = l;
l.u.funcall.fident = identcode("concat", 6); l.type = FUNCALL;
l.u.funcall.fident = FUNC_CONCAT;
l.u.funcall.argc = 1; l.u.funcall.argc = 1;
l.u.funcall.argv = malloc(MAXARGC*sizeof(Token)); l.u.funcall.argv = malloc(MAXARGC*sizeof(Token));
l.u.funcall.argv[0] = l; l.u.funcall.argv[0] = tmp;
} }
if (l.u.funcall.argc >= MAXARGC) if (l.u.funcall.argc >= MAXARGC)
{ {
@ -508,10 +556,49 @@ Token eval(Token **n, EvalMethod meth)
Token eval_safe(Token **n, EvalMethod meth) Token eval_safe(Token **n, EvalMethod meth)
{ {
Token result; Token result;
if (n == EMPTY_TVEC) if (n == EMPTY_TVEC || *n == NULLTOKEN)
{ {
result.type = EMPTY; result.type = EMPTY;
return result; return result;
} }
return eval(n, meth); return eval(n, meth);
} }
/* evaltoken -- like eval, but evaluate a single token
NOTE: caller owns the result and must arrrange that it be
eventually tfree()ed */ /*{{{*/
Token evaltoken(Token n, EvalMethod meth)
{
if (meth == LITERAL) return tcopy(n);
switch (n.type) {
case EMPTY: return n;
case STRING: return tcopy(n);
case FLOAT:
case INT:
return n;
case OPERATOR: {
Token err;
err.type = EEK;
const char *templ = _("Attempt to eval bare operator token: ");
err.u.err = malloc(strlen(templ) + MAX_OP_NAME_LENGTH + 2);
strcpy(err.u.err, templ);
strcat(err.u.err, Op_Name[n.u.op]);
return err;
}
case LIDENT: return findlabel(upd_sheet, n.u.lident);
case FIDENT: {
Token err;
err.type = EEK;
const char *templ = _("Attempt to eval bare function identifier token: ");
err.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 2);
strcpy(err.u.err, templ);
strcat(err.u.err, tfunc[n.u.fident].name);
return err;
}
case LOCATION: return n;
case FUNCALL: return full_eval_funcall(&n);
case EEK: return tcopy(n);
default: assert(0);
}
return n;
}

View File

@ -17,8 +17,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "eval.h" #include "eval.h"
#include "parser.h"
#include "main.h" #include "main.h"
#include "sheet.h" #include "sheet.h"
#include "sc.h" #include "sc.h"
@ -241,18 +241,21 @@ const char *loadsc(Sheet *sheet, const char *name)
setwidth(sheet, col, 0, colwidth); setwidth(sheet, col, 0, colwidth);
} }
/*}}}*/ /*}}}*/
else if (strncmp(buf,"leftstring ",11)==0 || strncmp(buf,"rightstring ",12)==0) /* rightstring/leftstring cell = "string" */ /*{{{*/ else if (strncmp(buf, "leftstring ", 11) == 0
|| strncmp(buf, "rightstring ", 12) == 0) /* rightstring/leftstring cell = "string" */ /*{{{*/
{ {
int x,y; int x,y;
const char *s; const char *s;
Token **contents; Token **contents;
if (strncmp(buf, "leftstring ", 11) == 0) s = buf+11; else s = buf+12; if (strncmp(buf, "leftstring ", 11) == 0) s = buf+11; else s = buf+12;
x=*s++-'A'; if (*s>='A' && *s<='Z') x=x*26+(*s++-'A'); x = *s++ - 'A';
y=*s++-'0'; while (*s>='0' && *s<='9') y=10*y+(*s++-'0'); if (*s >= 'A' && *s <= 'Z' ) x = x*26 + (*s++ - 'A');
y = *s++ - '0';
while (*s >= '0' && *s <= '9') y = 10*y + (*s++-'0');
s+=3; s+=3;
contents = scan(&s); contents = scan(&s);
if (contents==(Token**)0) if (contents == EMPTY_TVEC)
{ {
tvecfree(contents); tvecfree(contents);
sprintf(errbuf, _("Expression syntax error in line %d"), line); sprintf(errbuf, _("Expression syntax error in line %d"), line);
@ -262,7 +265,16 @@ const char *loadsc(Sheet *sheet, const char *name)
tmp[X] = x; tmp[Y] = y; tmp[Z] = 0; tmp[X] = x; tmp[Y] = y; tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp); cell = initcellofsheet(sheet, tmp);
cell->adjust = strncmp(buf, "leftstring ", 11) ? RIGHT : LEFT; cell->adjust = strncmp(buf, "leftstring ", 11) ? RIGHT : LEFT;
cell->contents[BASE] = contents; cell->tok[BASE_CONT] = eval_safe(contents, LITERAL);
tvecfree(contents);
if (cell->tok[BASE_CONT].type == EEK)
{
sprintf(errbuf, _("Parse error in line %d: %s"),
line, cell->tok[BASE_CONT].u.err);
tfree(&(cell->tok[BASE_CONT]));
err = errbuf;
goto eek;
}
} }
/*}}}*/ /*}}}*/
else if (strncmp(buf,"let ",4)==0) /* let cell = expression */ /*{{{*/ else if (strncmp(buf,"let ",4)==0) /* let cell = expression */ /*{{{*/
@ -277,7 +289,7 @@ const char *loadsc(Sheet *sheet, const char *name)
x=*s++-'A'; if (*s>='A' && *s<='Z') x=x*26+(*s++-'A'); x=*s++-'A'; if (*s>='A' && *s<='Z') x=x*26+(*s++-'A');
y=*s++-'0'; while (*s>='0' && *s<='9') y=10*y+(*s++-'0'); y=*s++-'0'; while (*s>='0' && *s<='9') y=10*y+(*s++-'0');
tmp[X] = x; tmp[Y] = y; tmp[Z] = 0; tmp[X] = x; tmp[Y] = y; tmp[Z] = 0;
if (getcont(CELL_ATC(sheet,x,y,0),BASE)==(Token**)0) if (gettok(CELL_ATC(sheet,x,y,0), BASE_CONT).type == EMPTY)
{ {
s += 3; s += 3;
s2t_s = s; s2t_t = newbuf; s2t_s = s; s2t_t = newbuf;
@ -303,7 +315,16 @@ const char *loadsc(Sheet *sheet, const char *name)
tmp[X] = x; tmp[Y] = y; tmp[Z] = 0; tmp[X] = x; tmp[Y] = y; tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp); cell = initcellofsheet(sheet, tmp);
cell->adjust = RIGHT; cell->adjust = RIGHT;
cell->contents[BASE] = contents; cell->tok[BASE_CONT] = eval_safe(contents, LITERAL);
tvecfree(contents);
if (cell->tok[BASE_CONT].type == EEK)
{
sprintf(errbuf, _("Parse error in line %d: %s"),
line, cell->tok[BASE_CONT].u.err);
tfree(&(cell->tok[BASE_CONT]));
err = errbuf;
goto eek;
}
} }
} }
/*}}}*/ /*}}}*/

View File

@ -18,6 +18,7 @@
#include <stdlib.h> #include <stdlib.h>
extern char *strdup(const char* s); extern char *strdup(const char* s);
extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */ extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */
extern long double strtold(const char *nptr, char **endptr); /* SunOS 4 hack */
#include <string.h> #include <string.h>
@ -43,18 +44,6 @@ const char *Op_Name[] =
[POW] = "^", [MOD] = "%" [POW] = "^", [MOD] = "%"
}; };
/* identcode -- return number of identifier */ /*{{{*/
int identcode(const char *s, size_t len)
{
Tfunc *p;
int fident;
for (p=tfunc,fident=0; p->name[0]!='\0' && (len!=strlen(p->name) || strncmp(s,p->name,len)); ++p,++fident);
if (p->name[0]=='\0') return -1;
else return fident;
}
/*}}}*/
/* loc_in_box -- returns true if test is in the box determined by b and c */ /* loc_in_box -- returns true if test is in the box determined by b and c */
bool loc_in_box(const Location test, bool loc_in_box(const Location test,
const Location b, const Location c) const Location b, const Location c)
@ -67,10 +56,21 @@ bool loc_in_box(const Location test,
return true; return true;
} }
/* cleartoken - Initialize all of the memory of a token */ /*{{{*/
void cleartoken(Token* tok)
{
tok->type = EMPTY;
tok->u.location[0] = 0;
tok->u.location[1] = 0;
tok->u.location[2] = 0;
}
/* duperror - Sets tok to an error and strdups the message into place */ /* duperror - Sets tok to an error and strdups the message into place */
void duperror(Token* tok, const char* erro) { Token duperror(Token* tok, const char* erro)
{
tok->type = EEK; tok->type = EEK;
tok->u.err = strdup(erro); tok->u.err = strdup(erro);
return *tok;
} }
/* charstring -- match quoted string and return token */ /*{{{*/ /* charstring -- match quoted string and return token */ /*{{{*/
@ -103,6 +103,7 @@ static Token *charstring(const char **s)
else return (Token*)0; else return (Token*)0;
} }
/*}}}*/ /*}}}*/
/* integer -- match an unsigned integer and return token */ /*{{{*/ /* integer -- match an unsigned integer and return token */ /*{{{*/
static Token *integer(const char **s) static Token *integer(const char **s)
{ {
@ -123,6 +124,7 @@ static Token *integer(const char **s)
else { *s=r; return (Token*)0; } else { *s=r; return (Token*)0; }
} }
/*}}}*/ /*}}}*/
/* flt -- match a floating point number */ /*{{{*/ /* flt -- match a floating point number */ /*{{{*/
static Token *flt(const char **s) static Token *flt(const char **s)
{ {
@ -130,11 +132,11 @@ static Token *flt(const char **s)
const char *t; const char *t;
char *end; char *end;
Token *n; Token *n;
double x; FltT x;
/*}}}*/ /*}}}*/
t=*s; t=*s;
x=strtod(t,&end); x = STRTOFLT(t,&end);
*s = end; *s = end;
if (t!=*s && dblfinite(x)==(const char*)0) if (t!=*s && dblfinite(x)==(const char*)0)
{ {
@ -150,6 +152,7 @@ static Token *flt(const char **s)
} }
} }
/*}}}*/ /*}}}*/
/* op -- match an op and return token */ /*{{{*/ /* op -- match an op and return token */ /*{{{*/
static Token *op(const char **s) static Token *op(const char **s)
{ {
@ -194,7 +197,7 @@ static Token *ident(const char **s)
begin=*s; ++(*s); begin=*s; ++(*s);
while (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$' || isdigit((int)**s)) ++(*s); while (isalpha((int)**s) || **s=='_' || **s=='@' || **s=='&' || **s=='.' || **s=='$' || isdigit((int)**s)) ++(*s);
result=malloc(sizeof(Token)); result=malloc(sizeof(Token));
if ((fident=identcode(begin,(size_t)(*s-begin)))==-1) if ((fident = identcode(begin,(size_t)(*s-begin))) == NOT_A_FUNCTION)
{ {
result->type=LIDENT; result->type=LIDENT;
result->u.lident=malloc((size_t)(*s-begin+1)); result->u.lident=malloc((size_t)(*s-begin+1));
@ -218,24 +221,24 @@ Token **scan(const char **s)
/* variables */ /*{{{*/ /* variables */ /*{{{*/
Token **na,*n; Token **na,*n;
const char *r; const char *r;
int i,j;
/*}}}*/ /*}}}*/
/* compute number of tokens */ /*{{{*/ /* compute number of tokens */ /*{{{*/
r = *s; r = *s;
while (*r == ' ') ++r; while (*r == ' ') ++r;
for (i=0; *r!='\0'; ++i) int i = 0;
for (; *r != '\0'; ++i)
{ {
const char *or; const char *or;
or = r; or = r;
while (*r == ' ') ++r; while (*r == ' ') ++r;
n = charstring(&r); n = charstring(&r);
if (n==(Token*)0) n=op(&r); if (n == NULLTOKEN) n = op(&r);
if (n==(Token*)0) n=integer(&r); if (n == NULLTOKEN) n = integer(&r);
if (n==(Token*)0) n=flt(&r); if (n == NULLTOKEN) n = flt(&r);
if (n==(Token*)0) n=ident(&r); if (n == NULLTOKEN) n = ident(&r);
if (or==r) { *s=r; return (Token**)0; } if (or == r) { *s = r; return EMPTY_TVEC; }
} }
/*}}}*/ /*}}}*/
/* allocate token space */ /*{{{*/ /* allocate token space */ /*{{{*/
@ -244,17 +247,17 @@ Token **scan(const char **s)
/* store tokens */ /*{{{*/ /* store tokens */ /*{{{*/
r = *s; r = *s;
while (*r==' ') ++r; while (*r==' ') ++r;
for (j=0; j<i; ++j) for (int j = 0; j < i; ++j)
{ {
while (*r == ' ') ++r; while (*r == ' ') ++r;
n = charstring(&r); n = charstring(&r);
if (n==(Token*)0) n=op(&r); if (n == NULLTOKEN) n = op(&r);
if (n==(Token*)0) n=integer(&r); if (n == NULLTOKEN) n = integer(&r);
if (n==(Token*)0) n=flt(&r); if (n == NULLTOKEN) n = flt(&r);
if (n==(Token*)0) n=ident(&r); if (n == NULLTOKEN) n = ident(&r);
na[j] = n; na[j] = n;
} }
na[j]=(Token*)0; na[i] = NULLTOKEN;
/*}}}*/ /*}}}*/
return na; return na;
} }
@ -273,20 +276,17 @@ static int print_fident(char* dest, size_t space, int id)
/* printtok -- print a single token, passed by address, although not changed */ /*{{{*/ /* printtok -- print a single token, passed by address, although not changed */ /*{{{*/
size_t printtok(char* dest, size_t size, size_t field_width, size_t printtok(char* dest, size_t size, size_t field_width,
int quote_strings, int use_scientific, bool quote_strings, bool use_scientific,
int precision, int verbose_error, const Token *tok) int precision, bool verbose_error, const Token *tok)
{ {
size_t cur;
if (debug_level > 2) { if (debug_level > 2) {
printf("..Entering printtok; bufsize %d, field_width %d, qs %d, us %d, prec %d, verr %d\n", size, field_width, quote_strings, use_scientific, precision, verbose_error); printf("..Entering printtok; bufsize %d, field_width %d, qs %d, us %d, prec %d, verr %d\n", size, field_width, quote_strings, use_scientific, precision, verbose_error);
} }
cur = 0; if (size > 0 ) *dest = '\0';
if (tok != NULLTOKEN) switch (tok->type) if (tok == NULLTOKEN || tok->type == EMPTY) return 0;
size_t cur = 0;
switch (tok->type)
{ {
/* EMPTY */ /*{{{*/
case EMPTY: if (size > 0) dest[cur] = '\0'; break;
/*}}}*/
/* STRING */ /*{{{*/ /* STRING */ /*{{{*/
case STRING: case STRING:
{ {
@ -305,10 +305,10 @@ size_t printtok(char* dest, size_t size, size_t field_width,
/* INT */ /*{{{*/ /* INT */ /*{{{*/
case INT: case INT:
{ {
char buf[20]; char buf[64];
size_t buflen; size_t buflen;
buflen=sprintf(buf,"%ld",tok->u.integer); buflen = sprintf(buf, INT_T_FMT, tok->u.integer);
assert(buflen < sizeof(buf)); assert(buflen < sizeof(buf));
(void)strncpy(dest+cur,buf,size-cur-1); (void)strncpy(dest+cur,buf,size-cur-1);
cur+=buflen; cur+=buflen;
@ -323,11 +323,14 @@ size_t printtok(char* dest, size_t size, size_t field_width,
size_t len; size_t len;
/*}}}*/ /*}}}*/
len=sprintf(buf, use_scientific ? "%.*e" : "%.*f", bool use_sci = use_scientific
precision == -1 ? DBL_DIG-2 : precision, || (int)ABSFLT(LOG10FLT(tok->u.flt)) > FLT_T_DIG;
len = sprintf(buf, use_sci ? FLT_T_SCI_FMT : FLT_T_STD_FMT,
precision == -1 ? FLT_T_DIG-2 : precision,
tok->u.flt); tok->u.flt);
assert(len<sizeof(buf)); assert(len<sizeof(buf));
if (!use_scientific && precision==-1) if (!use_sci)
{ {
p=&buf[len-1]; p=&buf[len-1];
while (p>buf && *p == '0' && *(p-1) != '.') { *p = '\0'; --p; --len; } while (p>buf && *p == '0' && *(p-1) != '.') { *p = '\0'; --p; --len; }

View File

@ -1,17 +1,21 @@
#ifndef SCANNER_H #ifndef SCANNER_H
#define SCANNER_H #define SCANNER_H
#include <math.h>
#include <sys/types.h> #include <sys/types.h>
#include <stdbool.h> #include <stdbool.h>
#include "func.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/* TO PRESERVE ABILITY TO READ OLD SAVE FILES, ONLY ADD ITEMS AT END */
typedef enum { typedef enum {
EMPTY EMPTY
#ifndef __cplusplus #ifndef __cplusplus
, STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, FUNCALL, EEK , STRING, FLOAT, INT, OPERATOR, LIDENT, FIDENT, LOCATION, EEK, FUNCALL
#endif #endif
} Type; } Type;
@ -47,26 +51,107 @@ typedef enum { X=0, Y=1, Z=2, HYPER} Dimensions;
bool loc_in_box(const Location test, bool loc_in_box(const Location test,
const Location b, const Location c); const Location b, const Location c);
typedef struct Token_struc Token;
typedef struct typedef struct
{ {
int fident; FunctionIdentifier fident;
int argc; int argc;
Token *argv; Token *argv;
} FunctionCall; } FunctionCall;
#define FLT_LONG_DOUBLE
#ifdef FLT_LONG_DOUBLE
typedef long double FltT;
/* To switch the internal type used to store a floating value, it is
supposed to suffice to redefine all of the following macros:
*/
#define FLT_T_STD_FMT "%.*Lf"
#define FLT_T_SCI_FMT "%.*Le"
#define FLT_T_DIG LDBL_DIG
#define FLTEPS LDBL_EPSILON
#define FLTMX LDBL_MAX
#define STRTOFLT strtold
#define ABSFLT fabsl
#define LOGFLT logl
#define LOG2FLT log2l
#define LOG10FLT log10l
#define SQRTFLT sqrtl
#define POWFLT powl
#define FMODFLT fmodl
#define MODFFLT modfl
#define SINFLT sinl
#define COSFLT cosl
#define TANFLT tanl
#define SINHFLT sinhl
#define COSHFLT coshl
#define TANHFLT tanhl
#define ASINFLT asinl
#define ACOSFLT acosl
#define ATANFLT atanl
#define FLOORFLT floorl
#define CEILFLT ceill
#define ROUNDFLT rintl
#define TRUNCFLT truncl
#else
typedef double FltT;
/* To switch the internal type used to store a floating value, it is
supposed to suffice to redefine all of the following macros:
*/
#define FLT_T_STD_FMT "%.*f"
#define FLT_T_SCI_FMT "%.*e"
#define FLT_T_DIG DBL_DIG
#define FLTEPS DBL_EPSILON
#define FLTMX DBL_MAX
#define STRTOFLT strtod
#define ABSFLT fabs
#define LOGFLT log
#define LOG2FLT log2
#define LOG10FLT log10
#define SQRTFLT sqrt
#define POWFLT pow
#define FMODFLT fmod
#define MODFFLT modf
#define SINFLT sin
#define COSFLT cos
#define TANFLT tan
#define SINHFLT sinh
#define COSHFLT cosh
#define TANHFLT tanh
#define ASINFLT asin
#define ACOSFLT acos
#define ATANFLT atan
#define FLOORFLT floor
#define CEILFLT ceil
#define ROUNDFLT rint
#define TRUNCFLT trunc
#endif
typedef long long IntT;
/* To switch the internal type used to store an integer value, it is
supposed to suffice to redefine all of the following macros and typedefs.
*/
typedef unsigned long long UIntT;
#define INT_T_FMT "%lld"
#define STRTOINT strtoll
/* This one depends on both the floating point and integer types */
#ifdef FLT_LONG_DOUBLE
#define ROUNDFLTINT llrintl
#else
#define ROUNDFLTINT llrint
#endif
/* End of subtype definitions */
/* The main type defined here; should this be token.h ?*/
typedef struct Token_struc typedef struct Token_struc
{ {
Type type; Type type;
union union
{ {
char *string; char *string;
double flt; FltT flt;
long integer; IntT integer;
Operator op; Operator op;
char *lident; char *lident;
int fident; FunctionIdentifier fident;
Location location; Location location;
FunctionCall funcall; FunctionCall funcall;
char *err; char *err;
@ -75,13 +160,14 @@ typedef struct Token_struc
#define NULLTOKEN ((Token*)0) #define NULLTOKEN ((Token*)0)
#define EMPTY_TVEC ((Token**)0) #define EMPTY_TVEC ((Token**)0)
#define TOKISNUM(t) ((t).type == INT || (t).type == FLOAT || (t).type == EMPTY)
int identcode(const char *s, size_t len); Token duperror(Token* tok, const char* erro);
void duperror(Token* tok, const char* erro);
Token **scan(const char **s); Token **scan(const char **s);
void cleartoken(Token* tok);
size_t printtok(char *dest, size_t size, size_t field_width, size_t printtok(char *dest, size_t size, size_t field_width,
int quote_strings, int use_scientific, bool quote_strings, bool use_scientific,
int precision, int verbose_error, const Token *tok); int precision, bool verbose_error, const Token *tok);
void print(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Token **n); void print(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Token **n);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -46,7 +46,8 @@ int max_eval;
/*}}}*/ /*}}}*/
/* copycelltosheet -- copy a cell into a sheet*/ /*{{{*/ /* copycelltosheet -- copy a cell into a sheet*/ /*{{{*/
void copycelltosheet(const Cell *fromcell, Sheet *sheet2, const Location to) void copycelltosheet(const Cell *fromcell, Sheet *sheet2,
const Location to, LabelHandling lh)
{ {
assert(sheet2 != (Sheet*)0); assert(sheet2 != (Sheet*)0);
Cell *tocell = safe_cell_at(sheet2, to); Cell *tocell = safe_cell_at(sheet2, to);
@ -59,7 +60,7 @@ void copycelltosheet(const Cell *fromcell, Sheet *sheet2, const Location to)
sheet2->changed = 1; sheet2->changed = 1;
Cell *alsotocell = initcellofsheet(sheet2, to); Cell *alsotocell = initcellofsheet(sheet2, to);
assert(tocell == NULLCELL || tocell == alsotocell); assert(tocell == NULLCELL || tocell == alsotocell);
copycell(alsotocell, fromcell); copycell(alsotocell, fromcell, lh);
} }
/*}}}*/ /*}}}*/
} }
@ -91,16 +92,14 @@ static void dump_cell(Sheet *sheet, int x, int y, int z)
return; return;
} }
printf("TEADUMP of &(%d,%d,%d):\n", x, y, z); printf("TEADUMP of &(%d,%d,%d):\n", x, y, z);
print(buf, sizeof(buf), 0, 1, 0, -1, c->contents[BASE]); for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
printf(" Base expr: %s.\n", buf); {
print(buf, sizeof(buf), 0, 1, 0, -1, c->contents[ITERATIVE]); printf(" %s: ", TokVariety_Name[tv]);
printf(" Update expr: %s.\n", buf); printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, c->tok + tv);
if (c->label == (char *)0) printf("\n No label.\n"); printf("%s.\n", buf);
}
if (c->label == NULL) printf("\n No label.\n");
else printf("\n Label: %s.\n", c->label); else printf("\n Label: %s.\n", c->label);
printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, &(c->value));
printf(" Current value: %s.\n", buf);
printtok(buf, sizeof(buf), 0, 1, 0, -1, 1, &(c->resvalue));
printf(" Stored result value: %s.\n Adjustment: ", buf);
switch (c->adjust) { switch (c->adjust) {
case LEFT: printf("LEFT\n"); break; case LEFT: printf("LEFT\n"); break;
case RIGHT: printf("RIGHT\n"); break; case RIGHT: printf("RIGHT\n"); break;
@ -108,6 +107,8 @@ static void dump_cell(Sheet *sheet, int x, int y, int z)
case AUTOADJUST: printf("AUTO\n"); break; case AUTOADJUST: printf("AUTO\n"); break;
} }
printf(" Precision: %d\n Attributes: ", c->precision); printf(" Precision: %d\n Attributes: ", c->precision);
for (ColorAspect ca = FOREGROUND; ca < NUM_COLOR_ASPECTS; ++ca)
printf(" %s color: %d\n", ColorAspect_Name[ca], c->aspect[ca]);
if (c->updated) printf("updated "); if (c->updated) printf("updated ");
if (c->shadowed) printf("shadowed "); if (c->shadowed) printf("shadowed ");
if (c->scientific) printf("scientific "); if (c->scientific) printf("scientific ");
@ -154,6 +155,30 @@ static void swapblock(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int
sheet2->changed=1; sheet2->changed=1;
} }
/*}}}*/ /*}}}*/
/* signum -- -1 if < 0, 0 if 0, 1 if > 0 */
static int signum(int x) {
return (x > 0) - (x < 0);
}
/* cmpflt -- -1 if <, 0 if =, 1 if > */
static int cmpflt(FltT x, FltT y) {
return (x > y) - (x < y);
}
/* cmpflint -- -1 if <, 0 if =, 1 if > */
static int cmpflint(FltT x, IntT y) {
return (x > y) - (x < y);
}
/* cmpint -- -1 if <, 0 if =, 1 if > */
static int cmpint(IntT x, IntT y) {
return (x > y) - (x < y);
}
/* cmpintfl -- -1 if <, 0 if =, 1 if > */
static int cmpintfl(IntT x, FltT y) {
return (x > y) - (x < y);
}
#define NOT_COMPARABLE 2
/* cmpcell -- compare to cells with given order flags */ /*{{{*/ /* cmpcell -- compare to cells with given order flags */ /*{{{*/
/* Notes */ /*{{{*/ /* Notes */ /*{{{*/
/* /*
@ -164,73 +189,42 @@ second. A result of 2 means they are not comparable.
/*}}}*/ /*}}}*/
static int cmpcell(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int x2, int y2, int z2, int sortkey) static int cmpcell(Sheet *sheet1, int x1, int y1, int z1, Sheet *sheet2, int x2, int y2, int z2, int sortkey)
{ {
Cell *leftcell, *rightcell; int ascending = (sortkey & ASCENDING) ? 1 : -1;
assert(sheet1!=(Sheet*)0); Token leftval = gettok(safe_cell_atc(sheet1, x1, y1, z1), CURR_VAL);
assert(sheet2!=(Sheet*)0); Token rightval = gettok(safe_cell_atc(sheet2, x2, y2, z2), CURR_VAL);
/* empty cells are smaller than any non-empty cell */ /*{{{*/ /* empty cells are smaller than any non-empty cell */ /*{{{*/
if (!CELL_IS_GOODC(sheet1,x1,y1,z1) || CELL_ATC(sheet1,x1,y1,z1)->value.type==EMPTY) if (leftval.type == EMPTY)
{ if (rightval.type == EMPTY) return 0;
if (!CELL_IS_GOODC(sheet2,x2,y2,z2) || CELL_ATC(sheet2,x2,y2,z2)->value.type==EMPTY) return 0; else return -ascending;
else return (sortkey&ASCENDING ? -1 : 1); if (rightval.type == EMPTY) return ascending;
}
if (!CELL_IS_GOODC(sheet2,x2,y2,z2) || CELL_ATC(sheet2,x2,y2,z2)->value.type==EMPTY) return (sortkey&ASCENDING ? 1 : -1);
/*}}}*/ /*}}}*/
leftcell = CELL_ATC(sheet1,x1,y1,z1); switch (leftval.type)
rightcell = CELL_ATC(sheet2,x2,y2,z2);
switch (leftcell->value.type)
{ {
/* STRING */ /*{{{*/ /* STRING */ /*{{{*/
case STRING: case STRING:
{ if (rightval.type == STRING)
if (rightcell->value.type==STRING) return signum(strcmp(leftval.u.string, rightval.u.string)) * ascending;
{ return NOT_COMPARABLE;
int r;
r=strcmp(leftcell->value.u.string, rightcell->value.u.string);
if (r<0) return (sortkey&ASCENDING ? -1 : 1);
else if (r==0) return 0;
else return (sortkey&ASCENDING ? 1 : -1);
}
return 2;
}
/*}}}*/ /*}}}*/
/* FLOAT */ /*{{{*/ /* FLOAT */ /*{{{*/
case FLOAT: case FLOAT:
{ if (rightval.type == FLOAT)
if (rightcell->value.type==FLOAT) return cmpflt(leftval.u.flt, rightval.u.flt)*ascending;
{ if (rightval.type == INT)
if (leftcell->value.u.flt<rightcell->value.u.flt) return (sortkey&ASCENDING ? -1 : 1); return cmpflint(leftval.u.flt, rightval.u.integer)*ascending;
else if (leftcell->value.u.flt==rightcell->value.u.flt) return 0; return NOT_COMPARABLE;
else return (sortkey&ASCENDING ? 1 : -1);
}
if (rightcell->value.type==INT)
{
if (leftcell->value.u.flt<rightcell->value.u.integer) return (sortkey&ASCENDING ? -1 : 1);
else if (leftcell->value.u.flt==rightcell->value.u.integer) return 0;
else return (sortkey&ASCENDING ? 1 : -1);
}
return 2;
}
/*}}}*/ /*}}}*/
/* INT */ /*{{{*/ /* INT */ /*{{{*/
case INT: case INT:
{ {
if (rightcell->value.type==INT) if (rightval.type == INT)
{ return cmpint(leftval.u.integer, rightval.u.integer)*ascending;
if (leftcell->value.u.integer<rightcell->value.u.integer) return (sortkey&ASCENDING ? -1 : 1); if (rightval.type == FLOAT)
else if (leftcell->value.u.integer==rightcell->value.u.integer) return 0; return cmpintfl(leftval.u.integer, rightval.u.flt)*ascending;
else return (sortkey&ASCENDING ? 1 : -1); return NOT_COMPARABLE;
}
if (rightcell->value.type==FLOAT)
{
if (leftcell->value.u.integer<rightcell->value.u.flt) return (sortkey&ASCENDING ? -1 : 1);
else if (leftcell->value.u.integer==rightcell->value.u.flt) return 0;
else return (sortkey&ASCENDING ? 1 : -1);
}
return 2;
} }
/*}}}*/ /*}}}*/
default: return 2; default: return NOT_COMPARABLE;
} }
} }
/*}}}*/ /*}}}*/
@ -334,6 +328,17 @@ Cell *initcellofsheet(Sheet *sheet, const Location at)
even in the face of various edge conditions. even in the face of various edge conditions.
*/ */
Cell *safe_cell_atc(Sheet *sheet, int x, int y, int z)
{
if (sheet == (Sheet*)0) return NULLCELL;
if (sheet->sheet == (Cell **)0) return NULLCELL;
if (!LOC_WITHINC(sheet,x,y,z)) return NULLCELL;
return CELL_ATC(sheet,x,y,z);
}
/* safe_cell_at -- returns pointer to the cell at the given location,
even in the face of various edge conditions.
*/
Cell *safe_cell_at(Sheet *sheet, const Location at) Cell *safe_cell_at(Sheet *sheet, const Location at)
{ {
if (sheet == (Sheet*)0) return NULLCELL; if (sheet == (Sheet*)0) return NULLCELL;
@ -511,21 +516,21 @@ int cellwidth(Sheet *sheet, const Location at)
/*}}}*/ /*}}}*/
/* putcont -- assign new contents */ /*{{{*/ /* putcont -- assign new contents */ /*{{{*/
void putcont(Sheet *sheet, const Location at, Token **t, ContentVariety v) void puttok(Sheet *sheet, const Location at, Token t, TokVariety v)
{ {
Cell *cell; Cell *cell;
assert(sheet != (Sheet*)0); assert(sheet != (Sheet*)0);
sheet->changed = 1; sheet->changed = 1;
cell = initcellofsheet(sheet, at); cell = initcellofsheet(sheet, at);
tvecfree(cell->contents[v]); tfree(&(cell->tok[v]));
cell->contents[v] = t; cell->tok[v] = t;
redraw_cell(sheet, at); redraw_cell(sheet, at);
} }
/*}}}*/ /*}}}*/
/* getvalue -- get tcopy()ed value */ /*{{{*/ /* recompvalue -- get tcopy()ed value */ /*{{{*/
Token getvalue(Sheet *sheet, const Location at) Token recompvalue(Sheet *sheet, const Location at)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
Token result; Token result;
@ -551,19 +556,13 @@ Token getvalue(Sheet *sheet, const Location at)
depending on this call to update the current value depending on this call to update the current value
*/ */
cell = CELL_AT(sheet, at); cell = CELL_AT(sheet, at);
if (!upd_clock && getcont(cell,2) == EMPTY_TVEC) return result; if (!upd_clock && gettok(cell, CONTINGENT).type == EMPTY) return result;
/* update value of this cell if needed and return it */ /*{{{*/ /* update value of this cell if needed and return it */ /*{{{*/
orig_upd_clock = upd_clock; orig_upd_clock = upd_clock;
if (cell->ignored) if (cell->ignored)
{ {
/* variables */ /*{{{*/ tfree(cell->tok + CURR_VAL);
Token oldvalue;
/*}}}*/
oldvalue = cell->value;
cell->updated = 1; cell->updated = 1;
cell->value.type = EMPTY;
tfree(&oldvalue);
} }
else if (cell->updated == 0) else if (cell->updated == 0)
{ {
@ -583,24 +582,24 @@ Token getvalue(Sheet *sheet, const Location at)
if (cell->clock_t1 == 0) if (cell->clock_t1 == 0)
{ {
cell->updated = 1; cell->updated = 1;
oldvalue = cell->value; oldvalue = gettok(cell, CURR_VAL);
upd_clock = 0; upd_clock = 0;
cell->value = eval_safe(getcont(cell, CONTINGENT), FULL); cell->tok[CURR_VAL] = evaltoken(gettok(cell, CONTINGENT), FULL);
tfree(&oldvalue); tfree(&oldvalue);
} }
else if (upd_clock) else if (upd_clock)
{ {
cell->updated = 1; cell->updated = 1;
upd_clock = 0; upd_clock = 0;
oldvalue = cell->resvalue; oldvalue = gettok(cell, RES_VAL);
cell->resvalue = eval_safe(getcont(cell, CONTINGENT), FULL); cell->tok[RES_VAL] = evaltoken(gettok(cell, CONTINGENT), FULL);
tfree(&oldvalue); tfree(&oldvalue);
} }
upd_sheet = old_sheet; upd_sheet = old_sheet;
LOCATION_GETS(upd_l, old_l); LOCATION_GETS(upd_l, old_l);
max_eval = old_max_eval; max_eval = old_max_eval;
} }
if (!orig_upd_clock) result = tcopy(cell->value); if (!orig_upd_clock) result = tcopy(cell->tok[CURR_VAL]);
/*}}}*/ /*}}}*/
return result; return result;
} }
@ -638,15 +637,15 @@ void update(Sheet *sheet)
for (ALL_LOCS_IN_SHEET(sheet,w)) for (ALL_LOCS_IN_SHEET(sheet,w))
{ {
upd_clock = 1; upd_clock = 1;
getvalue(sheet, w); recompvalue(sheet, w);
} }
for (ALL_CELLS_IN_SHEET(sheet,i,cell)) for (ALL_CELLS_IN_SHEET(sheet,i,cell))
{ {
if (cell && cell->clock_t1) if (cell && cell->clock_t1)
{ {
tfree(&(cell->value)); tfree(&(cell->tok[CURR_VAL]));
cell->value = cell->resvalue; cell->tok[CURR_VAL] = cell->tok[RES_VAL];;
cell->resvalue.type = EMPTY; cell->tok[RES_VAL].type = EMPTY;
cell->clock_t1 = 0; cell->clock_t1 = 0;
} }
} }
@ -663,25 +662,20 @@ char *geterror(Sheet *sheet, const Location at)
Token v; Token v;
assert(sheet!=(Sheet*)0); assert(sheet!=(Sheet*)0);
if ((v = getvalue(sheet,at)).type != EEK) if ((v = recompvalue(sheet,at)).type != EEK)
{ {
tfree(&v); tfree(&v);
return (char*)0; return NULL;
}
else
{
return (v.u.err);
} }
else return (v.u.err);
} }
/*}}}*/ /*}}}*/
/* printvalue -- get ASCII representation of value */ /*{{{*/ /* printvalue -- get ASCII representation of value */ /*{{{*/
void printvalue(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Sheet *sheet, const Location at) void printvalue(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Sheet *sheet, const Location at)
{ {
Token t;
assert(sheet != (Sheet*)0); assert(sheet != (Sheet*)0);
t = getvalue(sheet, at); Token t = recompvalue(sheet, at);
printtok(s, size, chars, quote, scientific, precision, 0, &t); printtok(s, size, chars, quote, scientific, precision, 0, &t);
tfree(&t); tfree(&t);
} }
@ -842,91 +836,46 @@ Token findlabel(Sheet *sheet, const char *label)
} }
/*}}}*/ /*}}}*/
/* search and replace for labels in a token */
static void relabel_token(Token *tok, const char *oldl, const char *newl)
{
switch (tok->type)
{
case LIDENT:
if (strcmp(tok->u.lident, oldl) == 0)
{
tfree(tok);
tok->type = LIDENT;
tok->u.lident = strdup(newl);
}
break;
case FUNCALL:
for (int ai = 0; ai < tok->u.funcall.argc; ++ai)
relabel_token((tok->u.funcall.argv) + ai, oldl, newl);
break;
default:
break;
}
}
/* relabel -- search and replace for labels */ /*{{{*/ /* relabel -- search and replace for labels */ /*{{{*/
void relabel(Sheet *sheet, const Location at, void relabel(Sheet *sheet, const Location at,
const char *oldlabel, const char *newlabel) const char *oldlabel, const char *newlabel)
{ {
/* variables */ /*{{{*/
Token **run;
ContentVariety v;
Cell *cell;
/*}}}*/
/* asserts */ /*{{{*/ /* asserts */ /*{{{*/
assert(oldlabel!=(const char*)0); assert(oldlabel!=(const char*)0);
assert(newlabel!=(const char*)0); assert(newlabel!=(const char*)0);
/*}}}*/ /*}}}*/
if (!LOC_WITHIN(sheet, at)) return; if (!LOC_WITHIN(sheet, at)) return;
cell = CELL_AT(sheet, at); Cell *cell = CELL_AT(sheet, at);
if (cell != NULLCELL) if (cell != NULLCELL)
for (v = BASE; v <= ITERATIVE; ++v) for (TokVariety v = BASE_CONT; v <= MAX_PERSIST_TV; ++v)
if (cell->contents[v] != EMPTY_TVEC) relabel_token(cell->tok + v, oldlabel, newlabel);
for (run = cell->contents[v]; *run != NULLTOKEN; ++run)
if ((*run)->type==LIDENT && strcmp((*run)->u.lident, oldlabel)==0)
{
free((*run)->u.lident);
(*run)->u.lident = strdup(newlabel);
}
cachelabels(sheet);
forceupdate(sheet);
} }
/*}}}*/ /*}}}*/
/* savexdr -- save a spread sheet in XDR */ /*{{{*/
const char *savexdr(Sheet *sheet, const char *name, unsigned int *count)
{
/* variables */ /*{{{*/
FILE *fp;
XDR xdrs;
int x,y,z;
/*}}}*/
*count=0;
if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
xdrstdio_create(&xdrs,fp,XDR_ENCODE);
if (!xdr_magic(&xdrs))
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
for (x=sheet->dimx-1; x>=0; --x) for (z=sheet->dimz-1; z>=0; --z)
{
int width;
int u;
width=columnwidth(sheet,x,z);
if (width!=DEF_COLUMNWIDTH)
{
u=0;
if (xdr_int(&xdrs,&u)==0 || xdr_column(&xdrs,&x,&z,&width)==0)
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
}
for (y=sheet->dimy-1; y>=0; --y)
{
if (CELL_ATC(sheet,x,y,z)!=NULLCELL)
{
u=1;
if (xdr_int(&xdrs,&u)==0 || xdr_int(&xdrs,&x)==0 || xdr_int(&xdrs,&y)==0 || xdr_int(&xdrs,&z)==0 || xdr_cell(&xdrs,CELL_ATC(sheet,x,y,z))==0)
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
++*count;
}
}
}
xdr_destroy(&xdrs);
if (fclose(fp)==EOF) return strerror(errno);
sheet->changed=0;
return (const char*)0;
}
/*}}}*/
/* savetbl -- save as tbl tyble */ /*{{{*/ /* savetbl -- save as tbl tyble */ /*{{{*/
const char *savetbl(Sheet *sheet, const char *name, int body, const char *savetbl(Sheet *sheet, const char *name, int body,
const Location beg, const Location end, const Location beg, const Location end,
@ -1131,7 +1080,6 @@ const char *savecsv(Sheet *sheet, const char *name, char sep,
/* variables */ /*{{{*/ /* variables */ /*{{{*/
FILE *fp; FILE *fp;
Location w; Location w;
Cell *cw;
/*}}}*/ /*}}}*/
/* asserts */ /*{{{*/ /* asserts */ /*{{{*/
@ -1150,36 +1098,40 @@ const char *savecsv(Sheet *sheet, const char *name, char sep,
{ {
for (w[X]=beg[X]; w[X]<=end[X]; ++(w[X])) for (w[X]=beg[X]; w[X]<=end[X]; ++(w[X]))
{ {
if (w[X]>beg[X]) if (fputc_close(sep,fp)==EOF) return strerror(errno); if (w[X] > beg[X])
cw = CELL_AT(sheet,w); if (fputc_close(sep,fp) == EOF) return strerror(errno);
Cell *cw = CELL_AT(sheet,w);
if (cw != NULLCELL) if (cw != NULLCELL)
{ {
char *buf,*s; static const int bufsz = 511*UTF8SZ + 1;
char *buf = malloc(bufsz);
buf=malloc(255*UTF8SZ+1); printvalue(buf, bufsz, 255, 0, getscientific(cw),
printvalue(buf, 255*UTF8SZ+1, 255, 0, getscientific(cw),
getprecision(cw), sheet, w); getprecision(cw), sheet, w);
if (cw->value.type == STRING && fputc_close('"',fp)==EOF) bool needquote = !transparent(cw)
&& (strlen(buf) != strcspn(buf, "\",\n\r"));
if (needquote && fputc_close('"',fp) == EOF)
{ {
free(buf); free(buf);
return strerror(errno); return strerror(errno);
} }
for (s=buf; *s; ++s) for (const char *s = buf; *s; ++s)
{ {
if (fputc_close(*s,fp)==EOF || (*s=='"' && fputc_close(*s,fp)==EOF)) if (fputc_close(*s,fp) == EOF
|| (*s=='"' && needquote && fputc_close(*s,fp) == EOF))
{ {
free(buf); free(buf);
return strerror(errno); return strerror(errno);
} }
} }
free(buf); free(buf);
if (cw->value.type==STRING && fputc_close('"',fp)==EOF) return strerror(errno); if (needquote && fputc_close('"', fp) == EOF) return strerror(errno);
} }
++*count; ++*count;
} }
if (fputc_close('\n', fp) == EOF) return strerror(errno); if (fputc_close('\n', fp) == EOF) return strerror(errno);
} }
if (w[Z] < end[Z] && fputs_close("\f",fp)==EOF) return strerror(errno); if (w[Z] < end[Z] && fputc_close('\f', fp) == EOF) return strerror(errno);
} }
if (fclose(fp) == EOF) return strerror(errno); if (fclose(fp) == EOF) return strerror(errno);
return (const char*)0; return (const char*)0;
@ -1187,7 +1139,7 @@ const char *savecsv(Sheet *sheet, const char *name, char sep,
/*}}}*/ /*}}}*/
static const char *saveport_contentleader[] = static const char *saveport_contentleader[] =
{ [BASE] = ":", [ITERATIVE] = "\\\n" { [BASE_CONT] = ":", [ITER_CONT] = "\\\n", [ATTR_REF] = "\\\n"
}; };
/* saveport -- save as portable text */ /*{{{*/ /* saveport -- save as portable text */ /*{{{*/
@ -1195,9 +1147,6 @@ const char *saveport(Sheet *sheet, const char *name, unsigned int *count)
{ {
/* variables */ /*{{{*/ /* variables */ /*{{{*/
FILE *fp; FILE *fp;
int x,y,z;
Cell *cell;
ContentVariety v;
/*}}}*/ /*}}}*/
/* asserts */ /*{{{*/ /* asserts */ /*{{{*/
@ -1207,15 +1156,15 @@ const char *saveport(Sheet *sheet, const char *name, unsigned int *count)
*count = 0; *count = 0;
if ((fp = fopen(name,"w")) == (FILE*)0) return strerror(errno); if ((fp = fopen(name,"w")) == (FILE*)0) return strerror(errno);
fprintf(fp,"# This is a work sheet generated with teapot %s.\n",VERSION); fprintf(fp,"# This is a work sheet generated with teapot %s.\n",VERSION);
for (z=sheet->dimz-1; z>=0; --z) for (int z = sheet->dimz - 1; z >= 0; --z)
{ {
for (y=sheet->dimy-1; y>=0; --y) for (int y = sheet->dimy - 1; y >= 0; --y)
{ {
for (x=sheet->dimx-1; x>=0; --x) for (int x = sheet->dimx - 1; x >= 0; --x)
{ {
if (y == 0 && columnwidth(sheet,x,z) != DEF_COLUMNWIDTH) if (y == 0 && columnwidth(sheet,x,z) != DEF_COLUMNWIDTH)
fprintf(fp,"W%d %d %d\n",x,z,columnwidth(sheet,x,z)); fprintf(fp,"W%d %d %d\n",x,z,columnwidth(sheet,x,z));
cell = CELL_ATC(sheet,x,y,z); Cell *cell = CELL_ATC(sheet,x,y,z);
if (cell != NULLCELL) if (cell != NULLCELL)
{ {
fprintf(fp,"C%d %d %d ",x,y,z); fprintf(fp,"C%d %d %d ",x,y,z);
@ -1224,23 +1173,36 @@ const char *saveport(Sheet *sheet, const char *name, unsigned int *count)
if (cell->label) fprintf(fp,"L%s ", cell->label); if (cell->label) fprintf(fp,"L%s ", cell->label);
if (cell->precision != -1) if (cell->precision != -1)
fprintf(fp,"P%d ", cell->precision); fprintf(fp,"P%d ", cell->precision);
bool needscolor = false;
for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a)
if (cell->aspect[a] != DefaultCN[a])
{ needscolor = true; break; }
if (needscolor)
{
fprintf(fp, "H%d ", NUM_COLOR_ASPECTS);
for (ColorAspect a = FOREGROUND; a < NUM_COLOR_ASPECTS; ++a)
fprintf(fp, "%d ", (int)cell->aspect[a]);
}
if (cell->shadowed) fprintf(fp,"S "); if (cell->shadowed) fprintf(fp,"S ");
if (cell->bold) fprintf(fp,"B "); if (cell->bold) fprintf(fp,"B ");
if (cell->underline) fprintf(fp,"U "); if (cell->underline) fprintf(fp,"U ");
if (cell->scientific != DEF_SCIENTIFIC) fprintf(fp,"E "); if (cell->scientific != DEF_SCIENTIFIC) fprintf(fp,"E ");
if (cell->locked) fprintf(fp,"C "); if (cell->locked) fprintf(fp, "K ");
if (cell->transparent) fprintf(fp,"T "); if (cell->transparent) fprintf(fp,"T ");
for (v = BASE; v <= ITERATIVE; ++v) if (cell->ignored) fprintf(fp, "I ");
if (cell->contents[v]) TokVariety lastv = MAX_PERSIST_TV;
while (lastv > BASE_CONT && cell->tok[lastv].type == EMPTY) --lastv;
for (TokVariety v = BASE_CONT; v <= lastv; ++v)
{ {
if (v == BASE_CONT && cell->tok[v].type == EMPTY) continue;
char buf[4096]; char buf[4096];
if (fputs_close(saveport_contentleader[v], fp) == EOF) if (fputs_close(saveport_contentleader[v], fp) == EOF)
return strerror(errno); return strerror(errno);
print(buf, sizeof(buf), 0, 1, cell->scientific, cell->precision, printtok(buf, sizeof(buf), 0, 1, cell->scientific,
cell->contents[v]); cell->precision, 0, cell->tok + v);
if (fputs_close(buf, fp) == EOF) return strerror(errno); if (fputs_close(buf, fp) == EOF) return strerror(errno);
} }
if (fputc_close('\n', fp) == EOF) return strerror(errno);
} }
} }
} }
@ -1281,9 +1243,9 @@ const char *loadport(Sheet *sheet, const char *name)
/* C -- parse cell */ /*{{{*/ /* C -- parse cell */ /*{{{*/
case 'C': case 'C':
{ {
ContentVariety cv = BASE, nextcv = BASE; TokVariety rv = BASE_CONT, nextrv = BASE_CONT;
if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextcv; } if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextrv; }
initcellcontents(&loaded); initcellcontents(&loaded);
/* parse x y and z */ /*{{{*/ /* parse x y and z */ /*{{{*/
os=ns=buf+1; os=ns=buf+1;
@ -1360,6 +1322,34 @@ const char *loadport(Sheet *sheet, const char *name)
break; break;
} }
/*}}}*/ /*}}}*/
/* H -- hue (colors) */ /*{{{*/
case 'H':
{
os = ++ns;
size_t na = posnumber(os, &ns);
if (os == ns || na > NUM_COLOR_ASPECTS)
{
sprintf(errbuf, _("Parse error for number of hues in line %d"),
line);
err = errbuf;
goto eek;
}
for (size_t a = 0; a < na; ++a)
{
while (*ns && (*ns == ' ')) { ++ns; }
os = ns;
loaded.aspect[a] = posnumber(os, &ns);
if (os == ns)
{
sprintf(errbuf, _("Parse error in hue %d on line %d"),
a, line);
err = errbuf;
goto eek;
}
}
break;
}
/*}}}*/
/* S -- shadowed */ /*{{{*/ /* S -- shadowed */ /*{{{*/
case 'S': case 'S':
{ {
@ -1413,8 +1403,9 @@ const char *loadport(Sheet *sheet, const char *name)
break; break;
} }
/*}}}*/ /*}}}*/
/* O -- locked */ /*{{{*/ /* K (formerly C) -- locked */ /*{{{*/
case 'O': case 'C':
case 'K':
{ {
++ns; ++ns;
loaded.locked=1; loaded.locked=1;
@ -1451,26 +1442,37 @@ const char *loadport(Sheet *sheet, const char *name)
/* convert remaining string(s) into token sequence */ /*{{{*/ /* convert remaining string(s) into token sequence */ /*{{{*/
while (true) while (true)
{ {
loaded.contents[cv] = scan(&ns); Token **t = scan(&ns);
if (loaded.contents[cv]==(Token**)0) if (t == EMPTY_TVEC)
{ {
sprintf(errbuf,_("Expression syntax error in line %d"),line); sprintf(errbuf,_("Expression syntax error in line %d"),line);
err=errbuf; err=errbuf;
goto eek; goto eek;
} }
if (nextcv == cv) break; Token pt = eval_safe(t, LITERAL);
cv = nextcv; tvecfree(t);
if (pt.type == EEK)
{
sprintf(errbuf, _("Parse error in line %d: %s"),
line, pt.u.err);
err = errbuf;
tfree(&pt);
goto eek;
}
loaded.tok[rv] = pt;
if (nextrv == rv) break;
rv = nextrv;
if (fgets(buf, sizeof(buf), fp) == (char*)0) break; if (fgets(buf, sizeof(buf), fp) == (char*)0) break;
++line; ++line;
width = strlen(buf); width = strlen(buf);
if (width>0 && buf[width-1]=='\n') buf[width-1]='\0'; if (width>0 && buf[width-1]=='\n') buf[width-1]='\0';
/* More content? */ /* More content? */
if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextcv; } if (width > 0 && buf[width-1]=='\\') { buf[--width]='\0'; ++nextrv; }
ns = buf; ns = buf;
} }
/*}}}*/ /*}}}*/
copycelltosheet(&loaded, sheet, loc); copycelltosheet(&loaded, sheet, loc, PRESERVE_LABEL);
break; break;
} }
/*}}}*/ /*}}}*/
@ -1534,96 +1536,6 @@ const char *loadport(Sheet *sheet, const char *name)
} }
/*}}}*/ /*}}}*/
/* loadxdr -- load a spread sheet in XDR */ /*{{{*/
const char *loadxdr(Sheet *sheet, const char *name)
{
/* variables */ /*{{{*/
FILE *fp;
XDR xdrs;
Location w;
int width;
int u;
int olderror;
Cell *nc;
/*}}}*/
if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno);
xdrstdio_create(&xdrs,fp,XDR_DECODE);
if (!xdr_magic(&xdrs))
{
#if 0
xdr_destroy(&xdrs);
fclose(fp);
return _("This is not a teapot worksheet in XDR format");
#else
xdr_destroy(&xdrs);
rewind(fp);
xdrstdio_create(&xdrs,fp,XDR_DECODE);
#endif
}
freesheet(sheet,0);
while (xdr_int(&xdrs,&u)) switch (u)
{
/* 0 -- column width element */ /*{{{*/
case 0:
{
if (xdr_column(&xdrs,&(w[X]),&(w[Z]),&width)==0)
{
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
setwidth(sheet,w[X],w[Z],width);
break;
}
/*}}}*/
/* 1 -- cell element */ /*{{{*/
case 1:
{
if (xdr_int(&xdrs,&(w[X]))==0 ||
xdr_int(&xdrs,&(w[Y]))==0 ||
xdr_int(&xdrs,&(w[Z]))==0)
{
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
nc = initcellofsheet(sheet, w);
if (xdr_cell(&xdrs, nc)==0)
{
freecellofsheet(sheet, w);
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
break;
}
/*}}}*/
/* default -- should not happen */ /*{{{*/
default:
{
xdr_destroy(&xdrs);
fclose(fp);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
return _("Invalid record, loading aborted");
}
/*}}}*/
}
xdr_destroy(&xdrs);
if (fclose(fp)==EOF) return strerror(errno);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
redraw_sheet(sheet);
return (const char*)0;
}
/*}}}*/
/* loadcsv -- load/merge CSVs */ /*{{{*/ /* loadcsv -- load/merge CSVs */ /*{{{*/
const char *loadcsv(Sheet *sheet, const char *name) const char *loadcsv(Sheet *sheet, const char *name)
{ {
@ -1634,8 +1546,8 @@ const char *loadcsv(Sheet *sheet, const char *name)
Location where; Location where;
char ln[4096]; char ln[4096];
const char *str; const char *str;
double value; FltT value;
long lvalue; IntT lvalue;
int separator = 0; int separator = 0;
/*}}}*/ /*}}}*/
@ -1661,15 +1573,15 @@ const char *loadcsv(Sheet *sheet, const char *name)
where[X] = sheet->cur[X]; where[X] = sheet->cur[X];
do do
{ {
t=malloc(2*sizeof(Token*)); Token t;
t[0]=malloc(sizeof(Token)); t.type = EMPTY;
t[1]=(Token*)0;
lvalue = csv_long(s, &cend); lvalue = csv_long(s, &cend);
if (s != cend) /* ok, it is a integer */ /*{{{*/ if (s != cend) /* ok, it is a integer */ /*{{{*/
{ {
t[0]->type=INT; t.type = INT;
t[0]->u.integer=lvalue; t.u.integer = lvalue;
putcont(sheet, where, t, BASE); puttok(sheet, where, t, BASE_CONT);
} }
/*}}}*/ /*}}}*/
else else
@ -1677,9 +1589,9 @@ const char *loadcsv(Sheet *sheet, const char *name)
value = csv_double(s, &cend); value = csv_double(s, &cend);
if (s != cend) /* ok, it is a double */ /*{{{*/ if (s != cend) /* ok, it is a double */ /*{{{*/
{ {
t[0]->type=FLOAT; t.type = FLOAT;
t[0]->u.flt=value; t.u.flt = value;
putcont(sheet, where, t, BASE); puttok(sheet, where, t, BASE_CONT);
} }
/*}}}*/ /*}}}*/
else else
@ -1687,14 +1599,13 @@ const char *loadcsv(Sheet *sheet, const char *name)
str = csv_string(s, &cend); str = csv_string(s, &cend);
if (s != cend) /* ok, it is a string */ /*{{{*/ if (s != cend) /* ok, it is a string */ /*{{{*/
{ {
t[0]->type=STRING; t.type = STRING;
t[0]->u.string=strdup(str); t.u.string = strdup(str);
putcont(sheet, where, t, BASE); puttok(sheet, where, t, BASE_CONT);
} }
/*}}}*/ /*}}}*/
else else
{ {
tvecfree(t);
csv_separator(s,&cend); csv_separator(s,&cend);
while (s == cend && *s && *s!='\n') while (s == cend && *s && *s!='\n')
{ {
@ -1884,7 +1795,8 @@ void moveblock(Sheet *sheet, const Location beg, const Location end,
get[X] != to[X]; get[X] += dir[X], go[X] += dir[X]) get[X] != to[X]; get[X] += dir[X], go[X] += dir[X])
{ {
if (!LOC_WITHIN(sheet, get)) freecellofsheet(sheet, go); if (!LOC_WITHIN(sheet, get)) freecellofsheet(sheet, go);
else if (copy) { copycelltosheet(CELL_AT(sheet, get), sheet, go); } else if (copy)
copycelltosheet(CELL_AT(sheet, get), sheet, go, ALTER_LABEL);
else else
{ {
resize(sheet, go[X], go[Y], go[Z]); resize(sheet, go[X], go[Y], go[Z]);
@ -1916,7 +1828,8 @@ const char *sortblock(Sheet *sheet, const Location beg, const Location end,
int x,y,z; int x,y,z;
int incx=0,incy=0,incz=0; int incx=0,incy=0,incz=0;
int distx,disty,distz; int distx,disty,distz;
int i,r=-3,norel,work; int i,r=-3,work;
bool norel = false;
/*}}}*/ /*}}}*/
/* unpack corners */ /* unpack corners */
@ -1955,7 +1868,7 @@ const char *sortblock(Sheet *sheet, const Location beg, const Location end,
for (i=0; i<sklen; ++i) for (i=0; i<sklen; ++i)
{ {
r = cmpcell(sheet,x+sk[i].x,y+sk[i].y,z+sk[i].z,sheet,x+incx+sk[i].x,y+incy+sk[i].y,z+incz+sk[i].z,sk[i].sortkey); r = cmpcell(sheet,x+sk[i].x,y+sk[i].y,z+sk[i].z,sheet,x+incx+sk[i].x,y+incy+sk[i].y,z+incz+sk[i].z,sk[i].sortkey);
if (r==2) norel=1; if (r == NOT_COMPARABLE) norel = true;
else if (r==-1 || r==1) break; else if (r==-1 || r==1) break;
else assert(r==0); else assert(r==0);
} }

View File

@ -75,8 +75,10 @@ extern int max_eval;
void initialize_sheet(Sheet *sheet); void initialize_sheet(Sheet *sheet);
void resize(Sheet *sheet, int x, int y, int z); void resize(Sheet *sheet, int x, int y, int z);
Cell *initcellofsheet(Sheet *sheet, const Location at); Cell *initcellofsheet(Sheet *sheet, const Location at);
void copycelltosheet(const Cell *fromcell, Sheet *sheet2, const Location to); void copycelltosheet(const Cell *fromcell, Sheet *sheet2,
const Location to, LabelHandling lh);
Cell *safe_cell_at(Sheet *sheet, const Location at); Cell *safe_cell_at(Sheet *sheet, const Location at);
Cell *safe_cell_atc(Sheet *sheet, int x, int y, int z);
Cell *curcell(Sheet *sheet); Cell *curcell(Sheet *sheet);
void cachelabels(Sheet *sheet); void cachelabels(Sheet *sheet);
void freesheet(Sheet *sheet, int all); void freesheet(Sheet *sheet, int all);
@ -87,8 +89,8 @@ void freecellofsheet(Sheet *sheet, const Location at);
int columnwidth(Sheet *sheet, int x, int z); int columnwidth(Sheet *sheet, int x, int z);
void setwidth(Sheet *sheet, int x, int z, int width); void setwidth(Sheet *sheet, int x, int z, int width);
int cellwidth(Sheet *sheet, const Location at); int cellwidth(Sheet *sheet, const Location at);
void putcont(Sheet *sheet, const Location at, Token **t, ContentVariety v); void puttok(Sheet *sheet, const Location at, Token t, TokVariety v);
Token getvalue(Sheet *sheet, const Location at); Token recompvalue(Sheet *sheet, const Location at);
void update(Sheet *sheet); void update(Sheet *sheet);
char *geterror(Sheet *sheet, const Location at); char *geterror(Sheet *sheet, const Location at);
void printvalue(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Sheet *sheet, const Location at); void printvalue(char *s, size_t size, size_t chars, int quote, int scientific, int precision, Sheet *sheet, const Location at);
@ -107,12 +109,10 @@ Token findlabel(Sheet *sheet, const char *label);
void relabel(Sheet* sheet, const Location at, void relabel(Sheet* sheet, const Location at,
const char *oldlabel, const char *newlabel); const char *oldlabel, const char *newlabel);
const char *savexdr(Sheet *sheet, const char *name, unsigned int *count);
const char *savetbl(Sheet *sheet, const char *name, int body, const Location beg, const Location end, unsigned int *count); const char *savetbl(Sheet *sheet, const char *name, int body, const Location beg, const Location end, unsigned int *count);
const char *savetext(Sheet *sheet, const char *name, const Location beg, const Location end, unsigned int *count); const char *savetext(Sheet *sheet, const char *name, const Location beg, const Location end, unsigned int *count);
const char *savecsv(Sheet *sheet, const char *name, char sep, const Location beg, const Location end, unsigned int *count); const char *savecsv(Sheet *sheet, const char *name, char sep, const Location beg, const Location end, unsigned int *count);
const char *saveport(Sheet *sheet, const char *name, unsigned int *count); const char *saveport(Sheet *sheet, const char *name, unsigned int *count);
const char *loadxdr(Sheet *sheet, const char *name);
const char *loadport(Sheet *sheet, const char *name); const char *loadport(Sheet *sheet, const char *name);
const char *loadcsv(Sheet *sheet, const char *name); const char *loadcsv(Sheet *sheet, const char *name);

View File

@ -24,6 +24,7 @@ extern char *strdup(const char *s);
#include "eval.h" #include "eval.h"
#include "parser.h"
#include "main.h" #include "main.h"
#include "misc.h" #include "misc.h"
#include "sheet.h" #include "sheet.h"
@ -50,9 +51,9 @@ static int it(const char *s)
} }
/*}}}*/ /*}}}*/
/* dbl -- convert string to double */ /*{{{*/ /* dbl -- convert string to double */ /*{{{*/
static double dbl(const unsigned char *s) static FltT dbl(const unsigned char *s)
{ {
double x; FltT x;
int sg,e,i; int sg,e,i;
x=0.0; x=0.0;
@ -80,6 +81,7 @@ static double dbl(const unsigned char *s)
return (sg*ldexp(x,e)); return (sg*ldexp(x,e));
} }
/*}}}*/ /*}}}*/
/* format -- convert string into format */ /*{{{*/ /* format -- convert string into format */ /*{{{*/
static void format(unsigned char s, Cell *cell) static void format(unsigned char s, Cell *cell)
{ {
@ -193,7 +195,7 @@ static int pair(const char *s, Token **t, int *tokens)
{ {
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+2]=malloc(sizeof(Token)))==(Token*)0) return -1; if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+2]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type = FIDENT; t[*tokens]->type = FIDENT;
t[*tokens]->u.fident=identcode("x",1); t[*tokens]->u.fident = FUNC_X;
t[*tokens+1]->type = OPERATOR; t[*tokens+1]->type = OPERATOR;
t[*tokens+1]->u.op = OP; t[*tokens+1]->u.op = OP;
t[*tokens+2]->type = OPERATOR; t[*tokens+2]->type = OPERATOR;
@ -270,7 +272,7 @@ static int pair(const char *s, Token **t, int *tokens)
{ {
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+2]=malloc(sizeof(Token)))==(Token*)0) return -1; if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+2]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type = FIDENT; t[*tokens]->type = FIDENT;
t[*tokens]->u.fident=identcode("y",1); t[*tokens]->u.fident = FUNC_Y;
t[*tokens+1]->type = OPERATOR; t[*tokens+1]->type = OPERATOR;
t[*tokens+1]->u.op = OP; t[*tokens+1]->u.op = OP;
t[*tokens+2]->type = OPERATOR; t[*tokens+2]->type = OPERATOR;
@ -356,7 +358,7 @@ static int sumup(const char *s, const int *offset, int top, Token **t, int *toke
{ {
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0) return -1; if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0 || (t[*tokens+1]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type = FIDENT; t[*tokens]->type = FIDENT;
t[*tokens]->u.fident=identcode("sum",3); t[*tokens]->u.fident = FUNC_SUM;
t[*tokens+1]->type = OPERATOR; t[*tokens+1]->type = OPERATOR;
t[*tokens+1]->u.op = OP; t[*tokens+1]->u.op = OP;
#if WK1DEBUG #if WK1DEBUG
@ -412,7 +414,7 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke
{ {
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1; if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type = FIDENT; t[*tokens]->type = FIDENT;
t[*tokens]->u.fident=identcode("@",1); t[*tokens]->u.fident = FUNC_AT_SYMBOL;
} }
if (tokens) ++(*tokens); if (tokens) ++(*tokens);
if (pair(s+offset[top]+1,t,tokens)==-1) low=-1; else low=top; if (pair(s+offset[top]+1,t,tokens)==-1) low=-1; else low=top;
@ -425,7 +427,7 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke
{ {
if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1; if ((t[*tokens]=malloc(sizeof(Token)))==(Token*)0) return -1;
t[*tokens]->type = FIDENT; t[*tokens]->type = FIDENT;
t[*tokens]->u.fident=identcode("&",1); t[*tokens]->u.fident = FUNC_AMPERSAND;
#if WK1DEBUG #if WK1DEBUG
fprintf(se,"[&]"); fprintf(se,"[&]");
#endif #endif
@ -438,7 +440,7 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke
t[*tokens]->type = OPERATOR; t[*tokens]->type = OPERATOR;
t[*tokens]->u.fident = COMMA; t[*tokens]->u.fident = COMMA;
t[*tokens+1]->type = FIDENT; t[*tokens+1]->type = FIDENT;
t[*tokens+1]->u.op=identcode("&",1); t[*tokens+1]->u.op = FUNC_AMPERSAND;
#if WK1DEBUG #if WK1DEBUG
fprintf(se,"[,&]"); fprintf(se,"[,&]");
#endif #endif
@ -683,6 +685,8 @@ static int unrpn(const char *s, const int *offset, int top, Token **t, int *toke
} }
/*}}}*/ /*}}}*/
static char internal_errbuf[4096];
/* loadwk1 -- load WK1 file */ /*{{{*/ /* loadwk1 -- load WK1 file */ /*{{{*/
const char *loadwk1(Sheet *sheet, const char *name) const char *loadwk1(Sheet *sheet, const char *name)
{ {
@ -847,17 +851,14 @@ const char *loadwk1(Sheet *sheet, const char *name)
/* INTEGER -- Integer number cell */ /*{{{*/ /* INTEGER -- Integer number cell */ /*{{{*/
case 0xD: case 0xD:
{ {
Token **t; Token t;
assert(bodylen == 7); assert(bodylen == 7);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp); cell = initcellofsheet(sheet, tmp);
t=malloc(2*sizeof(Token*)); t.type = INT;
t[0]=malloc(sizeof(Token)); t.u.integer = it(body+5);
t[1]=(Token*)0; puttok(sheet, tmp, t, BASE_CONT);
t[0]->type=INT;
t[0]->u.integer=it(body+5);
putcont(sheet, tmp, t, BASE);
format((unsigned char)body[0], cell); format((unsigned char)body[0], cell);
break; break;
} }
@ -865,17 +866,14 @@ const char *loadwk1(Sheet *sheet, const char *name)
/* NUMBER -- Floating point number */ /*{{{*/ /* NUMBER -- Floating point number */ /*{{{*/
case 0xE: case 0xE:
{ {
Token **t; Token t;
assert(bodylen == 13); assert(bodylen == 13);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp); cell = initcellofsheet(sheet, tmp);
t=malloc(2*sizeof(Token*)); t.type = FLOAT;
t[0]=malloc(sizeof(Token)); t.u.flt = dbl((unsigned char*)body+5);
t[1]=(Token*)0; puttok(sheet, tmp, t, BASE_CONT);
t[0]->type=FLOAT;
t[0]->u.flt=dbl((unsigned char*)body+5);
putcont(sheet, tmp, t, BASE);
format((unsigned char)body[0], cell); format((unsigned char)body[0], cell);
break; break;
} }
@ -883,17 +881,14 @@ const char *loadwk1(Sheet *sheet, const char *name)
/* _("lL)abel") -- Label cell */ /*{{{*/ /* _("lL)abel") -- Label cell */ /*{{{*/
case 0xF: case 0xF:
{ {
Token **t; Token t;
assert(bodylen >= 6 && bodylen <= 245); assert(bodylen >= 6 && bodylen <= 245);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp); cell = initcellofsheet(sheet, tmp);
t=malloc(2*sizeof(Token*)); t.type = STRING;
t[0]=malloc(sizeof(Token)); t.u.string = strdup(body+6);
t[1]=(Token*)0; puttok(sheet, tmp, t, BASE_CONT);
t[0]->type=STRING;
t[0]->u.string=strdup(body+6);
putcont(sheet, tmp, t, BASE);
format((unsigned char)body[0], cell); format((unsigned char)body[0], cell);
break; break;
} }
@ -907,7 +902,11 @@ const char *loadwk1(Sheet *sheet, const char *name)
Token **t; Token **t;
assert(bodylen > 15); assert(bodylen > 15);
if ((offset=malloc(it(body+13)*sizeof(int)))==0) { err=_("Out of memory"); goto ouch; } if ((offset = malloc(it(body+13)*sizeof(int))) == 0)
{
err = _("Out of memory");
goto ouch;
}
#if WK1DEBUG #if WK1DEBUG
fprintf(se,"FORMULA: &(%d,%d)=",it(body+1),it(body+3)); fprintf(se,"FORMULA: &(%d,%d)=",it(body+1),it(body+3));
#endif #endif
@ -971,15 +970,25 @@ const char *loadwk1(Sheet *sheet, const char *name)
} }
else else
{ {
Token tok = eval_safe(t, LITERAL);
tvecfree(t);
if (tok.type == EEK)
{
strcpy(internal_errbuf, _("Parse error: "));
strcat(internal_errbuf, tok.u.err);
tfree(&tok);
err = internal_errbuf;
goto ouch;
}
#if WK1DEBUG #if WK1DEBUG
fprintf(se,"[<-- %d tokens]\r\n",tokens); fprintf(se,"[<-- %d tokens]\r\n",tokens);
#endif #endif
free(offset); free(offset);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp); cell = initcellofsheet(sheet, tmp);
cell->value.type=FLOAT; cell->tok[CURR_VAL].type = FLOAT;
cell->value.u.flt=dbl((unsigned char*)body+5); cell->tok[CURR_VAL].u.flt = dbl((unsigned char*)body+5);
putcont(sheet, tmp, t, BASE); puttok(sheet, tmp, tok, BASE_CONT);
} }
break; break;
} }
@ -1090,17 +1099,14 @@ const char *loadwk1(Sheet *sheet, const char *name)
/* STRING -- Value of string formula */ /*{{{*/ /* STRING -- Value of string formula */ /*{{{*/
case 0x33: case 0x33:
{ {
Token **t; Token t;
assert(bodylen>=6 && bodylen<=245); assert(bodylen>=6 && bodylen<=245);
tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0; tmp[X] = it(body+1); tmp[Y] = it(body+3); tmp[Z] = 0;
cell = initcellofsheet(sheet, tmp); cell = initcellofsheet(sheet, tmp);
t=malloc(2*sizeof(Token*)); t.type = STRING;
t[0]=malloc(sizeof(Token)); t.u.string = strdup(body+5);
t[1]=(Token*)0; puttok(sheet, tmp, t, BASE_CONT);
t[0]->type=STRING;
t[0]->u.string=strdup(body+5);
putcont(sheet, tmp, t, BASE);
format((unsigned char)body[0], cell); format((unsigned char)body[0], cell);
break; break;
} }

View File

@ -21,20 +21,43 @@ array terminated by a special element.
#endif #endif
#include <assert.h> #include <assert.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "default.h"
#include "display.h"
#include "eval.h"
#include "main.h"
#include "parser.h"
#include "sheet.h" #include "sheet.h"
#include "xdr.h" #include "xdr.h"
/*}}}*/ /*}}}*/
typedef struct Old_token_struc
{
Type type;
union
{
char *string;
double flt;
long integer;
Operator op;
char *lident;
int fident;
int location[3];
char *err;
} u;
} OldToken;
/* xdr_token */ /*{{{*/ /* xdr_token */ /*{{{*/
static bool_t xdr_token(XDR *xdrs, Token *t) static bool_t xdr_token(XDR *xdrs, OldToken *t)
{ {
int x,result; int x,result;
if (xdrs->x_op==XDR_DECODE) (void)memset(t,0,sizeof(Token)); if (xdrs->x_op == XDR_DECODE) memset(t,0,sizeof(OldToken));
else return false;
x=t->type; x=t->type;
if (t->type==OPERATOR) x|=t->u.op<<8; if (t->type==OPERATOR) x|=t->u.op<<8;
result=xdr_int(xdrs,&x); result=xdr_int(xdrs,&x);
@ -104,31 +127,35 @@ static bool_t xdr_token(XDR *xdrs, Token *t)
return 0; return 0;
} }
/*}}}*/ /*}}}*/
/* xdr_tokenptr */ /*{{{*/ /* xdr_tokenptr */ /*{{{*/
static bool_t xdr_tokenptr(XDR *xdrs, Token **t) static bool_t xdr_tokenptr(XDR *xdrs, OldToken **t)
{ {
return xdr_pointer(xdrs,(char**)t,sizeof(Token),(xdrproc_t)xdr_token); bool_t nonnull;
if (!xdr_bool(xdrs, &nonnull)) return false;
if (!nonnull) { *t = (OldToken *)0; return true; }
*t = malloc(sizeof(OldToken));
return xdr_token(xdrs, *t);
} }
/*}}}*/ /*}}}*/
/* xdr_tokenptrvec */ /*{{{*/ /* xdr_tokenptrvec */ /*{{{*/
static bool_t xdr_tokenptrvec(XDR *xdrs, Token ***t) static bool_t xdr_tokenptrvec(XDR *xdrs, OldToken ***t)
{ {
unsigned int len; unsigned int len;
int result; int result;
assert(t!=(Token***)0); assert(t!=(OldToken***)0);
if (xdrs->x_op!=XDR_DECODE) if (xdrs->x_op!=XDR_DECODE) assert(0);
{ if (!xdr_u_int(xdrs, &len)) return false;
Token **run; *t = malloc(sizeof(OldToken*) * (len+1));
(*t)[len] = (OldToken*)0;
if (*t==(Token**)0) len=0; for (size_t i = 0; i < len; ++i)
else for (len=1,run=*t; *run!=(Token*)0; ++len,++run); if (!xdr_tokenptr(xdrs, *t + i)) return false;
} return true;
result=xdr_array(xdrs,(char**)t,&len,65536,sizeof(Token*),(xdrproc_t)xdr_tokenptr);
if (len==0) *t=(Token**)0;
return result;
} }
/*}}}*/ /*}}}*/
/* xdr_mystring */ /*{{{*/ /* xdr_mystring */ /*{{{*/
static bool_t xdr_mystring(XDR *xdrs, char **str) static bool_t xdr_mystring(XDR *xdrs, char **str)
{ {
@ -166,9 +193,48 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell)
int result,x; int result,x;
assert(cell != (Cell*)0); assert(cell != (Cell*)0);
if (!(xdr_tokenptrvec(xdrs, &(cell->contents[BASE])) assert(xdrs->x_op == XDR_DECODE);
&& xdr_tokenptrvec(xdrs, &(cell->contents[ITERATIVE])) /* && xdr_token(xdrs, &(cell->value)) */ )) OldToken **ot;
return 0; for (TokVariety tv = BASE_CONT; tv <= ITER_CONT; ++tv) {
if (!xdr_tokenptrvec(xdrs, &ot)) return false;
size_t veclen = 0;
while (ot[veclen]) ++veclen;
Token **nt = malloc(sizeof(Token*) * (veclen + 1));
nt[veclen] = NULLTOKEN;
for (size_t ti = 0; ti < veclen; ++ti)
{
nt[ti] = malloc(sizeof(Token));
cleartoken(nt[ti]);
nt[ti]->type = ot[ti]->type;
switch (ot[ti]->type) {
case EMPTY: break;
case STRING: nt[ti]->u.string = ot[ti]->u.string; break;
case FLOAT: nt[ti]->u.flt = ot[ti]->u.flt; break;
case INT: nt[ti]->u.integer = ot[ti]->u.integer; break;
case OPERATOR: nt[ti]->u.op = ot[ti]->u.op; break;
case LIDENT: nt[ti]->u.lident = ot[ti]->u.lident; break;
case FIDENT: nt[ti]->u.fident = ot[ti]->u.fident; break;
case LOCATION:
nt[ti]->u.location[0] = ot[ti]->u.location[0];
nt[ti]->u.location[1] = ot[ti]->u.location[1];
nt[ti]->u.location[2] = ot[ti]->u.location[2];
break;
case EEK:
nt[ti]->u.err = ot[ti]->u.err; break;
default:
assert(0);
}
free(ot[ti]);
}
free(ot);
Token tok = eval_safe(nt, LITERAL);
tvecfree(nt);
if (tok.type == EEK) {
tfree(&tok);
return false;
}
cell->tok[tv] = tok;
}
if (xdr_mystring(xdrs, &(cell->label))==0) return 0; if (xdr_mystring(xdrs, &(cell->label))==0) return 0;
x=cell->adjust; x=cell->adjust;
result=xdr_int(xdrs, &x); result=xdr_int(xdrs, &x);
@ -188,12 +254,14 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell)
return result; return result;
} }
/*}}}*/ /*}}}*/
/* xdr_column */ /*{{{*/ /* xdr_column */ /*{{{*/
bool_t xdr_column(XDR *xdrs, int *x, int *z, int *width) bool_t xdr_column(XDR *xdrs, int *x, int *z, int *width)
{ {
return (xdr_int(xdrs, x) && xdr_int(xdrs, z) && xdr_int(xdrs, width)); return (xdr_int(xdrs, x) && xdr_int(xdrs, z) && xdr_int(xdrs, width));
} }
/*}}}*/ /*}}}*/
/* xdr_magic */ /*{{{*/ /* xdr_magic */ /*{{{*/
#define MAGIC0 (('#'<<24)|('!'<<16)|('t'<<8)|'e') #define MAGIC0 (('#'<<24)|('!'<<16)|('t'<<8)|'e')
#define MAGIC1 (('a'<<24)|('p'<<16)|('o'<<8)|'t') #define MAGIC1 (('a'<<24)|('p'<<16)|('o'<<8)|'t')
@ -209,3 +277,150 @@ bool_t xdr_magic(XDR *xdrs)
return (xdr_long(xdrs,&m0) && m0==MAGIC0 && xdr_long(xdrs,&m1) && m1==MAGIC1 && xdr_long(xdrs,&m2) && m2==MAGIC2); return (xdr_long(xdrs,&m0) && m0==MAGIC0 && xdr_long(xdrs,&m1) && m1==MAGIC1 && xdr_long(xdrs,&m2) && m2==MAGIC2);
} }
/*}}}*/ /*}}}*/
/* savexdr -- save a spread sheet in XDR */ /*{{{*/
const char *savexdr(Sheet *sheet, const char *name, unsigned int *count)
{
return _("Saving in xdr format is no longer supported. Use portable text.");
/* variables */ /*{{{*/
FILE *fp;
XDR xdrs;
int x,y,z;
/*}}}*/
*count=0;
if ((fp=fopen(name,"w"))==(FILE*)0) return strerror(errno);
xdrstdio_create(&xdrs,fp,XDR_ENCODE);
if (!xdr_magic(&xdrs))
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
for (x=sheet->dimx-1; x>=0; --x) for (z=sheet->dimz-1; z>=0; --z)
{
int width;
int u;
width=columnwidth(sheet,x,z);
if (width!=DEF_COLUMNWIDTH)
{
u=0;
if (xdr_int(&xdrs,&u)==0 || xdr_column(&xdrs,&x,&z,&width)==0)
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
}
for (y=sheet->dimy-1; y>=0; --y)
{
if (CELL_ATC(sheet,x,y,z)!=NULLCELL)
{
u=1;
if (xdr_int(&xdrs,&u)==0 || xdr_int(&xdrs,&x)==0 || xdr_int(&xdrs,&y)==0 || xdr_int(&xdrs,&z)==0 || xdr_cell(&xdrs,CELL_ATC(sheet,x,y,z))==0)
{
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(errno);
}
++*count;
}
}
}
xdr_destroy(&xdrs);
if (fclose(fp)==EOF) return strerror(errno);
sheet->changed=0;
return (const char*)0;
}
/*}}}*/
/* loadxdr -- load a spread sheet in XDR */ /*{{{*/
const char *loadxdr(Sheet *sheet, const char *name)
{
/* variables */ /*{{{*/
FILE *fp;
XDR xdrs;
Location w;
int width;
int u;
int olderror;
Cell *nc;
/*}}}*/
if ((fp=fopen(name,"r"))==(FILE*)0) return strerror(errno);
xdrstdio_create(&xdrs,fp,XDR_DECODE);
if (!xdr_magic(&xdrs))
{
#if 0
xdr_destroy(&xdrs);
fclose(fp);
return _("This is not a teapot worksheet in XDR format");
#else
xdr_destroy(&xdrs);
rewind(fp);
xdrstdio_create(&xdrs,fp,XDR_DECODE);
#endif
}
freesheet(sheet,0);
while (xdr_int(&xdrs,&u)) switch (u)
{
/* 0 -- column width element */ /*{{{*/
case 0:
{
if (xdr_column(&xdrs,&(w[X]),&(w[Z]),&width)==0)
{
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
setwidth(sheet,w[X],w[Z],width);
break;
}
/*}}}*/
/* 1 -- cell element */ /*{{{*/
case 1:
{
if (xdr_int(&xdrs,&(w[X]))==0 ||
xdr_int(&xdrs,&(w[Y]))==0 ||
xdr_int(&xdrs,&(w[Z]))==0)
{
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
nc = initcellofsheet(sheet, w);
if (xdr_cell(&xdrs, nc)==0)
{
freecellofsheet(sheet, w);
olderror=errno;
xdr_destroy(&xdrs);
(void)fclose(fp);
return strerror(olderror);
}
break;
}
/*}}}*/
/* default -- should not happen */ /*{{{*/
default:
{
xdr_destroy(&xdrs);
fclose(fp);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
return _("Invalid record, loading aborted");
}
/*}}}*/
}
xdr_destroy(&xdrs);
if (fclose(fp)==EOF) return strerror(errno);
sheet->changed=0;
cachelabels(sheet);
forceupdate(sheet);
redraw_sheet(sheet);
return (const char*)0;
}
/*}}}*/

View File

@ -17,4 +17,7 @@ bool_t xdr_cell(XDR *xdrs, Cell *cell);
bool_t xdr_column(XDR *xdrs, int *x, int *z, int *width); bool_t xdr_column(XDR *xdrs, int *x, int *z, int *width);
bool_t xdr_magic(XDR *xdrs); bool_t xdr_magic(XDR *xdrs);
const char *savexdr(Sheet *sheet, const char *name, unsigned int *count);
const char *loadxdr(Sheet *sheet, const char *name);
#endif #endif

View File

@ -565,13 +565,15 @@ void redraw_sheet(Sheet *sheet)
else else
{ {
cell = curcell(sheet); cell = curcell(sheet);
print(buf+strlen(buf), bufsz-strlen(buf), 0, 1, getscientific(cell), Token bc = gettok(cell, BASE_CONT);
-1, getcont(cell, BASE)); printtok(buf+strlen(buf), bufsz-strlen(buf), 0, 1, getscientific(cell),
if (getcont(cell, ITERATIVE) && mbslen(buf) < (size_t)(sheet->maxx+1-4)) -1, false, &bc);
Token ic = gettok(cell, ITER_CONT);
if (ic.type != EMPTY && mbslen(buf) < (size_t)(sheet->maxx+1-4))
{ {
strcat(buf," -> "); strcat(buf," -> ");
print(buf+strlen(buf), bufsz-strlen(buf), 0, 1, printtok(buf+strlen(buf), bufsz-strlen(buf), 0, 1,
getscientific(cell), -1, getcont(cell, ITERATIVE)); getscientific(cell), -1, false, &ic);
} }
} }
*mbspos(buf, sheet->maxx) = 0; *mbspos(buf, sheet->maxx) = 0;

View File

@ -167,10 +167,11 @@ class TeapotTable {open : {public Fl_Table}}
fl_measure(s, ww, hh, 0); fl_measure(s, ww, hh, 0);
if (ww > W-6) for (int i = 0; s[i]; i++) s[i] = '\#'; if (ww > W-6) for (int i = 0; s[i]; i++) s[i] = '\#';
int adj = getadjust(cell); Adjust adj = getadjust(cell);
fl_draw(s, xx+3, yy+3, W-6, H-6, fl_draw(s, xx+3, yy+3, W-6, H-6,
adj == RIGHT ? FL_ALIGN_RIGHT : adj == RIGHT ? FL_ALIGN_RIGHT :
adj == LEFT ? FL_ALIGN_LEFT : FL_ALIGN_CENTER); adj == LEFT ? FL_ALIGN_LEFT : FL_ALIGN_CENTER,
NULL, 0);
if (underlined(cell)) fl_xyline(xx, yy+H-7, xx+W); if (underlined(cell)) fl_xyline(xx, yy+H-7, xx+W);
fl_pop_clip(); fl_pop_clip();
@ -660,14 +661,15 @@ class MainWindow {open}
free(err); free(err);
val[sizeof(val)-1] = 0; val[sizeof(val)-1] = 0;
} else { } else {
print(val, sizeof(val), 0, 1, getscientific(curcell(sheet)), Token t = gettok(curcell(sheet), BASE_CONT);
-1, getcont(curcell(sheet), BASE)); printtok(val, sizeof(val), 0, 1, getscientific(curcell(sheet)),
Token **iter = getcont(curcell(sheet), ITERATIVE); -1, true, &t);
if (iter != EMPTY_TVEC) { t = gettok(curcell(sheet), ITER_CONT);
if (t.type != EMPTY) {
snprintf(val+strlen(val),sizeof(val)-strlen(val), snprintf(val+strlen(val),sizeof(val)-strlen(val),
" -> "); " -> ");
print(val+strlen(val), sizeof(val)-strlen(val), 0, 1, printtok(val+strlen(val), sizeof(val)-strlen(val), 0, 1,
getscientific(curcell(sheet)), -1, iter); getscientific(curcell(sheet)), -1, true, &t);
} }
} }