@rowsncolumns/fast-formula-parser 6.2.41 → 6.2.44

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/esm/index.js CHANGED
@@ -5645,6 +5645,7 @@ const FORMULA_LOCALE_DATA = {
5645
5645
  "ISNUMBER": "ESTNUM",
5646
5646
  "ISO.CEILING": "ISO.PLAFOND",
5647
5647
  "ISODD": "EST.IMPAIR",
5648
+ "ISOMITTED": "ISOMITTED",
5648
5649
  "ISOWEEKNUM": "NO.SEMAINE.ISO",
5649
5650
  "ISPMT": "ISPMT",
5650
5651
  "ISREF": "ESTREF",
@@ -10949,6 +10950,66 @@ const Criteria = Object.freeze({
10949
10950
  return false;
10950
10951
  }
10951
10952
  },
10953
+
10954
+ /**
10955
+ * Excel's array lift for the criteria slot of the criteria aggregates. When
10956
+ * any accepted criterion value is a multi-cell array or range
10957
+ * (`SUMIF(rng,{"x","y"},sum)`, `SUMIFS(sum,rng,"x",rng2,{">1",">3"})`,
10958
+ * `COUNTIF(rng,rng)`), `evaluate` runs once per broadcast element with that
10959
+ * element parsed as a scalar criterion and the results come back as a 2-D
10960
+ * array — so `SUM(COUNTIF(rng,{…}))` / `SUMPRODUCT(SUMIF(…,{…}))` fold it and a
10961
+ * per-element error (`#DIV/0!` from an empty AVERAGEIF slot) stays in its
10962
+ * slot. Vectors broadcast along a unit dimension; a slot a shorter vector
10963
+ * does not reach is `#N/A`. A 1×1 array is a scalar. With no array criterion
10964
+ * `evaluate` runs exactly once with the parsed scalar criteria.
10965
+ * @param {Array} criteriaValues accepted (unparsed) criterion values
10966
+ * @param {(parsed: Array) => *} evaluate
10967
+ */
10968
+ lift: (criteriaValues, evaluate) => {
10969
+ const shapes = criteriaValues.map((v) => {
10970
+ if (!Array.isArray(v)) return null;
10971
+ const shape = [v.length, v[0]?.length ?? 0];
10972
+ return shape[0] === 1 && shape[1] === 1 ? null : shape;
10973
+ });
10974
+ const scalarOf = (v) => (Array.isArray(v) ? v[0][0] : v);
10975
+ if (shapes.every((shape) => shape === null)) {
10976
+ return evaluate(criteriaValues.map((v) => Criteria.parse(scalarOf(v))));
10977
+ }
10978
+ let rows = 1;
10979
+ let cols = 1;
10980
+ for (const shape of shapes) {
10981
+ if (!shape) continue;
10982
+ if (shape[0] !== 1) rows = rows === 1 ? shape[0] : Math.max(rows, shape[0]);
10983
+ if (shape[1] !== 1) cols = cols === 1 ? shape[1] : Math.max(cols, shape[1]);
10984
+ }
10985
+ const MISSING = Symbol("missing");
10986
+ const pick = (v, shape, i, j) => {
10987
+ const r = shape[0] === 1 ? 0 : i;
10988
+ const c = shape[1] === 1 ? 0 : j;
10989
+ return r < v.length && c < v[r].length ? v[r][c] : MISSING;
10990
+ };
10991
+ const out = [];
10992
+ for (let i = 0; i < rows; i++) {
10993
+ const row = [];
10994
+ for (let j = 0; j < cols; j++) {
10995
+ const elements = criteriaValues.map((v, k) =>
10996
+ shapes[k] ? pick(v, shapes[k], i, j) : scalarOf(v),
10997
+ );
10998
+ if (elements.includes(MISSING)) {
10999
+ row.push(FormulaError.NA);
11000
+ continue;
11001
+ }
11002
+ try {
11003
+ row.push(evaluate(elements.map((e) => Criteria.parse(e))));
11004
+ } catch (e) {
11005
+ if (e instanceof FormulaError) row.push(e);
11006
+ else throw e;
11007
+ }
11008
+ }
11009
+ out.push(row);
11010
+ }
11011
+ return out;
11012
+ },
10952
11013
  });
