/* #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; } /*}}}*/ /* fracpart */ /*{{{*/ static FltT fracpart(FltT x) { FltT intpart; return MODFFLT(x, &intpart); } /*}}}*/ #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; } /* & */ /*{{{*/ static Token adr_func(FunctionIdentifier self, int argc, const Token argv[]) { bool absolute = true; switch (self) { case FUNC_AMPERSAND: break; case FUNC_D: absolute = false; break; case FUNC_X_AMPERSAND: return excel_adr_func(argc, argv); default: assert(0); } assert(argc < 1 || argv != NULLTOKEN); Token result; result.type = LOCATION; LOCATION_GETS(result.u.location, upd_l); int offset = 0; if (argc > 0 && argv[0].type == LOCATION) { if (argc == 1 && !absolute) { 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 (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) { const char *templ = _("Usage: %s([location l,][integer x][,[integer y][,[integer z]]])"); result.type = EEK; result.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 1); sprintf(result.u.err, templ, tfunc[self].name); } return result; } /*}}}*/ /* @ */ /*{{{*/ static Token at_func(FunctionIdentifier self, int argc, const Token argv[]) { FunctionIdentifier sub = FUNC_AMPERSAND; switch (self) { case FUNC_AT_SYMBOL: break; case FUNC_R: sub = FUNC_D; break; case FUNC_CAP_X: sub = FUNC_X_AMPERSAND; break; default: assert(0); } Token location = adr_func(sub, argc, argv); if (location.type == EEK) { Token result; result.type = EEK; char *templ = _("Inside %s(): %s"); result.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + strlen(location.u.err) + 1); sprintf(result.u.err, templ, tfunc[self].name, location.u.err); /* don't free the location if it is the same as an argument, because those get freed later: */ for (int i = 0; i < argc; ++i) if (argv[i].type == EEK & argv[i].u.err == location.u.err) return result; tfree(&location); return result; } assert(location.type == LOCATION); return recompvalue(upd_sheet, location.u.location); } /* disp_func -- common implementation of the displacement functions */ /*{{{*/ static Token disp_func(FunctionIdentifier self, int argc, const Token argv[]) { Token off; off.type = LOCATION; OLOCATION(off.u.location); switch (self) { case FUNC_RIGHT: off.u.location[X] = 1; break; case FUNC_LEFT: off.u.location[X] = -1; break; case FUNC_DOWN: off.u.location[Y] = 1; break; case FUNC_UP: off.u.location[Y] = -1; break; case FUNC_BELOW: off.u.location[Z] = 1; break; case FUNC_ABOVE: off.u.location[Z] = -1; break; default: assert(0); } switch (argc) { case -1: return off; case 0: LOCATION_ADD(off.u.location, upd_l); return recompvalue(upd_sheet, off.u.location); case 1: if (argv[0].type == LOCATION) { LOCATION_ADD(off.u.location, argv[0].u.location); return recompvalue(upd_sheet, off.u.location); } /* drop through */ default: break; } const char* usage = _("Usage: %s[([location])]"); off.type = EEK; off.u.err = malloc(strlen(usage) + MAX_FUNC_NAME_LENGTH + 1); sprintf(off.u.err, usage, tfunc[self].name); return off; } /* dim_func -- common implementation of the coordinate functions */ /*{{{*/ static Token dim_func(FunctionIdentifier self, int argc, const Token argv[]) { Dimensions dim = X; switch (self) { case FUNC_X: break; case FUNC_Y: dim = Y; break; case FUNC_Z: dim = Z; break; default: assert(0); } 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; } /*}}}/ /* bit_func -- common implementation of the bitwise functions */ /*{{{*/ static Token bit_func(FunctionIdentifier self, int argc, const Token argv[]) { Token result; int accum; if (self == FUNC_BITAND) 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 (self == FUNC_BITAND) accum = accum & val; else accum = accum | val; } result.type = INT; result.u.integer = accum; return result; } /*}}}*/ /* constant_func - common implementation of constants */ /*{{{*/ static Token constant_func(FunctionIdentifier fi, int argc, const Token argv[]) { Token result; if (argc < 1) { result.type = FLOAT; switch (fi) { case FUNC_E: result.u.flt = CONST_E; case FUNC_TAU: result.u.flt = 2*CONST_PI; default: assert(0); } return result; } const char *usage = _("Usage: %s[()]"); result.type = EEK; result.u.err = malloc(strlen(usage) + MAX_FUNC_NAME_LENGTH + 1); sprintf(result.u.err, usage, tfunc[fi].name); } /*}}}*/ /* eval */ /*{{{*/ static Token eval_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_EVAL); /* 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; } /*}}}*/ /* blcnst_func -- common implementation of true/false */ /*{{{*/ static Token blcnst_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_FALSE || self == FUNC_TRUE); if (argc >= 0) { Token err; const char *templ = _("'%s' must be used as a bare word"); err.type = EEK; err.u.err = malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 1); sprintf(err.u.err, templ, tfunc[self].name); return err; } Token res; res.type = BOOL; res.u.bl = (self == FUNC_TRUE); return res; } /*}}}*/ /* blop_macro -- common handler for short-circuiting boolean ops */ /*{{{*/ static Token blop_macro(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_AND || self == FUNC_OR); Token result; result.type = BOOL; result.u.bl = (self == FUNC_AND); for (int i = 0; i < argc; ++i) { Token argres = evaltoken(argv[i], FULL); Token tmp = (self == FUNC_AND) ? tand(result, argres) : tor(result, argres); tfree_protected(&argres, tmp); tfree_protected(&result, tmp); result = tmp; if (result.type == EEK) return result; } return result; } /*}}}*/ /* self function, typically used for keywords */ /*{{{*/ static Token self_func(FunctionIdentifier self, int argc, const Token argv[]) { Token result; if (argc == -1) { result.type = FIDENT; result.u.fident = self; 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[self].name); return result; } /*}}}*/ /* empty -- ignores arguments and returns empty value */ /*{{{*/ static Token empty_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_EMPTY); if (argc == -1) return self_func(self, argc, argv); Token emp; emp.type = EMPTY; return emp; } /*}}}*/ /* float */ /*{{{*/ static Token float_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_FLOAT); Token arg, result; result.type = FLOAT; result.u.flt = 0.0; const char *usage = _("Usage: float[([float|string|int|empty])]"); switch (argc) { case -1: return self_func(self, argc, argv); case 0: arg = recompvalue(upd_sheet, upd_l); break; case 1: arg = tcopy(argv[0]); break; default: return duperror(&result, usage); } switch (arg.type) { case FLOAT: return arg; case STRING: { char *p = arg.u.string; Token *sf = scan_flt(&p); if (sf == NULLTOKEN || *p != '\0') duperror(&result, _("Not a (finite) floating point number")); else result = *sf; free(sf); tfree(&arg); return result; } case INT: result.u.flt = (FltT)arg.u.integer; /* fall through */ case EMPTY: return result; default: break; } tfree(&arg); return duperror(&result, usage); } /*}}}*/ /* number -- convert to most appropriate number type */ /*{{{*/ static Token number_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_NUMBER); const char *usage = _("Usage: number[([bool|empty|int|float|string x])]"); Token result, arg; result.type = INT; result.u.integer = 0; switch (argc) { case -1: return self_func(self, argc, argv); case 0: arg = recompvalue(upd_sheet, upd_l); break; case 1: arg = tcopy(argv[0]); break; default: return duperror(&result, usage); } switch (arg.type) { case INT: return arg; case FLOAT: return arg; case BOOL: if (arg.u.bl) result.u.integer = 1; /* FALL THROUGH */ case EMPTY: return result; case STRING: { char *p = arg.u.string; Token *st = scan_integer(&p); if (st != NULLTOKEN && *p == '\0') result = *st; else { p = arg.u.string; st = scan_flt(&p); if (st == NULLTOKEN || *p != '\0') duperror(&result, _("string does not represent a number")); else result = *st; } free(st); tfree(&arg); return result; } default: break; } tfree(&arg); return duperror(&result, usage); } /*}}}*/ /* int */ /*{{{*/ static Token int_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_INT); const char *usage = _("Usage: int[([int|string|bool|float|empty][,rounding_function])]"); Token result, arg; result.type = INT; result.u.integer = 0; FunctionIdentifier dir = FUNC_TRUNC; switch (argc) { case -1: return self_func(self, argc, argv); case 0: arg = recompvalue(upd_sheet, upd_l); break; case 2: if (argv[1].type != FIDENT) return duperror(&result, usage); dir = argv[1].u.fident; /* FALL THROUGH */ case 1: arg = tcopy(argv[0]); break; default: return duperror(&result, usage); } switch (arg.type) { case INT: return arg; case FLOAT: switch (dir) { case FUNC_TRUNC: result.u.integer = (IntT)arg.u.flt; break; case FUNC_ROUND: result.u.integer = ROUNDFLTINT(arg.u.flt); break; case FUNC_FLOOR: result.u.integer = (IntT)FLOORFLT(arg.u.flt); break; case FUNC_CEIL: result.u.integer = (IntT) CEILFLT(arg.u.flt); break; default: assert(0); } return result; case STRING: { char *s = arg.u.string; Token *si = scan_integer(&s); if (si == NULLTOKEN || *s != '\0') duperror(&result, _("int(string): invalid string")); else result = *si; free(si); tfree(&arg); return result; } case BOOL: if (arg.u.bl) result.u.integer = 1; /* fall through */ case EMPTY: return result; default: break; } tfree(&arg); return duperror(&result, usage); } /*}}}*/ /* string */ /*{{{*/ static Token string_func(FunctionIdentifier self, int argc, const Token argv[]) { /* variables */ /*{{{*/ Token result, arg; int precision = def_precision; bool ff = DEF_FLOATFORM; bool bad_args = false; char staticbuf[4096]; /*}}}*/ switch (argc) { case -1: return self_func(self, argc, argv); case 0: arg = recompvalue(upd_sheet, upd_l); break; case 3: if (argv[2].type != FIDENT) bad_args = true; break; switch (argv[2].u.fident) { case FUNC_DECIMAL: ff = FLT_DECIMAL; break; case FUNC_SCIENTIFIC: ff = FLT_SCIENTIFIC; break; case FUNC_COMPACT: ff = FLT_COMPACT; break; case FUNC_HEXACT: ff = FLT_HEXACT; break; default: assert(0); } /* FALL THROUGH */ case 2: if (argv[1].type == INT) precision = argv[1].u.integer; else if (argv[1].type != EMPTY) bad_args = true; break; /* FALL THROUGH */ case 1: arg = tcopy(argv[0]); break; default: bad_args = true; } if (bad_args) { const char *templ = _("Usage: %s[([x][,[int prec][,format_for_floats]])]"); sprintf(staticbuf, templ, tfunc[self].name); return duperror(&result, staticbuf); } result.type = STRING; size_t size = printtok(staticbuf, sizeof(staticbuf), 0, DIRECT_STRING, ff, precision, VERBOSE_ERROR, &arg); tfree(&arg); if (size > sizeof(staticbuf) - 2) return duperror(&result, _("string: Overflow of internal buffer")); result.u.string = strdup(staticbuf); return result; } /*}}}*/ /* error */ /*{{{*/ static Token error_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_ERROR); if (argc == -1) return self_func(self, argc, argv); Token tmsg = string_func(self, argc, argv); if (tmsg.type == EEK) return tmsg; assert(tmsg.type == STRING); char *msg = tmsg.u.string; tmsg.type = EEK; tmsg.u.err = msg; return tmsg; } /*}}}*/ /* is_func -- test the type of a value */ /*{{{*/ static Token is_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_IS); Token val; val.type = EMPTY; bool need_value = true; bool tested_something = false; Token result; result.type = BOOL; result.u.bl = false; for (int ai = 0; ai < argc; ++ai) { if (argv[ai].type == FIDENT && IS_TYPE_FUNC(argv[ai].u.fident)) { /* OK, test this type */ tested_something = true; if (need_value) { val = recompvalue(upd_sheet, upd_l); need_value = false; } switch (argv[ai].u.fident) { case FUNC_EMPTY: result.u.bl = (val.type == EMPTY); break; case FUNC_STRING: result.u.bl = (val.type == STRING); break; case FUNC_FLOAT: result.u.bl = (val.type == FLOAT); break; case FUNC_INT: result.u.bl = (val.type == INT); break; case FUNC_OPERATOR: result.u.bl = (val.type == OPERATOR); break; case FUNC_LIDENT: result.u.bl = (val.type == LIDENT); break; case FUNC_FIDENT: result.u.bl = (val.type == FIDENT); break; case FUNC_LOCATION: result.u.bl = (val.type == LOCATION); break; case FUNC_ERROR: result.u.bl = (val.type == EEK); break; case FUNC_FUNCALL: result.u.bl = (val.type == FUNCALL); break; case FUNC_BOOL: result.u.bl = (val.type == BOOL); break; case FUNC_NUMBER: result.u.bl = val.type==FLOAT || val.type==INT || val.type==EMPTY; break; default: assert(0); } if (result.u.bl) { tfree(&val); return result; } } else if (need_value) { val = tcopy(argv[ai]); need_value = false; } else { tfree(&val); const char* usage = _("Usage: is([x][,type1, type2, ...])"); return duperror(&result, usage); } } if (tested_something) { tfree(&val); return result; } if (need_value) val = recompvalue(upd_sheet, upd_l); result.u.bl = (val.type != EMPTY); tfree(&val); return result; } /*}}}*/ /* accum_func -- common implementation of functions that accumulate all of their arguments into a final value. */ static Token accum_func(FunctionIdentifier self, int argc, const Token argv[]) { Token (*tbin)(Token, Token); int start = 0; switch (self) { case FUNC_CONCAT: tbin = tconcat; break; case FUNC_PLUS_SYMBOL: tbin = tadd; break; case FUNC_ASTERISK: tbin = tmul; start = 1; break; case FUNC_CARET: tbin = tpow; start = 1; break; default: assert(0); } Token result; result.type = EMPTY; if (start > 0) { result = tcopy(argv[start-1]); } for (int i = start; i < argc; ++i) { Token tmp = tbin(result, argv[i]); tfree_protected(&result, tmp); result = tmp; if (result.type == EEK) return result; } return result; } typedef void (*RegFuncInit)(FunctionIdentifier id, Location *loc, Token *tok); typedef void (*RegFuncUpdt)(FunctionIdentifier id, Location *loc, Token *tok, const Location *newloc, const Token* newtok); typedef void (*RegFuncFinl)(FunctionIdentifier id, Location *loc, Token *tok); /* region_func -- apply an operation over a whole region */ static Token region_func(RegFuncInit init, RegFuncUpdt updt, RegFuncFinl finl, FunctionIdentifier id, int argc, const Token argv[]) { if (argc == 2 && argv[0].type == LOCATION && argv[1].type == LOCATION) { int x1 = argv[0].u.location[X]; int x2 = argv[1].u.location[X]; posorder(&x1, &x2); int y1 = argv[0].u.location[Y]; int y2 = argv[1].u.location[Y]; posorder(&y1, &y2); int z1 = argv[0].u.location[Z]; int z2 = argv[1].u.location[Z]; posorder(&z1,&z2); Location l; l[X] = x1; l[Y] = y1; l[Z] = z1; Token t = recompvalue(upd_sheet, l); if (init != (RegFuncInit)0) init(id, &l, &t); if (t.type == EEK) return t; Location w; 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); updt(id, &l, &t, &w, &tmp); tfree_protected(&tmp, t); if (t.type == EEK) return t; } if (finl != (RegFuncFinl)0) finl(id, &l, &t); return t; } if (argc > 0) /* try to accumulate over all arguments */ { Location l; OLOCATION(l); Token t = argv[0]; if (init != (RegFuncInit)0) init(id, &l, &t); for (int i = 0; i < argc; ++i) { Location fake; OLOCATION(fake); fake[X] = i; updt(id, &l, &t, &fake, argv + i); if (t.type == EEK) return t; } /* don't call finalize in this case because the region is fake */ return t; } const char* templ = _("Usage:%s(loc_start,loc_end)|%s(val2,val2,...)"); Token err; err.type = EEK; err.u.err = malloc(strlen(templ) + 2*MAX_FUNC_NAME_LENGTH + 1); sprintf(err.u.err, templ, tfunc[id].name, tfunc[id].name); return err; } static void sum_init(FunctionIdentifier id, Location *loc, Token *tok) { assert(id == FUNC_SUM); tfree(tok); tok->type = EMPTY; } static void sum_updt(FunctionIdentifier id, Location *loc, Token *tok, const Location* newloc, const Token *newtok) { assert(id == FUNC_SUM); Token tmp = tadd(*tok, *newtok); tfree_protected(tok, tmp); *tok = tmp; } static void minmax_updt(FunctionIdentifier id, Location *loc, Token *tok, const Location* newloc, const Token *newtok) { Token tmp = (id == FUNC_MIN) ? tle(*tok, *newtok) : tge(*tok, *newtok); if (tmp.type == INT) /* Succesful comparison */ { if (tmp.u.integer == 0) { tfree(tok); *tok = *newtok; LOCATION_GETS(*loc, *newloc); } tfree(&tmp); } else { /* failed comparison */ tfree(tok); *tok = tmp; } } static void minmax_finl(FunctionIdentifier id, Location *loc, Token *tok) { tfree(tok); tok->type = LOCATION; LOCATION_GETS(tok->u.location, *loc); } static Token reg_disp(FunctionIdentifier self, int argc, const Token argv[]) { RegFuncInit i = 0; RegFuncUpdt u = 0; RegFuncFinl f = 0; switch (self) { case FUNC_SUM: i = sum_init; u = sum_updt; break; case FUNC_MIN: case FUNC_MAX: u = minmax_updt; f = minmax_finl; break; default: assert(0); } return region_func(i, u, f, self, argc, argv); } /* n -- return whether a value is non-empty, or count the number of non-empty values in a region or in the argument list */ /*{{{*/ static Token n_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_N); /* 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; } /*}}}*/ /* binop_func -- common implementation of all binary operations as function calls */ /*{{{*/ static Token binop_func(FunctionIdentifier self, int argc, const Token argv[]) { Token (*tbin)(Token, Token); switch (self) { case FUNC_MINUS_SYMBOL: tbin = tsub; break; case FUNC_SLASH: tbin = tdiv; break; case FUNC_LESS_EQUAL: tbin = tle; break; case FUNC_GREATER_EQUAL: tbin = tge; break; case FUNC_LESS_THAN: tbin = tlt; break; case FUNC_GREATER_THAN: tbin = tgt; break; case FUNC_EQUAL_EQUAL: tbin = teq; break; case FUNC_TILDE_EQUAL: tbin = tabouteq; break; case FUNC_BANG_EQUAL: tbin = tne; break; case FUNC_PER_CENT: tbin = tmod; break; default: assert(0); } if (argc == 2) return tbin(argv[0], argv[1]); Token err; return duperror(&err, _("Binary infix op as function requires exactly 2 args")); } /*}}}*/ /* len */ /*{{{*/ static Token len_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_LEN); /* 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(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_LOG); /* 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; } /*}}}*/ /* abs */ /*{{{*/ static Token abs_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_ABS); /* 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(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_DOLLAR_SIGN); /* 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; } /*}}}*/ /* strftime */ /*{{{*/ static Token strftime_func(FunctionIdentifier fi, int argc, const Token argv[]) { assert(fi == FUNC_STRFTIME); /* 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(FunctionIdentifier self,int argc, const Token argv[]) { assert(self == FUNC_CLOCK); /* 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(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_POLY); 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_protected(&result, tmp); result = tmp; tmp = tadd(result,argv[i]); tfree_protected(&result, tmp); result = tmp; if (result.type == EEK) return result; } return result; } /*}}}*/ static Token sci_disp(FunctionIdentifier self, int argc, const Token argv[]) { FltT (*fsci)(FltT); switch (self) { case FUNC_SQRT: fsci = SQRTFLT; break; case FUNC_SIN: fsci = SINFLT; break; case FUNC_COS: fsci = COSFLT; break; case FUNC_TAN: fsci = TANFLT; break; case FUNC_SINH: fsci = SINHFLT; break; case FUNC_COSH: fsci = COSHFLT; break; case FUNC_TANH: fsci = TANHFLT; break; case FUNC_ASIN: fsci = ASINFLT; break; case FUNC_ACOS: fsci = ACOSFLT; break; case FUNC_ATAN: fsci = ATANFLT; break; case FUNC_ARSINH: fsci = arsinh; break; case FUNC_ARCOSH: fsci = arcosh; break; case FUNC_ARTANH: fsci = artanh; break; case FUNC_RAD2DEG: fsci = rad2deg; break; case FUNC_DEG2RAD: fsci = deg2rad; break; case FUNC_FRAC: fsci = fracpart; break; default: assert(0); } return sci_func(argc, argv, fsci, tfunc[self].name); } /* nearby integer (rounding) function */ /*{{{*/ static Token near_func(FunctionIdentifier self, int argc, const Token argv[]) { FltT (*frnd)(FltT); switch (self) { case FUNC_FLOOR: frnd = FLOORFLT; break; case FUNC_CEIL: frnd = CEILFLT; break; case FUNC_TRUNC: frnd = TRUNCFLT; break; case FUNC_ROUND: frnd = ROUNDFLT; break; default: assert(0); } if (argc == -1) return self_func(self, argc, argv); return sci_func(argc, argv, frnd, tfunc[self].name); } /*}}}*/ /* rnd */ /*{{{*/ static Token rnd_func(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_RND); /* 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(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_SUBSTR); /* 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(FunctionIdentifier fi, int argc, const Token argv[]) { assert(fi == FUNC_STRPTIME); /* 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(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_TIME); 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(FunctionIdentifier self, int argc, const Token argv[]) { assert(self == FUNC_NEGATE); 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", accum_func, INFIX_CONC, FUNCT, "" }, [FUNC_LESS_EQUAL] = { "<=", binop_func, INFIX_REL, FUNCT, 0 }, [FUNC_GREATER_EQUAL] = { ">=", binop_func, INFIX_REL, FUNCT, 0 }, [FUNC_LESS_THAN] = { "<", binop_func, INFIX_REL, FUNCT, 0 }, [FUNC_GREATER_THAN] = { ">", binop_func, INFIX_REL, FUNCT, 0 }, [FUNC_EQUAL_EQUAL] = { "==", binop_func, INFIX_REL, FUNCT, 0 }, [FUNC_TILDE_EQUAL] = { "~=", binop_func, INFIX_REL, FUNCT, 0 }, [FUNC_BANG_EQUAL] = { "!=", binop_func, INFIX_REL, FUNCT, 0 }, [FUNC_PLUS_SYMBOL] = { "+", accum_func, INFIX_PLUS, FUNCT, 0 }, [FUNC_MINUS_SYMBOL] = { "-", binop_func, INFIX_PLUS, FUNCT, 0 }, [FUNC_ASTERISK] = { "*", accum_func, INFIX_MUL, FUNCT, 0 }, [FUNC_SLASH] = { "/", binop_func, INFIX_MUL, FUNCT, 0 }, [FUNC_PER_CENT] = { "%", binop_func, INFIX_MUL, FUNCT, 0 }, [FUNC_NEGATE] = { "negate", negate_func, PREFIX_NEG, FUNCT, "-" }, [FUNC_CARET] = { "^", accum_func, INFIX_POW, FUNCT, 0 }, /* Addressing/cell fetching/cell-position functions */ [FUNC_AMPERSAND] = { "&", adr_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_D] = { "D", adr_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_X_AMPERSAND] = { "X&", adr_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_AT_SYMBOL] = { "@", at_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_R] = { "R", at_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_CAP_X] = { "X", at_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_RIGHT] = { "right", disp_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_LEFT] = { "left", disp_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_DOWN] = { "down", disp_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_UP] = { "up", disp_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_BELOW] = { "below", disp_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ABOVE] = { "above", disp_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_X] = { "x", dim_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_Y] = { "y", dim_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_Z] = { "z", dim_func, PREFIX_FUNC, FUNCT, 0 }, /* Evaluation control functions */ [FUNC_CLOCK] = { "clock", clock_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_EVAL] = { "eval", eval_func, PREFIX_FUNC, FUNCT, 0 }, /* Boolean functions */ [FUNC_AND] = { "and", blop_macro, INFIX_BOOL, MACRO, 0 }, [FUNC_FALSE] = { "false", blcnst_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_OR] = { "or", blop_macro, INFIX_BOOL, MACRO, 0 }, [FUNC_TRUE] = { "true", blcnst_func, PREFIX_FUNC, FUNCT, 0 }, /* Type conversion/testing functions and keywords */ [FUNC_BOOL] = { "bool", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_EMPTY] = { "empty", empty_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ERROR] = { "error", error_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_FIDENT] = { "fident", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_FLOAT] = { "float", float_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_FUNCALL] = { "funcall", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_INT] = { "int", int_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_IS] = { "is", is_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_LIDENT] = { "lident", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_LOCATION] = { "location", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_NUMBER] = { "number", number_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_OPERATOR] = { "operator", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_STRING] = { "string", string_func, PREFIX_FUNC, FUNCT, 0 }, /* Block operations */ [FUNC_MAX] = { "max", reg_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_MIN] = { "min", reg_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_N] = { "n", n_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SUM] = { "sum", reg_disp, 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", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ARCOSH] = { "arcosh", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ARSINH] = { "arsinh", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ARTANH] = { "artanh", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ASIN] = { "asin", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ATAN] = { "atan", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_DEG2RAD] = { "deg2rad", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_E] = { "e", constant_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_COS] = { "cos", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_COSH] = { "cosh", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_LOG] = { "log", log_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_RAD2DEG] = { "rad2deg", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SIN] = { "sin", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SINH] = { "sinh", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TAN] = { "tan", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TANH] = { "tanh", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TAU] = { "tau", constant_func, PREFIX_FUNC, FUNCT, 0 }, /* Other mathematical functions */ [FUNC_ABS] = { "abs", abs_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_BITAND] = { "bitand", bit_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_BITOR] = { "bitor", bit_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_CEIL] = { "ceil", near_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_FLOOR] = { "floor", near_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_FRAC] = { "frac", sci_disp, 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", near_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SQRT] = { "sqrt", sci_disp, PREFIX_FUNC, FUNCT, 0 }, [FUNC_TRUNC] = { "trunc", near_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_COMPACT] = { "compact", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_DECIMAL] = { "decimal", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_HEXACT] = { "hexact", self_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_SCIENTIFIC] = { "scientific", self_func, PREFIX_FUNC, FUNCT, 0 }, }; /*}}}*/