teapot-spreadsheet/src/common/cell.c
Glen Whitney 7b794f90b9 fix: Always compute cells without clocked expressions using current values (#91)
Prior to this change, when a cell was clocked, it was always computed with prior values of referred-to cells. This had the effect, for example, of making cells defined only with a base expression as a total of a column, for example, to be out-of-date in that they would take on the value of the total of the prior values of the column, not the total of new current values of the column. This behavior was very counterintuitive.

With this change, updates of cells that have no clocked expression are delayed until after clocked expressions have been recomputed based on prior values and their cells' current values have been updated. Then the computations of those base-only cells use all of the new current values, leaving the spreadsheet in a (maximally) self-consistent state.

Resolves #90.

Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Reviewed-on: #91
2023-04-30 16:19:12 +00:00

107 lines
2.8 KiB
C

#ifndef NO_POSIX_SOURCE
#undef _POSIX_SOURCE
#define _POSIX_SOURCE 1
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 2
#endif
#include <assert.h>
#include <limits.h>
#include <string.h>
#include "cell.h"
#include "default.h"
#include "display.h"
#include "eval.h"
#include "parser.h"
#include "main.h"
const char *TokVariety_Name[] =
{ [BASE_CONT] = "Base Content", [ITER_CONT] = "Iterative Content",
[STYLE_CONT] = "Style Content", [CURR_VAL] = "Current Value",
[RES_VAL] = "Resultant Value", [STYLE_VAL] = "Current Style",
[CONTINGENT] = "Contingent Content"
};
/* initcellcontents - make a fresh cell into the "empty" one; don't worry
about freeing anything there, that will have been handled. */
void initcellcontents(Cell *fresh)
{
for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
fresh->tok[tv].type = EMPTY;
fresh->label = NULL;
fresh->updated = false;
fresh->locked = false;
fresh->ignored = false;
fresh->use_iter_cont = false;
fresh->clock_resolving = false;
fresh->clock_requested = false;
}
/* getcont -- get contents */
Token gettok(const Cell *cell, TokVariety v)
{
Token emp;
emp.type = EMPTY;
if (cell == NULLCELL) return emp;
if (v == CONTINGENT)
v = (cell->use_iter_cont && cell->tok[ITER_CONT].type != EMPTY)
? ITER_CONT : BASE_CONT;
return cell->tok[v];
}
/* iterable -- does cell have iteration content? */
bool iterable(const Cell* cell) {
return (cell != NULLCELL) && cell->tok[ITER_CONT].type != EMPTY;
}
/* locked -- is cell locked? */
bool locked(const Cell *cell)
{
return (cell != NULLCELL) && cell->locked;
}
/* ignored -- is cell ignored? */
bool ignored(const Cell *cell)
{
return (cell != NULLCELL) && cell->ignored;
}
/* getlabel -- get cell label */
const char *getlabel(const Cell* cell)
{
if (cell == NULLCELL || cell->label == (char*)0) return "";
return cell->label;
}
/* freecellcontents -- free the resources of the cell at destruction time */
void freecellcontents(Cell *faded)
{
for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
tfree(&(faded->tok[tv]));
if (faded->label != NULL) free(faded->label);
}
/* copycell - copies one Cell to another, handling any allocation issues */
void copycell(Cell *to, const Cell *fromcell, LabelHandling lh)
{
assert(to != NULLCELL);
if (to == fromcell) return;
freecellcontents(to);
if (fromcell != NULLCELL) {
memcpy(to, fromcell, sizeof(Cell));
for (TokVariety tv = BASE_CONT; tv < CONTINGENT; ++tv)
if (tv <= MAX_PERSIST_TV) to->tok[tv] = tcopy(fromcell->tok[tv]);
else to->tok[tv].type = EMPTY;
if (lh != PRESERVE_LABEL && fromcell->label != (char*)0) {
size_t len = strlen(fromcell->label);
to->label = strcpy(malloc(len+2), fromcell->label);
(to->label)[len] = '_';
(to->label)[len+1] = '\0';
}
} else {
initcellcontents(to);
}
}