Skip to content

feat(formulas): support spill references and fix spill read order - #68

Open
chh-ay wants to merge 4 commits into
fix/array-constantsfrom
fix/spill-references
Open

chh-ay wants to merge 4 commits into
fix/array-constantsfrom
fix/spill-references

Conversation

@chh-ay

@chh-ay chh-ay commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Scope

Spill references (A1#) in both engines, and a fix for spills that read other spills. Part of #59. Stacked on #64 (base fix/array-constants).

Problems

  1. A spill that read a later spill on the same pass saw old values. With B1 = SORT(C1:C5) and C1 = SEQUENCE(5), B1 got [1,0,0,0,0]. Spills that read each other could also loop without an error.
  2. A1# was not supported. Excel uses it to refer to the whole current spill of a cell, and XLSX files store it as _xlfn.ANCHORARRAY(A1).

Change

  • Spill recalculation records each spill change with a sequence number. A spill reader that ran before a spill it reads is queued again. A reader that keeps coming back ends as #CYCLE!.
  • The parser reads # after a cell reference (also with a sheet name: 'Report'!B2#) as ANCHORARRAY(cell). ANCHORARRAY cannot be typed as a function. The formula text keeps the A1# form.
  • Before evaluation, each A1# becomes the range that the anchor spills into now, so SUM, INDEX, ROWS, COUNTIF, SORT and the other range functions accept it without special code. Only formulas that contain A1# (a flag set at parse time) do this step.
  • A1# returns #REF! when the anchor holds no spill (a value, or a blocked #SPILL!). Row and column edits move the reference with its anchor; deleting the anchor gives #REF! (Rust and remapFormulaA1Refs).
  • When an anchor's spill range or spilled cells change, its A1# readers that already ran in the pass are queued again. This includes 1×1 spills.
  • XLSX import converts _xlfn.ANCHORARRAY(ref) to ref#; export converts back. Text in quotes is not changed.
  • A formula whose reference became invalid (for example =J1# after its anchor column was deleted) does not enter the spill path. It returns #REF! instead of an empty spill bound.

Limits

At this layer =A1#*10 returns #VALUE!, the same as =A1:A3*10: operators are scalar-only here. #71 adds element-wise array operators higher in the stack, after which =A1#*10 spills the scaled values.

Size

Binary Before After Change
@sheetwrite/wasm raw 774,243 786,284 +12,041
@sheetwrite/wasm Brotli (q11) 233,508 235,985 +2,477
@sheetwrite/formulas raw 1,047,429 1,059,622 +12,193

Verification

  • Rust regressions: spill read order (failed before: [1,0,0,0,0]), mutual spills end as #CYCLE!, A1# parse/serialize, live resize with SUM/ROWS/INDEX/COUNTIF, shift and delete, readers before their anchor (also 1×1 and same-size value changes), and =J1# with an invalid anchor returns #REF!. A mutation that removes the anchor notice fails the reader-before-anchor test.
  • Rust 144 (default) and 219 (--features analysis) pass; clippy passes in both configurations.
  • XLSX round trip writes SUM(_xlfn.ANCHORARRAY(A1)) and reads back =SUM(A1#). A1 remap tests cover the # suffix.
  • Packages 880 pass / 6 skip / 0 fail. Typecheck, lint, api:check and docs:check pass.
  • Public-API smoke: =SORT(A1#,1,-1) follows A1 = SEQUENCE(D1) from 3 to 5 rows, and becomes #REF! when A1 changes to a value.

@chh-ay
chh-ay added this pull request to stack #69 October 4, 2026 12:29
chh-ay added 2 commits October 4, 2026 19:46
Spills are calculated in cell order. A spill that reads cells of a spill placed later in the same pass read the previous values and was not calculated again, so it stayed one edit behind. The recalculation now logs the cells each spill changes, finds the spills that read them before the change, and calculates those again. A spill that keeps coming back reads its own output through other spills and gets #CYCLE!.
@chh-ay
chh-ay force-pushed the fix/spill-references branch from d2e3b1c to e725d98 Compare October 4, 2026 12:50
@chh-ay chh-ay mentioned this pull request Oct 4, 2026
86 of 96 tasks

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant