teapot-spreadsheet/src/common/func.c

1639 lines
46 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;
}
/*}}}*/
/* 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 },
};
/*}}}*/