@devmm/puredocs-excel-formula 1.0.7 → 1.0.8
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 +169 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +169 -22
- 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,19 @@ 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();
|
|
762
810
|
var BlankNode = class extends FormulaNode {
|
|
763
811
|
evaluate() {
|
|
764
812
|
return FormulaValue.blank;
|
|
@@ -1036,7 +1084,7 @@ var CallNode = class extends FormulaNode {
|
|
|
1036
1084
|
};
|
|
1037
1085
|
|
|
1038
1086
|
// 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;
|
|
1087
|
+
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
1088
|
var FormulaParser = class {
|
|
1041
1089
|
constructor(tokens) {
|
|
1042
1090
|
__privateAdd(this, _FormulaParser_instances);
|
|
@@ -1047,7 +1095,7 @@ var FormulaParser = class {
|
|
|
1047
1095
|
parse() {
|
|
1048
1096
|
__privateSet(this, _pos2, 0);
|
|
1049
1097
|
const node = __privateMethod(this, _FormulaParser_instances, parseComparison_fn).call(this);
|
|
1050
|
-
if (__privateGet(this, _FormulaParser_instances, current_get).type !==
|
|
1098
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type !== 31 /* EOF */) {
|
|
1051
1099
|
throw new FormulaException(
|
|
1052
1100
|
`Unexpected token '${__privateGet(this, _FormulaParser_instances, current_get).value}' at position ${__privateGet(this, _FormulaParser_instances, current_get).position}.`
|
|
1053
1101
|
);
|
|
@@ -1059,7 +1107,7 @@ _tokens = new WeakMap();
|
|
|
1059
1107
|
_pos2 = new WeakMap();
|
|
1060
1108
|
_FormulaParser_instances = new WeakSet();
|
|
1061
1109
|
current_get = function() {
|
|
1062
|
-
return __privateGet(this, _tokens)[__privateGet(this, _pos2)] ?? { type:
|
|
1110
|
+
return __privateGet(this, _tokens)[__privateGet(this, _pos2)] ?? { type: 31 /* EOF */, value: "", position: -1 };
|
|
1063
1111
|
};
|
|
1064
1112
|
advance_fn = function() {
|
|
1065
1113
|
const t = __privateGet(this, _FormulaParser_instances, current_get);
|
|
@@ -1174,7 +1222,7 @@ parsePrimary_fn = function() {
|
|
|
1174
1222
|
case 2 /* Boolean */:
|
|
1175
1223
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1176
1224
|
return new BooleanNode(token.value === "TRUE");
|
|
1177
|
-
case
|
|
1225
|
+
case 30 /* ErrorLiteral */:
|
|
1178
1226
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1179
1227
|
return new ErrorNode(FormulaValue.error(FormulaValue.errorFromString(token.value)));
|
|
1180
1228
|
case 3 /* CellReference */: {
|
|
@@ -1213,6 +1261,8 @@ parsePrimary_fn = function() {
|
|
|
1213
1261
|
return new SpillReferenceNode(token.value);
|
|
1214
1262
|
case 5 /* Function */:
|
|
1215
1263
|
return __privateMethod(this, _FormulaParser_instances, parseFunction_fn).call(this);
|
|
1264
|
+
case 27 /* LeftBrace */:
|
|
1265
|
+
return __privateMethod(this, _FormulaParser_instances, parseArrayLiteral_fn).call(this);
|
|
1216
1266
|
case 6 /* LeftParen */: {
|
|
1217
1267
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1218
1268
|
const expr = __privateMethod(this, _FormulaParser_instances, parseComparison_fn).call(this);
|
|
@@ -1247,6 +1297,79 @@ tryParseFullRange_fn = function(startText, sheetName) {
|
|
|
1247
1297
|
const hi = Math.max(start.index, end.index);
|
|
1248
1298
|
return start.kind === "col" ? new FullRangeReferenceNode(lo, hi, null, null, sheetName) : new FullRangeReferenceNode(null, null, lo, hi, sheetName);
|
|
1249
1299
|
};
|
|
1300
|
+
/**
|
|
1301
|
+
* Parses an array constant: `{1,2;3,4}` — ',' separates columns, ';' rows.
|
|
1302
|
+
*
|
|
1303
|
+
* Excel permits only literals inside one (no references, no calls, not even
|
|
1304
|
+
* arithmetic beyond a leading sign), so the value is fully determined here and
|
|
1305
|
+
* is materialised once into the node instead of being rebuilt on every eval.
|
|
1306
|
+
*
|
|
1307
|
+
* A ragged constant is rejected rather than padded: Excel refuses to accept
|
|
1308
|
+
* one at entry, so a file cannot legitimately contain it, and silently filling
|
|
1309
|
+
* the gaps would invent data the author never wrote.
|
|
1310
|
+
*/
|
|
1311
|
+
parseArrayLiteral_fn = function() {
|
|
1312
|
+
const open = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1313
|
+
const rows2 = [];
|
|
1314
|
+
let row2 = [];
|
|
1315
|
+
for (; ; ) {
|
|
1316
|
+
row2.push(__privateMethod(this, _FormulaParser_instances, parseArrayElement_fn).call(this));
|
|
1317
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 8 /* Comma */) {
|
|
1318
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 29 /* Semicolon */) {
|
|
1322
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1323
|
+
rows2.push(row2);
|
|
1324
|
+
row2 = [];
|
|
1325
|
+
continue;
|
|
1326
|
+
}
|
|
1327
|
+
break;
|
|
1328
|
+
}
|
|
1329
|
+
rows2.push(row2);
|
|
1330
|
+
__privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 28 /* RightBrace */);
|
|
1331
|
+
const cols = rows2[0].length;
|
|
1332
|
+
if (rows2.some((r) => r.length !== cols)) {
|
|
1333
|
+
throw new FormulaException(
|
|
1334
|
+
`Array constant at position ${open.position} has rows of differing lengths.`
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
const array = new ArrayValue(rows2.length, cols);
|
|
1338
|
+
for (let r = 0; r < rows2.length; r++) {
|
|
1339
|
+
for (let c = 0; c < cols; c++) array.set(r, c, rows2[r][c]);
|
|
1340
|
+
}
|
|
1341
|
+
return new ArrayLiteralNode(array);
|
|
1342
|
+
};
|
|
1343
|
+
/** One element of an array constant: a literal, optionally signed. */
|
|
1344
|
+
parseArrayElement_fn = function() {
|
|
1345
|
+
let negate = false;
|
|
1346
|
+
let signed = false;
|
|
1347
|
+
while (__privateGet(this, _FormulaParser_instances, current_get).type === 10 /* Minus */ || __privateGet(this, _FormulaParser_instances, current_get).type === 9 /* Plus */) {
|
|
1348
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 10 /* Minus */) negate = !negate;
|
|
1349
|
+
signed = true;
|
|
1350
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1351
|
+
}
|
|
1352
|
+
const token = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1353
|
+
switch (token.type) {
|
|
1354
|
+
case 0 /* Number */: {
|
|
1355
|
+
const n = parseFloat(token.value);
|
|
1356
|
+
if (isNaN(n)) throw new FormulaException(`Invalid number: ${token.value}`);
|
|
1357
|
+
return FormulaValue.number(negate ? -n : n);
|
|
1358
|
+
}
|
|
1359
|
+
case 1 /* String */:
|
|
1360
|
+
if (signed) break;
|
|
1361
|
+
return FormulaValue.text(token.value);
|
|
1362
|
+
case 2 /* Boolean */:
|
|
1363
|
+
if (signed) break;
|
|
1364
|
+
return FormulaValue.boolean(token.value === "TRUE");
|
|
1365
|
+
case 30 /* ErrorLiteral */:
|
|
1366
|
+
if (signed) break;
|
|
1367
|
+
return FormulaValue.error(FormulaValue.errorFromString(token.value));
|
|
1368
|
+
}
|
|
1369
|
+
throw new FormulaException(
|
|
1370
|
+
`Array constants accept only literal values; got '${token.value}' at position ${token.position}.`
|
|
1371
|
+
);
|
|
1372
|
+
};
|
|
1250
1373
|
parseFunction_fn = function() {
|
|
1251
1374
|
const nameToken = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1252
1375
|
__privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 6 /* LeftParen */);
|
|
@@ -4063,22 +4186,46 @@ function unique(a, c) {
|
|
|
4063
4186
|
const out = fromRows(result);
|
|
4064
4187
|
return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
|
|
4065
4188
|
}
|
|
4189
|
+
var numVector = (a, c, i, dflt) => {
|
|
4190
|
+
if (i >= a.length) return { ok: true, v: [dflt] };
|
|
4191
|
+
const val = a[i].evaluate(c);
|
|
4192
|
+
if (val.isError) return { ok: false, e: val };
|
|
4193
|
+
const out = [];
|
|
4194
|
+
for (const item of val.isArray ? val.arrayVal.values() : [val]) {
|
|
4195
|
+
if (item.isBlank) continue;
|
|
4196
|
+
const d = item.tryAsDouble();
|
|
4197
|
+
if (!d.ok) return { ok: false, e: FormulaValue.errorValue };
|
|
4198
|
+
out.push(d.value);
|
|
4199
|
+
}
|
|
4200
|
+
return out.length > 0 ? { ok: true, v: out } : { ok: true, v: [dflt] };
|
|
4201
|
+
};
|
|
4066
4202
|
function sort(a, c) {
|
|
4067
4203
|
const v = a[0].evaluate(c);
|
|
4068
4204
|
if (v.isError) return v;
|
|
4069
|
-
const
|
|
4070
|
-
if (!
|
|
4071
|
-
const
|
|
4072
|
-
if (!
|
|
4205
|
+
const indices = numVector(a, c, 1, 1);
|
|
4206
|
+
if (!indices.ok) return indices.e;
|
|
4207
|
+
const orders = numVector(a, c, 2, 1);
|
|
4208
|
+
if (!orders.ok) return orders.e;
|
|
4073
4209
|
const byCol2 = a.length > 3 ? a[3].evaluate(c).coerceToBool().booleanValue : false;
|
|
4210
|
+
if (orders.v.length !== 1 && orders.v.length !== indices.v.length) {
|
|
4211
|
+
return FormulaValue.errorValue;
|
|
4212
|
+
}
|
|
4074
4213
|
const base = byCol2 ? asArray2(v).transpose() : asArray2(v);
|
|
4075
4214
|
const rows2 = toRows(base);
|
|
4076
|
-
const
|
|
4077
|
-
|
|
4078
|
-
|
|
4215
|
+
const keys = indices.v.map((n, i) => ({
|
|
4216
|
+
col: Math.trunc(n) - 1,
|
|
4217
|
+
dir: (orders.v.length === 1 ? orders.v[0] : orders.v[i]) < 0 ? -1 : 1
|
|
4218
|
+
}));
|
|
4219
|
+
const width = rows2.length > 0 ? rows2[0].length : 0;
|
|
4220
|
+
if (rows2.length > 0 && keys.some((k) => k.col < 0 || k.col >= width)) {
|
|
4221
|
+
return FormulaValue.errorValue;
|
|
4222
|
+
}
|
|
4079
4223
|
const sorted = rows2.map((r, i) => ({ r, i })).sort((x, y) => {
|
|
4080
|
-
const
|
|
4081
|
-
|
|
4224
|
+
for (const k of keys) {
|
|
4225
|
+
const cmp = FormulaValue.compare(x.r[k.col], y.r[k.col]) * k.dir;
|
|
4226
|
+
if (cmp !== 0) return cmp;
|
|
4227
|
+
}
|
|
4228
|
+
return x.i - y.i;
|
|
4082
4229
|
}).map((o) => o.r);
|
|
4083
4230
|
const out = fromRows(sorted);
|
|
4084
4231
|
return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
|