@devmm/puredocs-excel-formula 1.0.8 → 1.1.0
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 +321 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +322 -107
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,13 @@ var FormulaError = /* @__PURE__ */ ((FormulaError2) => {
|
|
|
31
31
|
FormulaError2[FormulaError2["Spill"] = 9] = "Spill";
|
|
32
32
|
return FormulaError2;
|
|
33
33
|
})(FormulaError || {});
|
|
34
|
+
var DECIMAL = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
35
|
+
function numericText(text) {
|
|
36
|
+
const trimmed = text.trim();
|
|
37
|
+
if (!DECIMAL.test(trimmed)) return null;
|
|
38
|
+
const n = Number(trimmed);
|
|
39
|
+
return Number.isFinite(n) ? n : null;
|
|
40
|
+
}
|
|
34
41
|
var TailCall = class {
|
|
35
42
|
constructor(lambda, args) {
|
|
36
43
|
this.lambda = lambda;
|
|
@@ -807,6 +814,16 @@ var ArrayLiteralNode = class extends FormulaNode {
|
|
|
807
814
|
}
|
|
808
815
|
};
|
|
809
816
|
_value = new WeakMap();
|
|
817
|
+
var ValueNode = class extends FormulaNode {
|
|
818
|
+
/** Mutable so one wrapper can be re-pointed across a lifted call's cells. */
|
|
819
|
+
constructor(value2) {
|
|
820
|
+
super();
|
|
821
|
+
this.value = value2;
|
|
822
|
+
}
|
|
823
|
+
evaluate() {
|
|
824
|
+
return this.value;
|
|
825
|
+
}
|
|
826
|
+
};
|
|
810
827
|
var BlankNode = class extends FormulaNode {
|
|
811
828
|
evaluate() {
|
|
812
829
|
return FormulaValue.blank;
|
|
@@ -1433,6 +1450,7 @@ var indexes = /* @__PURE__ */ new WeakMap();
|
|
|
1433
1450
|
function build(arr) {
|
|
1434
1451
|
const byText = /* @__PURE__ */ new Map();
|
|
1435
1452
|
const byNumber = /* @__PURE__ */ new Map();
|
|
1453
|
+
const byTextNumber = /* @__PURE__ */ new Map();
|
|
1436
1454
|
const classes = /* @__PURE__ */ new Map();
|
|
1437
1455
|
const addToClass = (kind, v, i2) => {
|
|
1438
1456
|
const group = classes.get(kind);
|
|
@@ -1452,6 +1470,14 @@ function build(arr) {
|
|
|
1452
1470
|
else byNumber.set(n, [i]);
|
|
1453
1471
|
} else {
|
|
1454
1472
|
addToClass(v.kind, v, i);
|
|
1473
|
+
if (v.isText) {
|
|
1474
|
+
const n = numericText(v.textValue);
|
|
1475
|
+
if (n !== null) {
|
|
1476
|
+
const atText2 = byTextNumber.get(n);
|
|
1477
|
+
if (atText2) atText2.push(i);
|
|
1478
|
+
else byTextNumber.set(n, [i]);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1455
1481
|
}
|
|
1456
1482
|
i++;
|
|
1457
1483
|
}
|
|
@@ -1460,7 +1486,15 @@ function build(arr) {
|
|
|
1460
1486
|
for (let k = 0; k < sortedNumbers.length; k++) {
|
|
1461
1487
|
cumulative[k + 1] = cumulative[k] + byNumber.get(sortedNumbers[k]).length;
|
|
1462
1488
|
}
|
|
1463
|
-
return {
|
|
1489
|
+
return {
|
|
1490
|
+
byText,
|
|
1491
|
+
byNumber,
|
|
1492
|
+
sortedNumbers,
|
|
1493
|
+
cumulative,
|
|
1494
|
+
byTextNumber,
|
|
1495
|
+
sortedTextNumbers: Float64Array.from(byTextNumber.keys()).sort(),
|
|
1496
|
+
classes: [...classes.values()]
|
|
1497
|
+
};
|
|
1464
1498
|
}
|
|
1465
1499
|
function indexOf(arr) {
|
|
1466
1500
|
let idx = indexes.get(arr);
|
|
@@ -1498,19 +1532,25 @@ function matchingPositions(criteriaRange, criteria, minSize) {
|
|
|
1498
1532
|
if (criteria.kind === "equalsNumber") {
|
|
1499
1533
|
const target = criteria.number;
|
|
1500
1534
|
const idx = indexOf(arr);
|
|
1501
|
-
const
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1535
|
+
const groups = [];
|
|
1536
|
+
const collect = (byValue, sorted) => {
|
|
1537
|
+
const exact2 = byValue.get(target);
|
|
1538
|
+
if (exact2) groups.push(exact2);
|
|
1539
|
+
for (let k = lowerBound(sorted, target - EPSILON); k < sorted.length; k++) {
|
|
1540
|
+
const value2 = sorted[k];
|
|
1541
|
+
if (value2 > target + EPSILON) break;
|
|
1542
|
+
if (value2 === target) continue;
|
|
1543
|
+
if (!criteria.test(FormulaValue.number(value2))) continue;
|
|
1544
|
+
groups.push(byValue.get(value2));
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
collect(idx.byNumber, idx.sortedNumbers);
|
|
1548
|
+
collect(idx.byTextNumber, idx.sortedTextNumbers);
|
|
1511
1549
|
const blanks = criteria.test(FormulaValue.blank) ? blankPositions(idx) : void 0;
|
|
1512
|
-
if (
|
|
1513
|
-
|
|
1550
|
+
if (blanks?.length) groups.push(blanks);
|
|
1551
|
+
if (groups.length === 0) return EMPTY;
|
|
1552
|
+
if (groups.length === 1) return groups[0];
|
|
1553
|
+
const merged = groups.flat();
|
|
1514
1554
|
merged.sort((a, b) => a - b);
|
|
1515
1555
|
return merged;
|
|
1516
1556
|
}
|
|
@@ -1576,42 +1616,42 @@ function registerMath(r) {
|
|
|
1576
1616
|
r.register("PRODUCT", product, 1);
|
|
1577
1617
|
r.register("SUMPRODUCT", sumProduct, 1);
|
|
1578
1618
|
r.register("SUMSQ", sumSq, 1);
|
|
1579
|
-
r.
|
|
1580
|
-
r.
|
|
1581
|
-
r.
|
|
1582
|
-
r.
|
|
1583
|
-
r.
|
|
1584
|
-
r.
|
|
1585
|
-
r.
|
|
1586
|
-
r.
|
|
1587
|
-
r.
|
|
1588
|
-
r.
|
|
1589
|
-
r.
|
|
1590
|
-
r.
|
|
1591
|
-
r.
|
|
1592
|
-
r.
|
|
1593
|
-
r.
|
|
1619
|
+
r.registerElementwise("ABS", abs, 1, 1);
|
|
1620
|
+
r.registerElementwise("ROUND", round, 2, 2);
|
|
1621
|
+
r.registerElementwise("ROUNDUP", roundUp, 2, 2);
|
|
1622
|
+
r.registerElementwise("ROUNDDOWN", roundDown, 2, 2);
|
|
1623
|
+
r.registerElementwise("CEILING", ceiling, 2, 2);
|
|
1624
|
+
r.registerElementwise("CEILING.MATH", ceiling, 2, 2);
|
|
1625
|
+
r.registerElementwise("FLOOR", floor, 2, 2);
|
|
1626
|
+
r.registerElementwise("FLOOR.MATH", floor, 2, 2);
|
|
1627
|
+
r.registerElementwise("INT", int_, 1, 1);
|
|
1628
|
+
r.registerElementwise("TRUNC", trunc, 1, 2);
|
|
1629
|
+
r.registerElementwise("MOD", mod, 2, 2);
|
|
1630
|
+
r.registerElementwise("QUOTIENT", quotient, 2, 2);
|
|
1631
|
+
r.registerElementwise("POWER", power, 2, 2);
|
|
1632
|
+
r.registerElementwise("SQRT", sqrt, 1, 1);
|
|
1633
|
+
r.registerElementwise("SIGN", sign, 1, 1);
|
|
1594
1634
|
r.register("PI", pi, 0, 0);
|
|
1595
1635
|
r.register("RAND", rand, 0, 0, true);
|
|
1596
1636
|
r.register("RANDBETWEEN", randBetween, 2, 2, true);
|
|
1597
|
-
r.
|
|
1598
|
-
r.
|
|
1599
|
-
r.
|
|
1600
|
-
r.
|
|
1601
|
-
r.
|
|
1602
|
-
r.
|
|
1603
|
-
r.
|
|
1604
|
-
r.
|
|
1605
|
-
r.
|
|
1606
|
-
r.
|
|
1607
|
-
r.
|
|
1608
|
-
r.
|
|
1609
|
-
r.
|
|
1610
|
-
r.
|
|
1611
|
-
r.
|
|
1612
|
-
r.
|
|
1613
|
-
r.
|
|
1614
|
-
r.
|
|
1637
|
+
r.registerElementwise("LOG", log, 1, 2);
|
|
1638
|
+
r.registerElementwise("LOG10", log10, 1, 1);
|
|
1639
|
+
r.registerElementwise("LN", ln, 1, 1);
|
|
1640
|
+
r.registerElementwise("EXP", exp, 1, 1);
|
|
1641
|
+
r.registerElementwise("FACT", fact, 1, 1);
|
|
1642
|
+
r.registerElementwise("COMBIN", combin, 2, 2);
|
|
1643
|
+
r.registerElementwise("SIN", unary(Math.sin), 1, 1);
|
|
1644
|
+
r.registerElementwise("COS", unary(Math.cos), 1, 1);
|
|
1645
|
+
r.registerElementwise("TAN", unary(Math.tan), 1, 1);
|
|
1646
|
+
r.registerElementwise("ASIN", asin, 1, 1);
|
|
1647
|
+
r.registerElementwise("ACOS", acos, 1, 1);
|
|
1648
|
+
r.registerElementwise("ATAN", unary(Math.atan), 1, 1);
|
|
1649
|
+
r.registerElementwise("ATAN2", atan2, 2, 2);
|
|
1650
|
+
r.registerElementwise("SINH", unary(Math.sinh), 1, 1);
|
|
1651
|
+
r.registerElementwise("COSH", unary(Math.cosh), 1, 1);
|
|
1652
|
+
r.registerElementwise("TANH", unary(Math.tanh), 1, 1);
|
|
1653
|
+
r.registerElementwise("DEGREES", unary((r2d) => r2d * 180 / Math.PI), 1, 1);
|
|
1654
|
+
r.registerElementwise("RADIANS", unary((d) => d * Math.PI / 180), 1, 1);
|
|
1615
1655
|
}
|
|
1616
1656
|
function valueAt(v, i) {
|
|
1617
1657
|
return v.isArray ? v.arrayVal.getFlat(i) : v;
|
|
@@ -2043,31 +2083,31 @@ function combin(a, c) {
|
|
|
2043
2083
|
return FormulaValue.number(Math.round(result));
|
|
2044
2084
|
}
|
|
2045
2085
|
function registerText(r) {
|
|
2046
|
-
r.
|
|
2047
|
-
r.
|
|
2048
|
-
r.
|
|
2049
|
-
r.
|
|
2050
|
-
r.
|
|
2051
|
-
r.
|
|
2052
|
-
r.
|
|
2053
|
-
r.
|
|
2054
|
-
r.
|
|
2055
|
-
r.
|
|
2056
|
-
r.
|
|
2086
|
+
r.registerElementwise("LEFT", left, 1, 2);
|
|
2087
|
+
r.registerElementwise("RIGHT", right, 1, 2);
|
|
2088
|
+
r.registerElementwise("MID", mid, 3, 3);
|
|
2089
|
+
r.registerElementwise("LEN", len, 1, 1);
|
|
2090
|
+
r.registerElementwise("UPPER", upper, 1, 1);
|
|
2091
|
+
r.registerElementwise("LOWER", lower, 1, 1);
|
|
2092
|
+
r.registerElementwise("PROPER", proper, 1, 1);
|
|
2093
|
+
r.registerElementwise("TRIM", trim, 1, 1);
|
|
2094
|
+
r.registerElementwise("CLEAN", clean, 1, 1);
|
|
2095
|
+
r.registerElementwise("SUBSTITUTE", substitute, 3, 4);
|
|
2096
|
+
r.registerElementwise("REPLACE", replace, 4, 4);
|
|
2057
2097
|
r.register("CONCATENATE", concatenate, 1);
|
|
2058
2098
|
r.register("CONCAT", concatenate, 1);
|
|
2059
2099
|
r.register("TEXTJOIN", textJoin, 3);
|
|
2060
|
-
r.
|
|
2061
|
-
r.
|
|
2062
|
-
r.
|
|
2063
|
-
r.
|
|
2064
|
-
r.
|
|
2065
|
-
r.
|
|
2066
|
-
r.
|
|
2067
|
-
r.
|
|
2068
|
-
r.
|
|
2069
|
-
r.
|
|
2070
|
-
r.
|
|
2100
|
+
r.registerElementwise("TEXT", text_, 2, 2);
|
|
2101
|
+
r.registerElementwise("VALUE", value, 1, 1);
|
|
2102
|
+
r.registerElementwise("FIND", find, 2, 3);
|
|
2103
|
+
r.registerElementwise("SEARCH", search, 2, 3);
|
|
2104
|
+
r.registerElementwise("REPT", rept, 2, 2);
|
|
2105
|
+
r.registerElementwise("EXACT", exact, 2, 2);
|
|
2106
|
+
r.registerElementwise("CHAR", char_, 1, 1);
|
|
2107
|
+
r.registerElementwise("CODE", code, 1, 1);
|
|
2108
|
+
r.registerElementwise("T", t_, 1, 1);
|
|
2109
|
+
r.registerElementwise("TEXTBEFORE", textBefore, 2, 3);
|
|
2110
|
+
r.registerElementwise("TEXTAFTER", textAfter, 2, 3);
|
|
2071
2111
|
}
|
|
2072
2112
|
function str(a, c, idx) {
|
|
2073
2113
|
const r = exports.FormulaHelper.evalString(a[idx], c);
|
|
@@ -2381,6 +2421,7 @@ function registerLogical(r) {
|
|
|
2381
2421
|
function if_(a, c) {
|
|
2382
2422
|
const cond = a[0].evaluate(c);
|
|
2383
2423
|
if (cond.isError) return cond;
|
|
2424
|
+
if (cond.isArray) return ifArray(cond.arrayVal, a, c);
|
|
2384
2425
|
const b = cond.coerceToBool();
|
|
2385
2426
|
if (b.isError) return b;
|
|
2386
2427
|
return b.booleanValue ? a[1].evaluate(c) : a.length > 2 ? a[2].evaluate(c) : FormulaValue.false_;
|
|
@@ -2457,13 +2498,63 @@ function xor(a, c) {
|
|
|
2457
2498
|
}
|
|
2458
2499
|
return FormulaValue.boolean(trueCount % 2 === 1);
|
|
2459
2500
|
}
|
|
2460
|
-
function
|
|
2501
|
+
function ifArray(mask, a, c) {
|
|
2502
|
+
let wantTrue = false, wantFalse = false;
|
|
2503
|
+
for (const item of mask.values()) {
|
|
2504
|
+
const b = item.coerceToBool();
|
|
2505
|
+
if (b.isError) return b;
|
|
2506
|
+
if (b.booleanValue) wantTrue = true;
|
|
2507
|
+
else wantFalse = true;
|
|
2508
|
+
if (wantTrue && wantFalse) break;
|
|
2509
|
+
}
|
|
2510
|
+
const yes = wantTrue ? a[1].evaluate(c) : FormulaValue.blank;
|
|
2511
|
+
const no = wantFalse ? a.length > 2 ? a[2].evaluate(c) : FormulaValue.false_ : FormulaValue.blank;
|
|
2512
|
+
const pick = (v, r, col) => {
|
|
2513
|
+
if (!v.isArray) return v;
|
|
2514
|
+
const arr = v.arrayVal;
|
|
2515
|
+
return r < arr.rows && col < arr.columns ? arr.get(r, col) : FormulaValue.errorNA;
|
|
2516
|
+
};
|
|
2517
|
+
const out = new ArrayValue(mask.rows, mask.columns);
|
|
2518
|
+
for (let r = 0; r < mask.rows; r++) {
|
|
2519
|
+
for (let col = 0; col < mask.columns; col++) {
|
|
2520
|
+
const b = mask.get(r, col).coerceToBool();
|
|
2521
|
+
out.set(r, col, b.booleanValue ? pick(yes, r, col) : pick(no, r, col));
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
return FormulaValue.array(out);
|
|
2525
|
+
}
|
|
2526
|
+
function ifCaught(a, c, caught) {
|
|
2461
2527
|
const v = a[0].evaluate(c);
|
|
2462
|
-
|
|
2528
|
+
if (!v.isArray) return caught(v) ? a[1].evaluate(c) : v;
|
|
2529
|
+
const arr = v.arrayVal;
|
|
2530
|
+
let any = false;
|
|
2531
|
+
for (const item of arr.values()) {
|
|
2532
|
+
if (caught(item)) {
|
|
2533
|
+
any = true;
|
|
2534
|
+
break;
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
if (!any) return v;
|
|
2538
|
+
const fb = a[1].evaluate(c);
|
|
2539
|
+
const fbArr = fb.isArray ? fb.arrayVal : null;
|
|
2540
|
+
const out = new ArrayValue(arr.rows, arr.columns);
|
|
2541
|
+
for (let r = 0; r < arr.rows; r++) {
|
|
2542
|
+
for (let col = 0; col < arr.columns; col++) {
|
|
2543
|
+
const item = arr.get(r, col);
|
|
2544
|
+
if (!caught(item)) {
|
|
2545
|
+
out.set(r, col, item);
|
|
2546
|
+
continue;
|
|
2547
|
+
}
|
|
2548
|
+
out.set(r, col, fbArr ? r < fbArr.rows && col < fbArr.columns ? fbArr.get(r, col) : FormulaValue.errorNA : fb);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
return FormulaValue.array(out);
|
|
2552
|
+
}
|
|
2553
|
+
function ifError(a, c) {
|
|
2554
|
+
return ifCaught(a, c, (v) => v.isError);
|
|
2463
2555
|
}
|
|
2464
2556
|
function ifNa(a, c) {
|
|
2465
|
-
|
|
2466
|
-
return v.isError && v.errorCode === 7 /* NA */ ? a[1].evaluate(c) : v;
|
|
2557
|
+
return ifCaught(a, c, (v) => v.isError && v.errorCode === 7 /* NA */);
|
|
2467
2558
|
}
|
|
2468
2559
|
function ifs(a, c) {
|
|
2469
2560
|
for (let i = 0; i + 1 < a.length; i += 2) {
|
|
@@ -2520,21 +2611,21 @@ function fromOA(oa) {
|
|
|
2520
2611
|
function registerDate(r) {
|
|
2521
2612
|
r.register("NOW", now, 0, 0, true);
|
|
2522
2613
|
r.register("TODAY", today, 0, 0, true);
|
|
2523
|
-
r.
|
|
2524
|
-
r.
|
|
2525
|
-
r.
|
|
2526
|
-
r.
|
|
2527
|
-
r.
|
|
2528
|
-
r.
|
|
2529
|
-
r.
|
|
2530
|
-
r.
|
|
2531
|
-
r.
|
|
2532
|
-
r.
|
|
2533
|
-
r.
|
|
2534
|
-
r.
|
|
2535
|
-
r.
|
|
2614
|
+
r.registerElementwise("DATE", date_, 3, 3);
|
|
2615
|
+
r.registerElementwise("YEAR", year, 1, 1);
|
|
2616
|
+
r.registerElementwise("MONTH", month, 1, 1);
|
|
2617
|
+
r.registerElementwise("DAY", day, 1, 1);
|
|
2618
|
+
r.registerElementwise("HOUR", hour, 1, 1);
|
|
2619
|
+
r.registerElementwise("MINUTE", minute, 1, 1);
|
|
2620
|
+
r.registerElementwise("SECOND", second, 1, 1);
|
|
2621
|
+
r.registerElementwise("DATEVALUE", dateValue, 1, 1);
|
|
2622
|
+
r.registerElementwise("DAYS", days, 2, 2);
|
|
2623
|
+
r.registerElementwise("EDATE", edate, 2, 2);
|
|
2624
|
+
r.registerElementwise("EOMONTH", eomonth, 2, 2);
|
|
2625
|
+
r.registerElementwise("WEEKDAY", weekday, 1, 2);
|
|
2626
|
+
r.registerElementwise("WEEKNUM", weeknum, 1, 2);
|
|
2536
2627
|
r.register("NETWORKDAYS", networkdays, 2, 3);
|
|
2537
|
-
r.
|
|
2628
|
+
r.registerElementwise("DATEDIF", datedif, 3, 3);
|
|
2538
2629
|
}
|
|
2539
2630
|
function evalDate(a, c, idx) {
|
|
2540
2631
|
const r = exports.FormulaHelper.evalDouble(a[idx], c);
|
|
@@ -3347,20 +3438,20 @@ function ifsAggregate(a, c, mode2) {
|
|
|
3347
3438
|
|
|
3348
3439
|
// src/functions/info-functions.ts
|
|
3349
3440
|
function registerInfo(r) {
|
|
3350
|
-
r.
|
|
3351
|
-
r.
|
|
3352
|
-
r.
|
|
3353
|
-
r.
|
|
3354
|
-
r.
|
|
3355
|
-
r.
|
|
3356
|
-
r.
|
|
3357
|
-
r.
|
|
3441
|
+
r.registerElementwise("ISBLANK", isBlank, 1, 1);
|
|
3442
|
+
r.registerElementwise("ISNUMBER", isNumber, 1, 1);
|
|
3443
|
+
r.registerElementwise("ISTEXT", isText, 1, 1);
|
|
3444
|
+
r.registerElementwise("ISERROR", isError, 1, 1);
|
|
3445
|
+
r.registerElementwise("ISERR", isErr, 1, 1);
|
|
3446
|
+
r.registerElementwise("ISLOGICAL", isLogical, 1, 1);
|
|
3447
|
+
r.registerElementwise("ISNA", isNa, 1, 1);
|
|
3448
|
+
r.registerElementwise("ISNONTEXT", isNonText, 1, 1);
|
|
3358
3449
|
r.register("NA", na, 0, 0);
|
|
3359
|
-
r.
|
|
3360
|
-
r.
|
|
3361
|
-
r.
|
|
3362
|
-
r.
|
|
3363
|
-
r.
|
|
3450
|
+
r.registerElementwise("TYPE", type_, 1, 1);
|
|
3451
|
+
r.registerElementwise("N", n_, 1, 1);
|
|
3452
|
+
r.registerElementwise("ISODD", isOdd, 1, 1);
|
|
3453
|
+
r.registerElementwise("ISEVEN", isEven, 1, 1);
|
|
3454
|
+
r.registerElementwise("ERROR.TYPE", errorType, 1, 1);
|
|
3364
3455
|
}
|
|
3365
3456
|
function isBlank(a, c) {
|
|
3366
3457
|
return FormulaValue.boolean(a[0].evaluate(c).isBlank);
|
|
@@ -4448,7 +4539,8 @@ function toRow(a, c) {
|
|
|
4448
4539
|
}
|
|
4449
4540
|
|
|
4450
4541
|
// src/function-registry.ts
|
|
4451
|
-
var
|
|
4542
|
+
var MAX_LIFTED_CELLS = 1048576;
|
|
4543
|
+
var _default, _functions, _FunctionRegistry_static, executeElementwise_fn, _FunctionRegistry_instances, registerBuiltIns_fn;
|
|
4452
4544
|
var _FunctionRegistry = class _FunctionRegistry {
|
|
4453
4545
|
constructor() {
|
|
4454
4546
|
__privateAdd(this, _FunctionRegistry_instances);
|
|
@@ -4470,17 +4562,45 @@ var _FunctionRegistry = class _FunctionRegistry {
|
|
|
4470
4562
|
minArgs,
|
|
4471
4563
|
maxArgs,
|
|
4472
4564
|
isVolatile,
|
|
4473
|
-
handler
|
|
4565
|
+
handler,
|
|
4566
|
+
elementwise: false
|
|
4567
|
+
});
|
|
4568
|
+
}
|
|
4569
|
+
/**
|
|
4570
|
+
* Registers a **scalar** function — one whose arguments are single values —
|
|
4571
|
+
* and lifts it over arrays automatically.
|
|
4572
|
+
*
|
|
4573
|
+
* `LEFT(A2:A100, 3)` is not a special form in Excel: LEFT takes one string, and
|
|
4574
|
+
* the grid maps it over the range, giving a 99-row array. Without that lifting
|
|
4575
|
+
* the range collapses to one value, so the dynamic array built on top of it —
|
|
4576
|
+
* `FILTER(IFERROR(LEFT(A2:A100,3),""), …)` — filters a 1×1 input and returns
|
|
4577
|
+
* `#CALC!`. Lifting belongs here rather than in each handler: the ~40 scalar
|
|
4578
|
+
* text/math/date functions then get it for free and cannot each get it subtly
|
|
4579
|
+
* wrong.
|
|
4580
|
+
*
|
|
4581
|
+
* Only mark functions that are genuinely per-cell. Anything that *consumes* an
|
|
4582
|
+
* array (SUM, COUNTIF, FILTER, SORT, the lazy branch functions) must keep
|
|
4583
|
+
* `register`, or lifting would map it over the array it is meant to reduce.
|
|
4584
|
+
*/
|
|
4585
|
+
registerElementwise(name, handler, minArgs = 0, maxArgs = -1) {
|
|
4586
|
+
__privateGet(this, _functions).set(name.toUpperCase(), {
|
|
4587
|
+
name: name.toUpperCase(),
|
|
4588
|
+
minArgs,
|
|
4589
|
+
maxArgs,
|
|
4590
|
+
isVolatile: false,
|
|
4591
|
+
handler,
|
|
4592
|
+
elementwise: true
|
|
4474
4593
|
});
|
|
4475
4594
|
}
|
|
4476
4595
|
/** Executes a function by name with argument validation. */
|
|
4477
4596
|
execute(name, args, ctx) {
|
|
4597
|
+
var _a;
|
|
4478
4598
|
const def = __privateGet(this, _functions).get(name.toUpperCase());
|
|
4479
4599
|
if (!def) return FormulaValue.errorName;
|
|
4480
4600
|
if (args.length < def.minArgs) return FormulaValue.errorValue;
|
|
4481
4601
|
if (def.maxArgs >= 0 && args.length > def.maxArgs) return FormulaValue.errorValue;
|
|
4482
4602
|
try {
|
|
4483
|
-
return def.handler(args, ctx);
|
|
4603
|
+
return def.elementwise ? __privateMethod(_a = _FunctionRegistry, _FunctionRegistry_static, executeElementwise_fn).call(_a, def, args, ctx) : def.handler(args, ctx);
|
|
4484
4604
|
} catch {
|
|
4485
4605
|
return FormulaValue.errorValue;
|
|
4486
4606
|
}
|
|
@@ -4496,6 +4616,42 @@ var _FunctionRegistry = class _FunctionRegistry {
|
|
|
4496
4616
|
};
|
|
4497
4617
|
_default = new WeakMap();
|
|
4498
4618
|
_functions = new WeakMap();
|
|
4619
|
+
_FunctionRegistry_static = new WeakSet();
|
|
4620
|
+
executeElementwise_fn = function(def, args, ctx) {
|
|
4621
|
+
const values = args.map((a) => a.evaluate(ctx));
|
|
4622
|
+
let rows2 = 1, cols = 1;
|
|
4623
|
+
for (const v of values) {
|
|
4624
|
+
if (!v.isArray) continue;
|
|
4625
|
+
const a = v.arrayVal;
|
|
4626
|
+
if (a.rows > rows2) rows2 = a.rows;
|
|
4627
|
+
if (a.columns > cols) cols = a.columns;
|
|
4628
|
+
}
|
|
4629
|
+
if (rows2 === 1 && cols === 1) {
|
|
4630
|
+
return def.handler(values.map((v) => new ValueNode(v.isArray ? v.arrayVal.get(0, 0) : v)), ctx);
|
|
4631
|
+
}
|
|
4632
|
+
if (rows2 * cols > MAX_LIFTED_CELLS) return FormulaValue.errorValue;
|
|
4633
|
+
for (const v of values) {
|
|
4634
|
+
if (!v.isArray) continue;
|
|
4635
|
+
const a = v.arrayVal;
|
|
4636
|
+
if (a.rows !== 1 && a.rows !== rows2 || a.columns !== 1 && a.columns !== cols) {
|
|
4637
|
+
return FormulaValue.errorValue;
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
const nodes = values.map((v) => new ValueNode(v));
|
|
4641
|
+
const out = new ArrayValue(rows2, cols);
|
|
4642
|
+
for (let r = 0; r < rows2; r++) {
|
|
4643
|
+
for (let c = 0; c < cols; c++) {
|
|
4644
|
+
for (let i = 0; i < values.length; i++) {
|
|
4645
|
+
const v = values[i];
|
|
4646
|
+
if (!v.isArray) continue;
|
|
4647
|
+
const a = v.arrayVal;
|
|
4648
|
+
nodes[i].value = a.get(a.rows === 1 ? 0 : r, a.columns === 1 ? 0 : c);
|
|
4649
|
+
}
|
|
4650
|
+
out.set(r, c, def.handler(nodes, ctx));
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
return FormulaValue.array(out);
|
|
4654
|
+
};
|
|
4499
4655
|
_FunctionRegistry_instances = new WeakSet();
|
|
4500
4656
|
registerBuiltIns_fn = function() {
|
|
4501
4657
|
registerMath(this);
|
|
@@ -4510,6 +4666,7 @@ registerBuiltIns_fn = function() {
|
|
|
4510
4666
|
registerAggregate(this);
|
|
4511
4667
|
registerArray(this);
|
|
4512
4668
|
};
|
|
4669
|
+
__privateAdd(_FunctionRegistry, _FunctionRegistry_static);
|
|
4513
4670
|
__privateAdd(_FunctionRegistry, _default);
|
|
4514
4671
|
var FunctionRegistry = _FunctionRegistry;
|
|
4515
4672
|
exports.FormulaHelper = void 0;
|
|
@@ -4575,7 +4732,8 @@ exports.FormulaHelper = void 0;
|
|
|
4575
4732
|
return rhsNum !== null ? numberEquality(rhsNum) : textEquality(rhs);
|
|
4576
4733
|
}
|
|
4577
4734
|
if (op === "<>") {
|
|
4578
|
-
|
|
4735
|
+
const equals = rhsNum !== null ? numberEquality(rhsNum) : textEquality(rhs);
|
|
4736
|
+
return { kind: "other", test: (v) => !equals.test(v) };
|
|
4579
4737
|
}
|
|
4580
4738
|
const wanted = op === ">" ? (c) => c > 0 : op === ">=" ? (c) => c >= 0 : op === "<" ? (c) => c < 0 : (c) => c <= 0;
|
|
4581
4739
|
const test = (v) => wanted(FormulaValue.compare(v, rhsValue));
|
|
@@ -4591,7 +4749,16 @@ exports.FormulaHelper = void 0;
|
|
|
4591
4749
|
FormulaHelper2.compileCriteria = compileCriteria;
|
|
4592
4750
|
function numberEquality(n) {
|
|
4593
4751
|
const target = FormulaValue.number(n);
|
|
4594
|
-
return {
|
|
4752
|
+
return {
|
|
4753
|
+
kind: "equalsNumber",
|
|
4754
|
+
number: n,
|
|
4755
|
+
test: (v) => {
|
|
4756
|
+
if (FormulaValue.areEqual(v, target)) return true;
|
|
4757
|
+
if (!v.isText) return false;
|
|
4758
|
+
const asNumber = numericText(v.textValue);
|
|
4759
|
+
return asNumber !== null && Math.abs(asNumber - n) < 1e-10;
|
|
4760
|
+
}
|
|
4761
|
+
};
|
|
4595
4762
|
}
|
|
4596
4763
|
function textEquality(s) {
|
|
4597
4764
|
const upper2 = s.toUpperCase();
|
|
@@ -5371,6 +5538,7 @@ function createWorkbookRecalcModel(workbook) {
|
|
|
5371
5538
|
getDefinedName: (name, sheet) => workbook.getDefinedName(name, sheet)
|
|
5372
5539
|
};
|
|
5373
5540
|
}
|
|
5541
|
+
var MAX_ADOPTED_SPILL_CELLS = 262144;
|
|
5374
5542
|
var normSheet = (s) => s.toUpperCase();
|
|
5375
5543
|
var normRef = (r) => r.replace(/\$/g, "").toUpperCase();
|
|
5376
5544
|
var keyOf = (sheet, ref) => `${normSheet(sheet)}!${normRef(ref)}`;
|
|
@@ -5381,7 +5549,7 @@ function parseKey(key) {
|
|
|
5381
5549
|
const { row: row2, column: column2 } = puredocsExcel.parseCellRef(ref);
|
|
5382
5550
|
return { sheet, ref, row: row2, col: column2 };
|
|
5383
5551
|
}
|
|
5384
|
-
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;
|
|
5552
|
+
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;
|
|
5385
5553
|
var RecalcEngine = class {
|
|
5386
5554
|
constructor(model, registry = FunctionRegistry.default) {
|
|
5387
5555
|
__privateAdd(this, _RecalcEngine_instances);
|
|
@@ -5489,11 +5657,12 @@ var RecalcEngine = class {
|
|
|
5489
5657
|
const ast = parseFormulaUncached(text);
|
|
5490
5658
|
const { refs, volatile } = extractReferences(ast, s, __privateGet(this, _registry), resolveName2);
|
|
5491
5659
|
__privateMethod(this, _RecalcEngine_instances, rememberSheet_fn).call(this, sheet);
|
|
5660
|
+
const key = keyOf(s, r);
|
|
5492
5661
|
__privateMethod(this, _RecalcEngine_instances, register_fn).call(this, {
|
|
5493
5662
|
sheet: s,
|
|
5494
5663
|
sheetName: sheet,
|
|
5495
5664
|
ref: r,
|
|
5496
|
-
key
|
|
5665
|
+
key,
|
|
5497
5666
|
row: row2,
|
|
5498
5667
|
col: column2,
|
|
5499
5668
|
formula: text,
|
|
@@ -5501,6 +5670,7 @@ var RecalcEngine = class {
|
|
|
5501
5670
|
refs,
|
|
5502
5671
|
volatile
|
|
5503
5672
|
});
|
|
5673
|
+
__privateMethod(this, _RecalcEngine_instances, adoptDeclaredSpill_fn).call(this, s, sheet, r, key, row2, column2);
|
|
5504
5674
|
registered++;
|
|
5505
5675
|
} catch (err) {
|
|
5506
5676
|
failed.push({ sheet, ref, error: err instanceof Error ? err.message : String(err) });
|
|
@@ -5668,6 +5838,51 @@ unregister_fn = function(key) {
|
|
|
5668
5838
|
rememberSheet_fn = function(name) {
|
|
5669
5839
|
__privateGet(this, _sheetNames).set(normSheet(name), name);
|
|
5670
5840
|
};
|
|
5841
|
+
/**
|
|
5842
|
+
* Takes ownership of the spill block a *file* already declared for an anchor.
|
|
5843
|
+
*
|
|
5844
|
+
* Excel stores a dynamic array as one formula on the anchor plus the spilled
|
|
5845
|
+
* results written as plain cached values in the cells below/right, and records
|
|
5846
|
+
* the extent in the anchor's `<f t="array" ref="…">`. Without this step the
|
|
5847
|
+
* engine meets those cached values on the first recalculation, sees literal
|
|
5848
|
+
* content inside the block it wants to spill into, and reports `#SPILL!` for
|
|
5849
|
+
* every dynamic array in every real file. Registering the declared block as
|
|
5850
|
+
* this anchor's existing spill makes them what they actually are — the anchor's
|
|
5851
|
+
* own territory, free to overwrite — and reuses the ordinary shrink path, so a
|
|
5852
|
+
* result smaller than the declared block clears the leftovers.
|
|
5853
|
+
*
|
|
5854
|
+
* Cost is one Set entry per already-spilled cell, paid once at load. A declared
|
|
5855
|
+
* block far larger than any real spill (a corrupt or hand-written `ref`) is
|
|
5856
|
+
* skipped rather than materialised.
|
|
5857
|
+
*/
|
|
5858
|
+
adoptDeclaredSpill_fn = function(s, sheetName, ref, anchorKey, row2, col) {
|
|
5859
|
+
const declared = __privateGet(this, _model).getSpillRange?.(sheetName, ref);
|
|
5860
|
+
if (!declared || !declared.includes(":")) return;
|
|
5861
|
+
let rect;
|
|
5862
|
+
try {
|
|
5863
|
+
rect = puredocsExcel.parseRangeRef(declared);
|
|
5864
|
+
} catch {
|
|
5865
|
+
return;
|
|
5866
|
+
}
|
|
5867
|
+
if (rect.startRow !== row2 || rect.startColumn !== col) return;
|
|
5868
|
+
const height = rect.endRow - rect.startRow + 1;
|
|
5869
|
+
const width = rect.endColumn - rect.startColumn + 1;
|
|
5870
|
+
if (height < 1 || width < 1) return;
|
|
5871
|
+
if (height * width > MAX_ADOPTED_SPILL_CELLS) return;
|
|
5872
|
+
if (height === 1 && width === 1) return;
|
|
5873
|
+
const members = /* @__PURE__ */ new Set();
|
|
5874
|
+
for (let r = rect.startRow; r <= rect.endRow; r++) {
|
|
5875
|
+
for (let c = rect.startColumn; c <= rect.endColumn; c++) {
|
|
5876
|
+
if (r === row2 && c === col) continue;
|
|
5877
|
+
const key = keyOf(s, puredocsExcel.cellRefFromRowCol(r, c));
|
|
5878
|
+
if (__privateGet(this, _formulas).has(key)) continue;
|
|
5879
|
+
if (__privateGet(this, _spillOwner).has(key)) continue;
|
|
5880
|
+
members.add(key);
|
|
5881
|
+
__privateGet(this, _spillOwner).set(key, anchorKey);
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
if (members.size > 0) __privateGet(this, _spills).set(anchorKey, members);
|
|
5885
|
+
};
|
|
5671
5886
|
/**
|
|
5672
5887
|
* One pass in a caller-supplied order, watching for inversions.
|
|
5673
5888
|
*
|