The parser stripped _xludf. like _xlfn., but the two mean opposite things:
_xludf. marks a name Excel must not treat as a built-in. Keeping it in the
call name makes the call an unknown function (#NAME?, as in Excel) and the
writer emits it unchanged, so _xludf.EOMONTH() no longer saves as an
EOMONTH() call Excel repairs away.
Excel lets a worksheet function be passed by name where a function is
expected. In GridCraft GROUPBY(A2:A9,B2:B9,SUM) and PIVOTBY gave #NAME?
(their documented form), and BYROW, BYCOL, MAP, REDUCE, SCAN and
MAKEARRAY gave #VALUE!. A name that is a worksheet function, and not a
LET/LAMBDA variable or a defined name, now works as a function there.
Reading a file, the _xleta. prefix Excel writes for these names is
removed.
The file format has no `@` operator, but formulas with an explicit
implicit intersection were written with a literal `@`
(`<f>@A1:A3*2</f>`). Excel writes `@x` as `_xlfn.SINGLE(x)`, which only
dynamic-array formulas carry. The writer now does the same and marks such
formulas cm="1"; the parser reads `SINGLE(x)` back as `@x`, as it already
does for `ANCHORARRAY` and `#`.
Two cases in the #83 metadata test expected a redundant `@` (`SQRT(@A1:A3)`,
`@A1:A3*2`) to be written as an ordinary formula. Dropping the `@` there is
only lossless if the reader puts it back on legacy formulas, which it
doesn't yet (the read side of #64), so they now expect cm.
generated with Claude Code
formula::parse("Sheet1!$D$1#") failed with "unknown error literal".
A # right after a cell reference is now the spill range operator: it
evaluates to the anchor's current spill range, or #REF! when the cell
doesn't spill (or its spill is blocked). Its users depend on the
anchor, so they recalculate when the spill grows or shrinks. Names can
use it.
Files have no # operator: like Excel, the writer stores A1# as
_xlfn.ANCHORARRAY(A1), and the parser reads that back as A1#.