/* #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 #include #include #include #include extern double strtod(const char *nptr, char **endptr); /* SunOS 4 hack */ extern long double strtold(const char *nptr, char **endptr); /* SunOS 4 hack */ #include extern char *strdup(const char* s); #include #include #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 ((FltT)2.7182818284590452354) #endif #ifdef M_PI #define CONST_PI M_PI #else #define CONST_PI ((FltT)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 /* identcode -- return number of identifier */ /*{{{*/ FunctionIdentifier identcode(const char *s, size_t len) { for (FunctionIdentifier fi = FIRST_FUNCTION; fi < N_FUNCTION_IDS; ++fi) { if (len == strlen(tfunc[fi].name) && strncmp(s, tfunc[fi].name, len) == 0) return fi; } return NOT_A_FUNCTION; } /*}}}*/ /* sci_func -- map a double to a double */ /*{{{*/ static Token sci_func(int argc, const Token argv[], FltT (*func)(FltT), 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)((FltT)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 FltT arsinh(FltT x) { return LOGFLT(x + SQRTFLT(x*x + 1.0)); } /*}}}*/ /* arcosh */ /*{{{*/ static FltT arcosh(FltT x) { if (x >= 1.0) return LOGFLT(x + SQRTFLT(x*x - 1.0)); else { errno=EDOM; return 0.0; } } /*}}}*/ /* artanh */ /*{{{*/ static FltT artanh(FltT x) { if (x > -1.0 && x < 1.0) return 0.5*LOGFLT((1.0+x)/(1.0-x)); else { errno=EDOM; return 0.0; } } /*}}}*/ /* rad2deg */ /*{{{*/ static FltT rad2deg(FltT x) { return (360.0/(2.0*CONST_PI))*x; } /*}}}*/ /* deg2rad */ /*{{{*/ static FltT deg2rad(FltT 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; /*}}}*/ if (lcon == EXCEL) return excel_adr_func(argc, argv); /* asserts */ /*{{{*/ assert(argc < 1 || argv != NULLTOKEN); /*}}}*/ result.type = LOCATION; LOCATION_GETS(result.u.location, upd_l); int offset = 0; if (argc > 0 && argv[0].type == LOCATION) { if (argc == 1 && lcon == RELATIVE) { LOCATION_ADD(result.u.location, argv[0].u.location); return result; } LOCATION_GETS(result.u.location, argv[0].u.location); offset = 1; } int i = offset; while (i < argc && i - offset < HYPER) { if (argv[i].type == EEK) return argv[i]; if (argv[i].type == INT) if (lcon == ABSOLUTE) result.u.location[i-offset] = argv[i].u.integer; else result.u.location[i-offset] += argv[i].u.integer; else if (argv[i].type != EMPTY) break; ++i; } if (i < argc) duperror(&result, _("Usage: &([location l,][integer x][,[integer y][,[integer z]]])")); 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 recompvalue(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) return duperror(&result, usage); 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) return duperror(&result, usage); result.type = LOCATION; 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])) return duperror(&result, usage); 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])) return duperror(&result, _("Bitwise functions need integer arguments.")); 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 < 1) /* result is constant e */ /*{{{*/ { result.type = FLOAT; result.u.flt = CONST_E; return result; } return duperror(&result, _("Usage: e()")); } /*}}}*/ /* tau = 2pi */ /*{{{*/ static Token tau_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc < 1) /* result is constant e */ /*{{{*/ { result.type = FLOAT; result.u.flt = 2.0*CONST_PI; return result; } return duperror(&result, _("Usage: tau()")); } /*}}}*/ /* 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.type = EMPTY; if (LOC_WITHIN(upd_sheet, argv[0].u.location)) contents = gettok(CELL_AT(upd_sheet,argv[0].u.location), CONTINGENT); if (contents.type == EMPTY) result.type = EMPTY; else result = evaltoken(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; const char *usage = _("Usage: string(x[,[int prec][,decimal|scientific]])"); int precision = def_precision; bool use_sci = DEF_SCIENTIFIC; char staticbuf[4096]; size_t size; /*}}}*/ if (argc < 1 || argc > 3) return duperror(&result, usage); if (argc == 3) { if (argv[2].type != FIDENT) return duperror(&result, usage); switch (argv[2].u.fident) { case FUNC_DECIMAL: use_sci = false; break; case FUNC_SCIENTIFIC: use_sci = true; break; default: assert(0); } } if (argc >= 2 & argv[1].type != EMPTY) { if (argv[1].type != INT) return duperror(&result, usage); precision = argv[1].u.integer; } result.type = STRING; size = printtok(staticbuf, sizeof(staticbuf), 0, false, use_sci, precision, true, argv); if (size > sizeof(staticbuf) - 2) return duperror(&result, _("string: Overflow of internal buffer")); result.u.string = strdup(staticbuf); 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) return duperror(&result, usage); 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=recompvalue(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; const char *usage = _("Usage: n([location[,location]])|n(val1,val2,...)"); Location w; bool singleloc = true; bool loclist = true; /*}}}*/ if (argc < 0) return duperror(&result, usage); result.type = INT; result.u.integer = 0; switch (argc) { case 0: LOCATION_GETS(w, upd_l); break; case 1: if (argv[0].type == LOCATION) LOCATION_GETS(w, argv[0].u.location); else singleloc = false; break; case 2: if (argv[0].type == LOCATION && argv[1].type == LOCATION) loclist = false; /* FALL THROUGH */ case 3: singleloc = false; } if (singleloc) { Token tmp = recompvalue(upd_sheet, w); result.u.integer = (tmp.type != EMPTY); tfree(&tmp); return result; } if (loclist) { for (int i = 0; i < argc; ++i) switch (argv[i].type) { case EEK: return argv[i]; case EMPTY: break; default: result.u.integer++; } return result; } 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); 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 tmp = recompvalue(upd_sheet, w); if (tmp.type != EMPTY) result.u.integer++; tfree(&tmp); } 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; return duperror(&err, _("Binary infix op as function requires exactly 2 args")); } /*}}}*/ 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 = (IntT)argv[0].u.flt; return result; } /*}}}*/ if (argc == 2 && argv[0].type==FLOAT && argv[1].type == FIDENT) /* result is integer with given conversion */ /*{{{*/ { result.type = INT; switch (argv[1].u.fident) { case FUNC_TRUNC: result.u.integer = (IntT)argv[0].u.flt; break; case FUNC_ROUND: result.u.integer = ROUNDFLTINT(argv[0].u.flt); break; case FUNC_FLOOR: result.u.integer = (IntT)FLOORFLT(argv[0].u.flt); break; case FUNC_CEIL: result.u.integer = (IntT) CEILFLT(argv[0].u.flt); break; default: assert(0); } return result; } /*}}}*/ if (argc == 1 && argv[0].type == STRING) /* result is integer */ /*{{{*/ { char *s; errno=0; result.u.integer = STRTOINT(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; return result; } /*}}}*/ return duperror(&result, _("Usage: int(string|float[,rounding_function])")); } /*}}}*/ /* frac */ /*{{{*/ static Token frac_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; FltT foo; /*}}}*/ if (argc==1 && argv[0].type==FLOAT) /* result is fractional part */ /*{{{*/ { result.type = FLOAT; result.u.flt = MODFFLT(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 */ /*{{{*/ FltT 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 = (FltT)argv[0].u.integer; } if (argc==2) { if (argv[1].type == FLOAT) y = argv[1].u.flt; else if (argv[1].type == INT) y = (FltT)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 = LOGFLT(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 = LOGFLT(x); else if (y == 2.0) result.u.flt = LOG2FLT(x); else if (y == 10.0) result.u.flt = LOG10FLT(x); else result.u.flt = LOGFLT(x)/LOGFLT(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 = recompvalue(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 = recompvalue(upd_sheet, w); tmp = (min ? tle(result,t) : tge(result, t)); if (tmp.type == INT) /* successful comparison */ /*{{{*/ { if (tmp.u.integer == 0) { tfree(&result); result = t; LOCATION_GETS(minloc, w); } else tfree(&t); tfree(&tmp); } /*}}}*/ 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 = ABSFLT(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; const char *usage = _("Usage: float(string|int)"); if (argc != 1) return duperror(&result, usage); switch (argv[0].type) { case STRING: { char *p; result.u.flt = STRTOFLT(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")); return result; } case INT: result.type = FLOAT; result.u.flt = (FltT)argv[0].u.integer; return result; } return duperror(&result, usage); 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) return duperror(&result, usage); for (size_t i = 0; i < argc; ++i) if (argv[i].type != INT && argv[i].type != FLOAT) return duperror(&result, usage); 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; } /*}}}*/ /* sqrt */ /*{{{*/ static Token sqrt_func(int argc, const Token argv[]) { return sci_func(argc, argv, SQRTFLT, "sqrt"); } /* sin */ /*{{{*/ static Token sin_func(int argc, const Token argv[]) { return sci_func(argc, argv, SINFLT, "sin"); } /*}}}*/ /* cos */ /*{{{*/ static Token cos_func(int argc, const Token argv[]) { return sci_func(argc, argv, COSFLT, "cos"); } /*}}}*/ /* tan */ /*{{{*/ static Token tan_func(int argc, const Token argv[]) { return sci_func(argc, argv, TANFLT, "tan"); } /*}}}*/ /* sinh */ /*{{{*/ static Token sinh_func(int argc, const Token argv[]) { return sci_func(argc, argv, SINHFLT, "sinh"); } /*}}}*/ /* cosh */ /*{{{*/ static Token cosh_func(int argc, const Token argv[]) { return sci_func(argc, argv, COSHFLT, "cosh"); } /*}}}*/ /* tanh */ /*{{{*/ static Token tanh_func(int argc, const Token argv[]) { return sci_func(argc, argv, TANHFLT, "tanh"); } /*}}}*/ /* asin */ /*{{{*/ static Token asin_func(int argc, const Token argv[]) { return sci_func(argc, argv, ASINFLT, "asin"); } /*}}}*/ /* acos */ /*{{{*/ static Token acos_func(int argc, const Token argv[]) { return sci_func(argc, argv, ACOSFLT, "acos"); } /*}}}*/ /* atan */ /*{{{*/ static Token atan_func(int argc, const Token argv[]) { return sci_func(argc, argv, ATANFLT, "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, tfunc[FUNC_RAD2DEG].name); } /*}}}*/ /* deg2rad */ /*{{{*/ static Token deg2rad_func(int argc, const Token argv[]) { return sci_func(argc, argv, deg2rad, tfunc[FUNC_DEG2RAD].name); } /*}}}*/ /* self function, typically used for keywords */ /*{{{*/ static Token self_func(int argc, const Token argv[], FunctionIdentifier which) { Token result; if (argc == -1) { result.type = FIDENT; result.u.fident = which; return result; } const char *templ = _("%s may only be used as a bare identifier"); result.type = EEK; result.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 1); sprintf(result.u.err, templ, tfunc[which].name); return result; } /*}}}*/ /* decimal keyword */ /*{{{*/ static Token decimal_func(int argc, const Token argv[]) { return self_func(argc, argv, FUNC_DECIMAL); } /*}}}*/ /* scientific keyword */ /*{{{*/ static Token scientific_func(int argc, const Token argv[]) { return self_func(argc, argv, FUNC_SCIENTIFIC); } /*}}}*/ /* rounding function */ /*{{{*/ static Token rounding_func(int argc, const Token argv[], FunctionIdentifier which_func, FltT (*func)(FltT)) { if (argc == -1) return self_func(argc, argv, which_func); return sci_func(argc, argv, func, tfunc[which_func].name); } /*}}}*/ /* floor */ /*{{{*/ static Token floor_func(int argc, const Token argv[]) { return rounding_func(argc, argv, FUNC_FLOOR, FLOORFLT); } /*}}}*/ /* ceil */ /*{{{*/ static Token ceil_func(int argc, const Token argv[]) { return rounding_func(argc, argv, FUNC_CEIL, CEILFLT); } /*}}}*/ /* round */ /*{{{*/ static Token round_func(int argc, const Token argv[]) { return rounding_func(argc, argv, FUNC_ROUND, ROUNDFLT); } /*}}}*/ /* round */ /*{{{*/ static Token trunc_func(int argc, const Token argv[]) { return rounding_func(argc, argv, FUNC_TRUNC, TRUNCFLT); } /*}}}*/ /* rnd */ /*{{{*/ static Token rnd_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; /*}}}*/ if (argc==0) { result.type = FLOAT; result.u.flt = rand()/((FltT)RAND_MAX); } else duperror(&result, _("Usage: rnd()")); return result; } /*}}}*/ /* substr */ /*{{{*/ static Token substr_func(int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result; const char *usage = _("Usage: substr(string,integer[,integer])"); /*}}}*/ if (argc < 2 || argc > 3) return duperror(&result, usage); if (argc == 3 && argv[2].type != INT) duperror(&result, usage); if (argv[0].type == STRING && argv[1].type == INT) { char ss[1024]; int b = argv[1].u.integer; int l = strlen(argv[0].u.string); int e = l; if (argc == 3) e = argv[2].u.integer; if ( b < 0 ) b = 0; if ( b > l ) b = l; if ( e > l ) e = l; int 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); 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 < 1) { 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; return duperror(&err, _("Usage: -EXPR|negate(expr)")); } 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[]= { /* Operators in order of increasing precedence */ [FUNC_CONCAT] = { "concat", concat_func, INFIX_CONC, FUNCT, "" }, [FUNC_LESS_EQUAL] = { "<=", le_func, INFIX_REL, FUNCT, 0 }, [FUNC_GREATER_EQUAL] = { ">=", ge_func, INFIX_REL, FUNCT, 0 }, [FUNC_LESS_THAN] = { "<", lt_func, INFIX_REL, FUNCT, 0 }, [FUNC_GREATER_THAN] = { ">", gt_func, INFIX_REL, FUNCT, 0 }, [FUNC_EQUAL_EQUAL] = { "==", isequal_func, INFIX_REL, FUNCT, 0 }, [FUNC_TILDE_EQUAL] = { "~=", abouteq_func, INFIX_REL, FUNCT, 0 }, [FUNC_BANG_EQUAL] = { "!=", ne_func, INFIX_REL, FUNCT, 0 }, [FUNC_PLUS_SYMBOL] = { "+", plus_func, INFIX_PLUS, FUNCT, 0 }, [FUNC_MINUS_SYMBOL] = { "-", minus_func, INFIX_PLUS, FUNCT, 0 }, [FUNC_ASTERISK] = { "*", mul_func, INFIX_MUL, FUNCT, 0 }, [FUNC_SLASH] = { "/", div_func, INFIX_MUL, FUNCT, 0 }, [FUNC_PER_CENT] = { "%", mod_func, INFIX_MUL, FUNCT, 0 }, [FUNC_NEGATE] = { "negate", negate_func, PREFIX_NEG, FUNCT, "-" }, [FUNC_CARET] = { "^", pow_func, INFIX_POW, FUNCT, 0 }, /* Addressing/cell fetching/cell-position functions */ [FUNC_AMPERSAND] = { "&", abs_adr_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_D] = { "D", rel_adr_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_X_AMPERSAND] = { "X&", excel_adr_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_AT_SYMBOL] = { "@", abs_at_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_R] = { "R", rel_at_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_CAP_X] = { "X", excel_at_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_X] = { "x", x_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_Y] = { "y", y_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_Z] = { "z", z_func, PREFIX_FUNC, FUNCT, 0 }, /* Evaluation control functions */ [FUNC_CLOCK] = { "clock", clock_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ERROR] = { "error", error_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_EVAL] = { "eval", eval_func, PREFIX_FUNC, FUNCT, 0 }, /* Type conversion functions */ [FUNC_FLOAT] = { "float", float_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_INT] = { "int", int_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_STRING] = { "string", string_func, PREFIX_FUNC, FUNCT, 0 }, /* Block operations */ [FUNC_MAX] = { "max", max_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_MIN] = { "min", min_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_N] = { "n", n_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SUM] = { "sum", sum_func, PREFIX_FUNC, FUNCT, 0 }, /* String functions */ [FUNC_LEN] = { "len", len_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SUBSTR] = { "substr", substr_func, PREFIX_FUNC, FUNCT, 0 }, /* Transcendental functions */ [FUNC_ACOS] = { "acos", acos_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ARCOSH] = { "arcosh", arcosh_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ARSINH] = { "arsinh", arsinh_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ARTANH] = { "artanh", artanh_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ASIN] = { "asin", asin_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ATAN] = { "atan", atan_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_DEG2RAD] = { "deg2rad", deg2rad_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_E] = { "e", e_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_COS] = { "cos", cos_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_COSH] = { "cosh", cosh_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_LOG] = { "log", log_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_RAD2DEG] = { "rad2deg", rad2deg_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SIN] = { "sin", sin_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SINH] = { "sinh", sinh_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TAN] = { "tan", tan_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TANH] = { "tanh", tanh_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TAU] = { "tau", tau_func, PREFIX_FUNC, FUNCT, 0 }, /* Other mathematical functions */ [FUNC_ABS] = { "abs", abs_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_BITAND] = { "bitand", bitand_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_BITOR] = { "bitor", bitor_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_CEIL] = { "ceil", ceil_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_FLOOR] = { "floor", floor_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_FRAC] = { "frac", frac_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_POLY] = { "poly", poly_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_RND] = { "rnd", rnd_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ROUND] = { "round", round_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SQRT] = { "sqrt", sqrt_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TRUNC] = { "trunc", trunc_func, PREFIX_FUNC, FUNCT, 0 }, /* Time functions */ [FUNC_STRFTIME] = { "strftime", strftime_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_STRPTIME] = { "strptime", strptime_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TIME] = { "time", time_func, PREFIX_FUNC, FUNCT, 0 }, /* Miscellaneous other functions/keywords */ [FUNC_DOLLAR_SIGN] = { "$", env_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_DECIMAL] = { "decimal", decimal_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SCIENTIFIC] = { "scientific", scientific_func, PREFIX_FUNC, FUNCT, 0 }, }; /*}}}*/