@rowsncolumns/fast-formula-parser 6.2.42 → 6.2.45
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/cjs/index.js +89 -6
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +89 -6
- package/dist/esm/index.js.map +1 -1
- package/index.d.ts +12 -0
- package/package.json +4 -3
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",
|
|
@@ -25690,6 +25691,55 @@ const lookupEquals = (a, b) =>
|
|
|
25690
25691
|
? a.toLowerCase() === b.toLowerCase()
|
|
25691
25692
|
: a === b;
|
|
25692
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
|
+
|
|
25693
25743
|
/**
|
|
25694
25744
|
* Exact-mode match for VLOOKUP/HLOOKUP/XLOOKUP/XMATCH: wildcards when the lookup text has
|
|
25695
25745
|
* them, otherwise case-insensitive equality.
|
|
@@ -25956,6 +26006,7 @@ const ReferenceFunctions = {
|
|
|
25956
26006
|
HLOOKUP: (lookupValue, tableArray, rowIndexNum, rangeLookup) => {
|
|
25957
26007
|
// preserve type of lookupValue
|
|
25958
26008
|
lookupValue = H$2.accept(lookupValue);
|
|
26009
|
+
const tableArg = tableArray;
|
|
25959
26010
|
try {
|
|
25960
26011
|
tableArray = H$2.accept(tableArray, Types.ARRAY, undefined, false);
|
|
25961
26012
|
} catch (e) {
|
|
@@ -25967,6 +26018,7 @@ const ReferenceFunctions = {
|
|
|
25967
26018
|
}
|
|
25968
26019
|
throw e;
|
|
25969
26020
|
}
|
|
26021
|
+
const tableRange = rangeOf(tableArg, tableArray);
|
|
25970
26022
|
rowIndexNum = H$2.accept(rowIndexNum, Types.NUMBER);
|
|
25971
26023
|
rangeLookup = H$2.accept(rangeLookup, Types.BOOLEAN, true);
|
|
25972
26024
|
|
|
@@ -25979,7 +26031,7 @@ const ReferenceFunctions = {
|
|
|
25979
26031
|
if (rangeLookup) {
|
|
25980
26032
|
const index = approximateMatchIndex(tableArray[0], lookupValue);
|
|
25981
26033
|
if (index === -1) throw FormulaError.NA;
|
|
25982
|
-
return tableArray
|
|
26034
|
+
return matchedCell(tableRange, tableArray, rowIndexNum - 1, index);
|
|
25983
26035
|
}
|
|
25984
26036
|
// exact lookup: case-insensitive, with wildcard support
|
|
25985
26037
|
else {
|
|
@@ -25988,7 +26040,7 @@ const ReferenceFunctions = {
|
|
|
25988
26040
|
);
|
|
25989
26041
|
// the exact match is not found
|
|
25990
26042
|
if (index === -1) throw FormulaError.NA;
|
|
25991
|
-
return tableArray
|
|
26043
|
+
return matchedCell(tableRange, tableArray, rowIndexNum - 1, index);
|
|
25992
26044
|
}
|
|
25993
26045
|
},
|
|
25994
26046
|
|
|
@@ -26492,6 +26544,7 @@ const ReferenceFunctions = {
|
|
|
26492
26544
|
VLOOKUP: (lookupValue, tableArray, colIndexNum, rangeLookup) => {
|
|
26493
26545
|
// preserve type of lookupValue
|
|
26494
26546
|
lookupValue = H$2.accept(lookupValue);
|
|
26547
|
+
const tableArg = tableArray;
|
|
26495
26548
|
try {
|
|
26496
26549
|
tableArray = H$2.accept(tableArray, Types.ARRAY, undefined, false);
|
|
26497
26550
|
} catch (e) {
|
|
@@ -26503,6 +26556,7 @@ const ReferenceFunctions = {
|
|
|
26503
26556
|
}
|
|
26504
26557
|
throw e;
|
|
26505
26558
|
}
|
|
26559
|
+
const tableRange = rangeOf(tableArg, tableArray);
|
|
26506
26560
|
colIndexNum = H$2.accept(colIndexNum, Types.NUMBER);
|
|
26507
26561
|
rangeLookup = H$2.accept(rangeLookup, Types.BOOLEAN, true);
|
|
26508
26562
|
|
|
@@ -26518,7 +26572,7 @@ const ReferenceFunctions = {
|
|
|
26518
26572
|
lookupValue,
|
|
26519
26573
|
);
|
|
26520
26574
|
if (index === -1) throw FormulaError.NA;
|
|
26521
|
-
return tableArray
|
|
26575
|
+
return matchedCell(tableRange, tableArray, index, colIndexNum - 1);
|
|
26522
26576
|
}
|
|
26523
26577
|
// exact lookup: case-insensitive, with wildcard support
|
|
26524
26578
|
else {
|
|
@@ -26527,7 +26581,7 @@ const ReferenceFunctions = {
|
|
|
26527
26581
|
);
|
|
26528
26582
|
// the exact match is not found
|
|
26529
26583
|
if (index === -1) throw FormulaError.NA;
|
|
26530
|
-
return tableArray
|
|
26584
|
+
return matchedCell(tableRange, tableArray, index, colIndexNum - 1);
|
|
26531
26585
|
}
|
|
26532
26586
|
},
|
|
26533
26587
|
|
|
@@ -53173,6 +53227,11 @@ class FormulaParser {
|
|
|
53173
53227
|
);
|
|
53174
53228
|
this.onRange = config.onRange;
|
|
53175
53229
|
this.onCell = config.onCell;
|
|
53230
|
+
// Observer of every cell / range the evaluation actually reads, with the
|
|
53231
|
+
// cell being evaluated. `INDIRECT` / `OFFSET` targets only exist at run
|
|
53232
|
+
// time, so this is how a host learns the dynamic precedents of a formula
|
|
53233
|
+
// (the dependency parser cannot see them).
|
|
53234
|
+
this.onReferenceResolved = config.onReferenceResolved;
|
|
53176
53235
|
// Optional host callback reporting row visibility for SUBTOTAL:
|
|
53177
53236
|
// ({sheet, row}) => {hiddenByFilter?: boolean, hiddenByUser?: boolean}
|
|
53178
53237
|
this.onRowHidden = config.onRowHidden;
|
|
@@ -53322,18 +53381,36 @@ class FormulaParser {
|
|
|
53322
53381
|
const bounds = this.onSpilledRange(ref);
|
|
53323
53382
|
if (bounds instanceof FormulaError) return bounds;
|
|
53324
53383
|
if (bounds && bounds.from && bounds.to) {
|
|
53325
|
-
|
|
53384
|
+
const spilled = {
|
|
53326
53385
|
from: bounds.from,
|
|
53327
53386
|
to: bounds.to,
|
|
53328
53387
|
sheet: bounds.sheet ?? ref.sheet,
|
|
53329
|
-
}
|
|
53388
|
+
};
|
|
53389
|
+
this.noteReferenceResolved("range", spilled, finalPosition);
|
|
53390
|
+
return this.onRange(spilled);
|
|
53330
53391
|
}
|
|
53331
53392
|
}
|
|
53332
53393
|
return FormulaError.REF;
|
|
53333
53394
|
}
|
|
53395
|
+
this.noteReferenceResolved("cell", ref, finalPosition);
|
|
53334
53396
|
return this.onCell(ref);
|
|
53335
53397
|
}
|
|
53336
53398
|
|
|
53399
|
+
/**
|
|
53400
|
+
* Tell the host which reference an evaluation is about to read.
|
|
53401
|
+
* @param {"cell"|"range"} kind
|
|
53402
|
+
* @param ref - the resolved reference (sheet already defaulted)
|
|
53403
|
+
* @param position - the cell being evaluated, when known
|
|
53404
|
+
*/
|
|
53405
|
+
noteReferenceResolved(kind, ref, position) {
|
|
53406
|
+
if (typeof this.onReferenceResolved !== "function") return;
|
|
53407
|
+
try {
|
|
53408
|
+
this.onReferenceResolved({ kind, ref, position });
|
|
53409
|
+
} catch {
|
|
53410
|
+
// An observer must never break the evaluation.
|
|
53411
|
+
}
|
|
53412
|
+
}
|
|
53413
|
+
|
|
53337
53414
|
/**
|
|
53338
53415
|
* Get values from the range reference.
|
|
53339
53416
|
* @param ref
|
|
@@ -53348,6 +53425,7 @@ class FormulaParser {
|
|
|
53348
53425
|
if (ref.sheetEnd != null) {
|
|
53349
53426
|
return this.get3DRange(ref, true);
|
|
53350
53427
|
}
|
|
53428
|
+
this.noteReferenceResolved("range", ref, finalPosition);
|
|
53351
53429
|
return this.onRange(ref);
|
|
53352
53430
|
}
|
|
53353
53431
|
|
|
@@ -53384,6 +53462,11 @@ class FormulaParser {
|
|
|
53384
53462
|
for (const sheet of sheets) {
|
|
53385
53463
|
const { sheetEnd: _ignored, ...perSheet } = ref;
|
|
53386
53464
|
perSheet.sheet = sheet;
|
|
53465
|
+
this.noteReferenceResolved(
|
|
53466
|
+
isRange ? "range" : "cell",
|
|
53467
|
+
perSheet,
|
|
53468
|
+
this.position,
|
|
53469
|
+
);
|
|
53387
53470
|
const value = isRange ? this.onRange(perSheet) : this.onCell(perSheet);
|
|
53388
53471
|
if (value instanceof FormulaError) return value;
|
|
53389
53472
|
if (Array.isArray(value)) {
|