10953
11014
 
10954
11015
  const Address = {
@@ -20674,23 +20735,26 @@ const StatisticalFunctions = {
20674
20735
  range = ranges[0];
20675
20736
  averageRange = ranges[1];
20676
20737
 
20677
- criteria = Criteria.parse(H$7.accept(H$7.retrieveArg(context, criteria)));
20678
-
20679
- let sum = 0,
20680
- cnt = 0;
20681
- range.forEach((row, rowNum) => {
20682
- row.forEach((value, colNum) => {
20683
- const valueToAdd = averageRange[rowNum][colNum];
20684
- throwIfFormulaError(valueToAdd);
20685
- if (typeof valueToAdd !== "number") return;
20686
- if (Criteria.matches(criteria, value)) {
20687
- sum += valueToAdd;
20688
- cnt++;
20689
- }
20738
+ const criteriaValue = H$7.accept(H$7.retrieveArg(context, criteria));
20739
+
20740
+ const averageMatching = (parsed) => {
20741
+ let sum = 0,
20742
+ cnt = 0;
20743
+ range.forEach((row, rowNum) => {
20744
+ row.forEach((value, colNum) => {
20745
+ const valueToAdd = averageRange[rowNum][colNum];
20746
+ throwIfFormulaError(valueToAdd);
20747
+ if (typeof valueToAdd !== "number") return;
20748
+ if (Criteria.matches(parsed, value)) {
20749
+ sum += valueToAdd;
20750
+ cnt++;
20751
+ }
20752
+ });
20690
20753
  });
20691
- });
20692
- if (cnt === 0) throw FormulaError.DIV0;
20693
- return sum / cnt;
20754
+ if (cnt === 0) throw FormulaError.DIV0;
20755
+ return sum / cnt;
20756
+ };
20757
+ return Criteria.lift([criteriaValue], ([parsed]) => averageMatching(parsed));
20694
20758
  },
20695
20759
 
20696
20760
  /**
@@ -20720,36 +20784,42 @@ const StatisticalFunctions = {
20720
20784
  assertSameShape(averageRange, criteriaRange);
20721
20785
  criteriaPairs.push({
20722
20786
  range: criteriaRange,
20723
- criteria: Criteria.parse(H$7.accept(H$7.retrieveArg(context, args[i + 1]))),
20787
+ criteria: H$7.accept(H$7.retrieveArg(context, args[i + 1])),
20724
20788
  });
20725
20789
  }
20726
20790
 
20727
- let sum = 0;
20728
- let count = 0;
20791
+ const averageMatching = (parsedCriteria) => {
20792
+ let sum = 0;
20793
+ let count = 0;
20729
20794
 
20730
- averageRange.forEach((row, rowNum) => {
20731
- row.forEach((valueToAdd, colNum) => {
20732
- throwIfFormulaError(valueToAdd);
20733
- if (typeof valueToAdd !== "number") {
20734
- return;
20735
- }
20795
+ averageRange.forEach((row, rowNum) => {
20796
+ row.forEach((valueToAdd, colNum) => {
20797
+ throwIfFormulaError(valueToAdd);
20798
+ if (typeof valueToAdd !== "number") {
20799
+ return;
20800
+ }
20736
20801
 
20737
- const meetsAllCriteria = criteriaPairs.every(({ range, criteria }) =>
20738
- Criteria.matches(criteria, range[rowNum][colNum]),
20739
- );
20802
+ const meetsAllCriteria = criteriaPairs.every(({ range }, k) =>
20803
+ Criteria.matches(parsedCriteria[k], range[rowNum][colNum]),
20804
+ );
20740
20805
 
20741
- if (meetsAllCriteria) {
20742
- sum += valueToAdd;
20743
- count++;
20744
- }
20806
+ if (meetsAllCriteria) {
20807
+ sum += valueToAdd;
20808
+ count++;
20809
+ }
20810
+ });
20745
20811
  });
20746
- });
20747
20812
 
20748
- if (count === 0) {
20749
- throw FormulaError.DIV0;
20750
- }
20813
+ if (count === 0) {
20814
+ throw FormulaError.DIV0;
20815
+ }
20751
20816
 
20752
- return sum / count;
20817
+ return sum / count;
20818
+ };
20819
+ return Criteria.lift(
20820
+ criteriaPairs.map((pair) => pair.criteria),
20821
+ averageMatching,
20822
+ );
20753
20823
  },
20754
20824
 
20755
20825
  LARGE: (array, kth) => {
@@ -20821,34 +20891,40 @@ const StatisticalFunctions = {
20821
20891
  assertSameShape(maxRange, criteriaRange);
20822
20892
  criteriaPairs.push({
20823
20893
  range: criteriaRange,
20824
- criteria: Criteria.parse(H$7.accept(H$7.retrieveArg(context, args[i + 1]))),
20894
+ criteria: H$7.accept(H$7.retrieveArg(context, args[i + 1])),
20825
20895
  });
20826
20896
  }
20827
20897
 
20828
- let max = Number.NEGATIVE_INFINITY;
20829
- let matchFound = false;
20898
+ const maxMatching = (parsedCriteria) => {
20899
+ let max = Number.NEGATIVE_INFINITY;
20900
+ let matchFound = false;
20830
20901
 
20831
- maxRange.forEach((row, rowNum) => {
20832
- row.forEach((valueToAdd, colNum) => {
20833
- throwIfFormulaError(valueToAdd);
20834
- if (typeof valueToAdd !== "number") {
20835
- return;
20836
- }
20902
+ maxRange.forEach((row, rowNum) => {
20903
+ row.forEach((valueToAdd, colNum) => {
20904
+ throwIfFormulaError(valueToAdd);
20905
+ if (typeof valueToAdd !== "number") {
20906
+ return;
20907
+ }
20837
20908
 
20838
- const meetsAllCriteria = criteriaPairs.every(({ range, criteria }) =>
20839
- Criteria.matches(criteria, range[rowNum][colNum]),
20840
- );
20909
+ const meetsAllCriteria = criteriaPairs.every(({ range }, k) =>
20910
+ Criteria.matches(parsedCriteria[k], range[rowNum][colNum]),
20911
+ );
20841
20912
 
20842
- if (meetsAllCriteria) {
20843
- if (valueToAdd > max) {
20844
- max = valueToAdd;
20913
+ if (meetsAllCriteria) {
20914
+ if (valueToAdd > max) {
20915
+ max = valueToAdd;
20916
+ }
20917
+ matchFound = true;
20845
20918
  }
20846
- matchFound = true;
20847
- }
20919
+ });
20848
20920
  });
20849
- });
20850
20921
 
20851
- return matchFound ? max : 0;
20922
+ return matchFound ? max : 0;
20923
+ };
20924
+ return Criteria.lift(
20925
+ criteriaPairs.map((pair) => pair.criteria),
20926
+ maxMatching,
20927
+ );
20852
20928
  },
20853
20929
 
20854
20930
  MINA: (...params) => {
@@ -20899,34 +20975,40 @@ const StatisticalFunctions = {
20899
20975
  assertSameShape(minRange, criteriaRange);
20900
20976
  criteriaPairs.push({
20901
20977
  range: criteriaRange,
20902
- criteria: Criteria.parse(H$7.accept(H$7.retrieveArg(context, args[i + 1]))),
20978
+ criteria: H$7.accept(H$7.retrieveArg(context, args[i + 1])),
20903
20979
  });
20904
20980
  }
20905
20981
 
20906
- let min = Number.POSITIVE_INFINITY;
20907
- let matchFound = false;
20982
+ const minMatching = (parsedCriteria) => {
20983
+ let min = Number.POSITIVE_INFINITY;
20984
+ let matchFound = false;
20908
20985
 
20909
- minRange.forEach((row, rowNum) => {
20910
- row.forEach((valueToAdd, colNum) => {
20911
- throwIfFormulaError(valueToAdd);
20912
- if (typeof valueToAdd !== "number") {
20913
- return;
20914
- }
20986
+ minRange.forEach((row, rowNum) => {
20987
+ row.forEach((valueToAdd, colNum) => {
20988
+ throwIfFormulaError(valueToAdd);
20989
+ if (typeof valueToAdd !== "number") {
20990
+ return;
20991
+ }
20915
20992
 
20916
- const meetsAllCriteria = criteriaPairs.every(({ range, criteria }) =>
20917
- Criteria.matches(criteria, range[rowNum][colNum]),
20918
- );
20993
+ const meetsAllCriteria = criteriaPairs.every(({ range }, k) =>
20994
+ Criteria.matches(parsedCriteria[k], range[rowNum][colNum]),
20995
+ );
20919
20996
 
20920
- if (meetsAllCriteria) {
20921
- if (valueToAdd < min) {
20922
- min = valueToAdd;
20997
+ if (meetsAllCriteria) {
20998
+ if (valueToAdd < min) {
20999
+ min = valueToAdd;
21000
+ }
21001
+ matchFound = true;
20923
21002
  }
20924
- matchFound = true;
20925
- }
21003
+ });
20926
21004
  });
20927
- });
20928
21005
 
20929
- return matchFound ? min : 0;
21006
+ return matchFound ? min : 0;
21007
+ };
21008
+ return Criteria.lift(
21009
+ criteriaPairs.map((pair) => pair.criteria),
21010
+ minMatching,
21011
+ );
20930
21012
  },
20931
21013
 
20932
21014
  NPV: (rate, ...values) => {
@@ -23227,26 +23309,30 @@ const MathFunctions = {
23227
23309
  range = ranges[0];
23228
23310
  sumRange = ranges[1];
23229
23311
 
23230
- criteria = Criteria.parse(H$6.accept(H$6.retrieveArg(context, criteria)));
23231
-
23232
- let sum = 0;
23233
- const rowCount = range.length;
23234
- for (let r = 0; r < rowCount; r++) {
23235
- const row = range[r];
23236
- const sumRow = sumRange[r];
23237
- const colCount = row.length;
23238
- for (let c = 0; c < colCount; c++) {
23239
- const valueToAdd = sumRow[c];
23240
- if (valueToAdd instanceof FormulaError) {
23241
- throw valueToAdd;
23242
- }
23243
- if (typeof valueToAdd !== "number") continue;
23244
- if (Criteria.matches(criteria, row[c])) {
23245
- sum += valueToAdd;
23312
+ const criteriaValue = H$6.accept(H$6.retrieveArg(context, criteria));
23313
+
23314
+ const sumMatching = (parsed) => {
23315
+ let sum = 0;
23316
+ const rowCount = range.length;
23317
+ for (let r = 0; r < rowCount; r++) {
23318
+ const row = range[r];
23319
+ const sumRow = sumRange[r];
23320
+ const colCount = row.length;
23321
+ for (let c = 0; c < colCount; c++) {
23322
+ const valueToAdd = sumRow[c];
23323
+ if (valueToAdd instanceof FormulaError) {
23324
+ throw valueToAdd;
23325
+ }
23326
+ if (typeof valueToAdd !== "number") continue;
23327
+ if (Criteria.matches(parsed, row[c])) {
23328
+ sum += valueToAdd;
23329
+ }
23246
23330
  }
23247
23331
  }
23248
- }
23249
- return sum;
23332
+ return sum;
23333
+ };
23334
+ // An array criterion spills one total per element (SUMPRODUCT(SUMIF(rng,{…},sum))).
23335
+ return Criteria.lift([criteriaValue], ([parsed]) => sumMatching(parsed));
23250
23336
  },
23251
23337
 
23252
23338
  /**
@@ -23281,37 +23367,43 @@ const MathFunctions = {
23281
23367
  }
23282
23368
  criteriaPairs.push({
23283
23369
  range: criteriaRange,
23284
- criteria: Criteria.parse(H$6.accept(H$6.retrieveArg(context, args[i + 1]))),
23370
+ criteria: H$6.accept(H$6.retrieveArg(context, args[i + 1])),
23285
23371
  });
23286
23372
  }
23287
23373
 
23288
- let sum = 0;
23289
- const rowCount = sumRange.length;
23290
- for (let r = 0; r < rowCount; r++) {
23291
- const row = sumRange[r];
23292
- const colCount = row.length;
23293
- for (let c = 0; c < colCount; c++) {
23294
- const valueToAdd = row[c];
23295
- if (valueToAdd instanceof FormulaError) {
23296
- throw valueToAdd;
23297
- }
23298
- if (typeof valueToAdd !== "number") {
23299
- continue;
23300
- }
23301
- let allCriteriaMet = true;
23302
- for (let i = 0; i < criteriaPairs.length; i++) {
23303
- const { range, criteria } = criteriaPairs[i];
23304
- if (!Criteria.matches(criteria, range[r][c])) {
23305
- allCriteriaMet = false;
23306
- break;
23374
+ const sumMatching = (parsedCriteria) => {
23375
+ let sum = 0;
23376
+ const rowCount = sumRange.length;
23377
+ for (let r = 0; r < rowCount; r++) {
23378
+ const row = sumRange[r];
23379
+ const colCount = row.length;
23380
+ for (let c = 0; c < colCount; c++) {
23381
+ const valueToAdd = row[c];
23382
+ if (valueToAdd instanceof FormulaError) {
23383
+ throw valueToAdd;
23384
+ }
23385
+ if (typeof valueToAdd !== "number") {
23386
+ continue;
23387
+ }
23388
+ let allCriteriaMet = true;
23389
+ for (let i = 0; i < criteriaPairs.length; i++) {
23390
+ if (!Criteria.matches(parsedCriteria[i], criteriaPairs[i].range[r][c])) {
23391
+ allCriteriaMet = false;
23392
+ break;
23393
+ }
23394
+ }
23395
+ if (allCriteriaMet) {
23396
+ sum += valueToAdd;
23307
23397
  }
23308
- }
23309
- if (allCriteriaMet) {
23310
- sum += valueToAdd;
23311
23398
  }
23312
23399
  }
23313
- }
23314
- return sum;
23400
+ return sum;
23401
+ };
23402
+ // Array criteria spill one total per broadcast element.
23403
+ return Criteria.lift(
23404
+ criteriaPairs.map((pair) => pair.criteria),
23405
+ sumMatching,
23406
+ );
23315
23407
  },
23316
23408
 
23317
23409
  SUMPRODUCT: (array1, ...arrays) => {
@@ -23904,7 +23996,23 @@ const LogicalFunctions = {
23904
23996
  // never raw `undefined` (misreported as "Function IFNA is not
23905
23997
  // implemented.").
23906
23998
  const blankToZero = (v) => (v === undefined || v === null ? 0 : v);
23907
- return FormulaError.NA.equals(value.value)
23999
+ const isNa = (cell) => cell instanceof FormulaError && FormulaError.NA.equals(cell);
24000
+
24001
+ // Excel lifts IFNA element-wise over an array / multi-cell range value, like IFERROR:
24002
+ // only the #N/A elements take the fallback, every other value or error passes through
24003
+ // (`IFNA(K1:K3,0)` → {#DIV/0!;0;0}, `SUM(IFNA(rng,0))` sums the non-#N/A cells).
24004
+ const hasValueProp = value && typeof value === 'object' && Object.prototype.hasOwnProperty.call(value, 'value');
24005
+ if (hasValueProp && value.isArray) {
24006
+ const arrayValue = value.value;
24007
+ const handleCell = (cell) => (isNa(cell) ? blankToZero(H$4.accept(valueIfNa)) : blankToZero(cell));
24008
+ if (Array.isArray(arrayValue)) {
24009
+ return arrayValue.map((row) => (Array.isArray(row) ? row.map(handleCell) : handleCell(row)));
24010
+ }
24011
+ return handleCell(arrayValue);
24012
+ }
24013
+
24014
+ const actualValue = hasValueProp ? value.value : value;
24015
+ return isNa(actualValue)
23908
24016
  ? blankToZero(H$4.accept(valueIfNa))
23909
24017
  : blankToZero(H$4.accept(value));
23910
24018
  },
@@ -25583,6 +25691,55 @@ const lookupEquals = (a, b) =>
25583
25691
  ? a.toLowerCase() === b.toLowerCase()
25584
25692
  : a === b;
25585
25693
 
25694
+ /**
25695
+ * The sheet range a lookup table argument was read from, or undefined when the table is an
25696
+ * array (an array constant, a spilled range or another function's result). The matrix the
25697
+ * host returned must fit inside that range — it is a window starting at `from` (a full-column
25698
+ * reference is trimmed to the data extent) — or no cell can be derived from a matrix index.
25699
+ */
25700
+ const rangeOf = (arg, matrix) => {
25701
+ const ref = arg?.position;
25702
+ if (!ref || ref.isSpilledRange || ref.sheetEnd != null) return undefined;
25703
+ let range;
25704
+ if (ref.from && ref.to) {
25705
+ range = ref;
25706
+ } else if (ref.row != null && ref.col != null) {
25707
+ range = {
25708
+ sheet: ref.sheet,
25709
+ from: { row: ref.row, col: ref.col },
25710
+ to: { row: ref.row, col: ref.col },
25711
+ };
25712
+ } else {
25713
+ return undefined;
25714
+ }
25715
+ const fits =
25716
+ matrix.length <= range.to.row - range.from.row + 1 &&
25717
+ (matrix[0]?.length ?? 0) <= range.to.col - range.from.col + 1;
25718
+ return fits ? range : undefined;
25719
+ };
25720
+
25721
+ /**
25722
+ * The cell VLOOKUP/HLOOKUP found. Over a sheet range it is the cell's REFERENCE (as INDEX returns
25723
+ * one), so the result carries `=B2` semantics by construction: a blank cell displays 0, is 0 in
25724
+ * arithmetic and comparison, "" in text — `=VLOOKUP(…)+0` is 0, `=VLOOKUP(…)=""` is TRUE,
25725
+ * `=VLOOKUP(…)&"x"` is "x". The engine has no blank scalar to return instead: the dispatcher reads
25726
+ * a returned `undefined` as "function not implemented" (`#NAME?`) and `null` is the skipped-argument
25727
+ * marker. Over an array there is no cell to point at, so a blank slot coerces to 0 as blank does in
25728
+ * arithmetic. A cell holding `=""` is text, not blank, and comes back as "" either way.
25729
+ */
25730
+ const matchedCell = (tableRange, tableArray, rowIdx, colIdx) => {
25731
+ if (tableRange) {
25732
+ return {
25733
+ ref: {
25734
+ sheet: tableRange.sheet,
25735
+ row: tableRange.from.row + rowIdx,
25736
+ col: tableRange.from.col + colIdx,
25737
+ },
25738
+ };
25739
+ }
25740
+ return tableArray[rowIdx][colIdx] ?? 0;
25741
+ };
25742
+
25586
25743
  /**
25587
25744
  * Exact-mode match for VLOOKUP/HLOOKUP/XLOOKUP/XMATCH: wildcards when the lookup text has
25588
25745
  * them, otherwise case-insensitive equality.
@@ -25849,6 +26006,7 @@ const ReferenceFunctions = {
25849
26006
  HLOOKUP: (lookupValue, tableArray, rowIndexNum, rangeLookup) => {
25850
26007
  // preserve type of lookupValue
25851
26008
  lookupValue = H$2.accept(lookupValue);
26009
+ const tableArg = tableArray;
25852
26010
  try {
25853
26011
  tableArray = H$2.accept(tableArray, Types.ARRAY, undefined, false);
25854
26012
  } catch (e) {
@@ -25860,6 +26018,7 @@ const ReferenceFunctions = {
25860
26018
  }
25861
26019
  throw e;
25862
26020
  }
26021
+ const tableRange = rangeOf(tableArg, tableArray);
25863
26022
  rowIndexNum = H$2.accept(rowIndexNum, Types.NUMBER);
25864
26023
  rangeLookup = H$2.accept(rangeLookup, Types.BOOLEAN, true);
25865
26024
 
@@ -25872,7 +26031,7 @@ const ReferenceFunctions = {
25872
26031
  if (rangeLookup) {
25873
26032
  const index = approximateMatchIndex(tableArray[0], lookupValue);
25874
26033
  if (index === -1) throw FormulaError.NA;
25875
- return tableArray[rowIndexNum - 1][index];
26034
+ return matchedCell(tableRange, tableArray, rowIndexNum - 1, index);
25876
26035
  }
25877
26036
  // exact lookup: case-insensitive, with wildcard support
25878
26037
  else {
@@ -25881,7 +26040,7 @@ const ReferenceFunctions = {
25881
26040
  );
25882
26041
  // the exact match is not found
25883
26042
  if (index === -1) throw FormulaError.NA;
25884
- return tableArray[rowIndexNum - 1][index];
26043
+ return matchedCell(tableRange, tableArray, rowIndexNum - 1, index);
25885
26044
  }
25886
26045
  },
25887
26046
 
@@ -26385,6 +26544,7 @@ const ReferenceFunctions = {
26385
26544
  VLOOKUP: (lookupValue, tableArray, colIndexNum, rangeLookup) => {
26386
26545
  // preserve type of lookupValue
26387
26546
  lookupValue = H$2.accept(lookupValue);
26547
+ const tableArg = tableArray;
26388
26548
  try {
26389
26549
  tableArray = H$2.accept(tableArray, Types.ARRAY, undefined, false);
26390
26550
  } catch (e) {
@@ -26396,6 +26556,7 @@ const ReferenceFunctions = {
26396
26556
  }
26397
26557
  throw e;
26398
26558
  }
26559
+ const tableRange = rangeOf(tableArg, tableArray);
26399
26560
  colIndexNum = H$2.accept(colIndexNum, Types.NUMBER);
26400
26561
  rangeLookup = H$2.accept(rangeLookup, Types.BOOLEAN, true);
26401
26562
 
@@ -26411,7 +26572,7 @@ const ReferenceFunctions = {
26411
26572
  lookupValue,
26412
26573
  );
26413
26574
  if (index === -1) throw FormulaError.NA;
26414
- return tableArray[index][colIndexNum - 1] ?? "";
26575
+ return matchedCell(tableRange, tableArray, index, colIndexNum - 1);
26415
26576
  }
26416
26577
  // exact lookup: case-insensitive, with wildcard support
26417
26578
  else {
@@ -26420,7 +26581,7 @@ const ReferenceFunctions = {
26420
26581
  );
26421
26582
  // the exact match is not found
26422
26583
  if (index === -1) throw FormulaError.NA;
26423
- return tableArray[index][colIndexNum - 1] ?? "";
26584
+ return matchedCell(tableRange, tableArray, index, colIndexNum - 1);
26424
26585
  }
26425
26586
  },
26426
26587
 
@@ -50561,6 +50722,16 @@ class Parser extends EmbeddedActionsParser {
50561
50722
  ) {
50562
50723
  return arr[0][0];
50563
50724
  }
50725
+ // Excel refuses a ragged constant (`{1,2;3}`) at entry — every row must have the width
50726
+ // of the first. A parse-level `#ERROR!`, like any other syntax error the host stores as
50727
+ // the cell value (rowsncolumns/spreadsheet#939 G-02).
50728
+ const width = arr[0].length;
50729
+ const ragged = arr.findIndex((row) => row.length !== width);
50730
+ if (ragged !== -1) {
50731
+ throw FormulaError.ERROR(
50732
+ `Array constant rows must have the same length: row 1 has ${width} element(s), row ${ragged + 1} has ${arr[ragged].length}`
50733
+ );
50734
+ }
50564
50735
  const array = this.utils.toArray(arr);
50565
50736
  return context.async
50566
50737
  ? this.utils.settleArrayItems(array, $.position)
@@ -52980,8 +53151,13 @@ const normalizeNumberResult = (value, precision, epsilon, excelPrecision) => {
52980
53151
  * @param {*} value
52981
53152
  * @return {boolean}
52982
53153
  */
53154
+ // A name's definition text: `=…` (the OOXML `<definedName>` content), or a bare `LAMBDA(…)` —
53155
+ // the shape Excel's Name Manager and the host's "Text or Formula" names store a lambda in, which
53156
+ // the call path (`Dbl(21)`) already accepts without the leading `=` (spreadsheet#939 G-07).
53157
+ const BARE_LAMBDA_TEXT = /^(_xlfn\.)?LAMBDA\s*\(/i;
52983
53158
  const isNamedFormulaText = (value) =>
52984
- typeof value === "string" && value.trimStart().startsWith("=");
53159
+ typeof value === "string" &&
53160
+ (value.trimStart().startsWith("=") || BARE_LAMBDA_TEXT.test(value.trimStart()));
52985
53161
 
52986
53162
  class FormulaParser {
52987
53163
  /**
@@ -53350,7 +53526,8 @@ class FormulaParser {
53350
53526
  if (this.namedFormulaStack.has(key)) {
53351
53527
  return FormulaError.REF;
53352
53528
  }
53353
- const source = text.trim().slice(1).trim();
53529
+ const trimmed = text.trim();
53530
+ const source = (trimmed.startsWith("=") ? trimmed.slice(1) : trimmed).trim();
53354
53531
  if (source.length === 0) {
53355
53532
  return FormulaError.NAME;
53356
53533
  }
@@ -53359,12 +53536,18 @@ class FormulaParser {
53359
53536
  this.namedFormulaParser.namedFormulaStack = this.namedFormulaStack;
53360
53537
  }
53361
53538
  this.namedFormulaStack.add(key);
53539
+ // A name bound to a LAMBDA (`Dbl` = `=LAMBDA(x,x*2)`) is the lambda VALUE wherever the
53540
+ // name is read — `MAP(A1:A3,Dbl)`, `LET(f,Dbl,f(3))` — so the definition parser must hand
53541
+ // it back instead of turning it into `#CALC!`; the referencing cell's own
53542
+ // `checkFormulaResult` still makes a bare `=Dbl` `#CALC!` (spreadsheet#939 G-07).
53543
+ this.namedFormulaParser.keepLambdaResult = true;
53362
53544
  try {
53363
53545
  return this.namedFormulaParser.parse(source, this.position, true);
53364
53546
  } catch (e) {
53365
53547
  if (e instanceof FormulaError) return e;
53366
53548
  return FormulaError.NAME;
53367
53549
  } finally {
53550
+ this.namedFormulaParser.keepLambdaResult = false;
53368
53551
  this.namedFormulaStack.delete(key);
53369
53552
  }
53370
53553
  }