fix(parser,transpiler): #66 - computed-endpoint ranges, YEARFRAC 30/360, array-criteria SUMIFS, no silent partial parses#67
Merged
Conversation
…60, array-criteria SUMIFS, no silent partial parses
Three structure-fidelity classes found by the v0.3.0 release-day warm-GT
sweep (383k residual divergent cells on 4 A-1 sheets):
B. `ref:OFFSET(...)` computed-endpoint ranges (Technology, 284k cells).
The parser had no rule for a bare Colon token: it stopped AT the colon
and returned the partial AST — SUM(CF14:OFFSET(CF14,,-($F$12-2)))
became SUM(CF14) and the trailing *(...) factor vanished. New
Expr::DynRange parsed by a colon-loop over primaries; transpiles to
_dynRange(ctx, corners) with _offsetAddr() corner math (OFFSET h/w
extend to the far corner). Unsupported endpoints (INDIRECT/INDEX/...)
emit an honest NaN sentinel. Same formula exposed the EMPTY-argument
bug: parse_primary's fallback ATE the comma in OFFSET(x,,n) and
misaligned every later argument — list terminators now return an
empty-arg 0 without consuming. parse_formula now returns None when
tokens remain (a partial parse can never again pass as a formula);
parse-error cells emit NaN, not 0, in both emitters. Tokenizer: a
range endpoint followed by '(' is a function call, never a ref
(A1:MAX(...) previously folded into a column range).
A. YEARFRAC default basis is US-NASD 30/360, was (b-a)/365.25 (Lease
Amortization + Owned Asset PP&E, ~91k cells). Month-aligned spans are
now EXACT (1, 0.5) — the model gates on MOD(YEARFRAC(...),x)=0 and
counts months as YEARFRAC*12+1. _yearfrac implements bases 0-4
(NASD Feb/31st rules, actual/actual, A/360, A/365, Euro 30/360).
C. SUMIFS with a RANGE criteria value (Debt array formulas, 6.7k cells):
SUM(SUMIFS(vals, cats, $EK$973:$EK$977)) now yields one sum per
criteria element (Excel array semantics); previously the array
criteria matched nothing -> 0.
test-structure-fidelity.mjs (npm test): all classes through the REAL
parser -> engine, negative-controlled — RED on the pre-fix binary with
exactly the predicted truncation/misalignment/365.25 values (8 failures),
10/10 GREEN post-fix. cargo test 29/29; full npm test green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… textbook NASD Excel runs the d2=31 rule BEFORE the last-day-of-Feb adjustment (testing d1 after the 31-rule only) and has NO both-Feb rule, so a Feb-end start keeps a day-31 end: YEARFRAC(Feb28-2023, May31-2023) = 91/360 where SIA NASD gives 90/360. Hand-verified against the real A-1 PP&E row 92 (GT 465.9667 = 468 - (91/360)*12 + 1) and Lease Amortization row 608 (GT 43.0333 = (1261/360)*12 + 1 over Feb29-2024 -> Aug31-2027). Two new discriminating cases in test-structure-fidelity.mjs, RED on textbook NASD with the predicted 0.25/1 values, 12/12 GREEN after. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… the no-both-Feb hypothesis The a1-66b sweep residual concentrated on every-12th columns (Feb-to-Feb anniversary spans): GT holds EXACT integers there (PP&E!BA92 = 457 needs YEARFRAC(Feb28-2023, Feb29-2024) = 1.0, not 359/360). So Excel applies the textbook both-Feb d2=30 rule; what differs from SIA NASD is only the ORDER: the d2=31 rule tests d1 BEFORE the February adjustment (keeping the 91/360 case verified earlier). Rule order now: both-Feb d2, d1=31, d2=31-with-d1=30 (pre-Feb), Feb d1 last. All prior hand-verified cases still hold (91/360, 1261-day chain); new discriminating cases for both-Feb and leap-to-nonleap Feb pairs, RED pre-fix (359/360, 718/360), 13/13 GREEN after. The earlier G6 expectation had validated the implementation against its own hypothesis instead of Excel - the real model ground truth is the only oracle that counts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jun 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the three structure-fidelity classes behind #66 (the 383k residual divergent cells on 4 A-1 sheets after v0.3.0). Full mechanism write-ups in the commit message; headlines:
ref:OFFSET(...)computed-endpoint ranges (Technology, 284k): the parser had no rule for a bare:token — it stopped at the colon and silently returned the partial AST, soSUM(CF14:OFFSET(...))*(X<Y)lost both the dynamic window AND the trailing factor. NewExpr::DynRange+ runtime_dynRange/_offsetAddr. The same formula exposed the empty-argument bug (OFFSET(x,,n)'s comma was eaten, misaligning later args).parse_formulanow refuses partial parses (trailing tokens → parse-error), and parse-error cells emit NaN, not 0, in both emitters — a formula we can't fully parse is now detectably unusable, never a plausible number.(b−a)/365.25; month-aligned spans are now exact, soMOD(YEARFRAC(...),x)=0anniversary gates andYEARFRAC*12+1month counts behave like Excel. Bases 0–4 implemented.SUM(SUMIFS(vals, cats, $EK$973:$EK$977))now yields one sum per criteria element (Excel array semantics).A1:MAX(...)no longer foldsMAXinto a column-range endpoint.Negative control:
test-structure-fidelity.mjsruns every class through the real parser → engine; RED on the pre-fix binary with exactly the predicted wrong values (8 failures: truncated 10s, misaligned 0, 365.25 drift values), 10/10 GREEN post-fix.cargo test29/29, fullnpm testgreen. The A-1 rebuild + all-17-sheet warm-GT sweep is running; results will be posted to #66.🤖 Generated with Claude Code