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:
parent
eb5d576349
commit
7f005f171e
3 changed files with 430 additions and 445 deletions
|
@ -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 },
|
||||
|
|
|
@ -51,6 +51,8 @@ typedef enum
|
|||
|
||||
FUNC_DIM, FUNC_ITALIC,
|
||||
|
||||
FUNC_COUNT,
|
||||
|
||||
N_FUNCTION_IDS
|
||||
} FunctionIdentifier;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue