improvise-lnkb: the folded category reads as absent to IS and WHERE, as on None
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
@@ -222,7 +222,10 @@ fn eval_expr(
|
||||
agg_filter.as_ref(),
|
||||
&|e, at| eval_expr(e, at, model, target_category, depth, Reach::Record),
|
||||
),
|
||||
Expr::Is(filter) => Ok(eval_is(context, filter)),
|
||||
Expr::Is(filter) => Ok(eval_is(
|
||||
model.read_coordinate(context, &filter.category, reach),
|
||||
filter,
|
||||
)),
|
||||
Expr::If(cond, then, else_) => {
|
||||
match eval_bool(cond, context, model, target_category, depth, reach)? {
|
||||
true => eval(then),
|
||||
@@ -270,10 +273,8 @@ fn eval_bool(
|
||||
|
||||
/// `Cat IS Item` as a number. A cell with no `Cat` coordinate is **not** that
|
||||
/// item, matching `Formula::filter`'s own `unwrap_or(false)` — §13.
|
||||
fn eval_is(context: &CellKey, filter: &Filter) -> f64 {
|
||||
context
|
||||
.get(&filter.category)
|
||||
.is_some_and(|v| v == filter.item.as_str()) as i32 as f64
|
||||
fn eval_is(coordinate: Option<&str>, filter: &Filter) -> f64 {
|
||||
coordinate.is_some_and(|v| v == filter.item.as_str()) as i32 as f64
|
||||
}
|
||||
|
||||
/// Whether a grid hides dimensions a cell key does not name. The emptiness of
|
||||
@@ -678,6 +679,23 @@ impl Model {
|
||||
}
|
||||
}
|
||||
|
||||
/// A coordinate as a formula reads it. At a fold a group's name is no item, so
|
||||
/// the folded category reads as absent, as on `Axis::None` (improvise-lnkb).
|
||||
fn read_coordinate<'k>(&self, context: &'k CellKey, cat: &str, reach: Reach) -> Option<&'k str> {
|
||||
let value = context.get(cat)?;
|
||||
match reach {
|
||||
Reach::Fold { .. } if self.names_a_group(cat, value) => None,
|
||||
Reach::Fold { .. } | Reach::Record => Some(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether `name` is a group of `cat` rather than one of its items.
|
||||
fn names_a_group(&self, cat: &str, name: &str) -> bool {
|
||||
self.category(cat).is_some_and(|c| {
|
||||
!c.items.contains_key(name) && !c.items_in_group(name).is_empty()
|
||||
})
|
||||
}
|
||||
|
||||
/// The contexts an aggregate inside a formula pools over at `context`.
|
||||
fn contexts_of(&self, context: &CellKey, reach: Reach) -> Vec<CellKey> {
|
||||
let members = match reach {
|
||||
@@ -717,10 +735,9 @@ impl Model {
|
||||
agg: Aggregation,
|
||||
reach: Reach,
|
||||
) -> Option<CellValue> {
|
||||
let applies = formula
|
||||
.filter
|
||||
.as_ref()
|
||||
.is_none_or(|f| key.get(&f.category) == Some(f.item.as_str()));
|
||||
let applies = formula.filter.as_ref().is_none_or(|f| {
|
||||
self.read_coordinate(key, &f.category, reach) == Some(f.item.as_str())
|
||||
});
|
||||
if !applies {
|
||||
return self.fold_stored(key, members, agg);
|
||||
}
|
||||
@@ -905,7 +922,7 @@ impl Model {
|
||||
eval_expr(e, at, model, target_category, depth, Reach::Record)
|
||||
},
|
||||
),
|
||||
Expr::Is(filter) => Ok(eval_is(context, filter)),
|
||||
Expr::Is(filter) => Ok(eval_is(context.get(&filter.category), filter)),
|
||||
Expr::If(cond, then, else_) => {
|
||||
let cv = eval_bool_cached(cond, context, model, target_category, none_cats)?;
|
||||
if cv {
|
||||
|
||||
@@ -1819,6 +1819,28 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// improvise-lnkb: the folded category reads as absent, as on `Axis::None` — see
|
||||
/// context/test-evidence/core.md.
|
||||
#[test]
|
||||
fn a_fold_answers_is_and_where_on_its_category_as_the_none_axis_does() {
|
||||
let formulas = [
|
||||
"Flag = Month IS \"Q1\"",
|
||||
"Marked = Revenue * 2 WHERE Month = \"Q1\"",
|
||||
];
|
||||
for measure in ["Flag", "Marked"] {
|
||||
let mut folded = margin_book(&formulas);
|
||||
folded.active_view_mut().toggle_group_collapse("Month", "Q1");
|
||||
let fold = grid_value(&mut folded, "Q1", measure);
|
||||
let mut none = margin_book(&formulas);
|
||||
none.active_view_mut().set_axis("Month", Axis::None);
|
||||
let over_none = grid_value(&mut none, "", measure);
|
||||
assert_eq!(fold, over_none, "{measure}: the fold and the None axis differ");
|
||||
}
|
||||
let mut folded = margin_book(&formulas);
|
||||
folded.active_view_mut().toggle_group_collapse("Month", "Q1");
|
||||
assert_eq!(grid_value(&mut folded, "Q1", "Flag"), Some(0.0), "false, not absent");
|
||||
}
|
||||
|
||||
/// improvise-eccl's guard on the other side: an aggregate INSIDE the formula
|
||||
/// still pools every record the fold covers.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user