teapot-spreadsheet/src/common/func.c

1538 lines
35 KiB
C

/* #includes */ /*{{{C}}}*//*{{{*/
#ifndef NO_POSIX_SOURCE
#undef _POSIX_SOURCE
#define _POSIX_SOURCE 1
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 2
#endif
#define _XOPEN_SOURCE /* glibc2 needs this */
#ifdef DMALLOC
#include "dmalloc.h"
#endif
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <stdlib.h>
extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */
#include <stdio.h>
extern char *strdup(const char* s);
#include <string.h>
#include <time.h>
#include "default.h"
#include "eval.h"
#include "func.h"
#include "main.h"
#include "misc.h"
#include "parser.h"
#include "scanner.h"
#include "sheet.h"
/*}}}*/
/* #defines */ /*{{{*/
/* There is a BSD extensions, but they are possibly more exact. */
#ifdef M_E
#define CONST_E M_E
#else
#define CONST_E ((double)2.7182818284590452354)
#endif
#ifdef M_PI
#define CONST_PI M_PI
#else
#define CONST_PI ((double)3.14159265358979323846)
#endif
/*}}}*/
#ifdef WIN32
// This strptime implementation Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Implement strptime under windows
static const char* kWeekFull[] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
static const char* kWeekAbbr[] = {
"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"
};
static const char* kMonthFull[] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
static const char* kMonthAbbr[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static const char* _parse_num(const char* s, int low, int high, int* value) {
const char* p = s;
for (*value = 0; *p && isdigit(*p); ++p) {
*value = (*value) * 10 + *p - '0';
}
if (p == s || *value < low || *value > high) return NULL;
return p;
}
static char* _strptime(const char *s, const char *format, struct tm *tm) {
int i, len;
while (*format && *s) {
if (*format != '%') {
if (*s != *format) return NULL;
++format;
++s;
continue;
}
++format;
len = 0;
switch (*format) {
// weekday name.
case 'a':
case 'A':
tm->tm_wday = -1;
for (i = 0; i < 7; ++i) {
len = strlen(kWeekAbbr[i]);
if (strnicmp(kWeekAbbr[i], s, len) == 0) {
tm->tm_wday = i;
break;
}
len = strlen(kWeekFull[i]);
if (strnicmp(kWeekFull[i], s, len) == 0) {
tm->tm_wday = i;
break;
}
}
if (tm->tm_wday == -1) return NULL;
s += len;
break;
// month name.
case 'b':
case 'B':
case 'h':
tm->tm_mon = -1;
for (i = 0; i < 12; ++i) {
len = strlen(kMonthAbbr[i]);
if (strnicmp(kMonthAbbr[i], s, len) == 0) {
tm->tm_mon = i;
break;
}
len = strlen(kMonthFull[i]);
if (strnicmp(kMonthFull[i], s, len) == 0) {
tm->tm_mon = i;
break;
}
}
if (tm->tm_mon == -1) return NULL;
s += len;
break;
// month [1, 12].
case 'm':
s = _parse_num(s, 1, 12, &tm->tm_mon);
if (s == NULL) return NULL;
--tm->tm_mon;
break;
// day [1, 31].
case 'd':
case 'e':
s = _parse_num(s, 1, 31, &tm->tm_mday);
if (s == NULL) return NULL;
break;
// hour [0, 23].
case 'H':
s = _parse_num(s, 0, 23, &tm->tm_hour);
if (s == NULL) return NULL;
break;
// minute [0, 59]
case 'M':
s = _parse_num(s, 0, 59, &tm->tm_min);
if (s == NULL) return NULL;
break;
// seconds [0, 60]. 60 is for leap year.
case 'S':
s = _parse_num(s, 0, 60, &tm->tm_sec);
if (s == NULL) return NULL;
break;
// year [1900, 9999].
case 'Y':
s = _parse_num(s, 1900, 9999, &tm->tm_year);
if (s == NULL) return NULL;
tm->tm_year -= 1900;
break;
// year [0, 99].
case 'y':
s = _parse_num(s, 0, 99, &tm->tm_year);
if (s == NULL) return NULL;
if (tm->tm_year <= 68) {
tm->tm_year += 100;
}
break;
// arbitray whitespace.
case 't':
case 'n':
while (isspace(*s)) ++s;
break;
// '%'.
case '%':
if (*s != '%') return NULL;
++s;
break;
// All the other format are not supported.
default:
return NULL;
}
++format;
}
if (*format) {
return NULL;
} else {
return (char *)s;
}
}
char* strptime(const char *buf, const char *fmt, struct tm *tm) {
return _strptime(buf, fmt, tm);
}
#endif
/* sci_func -- map a double to a double */ /*{{{*/
static Token sci_func(int argc, const Token argv[], double (*func)(double), const char *func_name)
{
Token result;
if (argc==1 && argv[0].type==FLOAT)
{
result.type=FLOAT;
errno=0;
result.u.flt=(func)(argv[0].u.flt);
if (errno)
{
result.type=EEK;
result.u.err=malloc(strlen(func_name)+2+strlen(strerror(errno))+1);
sprintf(result.u.err,"%s: %s",func_name,strerror(errno));
}
}
else
{
if (argc==1 && argv[0].type==INT)
{
result.type=FLOAT;
errno=0;
result.u.flt=(func)((double)argv[0].u.integer);
if (errno)
{
result.type=EEK;
result.u.err=malloc(strlen(func_name)+2+strlen(strerror(errno))+1);
sprintf(result.u.err,"%s: %s",func_name,strerror(errno));
}
}
else
{
result.type=EEK;
/* This is actually too much, but always enough for %s formats. */
result.u.err=malloc(strlen(_("Usage: %s(float)"))+strlen(func_name)+1);
sprintf(result.u.err,_("Usage: %s(float)"),func_name);
}
}
return result;
}
/*}}}*/
/* arsinh */ /*{{{*/
static double arsinh(double x)
{
return log(x+sqrt(x*x+1.0));
}
/*}}}*/
/* arcosh */ /*{{{*/
static double arcosh(double x)
{
if (x>=1.0) return log(x+sqrt(x*x-1.0));
else { errno=EDOM; return 0.0; }
}
/*}}}*/
/* artanh */ /*{{{*/
static double artanh(double x)
{
if (x>-1.0 && x<1.0) return 0.5*log((1.0+x)/(1.0-x));
else { errno=EDOM; return 0.0; }
}
/*}}}*/
/* rad2deg */ /*{{{*/
static double rad2deg(double x)
{
return (360.0/(2.0*CONST_PI))*x;
}
/*}}}*/
/* deg2rad */ /*{{{*/
static double deg2rad(double x)
{
return (2.0*CONST_PI/360.0)*x;
}
/*}}}*/
typedef enum {ABSOLUTE, RELATIVE, EXCEL} LocConvention;
static Token excel_adr_func(int argc, const Token argv[]);
/* & */ /*{{{*/
static Token adr_func(int argc, const Token argv[], LocConvention lcon)
{
/* variables */ /*{{{*/
Token result;
Dimensions dim;
size_t i;
/*}}}*/
if (lcon == EXCEL) return excel_adr_func(argc, argv);
/* asserts */ /*{{{*/
assert(argv != (Token*)0);
/*}}}*/
LOCATION_GETS(result.u.location, upd_l);
if (argc == 1 && argv[0].type == LOCATION)
if (lcon == ABSOLUTE) return argv[0];
else
{
LOCATION_ADD(result.u.location, argv[0].u.location);
return result;
}
for (i = 0; i < argc && i < HYPER; ++i)
{
if (argv[0].type == EEK) return argv[0];
if (argv[i].type == INT)
if (lcon == ABSOLUTE) result.u.location[i] = argv[i].u.integer;
else result.u.location[i] += argv[i].u.integer;
else if (argv[i].type != EMPTY) break;
}
if (i < argc)
duperror(&result, _("Usage: &([integer x][,[integer y][,[integer z]]])"));
else result.type = LOCATION;
return result;
}
/*}}}*/
/* @ */ /*{{{*/
static Token at_func(int argc, const Token argv[], LocConvention lcon)
{
Token location;
location = adr_func(argc, argv, lcon);
if (location.type == EEK)
{
Token result;
result.type = EEK;
char *pref = _("Inside @: ");
if (lcon == RELATIVE) pref = _("Inside R(): ");
if (lcon == EXCEL) pref = _("Inside X(): ");
result.u.err = malloc(strlen(location.u.err) + strlen(pref) + 1);
strcpy(result.u.err, pref);
strcat(result.u.err, location.u.err);
/* don't free the location if it is the same as an argument, because
those get freed later: */
for (size_t i = 0; i < argc; ++i)
if (argv[i].type == EEK & argv[i].u.err == location.u.err) return result;
tfree(&location);
return result;
}
return getvalue(upd_sheet, location.u.location);
}
static Token abs_adr_func(int argc, const Token argv[])
{
return adr_func(argc, argv, ABSOLUTE);
}
static Token abs_at_func(int argc, const Token argv[])
{
return at_func(argc, argv, ABSOLUTE);
}
static Token rel_adr_func(int argc, const Token argv[])
{
return adr_func(argc, argv, RELATIVE);
}
static Token rel_at_func(int argc, const Token argv[])
{
return at_func(argc, argv, RELATIVE);
}
static Token excel_at_func(int argc, const Token argv[])
{
return at_func(argc, argv, EXCEL);
}
#define INTPATIBLE(t) (t.type == INT || t.type == EMPTY)
static Token excel_adr_func(int argc, const Token argv[])
{
Token result;
char *usage = _("Usage: X&(THERE_LABEL, HERE_LABEL, [fix_x], [fix_y], [fix_z])");
if (argc < 2) {
duperror(&result, usage);
return result;
}
if (argv[0].type == EEK) return argv[0];
if (argv[1].type == EEK) return argv[1];
if (argv[0].type != LOCATION || argv[1].type != LOCATION)
{
duperror(&result, usage);
return result;
}
LOCATION_GETS(result.u.location, upd_l);
for (Dimensions dim = X; dim < HYPER; ++dim)
{
int i = dim + 2;
bool fixed = false;
if (i < argc)
{
if (argv[i].type == EEK) return argv[i];
if (!INTPATIBLE(argv[i]))
{
duperror(&result, usage);
return result;
}
if (argv[i].type == INT && argv[i].u.integer > 0) fixed = true;
}
if (fixed) result.u.location[dim] = argv[0].u.location[dim];
else result.u.location[dim] +=
argv[0].u.location[dim] - argv[1].u.location[dim];
}
return result;
}
/* dim_func -- common implementation of the coordinate functions */ /*{{{*/
static Token dim_func(int argc, const Token argv[], Dimensions dim)
{
Token result;
result.type = INT;
if (argc == 0)
/* result is currently updated coordinate along dim */ /*{{{*/
result.u.integer = upd_l[dim];
/*}}}*/
else if (argc == 1 && argv[0].type == LOCATION)
/* return dim coordinate of location */ /*{{{*/
result.u.integer = argv[0].u.location[dim];
else /* type error */ /*{{{*/
{
result.type = EEK;
char *templ = _("Usage: %c([location])");
const char *DIMS = "xyz";
result.u.err = malloc(strlen(templ)+1);
sprintf(result.u.err, templ, DIMS[dim]);
}
/*}}}*/
return result;
}
/*}}}/
/* x */ /*{{{*/
static Token x_func(int argc, const Token argv[])
{
return dim_func(argc, argv, X);
}
/*}}}*/
/* y */ /*{{{*/
static Token y_func(int argc, const Token argv[])
{
return dim_func(argc, argv, Y);
}
/*}}}*/
/* z */ /*{{{*/
static Token z_func(int argc, const Token argv[])
{
return dim_func(argc, argv, Z);
}
/*}}}*/
typedef enum { LOG_AND, LOG_OR } LogicalFunction;
static Token bitwise_func(int argc, const Token argv[], LogicalFunction lop)
{
Token result;
int accum;
if (lop == LOG_AND) accum = -1;
else accum = 0;
for (size_t i = 0; i < argc; ++i) {
if (!INTPATIBLE(argv[i]))
{
duperror(&result, _("Bitwise functions operate only on integers."));
return result;
}
int val = 0;
if (argv[i].type == INT) val = argv[i].u.integer;
if (lop == LOG_AND) accum = accum & val;
else accum = accum | val;
}
result.type = INT;
result.u.integer = accum;
return result;
}
static Token bitand_func(int argc, const Token argv[])
{
return bitwise_func(argc, argv, LOG_AND);
}
static Token bitor_func(int argc, const Token argv[])
{
return bitwise_func(argc, argv, LOG_OR);
}
/* e */ /*{{{*/
static Token e_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==0) /* result is constant e */ /*{{{*/
{
result.type=FLOAT;
result.u.flt=CONST_E;
}
/*}}}*/
else duperror(&result, _("Usage: e()"));
return result;
}
/*}}}*/
/* eval */ /*{{{*/
static Token eval_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
--max_eval;
if (max_eval<0) duperror(&result, _("nested eval()"));
else if (argc==1 && argv[0].type==LOCATION)
/* evaluate expression in cell at given position */ /*{{{*/
{
Token **contents;
contents = EMPTY_TVEC;
if (LOC_WITHIN(upd_sheet, argv[0].u.location))
contents = getcont(CELL_AT(upd_sheet,argv[0].u.location), CONTINGENT);
if (contents == EMPTY_TVEC) result.type = EMPTY;
else result = eval(contents, FULL);
}
/*}}}*/
else duperror(&result, _("Usage: eval(location)"));
++max_eval;
return result;
}
/*}}}*/
/* error */ /*{{{*/
static Token error_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
/* asserts */ /*{{{*/
assert(argv!=(Token*)0);
/*}}}*/
if (argc != 1 || argv[0].type != STRING)
duperror(&result, _("Usage: error(string message)"));
else duperror(&result, argv[0].u.string);
return result;
}
/*}}}*/
/* string */ /*{{{*/
static Token string_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
Cell *cell;
char *buf;
char staticbuf[4096];
size_t size;
/*}}}*/
if (argc==1 && argv[0].type==LOCATION) /* cell to string */ /*{{{*/
{
result.type=STRING;
if (LOC_WITHIN(upd_sheet, argv[0].u.location))
{
cell = CELL_AT(upd_sheet, argv[0].u.location);
printvalue(staticbuf, sizeof(staticbuf), 0, 0, getscientific(cell),
getprecision(cell), upd_sheet, argv[0].u.location);
result.u.string = malloc(strlen(staticbuf) + 1);
strcpy(result.u.string, staticbuf);
}
else
{
result.u.string = malloc(1);
result.u.string[0] = '\0';
}
}
/*}}}*/
else if (argc==1 && argv[0].type==FLOAT) /* float to string */ /*{{{*/
{
result.u.string=malloc(def_precision+10);
sprintf(result.u.string,DEF_SCIENTIFIC ? "%.*e" : "%.*f", def_precision,argv[0].u.flt);
result.type=STRING;
}
/*}}}*/
else if (argc==1 && argv[0].type==INT) /* int to string */ /*{{{*/
{
int length=2;
int n=argv[0].u.integer;
while (n!=0) n/=10;
result.u.string=malloc(length);
sprintf(result.u.string,"%ld",argv[0].u.integer);
result.type=STRING;
}
/*}}}*/
else if (argc==2 && argv[0].type==FLOAT && argv[1].type==INT) /* float to string */ /*{{{*/
{
result.u.string=malloc(argv[1].u.integer==-1 ? def_precision+10 : argv[1].u.integer+10);
sprintf(result.u.string,DEF_SCIENTIFIC ? "%.*e" : "%.*f",argv[1].u.integer==-1 ? def_precision : (int)(argv[1].u.integer), argv[0].u.flt);
result.type=STRING;
}
/*}}}*/
else if (argc==3 && argv[0].type==FLOAT && (argv[1].type==INT || argv[1].type==EMPTY) && argv[2].type==INT) /* float to string */ /*{{{*/
{
result.u.string=malloc((argv[1].type==INT ? argv[1].u.integer : def_precision)+10);
sprintf(result.u.string,argv[2].u.integer ? "%.*e" : "%.*f",argv[1].type==INT && argv[1].u.integer>=0 ? (int)argv[1].u.integer : def_precision, argv[0].u.flt);
result.type=STRING;
}
/*}}}*/
else /* return string type error */ /*{{{*/
duperror(&result, _("Usage: string(location)|string(int)|string(float[,[int][,integer]])"));
return result;
}
/*}}}*/
/* plus */ /*{{{*/
static Token concat_func(int argc, const Token argv[])
{
Token result, tmp;
/* Try to add up the args */
result.type = EMPTY;
for (size_t i = 0; i < argc; ++i)
{
tmp = tconcat(result, argv[i]);
tfree(&result);
result = tmp;
if (result.type == EEK) return result;
}
return result;
}
/*}}}*/
/* plus */ /*{{{*/
static Token plus_func(int argc, const Token argv[])
{
Token result, tmp;
/* Try to add up the args */
result.type = EMPTY;
if (argc > 0) result = tcopy(argv[0]);
for (size_t i = 1; i < argc; ++i)
{
tmp = tadd(result, argv[i]);
tfree(&result);
result = tmp;
if (result.type == EEK) return result;
}
return result;
}
/*}}}*/
/* sum */ /*{{{*/
static Token sum_func(int argc, const Token argv[])
{
Token result;
const char *usage = _("Usage: sum(loc_start, loc_end)|sum(val1, val2,...)");
if (argc <= 0) {
duperror(&result, usage);
return result;
}
if (argc == 2 && argv[0].type == LOCATION && argv[1].type == LOCATION)
/* result is sum of entries in range */ /*{{{*/
{
/* variables */ /*{{{*/
Location w;
int x1,y1,z1;
int x2,y2,z2;
Token tmp;
/*}}}*/
x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; posorder(&x1,&x2);
y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; posorder(&y1,&y2);
z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2);
result.type = EMPTY;
for (w[X]=x1; w[X]<=x2; ++(w[X]))
for (w[Y]=y1; w[Y]<=y2; ++(w[Y]))
for (w[Z]=z1; w[Z]<=z2; ++(w[Z]))
{
Token t;
tmp = tadd(result, t=getvalue(upd_sheet,w));
tfree(&t);
tfree(&result);
result=tmp;
if (result.type==EEK) return result;
}
return result;
}
return plus_func(argc, argv);
}
/*}}}*/
/* n */ /*{{{*/
static Token n_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION)
/* result is number of elements */ /*{{{*/
{
/* variables */ /*{{{*/
Location w;
int x1,y1,z1;
int x2,y2,z2;
Token tmp;
int n;
/*}}}*/
x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; posorder(&x1,&x2);
y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; posorder(&y1,&y2);
z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2);
n=0;
for (w[X]=x1; w[X]<=x2; ++(w[X]))
for (w[Y]=y1; w[Y]<=y2; ++(w[Y]))
for (w[Z]=z1; w[Z]<=z2; ++(w[Z]))
{
tmp = getvalue(upd_sheet, w);
if (tmp.type != EMPTY) ++n;
tfree(&tmp);
}
result.type = INT;
result.u.integer = n;
}
/*}}}*/
else duperror(&result, _("Usage: n(location,location)"));
return result;
}
/*}}}*/
/* mul */ /*{{{*/
static Token mul_func(int argc, const Token argv[])
{
Token result, tmp;
/* Try to multiply up the args */
result.type = EMPTY;
if (argc > 0) result = tcopy(argv[0]);
for (size_t i = 1; i < argc; ++i)
{
tmp = tmul(result, argv[i]);
tfree(&result);
result = tmp;
if (result.type == EEK) return result;
}
return result;
}
/*}}}*/
/* pow */ /*{{{*/
static Token pow_func(int argc, const Token argv[])
{
Token result, tmp;
/* Try to power up the args, ((a^b)^c)^d etc */
result.type = EMPTY;
if (argc > 0) result = tcopy(argv[0]);
for (size_t i = 1; i < argc; ++i)
{
tmp = tpow(result, argv[i]);
tfree(&result);
result = tmp;
if (result.type == EEK) return result;
}
return result;
}
/*}}}*/
/* binop_func -- handles all binary operations as function calls */
static Token binop_func(int argc, const Token argv[],
Token (*tfunc)(Token, Token)) /*{{{*/
{
if (argc == 2) return tfunc(argv[0], argv[1]);
Token err;
duperror(&err, _("Binary infix op as function requires exactly 2 args"));
return err;
}
/*}}}*/
static Token minus_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tsub);
}
static Token div_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tdiv);
}
static Token le_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tle);
}
static Token ge_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tge);
}
static Token lt_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tlt);
}
static Token gt_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tgt);
}
static Token isequal_func(int argc, const Token argv[])
{
return binop_func(argc, argv, teq);
}
static Token abouteq_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tabouteq);
}
static Token ne_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tne);
}
static Token mod_func(int argc, const Token argv[])
{
return binop_func(argc, argv, tmod);
}
/* int */ /*{{{*/
static Token int_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==1 && argv[0].type==FLOAT)
/* result is integer with cutoff fractional part */ /*{{{*/
{
result.type=INT;
result.u.integer=(long)(argv[0].u.flt);
}
/*}}}*/
else if (argc==3 && argv[0].type==FLOAT && argv[1].type==INT && argv[2].type==INT)
/* result is integer with given conversion */ /*{{{*/
{
result.type=INT;
if (argv[0].u.flt<0)
{
if (argv[1].u.integer<-1) result.u.integer=(long)floor(argv[0].u.flt);
else if (argv[1].u.integer==-1) result.u.integer=(long)(argv[0].u.flt-0.5);
else if (argv[1].u.integer==0) result.u.integer=(long)(argv[0].u.flt);
else if (argv[1].u.integer==1) result.u.integer=(long)(argv[0].u.flt+0.5);
else result.u.integer=(long)ceil(argv[0].u.flt);
}
else
{
if (argv[2].u.integer<-1) result.u.integer=(long)floor(argv[0].u.flt);
else if (argv[2].u.integer==-1) result.u.integer=(long)(argv[0].u.flt-0.5);
else if (argv[2].u.integer==0) result.u.integer=(long)(argv[0].u.flt);
else if (argv[2].u.integer==1) result.u.integer=(long)(argv[0].u.flt+0.5);
else result.u.integer=(long)ceil(argv[0].u.flt);
}
}
/*}}}*/
else if (argc==1 && argv[0].type==STRING)
/* result is integer */ /*{{{*/
{
char *s;
errno=0;
result.u.integer=strtol(argv[0].u.string,&s,10);
if (s==(char*)0 || *s) duperror(&result, _("int(string): invalid string"));
else if (errno==ERANGE &&
(result.u.integer==LONG_MAX || result.u.integer==LONG_MIN))
duperror(&result, _("int(string): domain error"));
else result.type=INT;
}
/*}}}*/
else duperror(&result, _("Usage: int(float[,integer,integer])"));
return result;
}
/*}}}*/
/* frac */ /*{{{*/
static Token frac_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
double foo;
/*}}}*/
if (argc==1 && argv[0].type==FLOAT)
/* result is fractional part */ /*{{{*/
{
result.type=FLOAT;
result.u.flt=modf(argv[0].u.flt,&foo);
}
/*}}}*/
else duperror(&result, _("Usage: frac(float)"));
return result;
}
/*}}}*/
/* len */ /*{{{*/
static Token len_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==1 && argv[0].type==STRING)
/* result is length */ /*{{{*/
{
result.type=INT;
result.u.integer=strlen(argv[0].u.string);
}
/*}}}*/
else duperror(&result, _("Usage: len(string)"));
return result;
}
/*}}}*/
/* log */ /*{{{*/
static Token log_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
double x=-1.0,y=-1.0;
Token result;
/*}}}*/
/* set x and y to first two arguments */ /*{{{*/
if (argc>=1)
{
if (argv[0].type==FLOAT) x=argv[0].u.flt;
else if (argv[0].type==INT) x=(double)argv[0].u.integer;
}
if (argc==2)
{
if (argv[1].type==FLOAT) y=argv[1].u.flt;
else if (argv[1].type==INT) y=(double)argv[1].u.integer;
}
/*}}}*/
if (argc==1 && (argv[0].type==FLOAT || argv[0].type==INT)) /* result is ln(x) */ /*{{{*/
{
result.type=FLOAT;
result.u.flt=log(x);
}
/*}}}*/
else if (argc==2 && (argv[0].type==FLOAT || argv[0].type==INT) && (argv[1].type==FLOAT || argv[1].type==INT)) /* result is ln(x)/ln(y) */ /*{{{*/
{
result.type=FLOAT;
if (y==CONST_E) result.u.flt=log(x);
else if (y==10.0) result.u.flt=log10(x);
else result.u.flt=log(x)/log(y);
}
/*}}}*/
else duperror(&result, _("Usage: log(float[,float])"));
return result;
}
/*}}}*/
/* minmax */ /*{{{*/
static Token minmax_func(int argc, const Token argv[], int min)
{
/* variables */ /*{{{*/
Token result, tmp;
Location minloc;
/*}}}*/
if (argc==2 && argv[0].type==LOCATION && argv[1].type==LOCATION)
/* result is min/max */ /*{{{*/
{
/* variables */ /*{{{*/
Location w;
int x1,y1,z1;
int x2,y2,z2;
/*}}}*/
x1=argv[0].u.location[0]; x2=argv[1].u.location[0]; posorder(&x1,&x2);
y1=argv[0].u.location[1]; y2=argv[1].u.location[1]; posorder(&y1,&y2);
z1=argv[0].u.location[2]; z2=argv[1].u.location[2]; posorder(&z1,&z2);
minloc[X] = x1; minloc[Y] = y1; minloc[Z] = z1;
result = getvalue(upd_sheet, minloc);
for (w[X]=x1; w[X]<=x2; ++(w[X]))
for (w[Y]=y1; w[Y]<=y2; ++(w[Y]))
for (w[Z]=z1; w[Z]<=z2; ++(w[Z]))
{
Token t;
t = getvalue(upd_sheet, w);
tmp = (min ? tle(result,t) : tge(result, t));
if (tmp.type==INT)
/* successful comparison */ /*{{{*/
{
tfree(&tmp);
if (tmp.u.integer == 0)
{
tfree(&result);
result=t;
LOCATION_GETS(minloc, w);
}
else tfree(&t);
}
/*}}}*/
else
/* successless comparison, return with error */ /*{{{*/
{
tfree(&result);
tfree(&t);
return tmp;
}
/*}}}*/
}
tfree(&result);
result.type=LOCATION;
LOCATION_GETS(result.u.location, minloc);
return result;
}
/*}}}*/
else if (argc > 0) /* try to take min/max of all arguments */
{
size_t i, mini;
mini = 0;
for (i = 1; i < argc; ++i) {
tmp = (min ? tlt(argv[i], argv[mini]) : tgt(argv[i], argv[mini]));
if (tmp.type == INT) /* comparison succeeded */
{
if (tmp.u.integer) mini = i;
tfree(&tmp);
}
else /* failed comparison, return the error */
return tmp;
}
return argv[mini];
}
else
duperror(&result,
min ? _("Usage: min(location,location) or min(val1, val2,...)")
: _("Usage: max(location,location) or max(val1,val2,...)"));
return result;
}
/*}}}*/
/* min */ /*{{{*/
static Token min_func(int argc, const Token argv[])
{
return minmax_func(argc,argv,1);
}
/*}}}*/
/* max */ /*{{{*/
static Token max_func(int argc, const Token argv[])
{
return minmax_func(argc,argv,0);
}
/*}}}*/
/* abs */ /*{{{*/
static Token abs_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==1 && argv[0].type==FLOAT)
/* result is absolute floating point number */ /*{{{*/
{
result.type=FLOAT;
result.u.flt=fabs(argv[0].u.flt);
}
/*}}}*/
else if (argc==1 && argv[0].type==INT)
/* result is absolute integer number */ /*{{{*/
{
result.type=INT;
result.u.integer=(argv[0].u.integer<0 ? -argv[0].u.integer : argv[0].u.integer);
}
/*}}}*/
else duperror(&result, _("Usage: abs(float|integer)"));
return result;
}
/*}}}*/
/* $ */ /*{{{*/
static Token env_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==1 && argv[0].type==STRING)
{
const char *e;
if ((e=getenv(argv[0].u.string))==(char*)0) e="";
result.type=STRING;
result.u.string=strdup(e);
}
else duperror(&result, _("Usage: $(string)"));
return result;
}
/*}}}*/
/* float */ /*{{{*/
static Token float_func(int argc, const Token argv[])
{
Token result;
if (argc==1 && argv[0].type==STRING)
/* convert string to float */ /*{{{*/
{
char *p;
result.u.flt = strtod(argv[0].u.string, &p);
if (p != argv[0].u.string && *p=='\0'
&& dblfinite(result.u.flt) == (const char*)0)
result.type = FLOAT;
else duperror(&result, _("Not a (finite) floating point number"));
}
/*}}}*/
else duperror(&result, _("Usage: float(string)"));
return result;
}
/*}}}*/
/* strftime */ /*{{{*/
static Token strftime_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==1 && argv[0].type==STRING) /* format and return string */ /*{{{*/
{
time_t t;
struct tm *tm;
char s[1024];
t=time((time_t*)0);
tm=localtime(&t);
strftime(s,sizeof(s),argv[0].u.string,tm);
s[sizeof(s)-1]='\0';
result.u.string=strdup(s);
result.type=STRING;
}
/*}}}*/
else if (argc==2 && argv[0].type==STRING && argv[1].type==INT) /* format and return string */ /*{{{*/
{
time_t t;
struct tm *tm;
char s[1024];
t=argv[1].u.integer;
tm=localtime(&t);
strftime(s,sizeof(s),argv[0].u.string,tm);
s[sizeof(s)-1]='\0';
result.u.string=strdup(s);
result.type=STRING;
}
/*}}}*/
else duperror(&result, _("Usage: strftime(string[,integer])"));
return result;
}
/*}}}*/
/* clock */ /*{{{*/
static Token clock_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==2 && argv[0].type==INT && argv[1].type==LOCATION) /* clock(condition,location) */ /*{{{*/
{
if (argv[0].u.integer) clk(upd_sheet, argv[1].u.location);
result.type=EMPTY;
}
/*}}}*/
else if (argc==3 && argv[0].type==INT && argv[1].type==LOCATION && argv[2].type==LOCATION) /* clock(condition,location,location) */ /*{{{*/
{
if (argv[0].u.integer)
{
/* variables */ /*{{{*/
Location w;
int x1,y1,z1;
int x2,y2,z2;
/*}}}*/
x1=argv[1].u.location[0]; x2=argv[2].u.location[0]; posorder(&x1,&x2);
y1=argv[1].u.location[1]; y2=argv[2].u.location[1]; posorder(&y1,&y2);
z1=argv[1].u.location[2]; z2=argv[2].u.location[2]; posorder(&z1,&z2);
for (w[X]=x1; w[X]<=x2; ++(w[X]))
for (w[Y]=y1; w[Y]<=y2; ++(w[Y]))
for (w[Z]=z1; w[Z]<=z2; ++(w[Z]))
clk(upd_sheet, w);
}
result.type=EMPTY;
}
/*}}}*/
else duperror(&result, _("Usage: clock(condition,location[,location])"));
return result;
}
/*}}}*/
/* poly */ /*{{{*/
static Token poly_func(int argc, const Token argv[])
{
Token result, tmp;
const char *usage = _("Usage: poly(float|integer,float|integer,...)");
result.type = EMPTY;
if (argc < 2) duperror(&result, usage);
else for (size_t i = 0; i < argc; ++i)
if (argv[i].type != INT && argv[i].type != FLOAT)
duperror(&result, usage);
if (result.type == EEK) return result;
result = tcopy(argv[1]);
for (size_t i = 2; i < argc; ++i)
{
tmp = tmul(result,argv[0]);
tfree(&result);
result = tmp;
tmp = tadd(result,argv[i]);
tfree(&result);
result = tmp;
if (result.type == EEK) return result;
}
return result;
}
/*}}}*/
/* sin */ /*{{{*/
static Token sin_func(int argc, const Token argv[])
{
return sci_func(argc,argv,sin,"sin");
}
/*}}}*/
/* cos */ /*{{{*/
static Token cos_func(int argc, const Token argv[])
{
return sci_func(argc,argv,cos,"cos");
}
/*}}}*/
/* tan */ /*{{{*/
static Token tan_func(int argc, const Token argv[])
{
return sci_func(argc,argv,tan,"tan");
}
/*}}}*/
/* sinh */ /*{{{*/
static Token sinh_func(int argc, const Token argv[])
{
return sci_func(argc,argv,sinh,"sinh");
}
/*}}}*/
/* cosh */ /*{{{*/
static Token cosh_func(int argc, const Token argv[])
{
return sci_func(argc,argv,cosh,"cosh");
}
/*}}}*/
/* tanh */ /*{{{*/
static Token tanh_func(int argc, const Token argv[])
{
return sci_func(argc,argv,tanh,"tanh");
}
/*}}}*/
/* asin */ /*{{{*/
static Token asin_func(int argc, const Token argv[])
{
return sci_func(argc,argv,asin,"asin");
}
/*}}}*/
/* acos */ /*{{{*/
static Token acos_func(int argc, const Token argv[])
{
return sci_func(argc,argv,acos,"acos");
}
/*}}}*/
/* atan */ /*{{{*/
static Token atan_func(int argc, const Token argv[])
{
return sci_func(argc,argv,atan,"atan");
}
/*}}}*/
/* arsinh */ /*{{{*/
static Token arsinh_func(int argc, const Token argv[])
{
return sci_func(argc,argv,arsinh,"arsinh");
}
/*}}}*/
/* arcosh */ /*{{{*/
static Token arcosh_func(int argc, const Token argv[])
{
return sci_func(argc,argv,arcosh,"arcosh");
}
/*}}}*/
/* artanh */ /*{{{*/
static Token artanh_func(int argc, const Token argv[])
{
return sci_func(argc,argv,artanh,"artanh");
}
/*}}}*/
/* rad2deg */ /*{{{*/
static Token rad2deg_func(int argc, const Token argv[])
{
return sci_func(argc,argv,rad2deg,"rad2deg");
}
/*}}}*/
/* deg2rad */ /*{{{*/
static Token deg2rad_func(int argc, const Token argv[])
{
return sci_func(argc,argv,deg2rad,"deg2rad");
}
/*}}}*/
/* rnd */ /*{{{*/
static Token rnd_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==0)
{
result.type=FLOAT;
result.u.flt=rand()/((double)RAND_MAX);
}
else duperror(&result, _("Usage: rnd()"));
return result;
}
/*}}}*/
/* substr */ /*{{{*/
static Token substr_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==3 && argv[0].type==STRING && argv[1].type==INT && argv[2].type==INT)
{
char ss[1024];
int n, l, b, e;
b = argv[1].u.integer;
e = argv[2].u.integer;
l = strlen(argv[0].u.string);
if( b < 0 ) b = 0;
if( b > l ) b = l;
if( e > l ) e = l;
n = e - b + 1;
if( n >= 1024 ) n = 1024 - 1;
if(n > 0) {
ss[n] = '\0';
strncpy(ss, argv[0].u.string + b, n);
result.type=STRING;
result.u.string=strdup(ss);
}
else {
result.type=EMPTY;
}
}
else duperror(&result, _("Usage: substr(string,integer,integer)"));
return result;
}
/*}}}*/
/* strptime */ /*{{{*/
static Token strptime_func(int argc, const Token argv[])
{
/* variables */ /*{{{*/
Token result;
/*}}}*/
if (argc==2 && argv[0].type==STRING && argv[1].type==STRING) /* format and return string */ /*{{{*/
{
time_t t;
struct tm tm;
t=time((time_t*)0);
tm=*localtime(&t);
strptime(argv[1].u.string,argv[0].u.string,&tm);
result.u.integer=mktime(&tm);
result.type=INT;
}
/*}}}*/
else duperror(&result, _("Usage: strptime(string,string)"));
return result;
}
/*}}}*/
/* time */ /*{{{*/
static Token time_func(int argc, const Token argv[])
{
Token result;
if (argc==0)
{
result.type=INT;
result.u.integer=time((time_t*)0);
}
else duperror(&result, _("Usage: time()"));
return result;
}
/*}}}*/
/* negate -- unary - */
static Token negate_func(int argc, const Token argv[])
{
if (argc != 1)
{
Token err;
duperror(&err, _("Usage: -EXPR|negate(expr)"));
return err;
}
return tneg(argv[0]);
}
/* table of functions */ /*{{{*/
/* The order of these entries has no influence on performance, but to stay
compatible, new entries should be appended. */
Tfunc tfunc[]=
{
{ "@", abs_at_func },
{ "&", abs_adr_func },
{ "x", x_func },
{ "y", y_func },
{ "z", z_func },
{ "eval", eval_func },
{ "error", error_func },
{ "string", string_func },
{ "sum", sum_func },
{ "n", n_func },
{ "int", int_func },
{ "frac", frac_func },
{ "len", len_func },
{ "min", min_func },
{ "max", max_func },
{ "abs", abs_func },
{ "$", env_func },
{ "float", float_func },
{ "strftime", strftime_func },
{ "clock", clock_func },
{ "poly", poly_func },
{ "e", e_func },
{ "log", log_func },
{ "sin", sin_func },
{ "cos", cos_func },
{ "tan", tan_func },
{ "sinh", sinh_func },
{ "cosh", cosh_func },
{ "tanh", tanh_func },
{ "asin", asin_func },
{ "acos", acos_func },
{ "atan", atan_func },
{ "arsinh", arsinh_func },
{ "arcosh", arcosh_func },
{ "artanh", artanh_func },
{ "deg2rad", deg2rad_func },
{ "rad2deg", rad2deg_func },
{ "rnd", rnd_func },
{ "substr", substr_func },
{ "strptime", strptime_func },
{ "time", time_func },
{ "bitand", bitand_func },
{ "bitor", bitor_func },
{ "R", rel_at_func },
{ "D", rel_adr_func },
{ "X", excel_at_func },
{ "X&", excel_adr_func },
{ "negate", negate_func },
{ "+", plus_func },
{ "-", minus_func },
{ "*", mul_func },
{ "/", div_func },
{ "<=", le_func },
{ ">=", ge_func },
{ "<", lt_func },
{ ">", gt_func },
{ "==", isequal_func },
{ "~=", abouteq_func },
{ "!=", ne_func },
{ "^", pow_func },
{ "%", mod_func },
{ "concat", concat_func },
{ "", (Token (*)(int, const Token[]))0 }
};
/*}}}*/