@devmm/puredocs-excel-formula 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +435 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -3
- package/dist/index.d.ts +26 -3
- package/dist/index.js +436 -114
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -448,8 +448,11 @@ var TokenType = /* @__PURE__ */ ((TokenType2) => {
|
|
|
448
448
|
TokenType2[TokenType2["SpillReference"] = 24] = "SpillReference";
|
|
449
449
|
TokenType2[TokenType2["Exclamation"] = 25] = "Exclamation";
|
|
450
450
|
TokenType2[TokenType2["AtSign"] = 26] = "AtSign";
|
|
451
|
-
TokenType2[TokenType2["
|
|
452
|
-
TokenType2[TokenType2["
|
|
451
|
+
TokenType2[TokenType2["LeftBrace"] = 27] = "LeftBrace";
|
|
452
|
+
TokenType2[TokenType2["RightBrace"] = 28] = "RightBrace";
|
|
453
|
+
TokenType2[TokenType2["Semicolon"] = 29] = "Semicolon";
|
|
454
|
+
TokenType2[TokenType2["ErrorLiteral"] = 30] = "ErrorLiteral";
|
|
455
|
+
TokenType2[TokenType2["EOF"] = 31] = "EOF";
|
|
453
456
|
return TokenType2;
|
|
454
457
|
})(TokenType || {});
|
|
455
458
|
function makeToken(type, value2, position) {
|
|
@@ -463,6 +466,8 @@ var FormulaException = class extends Error {
|
|
|
463
466
|
this.name = "FormulaException";
|
|
464
467
|
}
|
|
465
468
|
};
|
|
469
|
+
var UNDERSCORE = 95;
|
|
470
|
+
var RESERVED_PREFIXES = ["_XLFN.", "_XLWS.", "_XLPM."];
|
|
466
471
|
var KNOWN_ERRORS = /* @__PURE__ */ new Set([
|
|
467
472
|
"#NULL!",
|
|
468
473
|
"#DIV/0!",
|
|
@@ -474,7 +479,7 @@ var KNOWN_ERRORS = /* @__PURE__ */ new Set([
|
|
|
474
479
|
"#CALC!",
|
|
475
480
|
"#SPILL!"
|
|
476
481
|
]);
|
|
477
|
-
var _formula, _pos, _FormulaLexer_instances, readNextToken_fn, readString_fn, readErrorLiteral_fn, readQuotedSheetRef_fn, readNumber_fn, readIdentifier_fn, readCellRefChars_fn, readOperator_fn, skipWhitespace_fn;
|
|
482
|
+
var _formula, _pos, _FormulaLexer_instances, readNextToken_fn, readString_fn, readErrorLiteral_fn, readQuotedSheetRef_fn, readNumber_fn, readIdentifier_fn, stripReservedPrefixes_fn, readCellRefChars_fn, readOperator_fn, skipWhitespace_fn;
|
|
478
483
|
var FormulaLexer = class {
|
|
479
484
|
constructor(formula) {
|
|
480
485
|
__privateAdd(this, _FormulaLexer_instances);
|
|
@@ -492,7 +497,7 @@ var FormulaLexer = class {
|
|
|
492
497
|
const token = __privateMethod(this, _FormulaLexer_instances, readNextToken_fn).call(this);
|
|
493
498
|
if (token) tokens.push(token);
|
|
494
499
|
}
|
|
495
|
-
tokens.push(makeToken(
|
|
500
|
+
tokens.push(makeToken(31 /* EOF */, "", __privateGet(this, _pos)));
|
|
496
501
|
return tokens;
|
|
497
502
|
}
|
|
498
503
|
};
|
|
@@ -546,7 +551,7 @@ readErrorLiteral_fn = function() {
|
|
|
546
551
|
if (!KNOWN_ERRORS.has(upper2)) {
|
|
547
552
|
throw new FormulaException(`Unknown error literal "${raw}" at position ${start}.`);
|
|
548
553
|
}
|
|
549
|
-
return makeToken(
|
|
554
|
+
return makeToken(30 /* ErrorLiteral */, upper2, start);
|
|
550
555
|
};
|
|
551
556
|
readQuotedSheetRef_fn = function() {
|
|
552
557
|
const start = __privateWrapper(this, _pos)._++;
|
|
@@ -609,10 +614,8 @@ readIdentifier_fn = function() {
|
|
|
609
614
|
} else break;
|
|
610
615
|
}
|
|
611
616
|
let upper2 = raw.toUpperCase();
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
upper2 = upper2.slice(6);
|
|
615
|
-
}
|
|
617
|
+
raw = __privateMethod(this, _FormulaLexer_instances, stripReservedPrefixes_fn).call(this, raw, upper2);
|
|
618
|
+
if (raw.length !== upper2.length) upper2 = upper2.slice(upper2.length - raw.length);
|
|
616
619
|
if (upper2 === "TRUE") return makeToken(2 /* Boolean */, "TRUE", start);
|
|
617
620
|
if (upper2 === "FALSE") return makeToken(2 /* Boolean */, "FALSE", start);
|
|
618
621
|
if (__privateGet(this, _formula)[__privateGet(this, _pos)] === "!") {
|
|
@@ -634,6 +637,30 @@ readIdentifier_fn = function() {
|
|
|
634
637
|
}
|
|
635
638
|
return makeToken(23 /* NamedRange */, raw, start);
|
|
636
639
|
};
|
|
640
|
+
/**
|
|
641
|
+
* Drops Excel's storage-level name decorations. They carry no semantics — the
|
|
642
|
+
* writer stamps them so an older reader fails loudly instead of silently
|
|
643
|
+
* mis-evaluating — and they *stack*: `_xlfn._xlws.FILTER` wears two. Stripping
|
|
644
|
+
* one prefix once, as this used to, leaves `_XLWS.FILTER` unresolvable.
|
|
645
|
+
*
|
|
646
|
+
* Every identifier in every formula passes through here, so the common case is
|
|
647
|
+
* gated on a single char-code test: a name not starting with '_' returns
|
|
648
|
+
* untouched without so much as a comparison against the prefix table.
|
|
649
|
+
*/
|
|
650
|
+
stripReservedPrefixes_fn = function(raw, upper2) {
|
|
651
|
+
if (raw.charCodeAt(0) !== UNDERSCORE) return raw;
|
|
652
|
+
let cut = 0;
|
|
653
|
+
outer: for (; ; ) {
|
|
654
|
+
for (const prefix of RESERVED_PREFIXES) {
|
|
655
|
+
if (upper2.startsWith(prefix, cut)) {
|
|
656
|
+
cut += prefix.length;
|
|
657
|
+
continue outer;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
return cut > 0 && cut < raw.length ? raw.slice(cut) : raw;
|
|
663
|
+
};
|
|
637
664
|
readCellRefChars_fn = function() {
|
|
638
665
|
let ref = "";
|
|
639
666
|
while (__privateGet(this, _pos) < __privateGet(this, _formula).length) {
|
|
@@ -677,6 +704,14 @@ readOperator_fn = function() {
|
|
|
677
704
|
return makeToken(26 /* AtSign */, "@", start);
|
|
678
705
|
case "!":
|
|
679
706
|
return makeToken(25 /* Exclamation */, "!", start);
|
|
707
|
+
case "{":
|
|
708
|
+
return makeToken(27 /* LeftBrace */, "{", start);
|
|
709
|
+
case "}":
|
|
710
|
+
return makeToken(28 /* RightBrace */, "}", start);
|
|
711
|
+
// Inside an array constant ';' separates rows. Excel always stores ',' as
|
|
712
|
+
// the argument separator regardless of locale, so ';' has no other role.
|
|
713
|
+
case ";":
|
|
714
|
+
return makeToken(29 /* Semicolon */, ";", start);
|
|
680
715
|
case "<":
|
|
681
716
|
if (__privateGet(this, _formula)[__privateGet(this, _pos)] === ">") {
|
|
682
717
|
__privateWrapper(this, _pos)._++;
|
|
@@ -759,6 +794,29 @@ var ErrorNode = class extends FormulaNode {
|
|
|
759
794
|
return this.errorValue;
|
|
760
795
|
}
|
|
761
796
|
};
|
|
797
|
+
var _value;
|
|
798
|
+
var ArrayLiteralNode = class extends FormulaNode {
|
|
799
|
+
constructor(array) {
|
|
800
|
+
super();
|
|
801
|
+
this.array = array;
|
|
802
|
+
__privateAdd(this, _value);
|
|
803
|
+
__privateSet(this, _value, FormulaValue.array(array));
|
|
804
|
+
}
|
|
805
|
+
evaluate() {
|
|
806
|
+
return __privateGet(this, _value);
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
_value = new WeakMap();
|
|
810
|
+
var ValueNode = class extends FormulaNode {
|
|
811
|
+
/** Mutable so one wrapper can be re-pointed across a lifted call's cells. */
|
|
812
|
+
constructor(value2) {
|
|
813
|
+
super();
|
|
814
|
+
this.value = value2;
|
|
815
|
+
}
|
|
816
|
+
evaluate() {
|
|
817
|
+
return this.value;
|
|
818
|
+
}
|
|
819
|
+
};
|
|
762
820
|
var BlankNode = class extends FormulaNode {
|
|
763
821
|
evaluate() {
|
|
764
822
|
return FormulaValue.blank;
|
|
@@ -1036,7 +1094,7 @@ var CallNode = class extends FormulaNode {
|
|
|
1036
1094
|
};
|
|
1037
1095
|
|
|
1038
1096
|
// src/formula-parser.ts
|
|
1039
|
-
var _tokens, _pos2, _FormulaParser_instances, current_get, advance_fn, expect_fn, parseComparison_fn, parseConcatenation_fn, parseAddSub_fn, parseMulDiv_fn, parseUnary_fn, parsePower_fn, parsePercent_fn, parsePostfix_fn, parsePrimary_fn, tryParseFullRange_fn, parseFunction_fn, parseArgList_fn;
|
|
1097
|
+
var _tokens, _pos2, _FormulaParser_instances, current_get, advance_fn, expect_fn, parseComparison_fn, parseConcatenation_fn, parseAddSub_fn, parseMulDiv_fn, parseUnary_fn, parsePower_fn, parsePercent_fn, parsePostfix_fn, parsePrimary_fn, tryParseFullRange_fn, parseArrayLiteral_fn, parseArrayElement_fn, parseFunction_fn, parseArgList_fn;
|
|
1040
1098
|
var FormulaParser = class {
|
|
1041
1099
|
constructor(tokens) {
|
|
1042
1100
|
__privateAdd(this, _FormulaParser_instances);
|
|
@@ -1047,7 +1105,7 @@ var FormulaParser = class {
|
|
|
1047
1105
|
parse() {
|
|
1048
1106
|
__privateSet(this, _pos2, 0);
|
|
1049
1107
|
const node = __privateMethod(this, _FormulaParser_instances, parseComparison_fn).call(this);
|
|
1050
|
-
if (__privateGet(this, _FormulaParser_instances, current_get).type !==
|
|
1108
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type !== 31 /* EOF */) {
|
|
1051
1109
|
throw new FormulaException(
|
|
1052
1110
|
`Unexpected token '${__privateGet(this, _FormulaParser_instances, current_get).value}' at position ${__privateGet(this, _FormulaParser_instances, current_get).position}.`
|
|
1053
1111
|
);
|
|
@@ -1059,7 +1117,7 @@ _tokens = new WeakMap();
|
|
|
1059
1117
|
_pos2 = new WeakMap();
|
|
1060
1118
|
_FormulaParser_instances = new WeakSet();
|
|
1061
1119
|
current_get = function() {
|
|
1062
|
-
return __privateGet(this, _tokens)[__privateGet(this, _pos2)] ?? { type:
|
|
1120
|
+
return __privateGet(this, _tokens)[__privateGet(this, _pos2)] ?? { type: 31 /* EOF */, value: "", position: -1 };
|
|
1063
1121
|
};
|
|
1064
1122
|
advance_fn = function() {
|
|
1065
1123
|
const t = __privateGet(this, _FormulaParser_instances, current_get);
|
|
@@ -1174,7 +1232,7 @@ parsePrimary_fn = function() {
|
|
|
1174
1232
|
case 2 /* Boolean */:
|
|
1175
1233
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1176
1234
|
return new BooleanNode(token.value === "TRUE");
|
|
1177
|
-
case
|
|
1235
|
+
case 30 /* ErrorLiteral */:
|
|
1178
1236
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1179
1237
|
return new ErrorNode(FormulaValue.error(FormulaValue.errorFromString(token.value)));
|
|
1180
1238
|
case 3 /* CellReference */: {
|
|
@@ -1213,6 +1271,8 @@ parsePrimary_fn = function() {
|
|
|
1213
1271
|
return new SpillReferenceNode(token.value);
|
|
1214
1272
|
case 5 /* Function */:
|
|
1215
1273
|
return __privateMethod(this, _FormulaParser_instances, parseFunction_fn).call(this);
|
|
1274
|
+
case 27 /* LeftBrace */:
|
|
1275
|
+
return __privateMethod(this, _FormulaParser_instances, parseArrayLiteral_fn).call(this);
|
|
1216
1276
|
case 6 /* LeftParen */: {
|
|
1217
1277
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1218
1278
|
const expr = __privateMethod(this, _FormulaParser_instances, parseComparison_fn).call(this);
|
|
@@ -1247,6 +1307,79 @@ tryParseFullRange_fn = function(startText, sheetName) {
|
|
|
1247
1307
|
const hi = Math.max(start.index, end.index);
|
|
1248
1308
|
return start.kind === "col" ? new FullRangeReferenceNode(lo, hi, null, null, sheetName) : new FullRangeReferenceNode(null, null, lo, hi, sheetName);
|
|
1249
1309
|
};
|
|
1310
|
+
/**
|
|
1311
|
+
* Parses an array constant: `{1,2;3,4}` — ',' separates columns, ';' rows.
|
|
1312
|
+
*
|
|
1313
|
+
* Excel permits only literals inside one (no references, no calls, not even
|
|
1314
|
+
* arithmetic beyond a leading sign), so the value is fully determined here and
|
|
1315
|
+
* is materialised once into the node instead of being rebuilt on every eval.
|
|
1316
|
+
*
|
|
1317
|
+
* A ragged constant is rejected rather than padded: Excel refuses to accept
|
|
1318
|
+
* one at entry, so a file cannot legitimately contain it, and silently filling
|
|
1319
|
+
* the gaps would invent data the author never wrote.
|
|
1320
|
+
*/
|
|
1321
|
+
parseArrayLiteral_fn = function() {
|
|
1322
|
+
const open = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1323
|
+
const rows2 = [];
|
|
1324
|
+
let row2 = [];
|
|
1325
|
+
for (; ; ) {
|
|
1326
|
+
row2.push(__privateMethod(this, _FormulaParser_instances, parseArrayElement_fn).call(this));
|
|
1327
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 8 /* Comma */) {
|
|
1328
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1329
|
+
continue;
|
|
1330
|
+
}
|
|
1331
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 29 /* Semicolon */) {
|
|
1332
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1333
|
+
rows2.push(row2);
|
|
1334
|
+
row2 = [];
|
|
1335
|
+
continue;
|
|
1336
|
+
}
|
|
1337
|
+
break;
|
|
1338
|
+
}
|
|
1339
|
+
rows2.push(row2);
|
|
1340
|
+
__privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 28 /* RightBrace */);
|
|
1341
|
+
const cols = rows2[0].length;
|
|
1342
|
+
if (rows2.some((r) => r.length !== cols)) {
|
|
1343
|
+
throw new FormulaException(
|
|
1344
|
+
`Array constant at position ${open.position} has rows of differing lengths.`
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
const array = new ArrayValue(rows2.length, cols);
|
|
1348
|
+
for (let r = 0; r < rows2.length; r++) {
|
|
1349
|
+
for (let c = 0; c < cols; c++) array.set(r, c, rows2[r][c]);
|
|
1350
|
+
}
|
|
1351
|
+
return new ArrayLiteralNode(array);
|
|
1352
|
+
};
|
|
1353
|
+
/** One element of an array constant: a literal, optionally signed. */
|
|
1354
|
+
parseArrayElement_fn = function() {
|
|
1355
|
+
let negate = false;
|
|
1356
|
+
let signed = false;
|
|
1357
|
+
while (__privateGet(this, _FormulaParser_instances, current_get).type === 10 /* Minus */ || __privateGet(this, _FormulaParser_instances, current_get).type === 9 /* Plus */) {
|
|
1358
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 10 /* Minus */) negate = !negate;
|
|
1359
|
+
signed = true;
|
|
1360
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1361
|
+
}
|
|
1362
|
+
const token = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1363
|
+
switch (token.type) {
|
|
1364
|
+
case 0 /* Number */: {
|
|
1365
|
+
const n = parseFloat(token.value);
|
|
1366
|
+
if (isNaN(n)) throw new FormulaException(`Invalid number: ${token.value}`);
|
|
1367
|
+
return FormulaValue.number(negate ? -n : n);
|
|
1368
|
+
}
|
|
1369
|
+
case 1 /* String */:
|
|
1370
|
+
if (signed) break;
|
|
1371
|
+
return FormulaValue.text(token.value);
|
|
1372
|
+
case 2 /* Boolean */:
|
|
1373
|
+
if (signed) break;
|
|
1374
|
+
return FormulaValue.boolean(token.value === "TRUE");
|
|
1375
|
+
case 30 /* ErrorLiteral */:
|
|
1376
|
+
if (signed) break;
|
|
1377
|
+
return FormulaValue.error(FormulaValue.errorFromString(token.value));
|
|
1378
|
+
}
|
|
1379
|
+
throw new FormulaException(
|
|
1380
|
+
`Array constants accept only literal values; got '${token.value}' at position ${token.position}.`
|
|
1381
|
+
);
|
|
1382
|
+
};
|
|
1250
1383
|
parseFunction_fn = function() {
|
|
1251
1384
|
const nameToken = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1252
1385
|
__privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 6 /* LeftParen */);
|
|
@@ -1453,42 +1586,42 @@ function registerMath(r) {
|
|
|
1453
1586
|
r.register("PRODUCT", product, 1);
|
|
1454
1587
|
r.register("SUMPRODUCT", sumProduct, 1);
|
|
1455
1588
|
r.register("SUMSQ", sumSq, 1);
|
|
1456
|
-
r.
|
|
1457
|
-
r.
|
|
1458
|
-
r.
|
|
1459
|
-
r.
|
|
1460
|
-
r.
|
|
1461
|
-
r.
|
|
1462
|
-
r.
|
|
1463
|
-
r.
|
|
1464
|
-
r.
|
|
1465
|
-
r.
|
|
1466
|
-
r.
|
|
1467
|
-
r.
|
|
1468
|
-
r.
|
|
1469
|
-
r.
|
|
1470
|
-
r.
|
|
1589
|
+
r.registerElementwise("ABS", abs, 1, 1);
|
|
1590
|
+
r.registerElementwise("ROUND", round, 2, 2);
|
|
1591
|
+
r.registerElementwise("ROUNDUP", roundUp, 2, 2);
|
|
1592
|
+
r.registerElementwise("ROUNDDOWN", roundDown, 2, 2);
|
|
1593
|
+
r.registerElementwise("CEILING", ceiling, 2, 2);
|
|
1594
|
+
r.registerElementwise("CEILING.MATH", ceiling, 2, 2);
|
|
1595
|
+
r.registerElementwise("FLOOR", floor, 2, 2);
|
|
1596
|
+
r.registerElementwise("FLOOR.MATH", floor, 2, 2);
|
|
1597
|
+
r.registerElementwise("INT", int_, 1, 1);
|
|
1598
|
+
r.registerElementwise("TRUNC", trunc, 1, 2);
|
|
1599
|
+
r.registerElementwise("MOD", mod, 2, 2);
|
|
1600
|
+
r.registerElementwise("QUOTIENT", quotient, 2, 2);
|
|
1601
|
+
r.registerElementwise("POWER", power, 2, 2);
|
|
1602
|
+
r.registerElementwise("SQRT", sqrt, 1, 1);
|
|
1603
|
+
r.registerElementwise("SIGN", sign, 1, 1);
|
|
1471
1604
|
r.register("PI", pi, 0, 0);
|
|
1472
1605
|
r.register("RAND", rand, 0, 0, true);
|
|
1473
1606
|
r.register("RANDBETWEEN", randBetween, 2, 2, true);
|
|
1474
|
-
r.
|
|
1475
|
-
r.
|
|
1476
|
-
r.
|
|
1477
|
-
r.
|
|
1478
|
-
r.
|
|
1479
|
-
r.
|
|
1480
|
-
r.
|
|
1481
|
-
r.
|
|
1482
|
-
r.
|
|
1483
|
-
r.
|
|
1484
|
-
r.
|
|
1485
|
-
r.
|
|
1486
|
-
r.
|
|
1487
|
-
r.
|
|
1488
|
-
r.
|
|
1489
|
-
r.
|
|
1490
|
-
r.
|
|
1491
|
-
r.
|
|
1607
|
+
r.registerElementwise("LOG", log, 1, 2);
|
|
1608
|
+
r.registerElementwise("LOG10", log10, 1, 1);
|
|
1609
|
+
r.registerElementwise("LN", ln, 1, 1);
|
|
1610
|
+
r.registerElementwise("EXP", exp, 1, 1);
|
|
1611
|
+
r.registerElementwise("FACT", fact, 1, 1);
|
|
1612
|
+
r.registerElementwise("COMBIN", combin, 2, 2);
|
|
1613
|
+
r.registerElementwise("SIN", unary(Math.sin), 1, 1);
|
|
1614
|
+
r.registerElementwise("COS", unary(Math.cos), 1, 1);
|
|
1615
|
+
r.registerElementwise("TAN", unary(Math.tan), 1, 1);
|
|
1616
|
+
r.registerElementwise("ASIN", asin, 1, 1);
|
|
1617
|
+
r.registerElementwise("ACOS", acos, 1, 1);
|
|
1618
|
+
r.registerElementwise("ATAN", unary(Math.atan), 1, 1);
|
|
1619
|
+
r.registerElementwise("ATAN2", atan2, 2, 2);
|
|
1620
|
+
r.registerElementwise("SINH", unary(Math.sinh), 1, 1);
|
|
1621
|
+
r.registerElementwise("COSH", unary(Math.cosh), 1, 1);
|
|
1622
|
+
r.registerElementwise("TANH", unary(Math.tanh), 1, 1);
|
|
1623
|
+
r.registerElementwise("DEGREES", unary((r2d) => r2d * 180 / Math.PI), 1, 1);
|
|
1624
|
+
r.registerElementwise("RADIANS", unary((d) => d * Math.PI / 180), 1, 1);
|
|
1492
1625
|
}
|
|
1493
1626
|
function valueAt(v, i) {
|
|
1494
1627
|
return v.isArray ? v.arrayVal.getFlat(i) : v;
|
|
@@ -1920,31 +2053,31 @@ function combin(a, c) {
|
|
|
1920
2053
|
return FormulaValue.number(Math.round(result));
|
|
1921
2054
|
}
|
|
1922
2055
|
function registerText(r) {
|
|
1923
|
-
r.
|
|
1924
|
-
r.
|
|
1925
|
-
r.
|
|
1926
|
-
r.
|
|
1927
|
-
r.
|
|
1928
|
-
r.
|
|
1929
|
-
r.
|
|
1930
|
-
r.
|
|
1931
|
-
r.
|
|
1932
|
-
r.
|
|
1933
|
-
r.
|
|
2056
|
+
r.registerElementwise("LEFT", left, 1, 2);
|
|
2057
|
+
r.registerElementwise("RIGHT", right, 1, 2);
|
|
2058
|
+
r.registerElementwise("MID", mid, 3, 3);
|
|
2059
|
+
r.registerElementwise("LEN", len, 1, 1);
|
|
2060
|
+
r.registerElementwise("UPPER", upper, 1, 1);
|
|
2061
|
+
r.registerElementwise("LOWER", lower, 1, 1);
|
|
2062
|
+
r.registerElementwise("PROPER", proper, 1, 1);
|
|
2063
|
+
r.registerElementwise("TRIM", trim, 1, 1);
|
|
2064
|
+
r.registerElementwise("CLEAN", clean, 1, 1);
|
|
2065
|
+
r.registerElementwise("SUBSTITUTE", substitute, 3, 4);
|
|
2066
|
+
r.registerElementwise("REPLACE", replace, 4, 4);
|
|
1934
2067
|
r.register("CONCATENATE", concatenate, 1);
|
|
1935
2068
|
r.register("CONCAT", concatenate, 1);
|
|
1936
2069
|
r.register("TEXTJOIN", textJoin, 3);
|
|
1937
|
-
r.
|
|
1938
|
-
r.
|
|
1939
|
-
r.
|
|
1940
|
-
r.
|
|
1941
|
-
r.
|
|
1942
|
-
r.
|
|
1943
|
-
r.
|
|
1944
|
-
r.
|
|
1945
|
-
r.
|
|
1946
|
-
r.
|
|
1947
|
-
r.
|
|
2070
|
+
r.registerElementwise("TEXT", text_, 2, 2);
|
|
2071
|
+
r.registerElementwise("VALUE", value, 1, 1);
|
|
2072
|
+
r.registerElementwise("FIND", find, 2, 3);
|
|
2073
|
+
r.registerElementwise("SEARCH", search, 2, 3);
|
|
2074
|
+
r.registerElementwise("REPT", rept, 2, 2);
|
|
2075
|
+
r.registerElementwise("EXACT", exact, 2, 2);
|
|
2076
|
+
r.registerElementwise("CHAR", char_, 1, 1);
|
|
2077
|
+
r.registerElementwise("CODE", code, 1, 1);
|
|
2078
|
+
r.registerElementwise("T", t_, 1, 1);
|
|
2079
|
+
r.registerElementwise("TEXTBEFORE", textBefore, 2, 3);
|
|
2080
|
+
r.registerElementwise("TEXTAFTER", textAfter, 2, 3);
|
|
1948
2081
|
}
|
|
1949
2082
|
function str(a, c, idx) {
|
|
1950
2083
|
const r = exports.FormulaHelper.evalString(a[idx], c);
|
|
@@ -2258,6 +2391,7 @@ function registerLogical(r) {
|
|
|
2258
2391
|
function if_(a, c) {
|
|
2259
2392
|
const cond = a[0].evaluate(c);
|
|
2260
2393
|
if (cond.isError) return cond;
|
|
2394
|
+
if (cond.isArray) return ifArray(cond.arrayVal, a, c);
|
|
2261
2395
|
const b = cond.coerceToBool();
|
|
2262
2396
|
if (b.isError) return b;
|
|
2263
2397
|
return b.booleanValue ? a[1].evaluate(c) : a.length > 2 ? a[2].evaluate(c) : FormulaValue.false_;
|
|
@@ -2334,13 +2468,63 @@ function xor(a, c) {
|
|
|
2334
2468
|
}
|
|
2335
2469
|
return FormulaValue.boolean(trueCount % 2 === 1);
|
|
2336
2470
|
}
|
|
2337
|
-
function
|
|
2471
|
+
function ifArray(mask, a, c) {
|
|
2472
|
+
let wantTrue = false, wantFalse = false;
|
|
2473
|
+
for (const item of mask.values()) {
|
|
2474
|
+
const b = item.coerceToBool();
|
|
2475
|
+
if (b.isError) return b;
|
|
2476
|
+
if (b.booleanValue) wantTrue = true;
|
|
2477
|
+
else wantFalse = true;
|
|
2478
|
+
if (wantTrue && wantFalse) break;
|
|
2479
|
+
}
|
|
2480
|
+
const yes = wantTrue ? a[1].evaluate(c) : FormulaValue.blank;
|
|
2481
|
+
const no = wantFalse ? a.length > 2 ? a[2].evaluate(c) : FormulaValue.false_ : FormulaValue.blank;
|
|
2482
|
+
const pick = (v, r, col) => {
|
|
2483
|
+
if (!v.isArray) return v;
|
|
2484
|
+
const arr = v.arrayVal;
|
|
2485
|
+
return r < arr.rows && col < arr.columns ? arr.get(r, col) : FormulaValue.errorNA;
|
|
2486
|
+
};
|
|
2487
|
+
const out = new ArrayValue(mask.rows, mask.columns);
|
|
2488
|
+
for (let r = 0; r < mask.rows; r++) {
|
|
2489
|
+
for (let col = 0; col < mask.columns; col++) {
|
|
2490
|
+
const b = mask.get(r, col).coerceToBool();
|
|
2491
|
+
out.set(r, col, b.booleanValue ? pick(yes, r, col) : pick(no, r, col));
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
return FormulaValue.array(out);
|
|
2495
|
+
}
|
|
2496
|
+
function ifCaught(a, c, caught) {
|
|
2338
2497
|
const v = a[0].evaluate(c);
|
|
2339
|
-
|
|
2498
|
+
if (!v.isArray) return caught(v) ? a[1].evaluate(c) : v;
|
|
2499
|
+
const arr = v.arrayVal;
|
|
2500
|
+
let any = false;
|
|
2501
|
+
for (const item of arr.values()) {
|
|
2502
|
+
if (caught(item)) {
|
|
2503
|
+
any = true;
|
|
2504
|
+
break;
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
if (!any) return v;
|
|
2508
|
+
const fb = a[1].evaluate(c);
|
|
2509
|
+
const fbArr = fb.isArray ? fb.arrayVal : null;
|
|
2510
|
+
const out = new ArrayValue(arr.rows, arr.columns);
|
|
2511
|
+
for (let r = 0; r < arr.rows; r++) {
|
|
2512
|
+
for (let col = 0; col < arr.columns; col++) {
|
|
2513
|
+
const item = arr.get(r, col);
|
|
2514
|
+
if (!caught(item)) {
|
|
2515
|
+
out.set(r, col, item);
|
|
2516
|
+
continue;
|
|
2517
|
+
}
|
|
2518
|
+
out.set(r, col, fbArr ? r < fbArr.rows && col < fbArr.columns ? fbArr.get(r, col) : FormulaValue.errorNA : fb);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
return FormulaValue.array(out);
|
|
2522
|
+
}
|
|
2523
|
+
function ifError(a, c) {
|
|
2524
|
+
return ifCaught(a, c, (v) => v.isError);
|
|
2340
2525
|
}
|
|
2341
2526
|
function ifNa(a, c) {
|
|
2342
|
-
|
|
2343
|
-
return v.isError && v.errorCode === 7 /* NA */ ? a[1].evaluate(c) : v;
|
|
2527
|
+
return ifCaught(a, c, (v) => v.isError && v.errorCode === 7 /* NA */);
|
|
2344
2528
|
}
|
|
2345
2529
|
function ifs(a, c) {
|
|
2346
2530
|
for (let i = 0; i + 1 < a.length; i += 2) {
|
|
@@ -2397,21 +2581,21 @@ function fromOA(oa) {
|
|
|
2397
2581
|
function registerDate(r) {
|
|
2398
2582
|
r.register("NOW", now, 0, 0, true);
|
|
2399
2583
|
r.register("TODAY", today, 0, 0, true);
|
|
2400
|
-
r.
|
|
2401
|
-
r.
|
|
2402
|
-
r.
|
|
2403
|
-
r.
|
|
2404
|
-
r.
|
|
2405
|
-
r.
|
|
2406
|
-
r.
|
|
2407
|
-
r.
|
|
2408
|
-
r.
|
|
2409
|
-
r.
|
|
2410
|
-
r.
|
|
2411
|
-
r.
|
|
2412
|
-
r.
|
|
2584
|
+
r.registerElementwise("DATE", date_, 3, 3);
|
|
2585
|
+
r.registerElementwise("YEAR", year, 1, 1);
|
|
2586
|
+
r.registerElementwise("MONTH", month, 1, 1);
|
|
2587
|
+
r.registerElementwise("DAY", day, 1, 1);
|
|
2588
|
+
r.registerElementwise("HOUR", hour, 1, 1);
|
|
2589
|
+
r.registerElementwise("MINUTE", minute, 1, 1);
|
|
2590
|
+
r.registerElementwise("SECOND", second, 1, 1);
|
|
2591
|
+
r.registerElementwise("DATEVALUE", dateValue, 1, 1);
|
|
2592
|
+
r.registerElementwise("DAYS", days, 2, 2);
|
|
2593
|
+
r.registerElementwise("EDATE", edate, 2, 2);
|
|
2594
|
+
r.registerElementwise("EOMONTH", eomonth, 2, 2);
|
|
2595
|
+
r.registerElementwise("WEEKDAY", weekday, 1, 2);
|
|
2596
|
+
r.registerElementwise("WEEKNUM", weeknum, 1, 2);
|
|
2413
2597
|
r.register("NETWORKDAYS", networkdays, 2, 3);
|
|
2414
|
-
r.
|
|
2598
|
+
r.registerElementwise("DATEDIF", datedif, 3, 3);
|
|
2415
2599
|
}
|
|
2416
2600
|
function evalDate(a, c, idx) {
|
|
2417
2601
|
const r = exports.FormulaHelper.evalDouble(a[idx], c);
|
|
@@ -3224,20 +3408,20 @@ function ifsAggregate(a, c, mode2) {
|
|
|
3224
3408
|
|
|
3225
3409
|
// src/functions/info-functions.ts
|
|
3226
3410
|
function registerInfo(r) {
|
|
3227
|
-
r.
|
|
3228
|
-
r.
|
|
3229
|
-
r.
|
|
3230
|
-
r.
|
|
3231
|
-
r.
|
|
3232
|
-
r.
|
|
3233
|
-
r.
|
|
3234
|
-
r.
|
|
3411
|
+
r.registerElementwise("ISBLANK", isBlank, 1, 1);
|
|
3412
|
+
r.registerElementwise("ISNUMBER", isNumber, 1, 1);
|
|
3413
|
+
r.registerElementwise("ISTEXT", isText, 1, 1);
|
|
3414
|
+
r.registerElementwise("ISERROR", isError, 1, 1);
|
|
3415
|
+
r.registerElementwise("ISERR", isErr, 1, 1);
|
|
3416
|
+
r.registerElementwise("ISLOGICAL", isLogical, 1, 1);
|
|
3417
|
+
r.registerElementwise("ISNA", isNa, 1, 1);
|
|
3418
|
+
r.registerElementwise("ISNONTEXT", isNonText, 1, 1);
|
|
3235
3419
|
r.register("NA", na, 0, 0);
|
|
3236
|
-
r.
|
|
3237
|
-
r.
|
|
3238
|
-
r.
|
|
3239
|
-
r.
|
|
3240
|
-
r.
|
|
3420
|
+
r.registerElementwise("TYPE", type_, 1, 1);
|
|
3421
|
+
r.registerElementwise("N", n_, 1, 1);
|
|
3422
|
+
r.registerElementwise("ISODD", isOdd, 1, 1);
|
|
3423
|
+
r.registerElementwise("ISEVEN", isEven, 1, 1);
|
|
3424
|
+
r.registerElementwise("ERROR.TYPE", errorType, 1, 1);
|
|
3241
3425
|
}
|
|
3242
3426
|
function isBlank(a, c) {
|
|
3243
3427
|
return FormulaValue.boolean(a[0].evaluate(c).isBlank);
|
|
@@ -4063,22 +4247,46 @@ function unique(a, c) {
|
|
|
4063
4247
|
const out = fromRows(result);
|
|
4064
4248
|
return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
|
|
4065
4249
|
}
|
|
4250
|
+
var numVector = (a, c, i, dflt) => {
|
|
4251
|
+
if (i >= a.length) return { ok: true, v: [dflt] };
|
|
4252
|
+
const val = a[i].evaluate(c);
|
|
4253
|
+
if (val.isError) return { ok: false, e: val };
|
|
4254
|
+
const out = [];
|
|
4255
|
+
for (const item of val.isArray ? val.arrayVal.values() : [val]) {
|
|
4256
|
+
if (item.isBlank) continue;
|
|
4257
|
+
const d = item.tryAsDouble();
|
|
4258
|
+
if (!d.ok) return { ok: false, e: FormulaValue.errorValue };
|
|
4259
|
+
out.push(d.value);
|
|
4260
|
+
}
|
|
4261
|
+
return out.length > 0 ? { ok: true, v: out } : { ok: true, v: [dflt] };
|
|
4262
|
+
};
|
|
4066
4263
|
function sort(a, c) {
|
|
4067
4264
|
const v = a[0].evaluate(c);
|
|
4068
4265
|
if (v.isError) return v;
|
|
4069
|
-
const
|
|
4070
|
-
if (!
|
|
4071
|
-
const
|
|
4072
|
-
if (!
|
|
4266
|
+
const indices = numVector(a, c, 1, 1);
|
|
4267
|
+
if (!indices.ok) return indices.e;
|
|
4268
|
+
const orders = numVector(a, c, 2, 1);
|
|
4269
|
+
if (!orders.ok) return orders.e;
|
|
4073
4270
|
const byCol2 = a.length > 3 ? a[3].evaluate(c).coerceToBool().booleanValue : false;
|
|
4271
|
+
if (orders.v.length !== 1 && orders.v.length !== indices.v.length) {
|
|
4272
|
+
return FormulaValue.errorValue;
|
|
4273
|
+
}
|
|
4074
4274
|
const base = byCol2 ? asArray2(v).transpose() : asArray2(v);
|
|
4075
4275
|
const rows2 = toRows(base);
|
|
4076
|
-
const
|
|
4077
|
-
|
|
4078
|
-
|
|
4276
|
+
const keys = indices.v.map((n, i) => ({
|
|
4277
|
+
col: Math.trunc(n) - 1,
|
|
4278
|
+
dir: (orders.v.length === 1 ? orders.v[0] : orders.v[i]) < 0 ? -1 : 1
|
|
4279
|
+
}));
|
|
4280
|
+
const width = rows2.length > 0 ? rows2[0].length : 0;
|
|
4281
|
+
if (rows2.length > 0 && keys.some((k) => k.col < 0 || k.col >= width)) {
|
|
4282
|
+
return FormulaValue.errorValue;
|
|
4283
|
+
}
|
|
4079
4284
|
const sorted = rows2.map((r, i) => ({ r, i })).sort((x, y) => {
|
|
4080
|
-
const
|
|
4081
|
-
|
|
4285
|
+
for (const k of keys) {
|
|
4286
|
+
const cmp = FormulaValue.compare(x.r[k.col], y.r[k.col]) * k.dir;
|
|
4287
|
+
if (cmp !== 0) return cmp;
|
|
4288
|
+
}
|
|
4289
|
+
return x.i - y.i;
|
|
4082
4290
|
}).map((o) => o.r);
|
|
4083
4291
|
const out = fromRows(sorted);
|
|
4084
4292
|
return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
|
|
@@ -4301,7 +4509,8 @@ function toRow(a, c) {
|
|
|
4301
4509
|
}
|
|
4302
4510
|
|
|
4303
4511
|
// src/function-registry.ts
|
|
4304
|
-
var
|
|
4512
|
+
var MAX_LIFTED_CELLS = 1048576;
|
|
4513
|
+
var _default, _functions, _FunctionRegistry_static, executeElementwise_fn, _FunctionRegistry_instances, registerBuiltIns_fn;
|
|
4305
4514
|
var _FunctionRegistry = class _FunctionRegistry {
|
|
4306
4515
|
constructor() {
|
|
4307
4516
|
__privateAdd(this, _FunctionRegistry_instances);
|
|
@@ -4323,17 +4532,45 @@ var _FunctionRegistry = class _FunctionRegistry {
|
|
|
4323
4532
|
minArgs,
|
|
4324
4533
|
maxArgs,
|
|
4325
4534
|
isVolatile,
|
|
4326
|
-
handler
|
|
4535
|
+
handler,
|
|
4536
|
+
elementwise: false
|
|
4537
|
+
});
|
|
4538
|
+
}
|
|
4539
|
+
/**
|
|
4540
|
+
* Registers a **scalar** function — one whose arguments are single values —
|
|
4541
|
+
* and lifts it over arrays automatically.
|
|
4542
|
+
*
|
|
4543
|
+
* `LEFT(A2:A100, 3)` is not a special form in Excel: LEFT takes one string, and
|
|
4544
|
+
* the grid maps it over the range, giving a 99-row array. Without that lifting
|
|
4545
|
+
* the range collapses to one value, so the dynamic array built on top of it —
|
|
4546
|
+
* `FILTER(IFERROR(LEFT(A2:A100,3),""), …)` — filters a 1×1 input and returns
|
|
4547
|
+
* `#CALC!`. Lifting belongs here rather than in each handler: the ~40 scalar
|
|
4548
|
+
* text/math/date functions then get it for free and cannot each get it subtly
|
|
4549
|
+
* wrong.
|
|
4550
|
+
*
|
|
4551
|
+
* Only mark functions that are genuinely per-cell. Anything that *consumes* an
|
|
4552
|
+
* array (SUM, COUNTIF, FILTER, SORT, the lazy branch functions) must keep
|
|
4553
|
+
* `register`, or lifting would map it over the array it is meant to reduce.
|
|
4554
|
+
*/
|
|
4555
|
+
registerElementwise(name, handler, minArgs = 0, maxArgs = -1) {
|
|
4556
|
+
__privateGet(this, _functions).set(name.toUpperCase(), {
|
|
4557
|
+
name: name.toUpperCase(),
|
|
4558
|
+
minArgs,
|
|
4559
|
+
maxArgs,
|
|
4560
|
+
isVolatile: false,
|
|
4561
|
+
handler,
|
|
4562
|
+
elementwise: true
|
|
4327
4563
|
});
|
|
4328
4564
|
}
|
|
4329
4565
|
/** Executes a function by name with argument validation. */
|
|
4330
4566
|
execute(name, args, ctx) {
|
|
4567
|
+
var _a;
|
|
4331
4568
|
const def = __privateGet(this, _functions).get(name.toUpperCase());
|
|
4332
4569
|
if (!def) return FormulaValue.errorName;
|
|
4333
4570
|
if (args.length < def.minArgs) return FormulaValue.errorValue;
|
|
4334
4571
|
if (def.maxArgs >= 0 && args.length > def.maxArgs) return FormulaValue.errorValue;
|
|
4335
4572
|
try {
|
|
4336
|
-
return def.handler(args, ctx);
|
|
4573
|
+
return def.elementwise ? __privateMethod(_a = _FunctionRegistry, _FunctionRegistry_static, executeElementwise_fn).call(_a, def, args, ctx) : def.handler(args, ctx);
|
|
4337
4574
|
} catch {
|
|
4338
4575
|
return FormulaValue.errorValue;
|
|
4339
4576
|
}
|
|
@@ -4349,6 +4586,42 @@ var _FunctionRegistry = class _FunctionRegistry {
|
|
|
4349
4586
|
};
|
|
4350
4587
|
_default = new WeakMap();
|
|
4351
4588
|
_functions = new WeakMap();
|
|
4589
|
+
_FunctionRegistry_static = new WeakSet();
|
|
4590
|
+
executeElementwise_fn = function(def, args, ctx) {
|
|
4591
|
+
const values = args.map((a) => a.evaluate(ctx));
|
|
4592
|
+
let rows2 = 1, cols = 1;
|
|
4593
|
+
for (const v of values) {
|
|
4594
|
+
if (!v.isArray) continue;
|
|
4595
|
+
const a = v.arrayVal;
|
|
4596
|
+
if (a.rows > rows2) rows2 = a.rows;
|
|
4597
|
+
if (a.columns > cols) cols = a.columns;
|
|
4598
|
+
}
|
|
4599
|
+
if (rows2 === 1 && cols === 1) {
|
|
4600
|
+
return def.handler(values.map((v) => new ValueNode(v.isArray ? v.arrayVal.get(0, 0) : v)), ctx);
|
|
4601
|
+
}
|
|
4602
|
+
if (rows2 * cols > MAX_LIFTED_CELLS) return FormulaValue.errorValue;
|
|
4603
|
+
for (const v of values) {
|
|
4604
|
+
if (!v.isArray) continue;
|
|
4605
|
+
const a = v.arrayVal;
|
|
4606
|
+
if (a.rows !== 1 && a.rows !== rows2 || a.columns !== 1 && a.columns !== cols) {
|
|
4607
|
+
return FormulaValue.errorValue;
|
|
4608
|
+
}
|
|
4609
|
+
}
|
|
4610
|
+
const nodes = values.map((v) => new ValueNode(v));
|
|
4611
|
+
const out = new ArrayValue(rows2, cols);
|
|
4612
|
+
for (let r = 0; r < rows2; r++) {
|
|
4613
|
+
for (let c = 0; c < cols; c++) {
|
|
4614
|
+
for (let i = 0; i < values.length; i++) {
|
|
4615
|
+
const v = values[i];
|
|
4616
|
+
if (!v.isArray) continue;
|
|
4617
|
+
const a = v.arrayVal;
|
|
4618
|
+
nodes[i].value = a.get(a.rows === 1 ? 0 : r, a.columns === 1 ? 0 : c);
|
|
4619
|
+
}
|
|
4620
|
+
out.set(r, c, def.handler(nodes, ctx));
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
return FormulaValue.array(out);
|
|
4624
|
+
};
|
|
4352
4625
|
_FunctionRegistry_instances = new WeakSet();
|
|
4353
4626
|
registerBuiltIns_fn = function() {
|
|
4354
4627
|
registerMath(this);
|
|
@@ -4363,6 +4636,7 @@ registerBuiltIns_fn = function() {
|
|
|
4363
4636
|
registerAggregate(this);
|
|
4364
4637
|
registerArray(this);
|
|
4365
4638
|
};
|
|
4639
|
+
__privateAdd(_FunctionRegistry, _FunctionRegistry_static);
|
|
4366
4640
|
__privateAdd(_FunctionRegistry, _default);
|
|
4367
4641
|
var FunctionRegistry = _FunctionRegistry;
|
|
4368
4642
|
exports.FormulaHelper = void 0;
|
|
@@ -5224,6 +5498,7 @@ function createWorkbookRecalcModel(workbook) {
|
|
|
5224
5498
|
getDefinedName: (name, sheet) => workbook.getDefinedName(name, sheet)
|
|
5225
5499
|
};
|
|
5226
5500
|
}
|
|
5501
|
+
var MAX_ADOPTED_SPILL_CELLS = 262144;
|
|
5227
5502
|
var normSheet = (s) => s.toUpperCase();
|
|
5228
5503
|
var normRef = (r) => r.replace(/\$/g, "").toUpperCase();
|
|
5229
5504
|
var keyOf = (sheet, ref) => `${normSheet(sheet)}!${normRef(ref)}`;
|
|
@@ -5234,7 +5509,7 @@ function parseKey(key) {
|
|
|
5234
5509
|
const { row: row2, column: column2 } = puredocsExcel.parseCellRef(ref);
|
|
5235
5510
|
return { sheet, ref, row: row2, col: column2 };
|
|
5236
5511
|
}
|
|
5237
|
-
var _model, _registry, _formulas, _sheetNames, _spills, _spillOwner, _dependents, _volatiles, _sheetLikes, _workbookLike, _rangeCache, _RecalcEngine_instances, writeCell_fn, register_fn, unregister_fn, rememberSheet_fn, recalcInOrder_fn, applyShift_fn, shiftPrecedents_fn, shiftSpillBookkeeping_fn, recalcFrom_fn, runFixpoint_fn, addVolatiles_fn, dependentClosure_fn, forEachDirectDependent_fn, evaluatePass_fn, writeResult_fn, isOccupied_fn, clearSpill_fn, evaluateFormulaValue_fn, sheetLike_fn, buildSheetLike_fn, buildWorkbookLike_fn;
|
|
5512
|
+
var _model, _registry, _formulas, _sheetNames, _spills, _spillOwner, _dependents, _volatiles, _sheetLikes, _workbookLike, _rangeCache, _RecalcEngine_instances, writeCell_fn, register_fn, unregister_fn, rememberSheet_fn, adoptDeclaredSpill_fn, recalcInOrder_fn, applyShift_fn, shiftPrecedents_fn, shiftSpillBookkeeping_fn, recalcFrom_fn, runFixpoint_fn, addVolatiles_fn, dependentClosure_fn, forEachDirectDependent_fn, evaluatePass_fn, writeResult_fn, isOccupied_fn, clearSpill_fn, evaluateFormulaValue_fn, sheetLike_fn, buildSheetLike_fn, buildWorkbookLike_fn;
|
|
5238
5513
|
var RecalcEngine = class {
|
|
5239
5514
|
constructor(model, registry = FunctionRegistry.default) {
|
|
5240
5515
|
__privateAdd(this, _RecalcEngine_instances);
|
|
@@ -5342,11 +5617,12 @@ var RecalcEngine = class {
|
|
|
5342
5617
|
const ast = parseFormulaUncached(text);
|
|
5343
5618
|
const { refs, volatile } = extractReferences(ast, s, __privateGet(this, _registry), resolveName2);
|
|
5344
5619
|
__privateMethod(this, _RecalcEngine_instances, rememberSheet_fn).call(this, sheet);
|
|
5620
|
+
const key = keyOf(s, r);
|
|
5345
5621
|
__privateMethod(this, _RecalcEngine_instances, register_fn).call(this, {
|
|
5346
5622
|
sheet: s,
|
|
5347
5623
|
sheetName: sheet,
|
|
5348
5624
|
ref: r,
|
|
5349
|
-
key
|
|
5625
|
+
key,
|
|
5350
5626
|
row: row2,
|
|
5351
5627
|
col: column2,
|
|
5352
5628
|
formula: text,
|
|
@@ -5354,6 +5630,7 @@ var RecalcEngine = class {
|
|
|
5354
5630
|
refs,
|
|
5355
5631
|
volatile
|
|
5356
5632
|
});
|
|
5633
|
+
__privateMethod(this, _RecalcEngine_instances, adoptDeclaredSpill_fn).call(this, s, sheet, r, key, row2, column2);
|
|
5357
5634
|
registered++;
|
|
5358
5635
|
} catch (err) {
|
|
5359
5636
|
failed.push({ sheet, ref, error: err instanceof Error ? err.message : String(err) });
|
|
@@ -5521,6 +5798,51 @@ unregister_fn = function(key) {
|
|
|
5521
5798
|
rememberSheet_fn = function(name) {
|
|
5522
5799
|
__privateGet(this, _sheetNames).set(normSheet(name), name);
|
|
5523
5800
|
};
|
|
5801
|
+
/**
|
|
5802
|
+
* Takes ownership of the spill block a *file* already declared for an anchor.
|
|
5803
|
+
*
|
|
5804
|
+
* Excel stores a dynamic array as one formula on the anchor plus the spilled
|
|
5805
|
+
* results written as plain cached values in the cells below/right, and records
|
|
5806
|
+
* the extent in the anchor's `<f t="array" ref="…">`. Without this step the
|
|
5807
|
+
* engine meets those cached values on the first recalculation, sees literal
|
|
5808
|
+
* content inside the block it wants to spill into, and reports `#SPILL!` for
|
|
5809
|
+
* every dynamic array in every real file. Registering the declared block as
|
|
5810
|
+
* this anchor's existing spill makes them what they actually are — the anchor's
|
|
5811
|
+
* own territory, free to overwrite — and reuses the ordinary shrink path, so a
|
|
5812
|
+
* result smaller than the declared block clears the leftovers.
|
|
5813
|
+
*
|
|
5814
|
+
* Cost is one Set entry per already-spilled cell, paid once at load. A declared
|
|
5815
|
+
* block far larger than any real spill (a corrupt or hand-written `ref`) is
|
|
5816
|
+
* skipped rather than materialised.
|
|
5817
|
+
*/
|
|
5818
|
+
adoptDeclaredSpill_fn = function(s, sheetName, ref, anchorKey, row2, col) {
|
|
5819
|
+
const declared = __privateGet(this, _model).getSpillRange?.(sheetName, ref);
|
|
5820
|
+
if (!declared || !declared.includes(":")) return;
|
|
5821
|
+
let rect;
|
|
5822
|
+
try {
|
|
5823
|
+
rect = puredocsExcel.parseRangeRef(declared);
|
|
5824
|
+
} catch {
|
|
5825
|
+
return;
|
|
5826
|
+
}
|
|
5827
|
+
if (rect.startRow !== row2 || rect.startColumn !== col) return;
|
|
5828
|
+
const height = rect.endRow - rect.startRow + 1;
|
|
5829
|
+
const width = rect.endColumn - rect.startColumn + 1;
|
|
5830
|
+
if (height < 1 || width < 1) return;
|
|
5831
|
+
if (height * width > MAX_ADOPTED_SPILL_CELLS) return;
|
|
5832
|
+
if (height === 1 && width === 1) return;
|
|
5833
|
+
const members = /* @__PURE__ */ new Set();
|
|
5834
|
+
for (let r = rect.startRow; r <= rect.endRow; r++) {
|
|
5835
|
+
for (let c = rect.startColumn; c <= rect.endColumn; c++) {
|
|
5836
|
+
if (r === row2 && c === col) continue;
|
|
5837
|
+
const key = keyOf(s, puredocsExcel.cellRefFromRowCol(r, c));
|
|
5838
|
+
if (__privateGet(this, _formulas).has(key)) continue;
|
|
5839
|
+
if (__privateGet(this, _spillOwner).has(key)) continue;
|
|
5840
|
+
members.add(key);
|
|
5841
|
+
__privateGet(this, _spillOwner).set(key, anchorKey);
|
|
5842
|
+
}
|
|
5843
|
+
}
|
|
5844
|
+
if (members.size > 0) __privateGet(this, _spills).set(anchorKey, members);
|
|
5845
|
+
};
|
|
5524
5846
|
/**
|
|
5525
5847
|
* One pass in a caller-supplied order, watching for inversions.
|
|
5526
5848
|
*
|