feat: Allow expressions in region functions and add count() accumulator (#86)

Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Reviewed-on: #86
This commit is contained in:
Glen Whitney 2023-04-29 23:18:57 +00:00
parent eb5d576349
commit 7f005f171e
3 changed files with 430 additions and 445 deletions

View file

@ -1175,68 +1175,88 @@ 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[])
/* region_macro -- apply an operation over an expression evaluated at
every location in a whole region, or every value in the region, or the
evaluated value of every argument to the macro. */
static Token region_macro(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) {
const char *templ = _("While computing %s() at &(%d,%d,%d): %s");
Token report;
report.type = EEK;
report.u.err =
malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 3*20 + strlen(t.u.err));
sprintf(report.u.err, templ, tfunc[id].name, w[X], w[Y], w[Z], t.u.err);
tfree(&t);
return report;
}
Location l1; LOCATION_GETS(l1, upd_l);
Location l2; LOCATION_GETS(l2, upd_l);
bool block = argc > 0 && argc < 4; // could be a block with 1, 2, or 3 args
if (block) {
Token first = evaltoken(argv[0], FULL);
if (first.type == LOCATION) {
LOCATION_GETS(l1, first.u.location);
LOCATION_GETS(l2, l1);
} else {
block = false;
}
tfree_protected(&first, argv[0]);
if (block && argc > 1) {
Token second = evaltoken(argv[1], FULL);
if (second.type == LOCATION) {
LOCATION_GETS(l2, second.u.location);
} else {
block = false;
}
tfree_protected(&second, argv[1]);
}
if (finl != (RegFuncFinl)0) finl(id, &l, &t);
return t;
}
if (argc > 0) /* try to accumulate over all arguments */
{
if (!block) { // accumulate over all (evaluated) arguments
Location l; OLOCATION(l);
Token t = argv[0];
Token t = evaltoken(argv[0], FULL);
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);
Token u = evaltoken(argv[i], FULL);
updt(id, &l, &t, &fake, &u);
tfree_protected(&u, t);
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;
// Evaluate over all cells in block defined by locations l1 and l2
int x1 = l1[X], x2 = l2[X]; posorder(&x1, &x2);
int y1 = l1[Y], y2 = l2[Y]; posorder(&y1, &y2);
int z1 = l1[Y], z2 = l2[Z]; posorder(&z1, &z2);
Location l; l[X] = x1; l[Y] = y1; l[Z] = z1;
Token t;
if (argc == 3) {
t = evaluate_at(argv[2], upd_sheet, l);
} else {
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;
if (argc == 3) {
tmp = evaluate_at(argv[2], upd_sheet, w);
} else {
tmp = recompvalue(upd_sheet, w);
}
updt(id, &l, &t, &w, &tmp);
tfree_protected(&tmp, t);
if (t.type == EEK) {
const char *templ = _("While computing %s() at &(%d,%d,%d): %s");
Token report;
report.type = EEK;
report.u.err =
malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 3*20 + strlen(t.u.err));
sprintf(report.u.err, templ, tfunc[id].name, w[X], w[Y], w[Z], t.u.err);
tfree(&t);
return report;
}
}
if (finl != (RegFuncFinl)0) finl(id, &l, &t);
return t;
}
static void sum_init(FunctionIdentifier id, Location *loc, Token *tok)
@ -1280,11 +1300,9 @@ static void minmax_finl(FunctionIdentifier id, Location *loc, Token *tok)
LOCATION_GETS(tok->u.location, *loc);
}
static void n_init(FunctionIdentifier id, Location *loc, Token *tok)
static void init_zero(FunctionIdentifier id, Location *loc, Token *tok)
{
assert(id == FUNC_N);
tfree(tok);
tok->type = EMPTY;
tok->type = INT;
tok->u.integer = 0;
}
@ -1296,6 +1314,20 @@ static void n_updt(FunctionIdentifier id, Location *loc, Token *tok,
tok->u.integer += (newtok->type != EMPTY);
}
static void count_updt(FunctionIdentifier id, Location *loc, Token *tok,
const Location* newloc, const Token *newtok)
{
assert(id == FUNC_COUNT);
Token countit = tbool(*newtok);
if (countit.type == EEK) {
tfree_protected(tok, countit);
*tok = countit;
return;
}
assert(countit.type == BOOL);
tok->u.integer += countit.u.bl;
}
static Token reg_disp(FunctionIdentifier self, int argc, const Token argv[])
{
RegFuncInit i = 0;
@ -1308,10 +1340,12 @@ static Token reg_disp(FunctionIdentifier self, int argc, const Token argv[])
case FUNC_MAX:
u = minmax_updt; f = minmax_finl; break;
case FUNC_N:
i = n_init; u = n_updt; break;
i = init_zero; u = n_updt; break;
case FUNC_COUNT:
i = init_zero; u = count_updt; break;
default: assert(0);
}
return region_func(i, u, f, self, argc, argv);
return region_macro(i, u, f, self, argc, argv);
}
/* binop_func -- common implementation of all binary operations
@ -1702,8 +1736,8 @@ static Token negate_func(FunctionIdentifier self, int argc, const Token argv[])
}
/* table of functions */ /*{{{*/
/* The order of these entries has no influence on performance, but to stay
compatible, new entries should be appended. */
/* The order of these entries is irrelevant because they just depend on the
values of the FUNC_XXX enum values. */
Tfunc tfunc[]=
{
/* Operators in order of increasing precedence */
@ -1782,10 +1816,11 @@ Tfunc tfunc[]=
[FUNC_CENTER] = { "center", self_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", reg_disp, PREFIX_FUNC, FUNCT, 0 },
[FUNC_SUM] = { "sum", reg_disp, PREFIX_FUNC, FUNCT, 0 },
[FUNC_COUNT] = { "count", reg_disp, PREFIX_FUNC, MACRO, 0 },
[FUNC_MAX] = { "max", reg_disp, PREFIX_FUNC, MACRO, 0 },
[FUNC_MIN] = { "min", reg_disp, PREFIX_FUNC, MACRO, 0 },
[FUNC_N] = { "n", reg_disp, PREFIX_FUNC, MACRO, 0 },
[FUNC_SUM] = { "sum", reg_disp, PREFIX_FUNC, MACRO, 0 },
/* String functions */
[FUNC_LEN] = { "len", len_func, PREFIX_FUNC, FUNCT, 0 },

View file

@ -51,6 +51,8 @@ typedef enum
FUNC_DIM, FUNC_ITALIC,
FUNC_COUNT,
N_FUNCTION_IDS
} FunctionIdentifier;