teapot-spreadsheet/src/common/func.c

1685 lines
41 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 */
extern long double strtold(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 ((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 (int 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][,format_for_floats]])");
int precision = def_precision;
bool ff = DEF_FLOATFORM;
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: 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);
}
}
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, DIRECT_STRING, ff,
precision, VERBOSE_ERROR, 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);
}
/*}}}*/
/* compact keyword */ /*{{{*/
static Token compact_func(int argc, const Token argv[])
{
return self_func(argc, argv, FUNC_COMPACT);
}
/*}}}*/
/* hexact keyword */ /*{{{*/
static Token hexact_func(int argc, const Token argv[])
{
return self_func(argc, argv, FUNC_HEXACT);
}
/*}}}*/
/* 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_COMPACT] = { "compact", compact_func, PREFIX_FUNC, FUNCT, 0 },
[FUNC_DECIMAL] = { "decimal", decimal_func, PREFIX_FUNC, FUNCT, 0 },
[FUNC_HEXACT] = { "hexact", hexact_func, PREFIX_FUNC, FUNCT, 0 },
[FUNC_SCIENTIFIC] = { "scientific", scientific_func, PREFIX_FUNC, FUNCT, 0 },
};
/*}}}*/