@pixerate/editor 0.24.0 → 0.26.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/README.md +101 -1
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/agent/index.cjs +175 -11
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +18 -4
- package/dist/agent/index.d.ts +18 -4
- package/dist/agent/index.js +168 -4
- package/dist/agent/index.js.map +1 -1
- package/dist/{arrangeShortcuts-B8O3f8-P.d.cts → arrange-BICutYPk.d.cts} +243 -29
- package/dist/{arrangeShortcuts-sLlD2rSX.d.ts → arrange-BICutYPk.d.ts} +243 -29
- package/dist/builtins-CdPcLwwF.d.cts +176 -0
- package/dist/builtins-Chj6zv_O.d.ts +176 -0
- package/dist/canvas/index.cjs +8588 -1857
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +1847 -9
- package/dist/canvas/index.d.ts +1847 -9
- package/dist/canvas/index.js +8577 -1846
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-3CC7OPUQ.cjs +831 -0
- package/dist/chunk-3CC7OPUQ.cjs.map +1 -0
- package/dist/chunk-4BVP64JJ.cjs +1406 -0
- package/dist/chunk-4BVP64JJ.cjs.map +1 -0
- package/dist/chunk-BVP3TSLH.cjs +69 -0
- package/dist/chunk-BVP3TSLH.cjs.map +1 -0
- package/dist/{chunk-OLZJXL4R.cjs → chunk-CS6NZWWA.cjs} +59 -25
- package/dist/chunk-CS6NZWWA.cjs.map +1 -0
- package/dist/{chunk-MCSJFWAO.cjs → chunk-EWCOFSCQ.cjs} +685 -100
- package/dist/chunk-EWCOFSCQ.cjs.map +1 -0
- package/dist/chunk-HDYMLS3X.cjs +409 -0
- package/dist/chunk-HDYMLS3X.cjs.map +1 -0
- package/dist/chunk-HRMEDT2L.js +1406 -0
- package/dist/chunk-HRMEDT2L.js.map +1 -0
- package/dist/{chunk-D27YFC46.cjs → chunk-IFHKKVXH.cjs} +382 -63
- package/dist/chunk-IFHKKVXH.cjs.map +1 -0
- package/dist/{chunk-PGNZKFQS.js → chunk-JOIREKL7.js} +137 -58
- package/dist/chunk-JOIREKL7.js.map +1 -0
- package/dist/chunk-KRGXUQ2Y.js +1696 -0
- package/dist/chunk-KRGXUQ2Y.js.map +1 -0
- package/dist/{chunk-3LV6RRPH.js → chunk-MHEPLBFB.js} +2 -2
- package/dist/chunk-MR2764X2.js +831 -0
- package/dist/chunk-MR2764X2.js.map +1 -0
- package/dist/{chunk-XNORY7M6.cjs → chunk-NFVBT3UZ.cjs} +166 -87
- package/dist/chunk-NFVBT3UZ.cjs.map +1 -0
- package/dist/chunk-NQF32I6Z.cjs +6478 -0
- package/dist/chunk-NQF32I6Z.cjs.map +1 -0
- package/dist/chunk-O5RYH2PG.js +69 -0
- package/dist/chunk-O5RYH2PG.js.map +1 -0
- package/dist/{chunk-ITBSKI26.js → chunk-Q2OHWD2A.js} +1064 -360
- package/dist/chunk-Q2OHWD2A.js.map +1 -0
- package/dist/{chunk-FODNLX5N.js → chunk-QJSOZFCZ.js} +38 -4
- package/dist/chunk-QJSOZFCZ.js.map +1 -0
- package/dist/{chunk-SML64VJG.cjs → chunk-RRREAOVM.cjs} +4 -4
- package/dist/{chunk-SML64VJG.cjs.map → chunk-RRREAOVM.cjs.map} +1 -1
- package/dist/{chunk-5S3Z4PPF.js → chunk-SWE3NPOU.js} +347 -28
- package/dist/chunk-SWE3NPOU.js.map +1 -0
- package/dist/chunk-TXXNBDQX.cjs +1696 -0
- package/dist/chunk-TXXNBDQX.cjs.map +1 -0
- package/dist/chunk-UBCFIABU.js +409 -0
- package/dist/chunk-UBCFIABU.js.map +1 -0
- package/dist/chunk-X7HXL2SB.js +6478 -0
- package/dist/chunk-X7HXL2SB.js.map +1 -0
- package/dist/{chunk-SBCZ6RN5.cjs → chunk-YOMYSFJU.cjs} +1163 -459
- package/dist/chunk-YOMYSFJU.cjs.map +1 -0
- package/dist/{chunk-3CN2WD3X.js → chunk-ZRNHGAQT.js} +661 -76
- package/dist/chunk-ZRNHGAQT.js.map +1 -0
- package/dist/commands/index.cjs +425 -4
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.d.cts +39 -77
- package/dist/commands/index.d.ts +39 -77
- package/dist/commands/index.js +424 -3
- package/dist/commands/index.js.map +1 -1
- package/dist/compare/index.cjs +3 -3
- package/dist/compare/index.d.cts +1 -1
- package/dist/compare/index.d.ts +1 -1
- package/dist/compare/index.js +2 -2
- package/dist/controller-CxXGOEmy.d.cts +2038 -0
- package/dist/controller-CxXGOEmy.d.ts +2038 -0
- package/dist/{documents-BQjzJNe2.d.ts → documents-CHT1U5Vs.d.ts} +10 -0
- package/dist/{documents-KRTPhvst.d.cts → documents-Gp7wb0Me.d.cts} +10 -0
- package/dist/export/index.cjs +36 -0
- package/dist/export/index.cjs.map +1 -0
- package/dist/export/index.d.cts +243 -0
- package/dist/export/index.d.ts +243 -0
- package/dist/export/index.js +36 -0
- package/dist/export/index.js.map +1 -0
- package/dist/extensions/index.cjs +101 -6
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +18 -5
- package/dist/extensions/index.d.ts +18 -5
- package/dist/extensions/index.js +101 -6
- package/dist/generation/index.cjs +1013 -69
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.d.cts +474 -13
- package/dist/generation/index.d.ts +474 -13
- package/dist/generation/index.js +976 -32
- package/dist/generation/index.js.map +1 -1
- package/dist/image-editor/index.cjs +3385 -1275
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +693 -128
- package/dist/image-editor/index.d.ts +693 -128
- package/dist/image-editor/index.js +3325 -1215
- package/dist/image-editor/index.js.map +1 -1
- package/dist/{index-fJrlkJCd.d.ts → index-B273Zllk.d.ts} +500 -7
- package/dist/{index-BhJBSm2d.d.cts → index-jcJwmBYt.d.cts} +500 -7
- package/dist/index.cjs +1603 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -27
- package/dist/index.d.ts +154 -27
- package/dist/index.js +1584 -26
- package/dist/index.js.map +1 -1
- package/dist/mention-text-CDnCFd3c.d.cts +53 -0
- package/dist/mention-text-CDnCFd3c.d.ts +53 -0
- package/dist/model-C1zApQM8.d.cts +219 -0
- package/dist/model-C1zApQM8.d.ts +219 -0
- package/dist/persistence/index.d.cts +3 -120
- package/dist/persistence/index.d.ts +3 -120
- package/dist/registry-CED5C0lA.d.cts +204 -0
- package/dist/registry-CED5C0lA.d.ts +204 -0
- package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.cts} +12 -1
- package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.ts} +12 -1
- package/dist/{registry-D32LpIc4.d.cts → registry-D0GADKc7.d.cts} +1 -1
- package/dist/{registry-evKTP4ls.d.ts → registry-DhVUZioj.d.ts} +1 -1
- package/dist/serializers/index.cjs +12 -4
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.d.cts +41 -13
- package/dist/serializers/index.d.ts +41 -13
- package/dist/serializers/index.js +12 -4
- package/dist/spreadsheet/index.cjs +157 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +534 -11
- package/dist/spreadsheet/index.d.ts +534 -11
- package/dist/spreadsheet/index.js +157 -3
- package/dist/storage-BpFfspHm.d.cts +120 -0
- package/dist/storage-BpFfspHm.d.ts +120 -0
- package/dist/tokens/index.cjs +115 -0
- package/dist/tokens/index.cjs.map +1 -0
- package/dist/tokens/index.d.cts +212 -0
- package/dist/tokens/index.d.ts +212 -0
- package/dist/tokens/index.js +115 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/{track-changes-BK5sVf4_.d.cts → track-changes-D_VAPELi.d.cts} +95 -11
- package/dist/{track-changes-CDjCrwKe.d.ts → track-changes-Db_fGEvx.d.ts} +95 -11
- package/package.json +15 -1
- package/dist/chunk-3CN2WD3X.js.map +0 -1
- package/dist/chunk-5S3Z4PPF.js.map +0 -1
- package/dist/chunk-D27YFC46.cjs.map +0 -1
- package/dist/chunk-FODNLX5N.js.map +0 -1
- package/dist/chunk-ITBSKI26.js.map +0 -1
- package/dist/chunk-MA2F5KHF.cjs +0 -3223
- package/dist/chunk-MA2F5KHF.cjs.map +0 -1
- package/dist/chunk-MCSJFWAO.cjs.map +0 -1
- package/dist/chunk-OLZJXL4R.cjs.map +0 -1
- package/dist/chunk-PGNZKFQS.js.map +0 -1
- package/dist/chunk-SBCZ6RN5.cjs.map +0 -1
- package/dist/chunk-U5KWZU4X.js +0 -3223
- package/dist/chunk-U5KWZU4X.js.map +0 -1
- package/dist/chunk-XNORY7M6.cjs.map +0 -1
- package/dist/controller-BhLLAoi8.d.cts +0 -1000
- package/dist/controller-BhLLAoi8.d.ts +0 -1000
- package/dist/types-DX4Mh4SL.d.cts +0 -217
- package/dist/types-DX4Mh4SL.d.ts +0 -217
- /package/dist/{chunk-3LV6RRPH.js.map → chunk-MHEPLBFB.js.map} +0 -0
|
@@ -1,4 +1,30 @@
|
|
|
1
1
|
// src/spreadsheet/formula/lexer.ts
|
|
2
|
+
var MAX_QUOTED_SHEET_LENGTH = 2 * 31 + 2;
|
|
3
|
+
var isWordStart = (char) => /[A-Za-z_$\\]/.test(char) || char > "\x7F" && /\p{L}/u.test(char);
|
|
4
|
+
var isWordChar = (char) => /[A-Za-z0-9_$.\\?]/.test(char) || char > "\x7F" && /[\p{L}\p{N}]/u.test(char);
|
|
5
|
+
var CELL_WORD = /^\$?[A-Za-z]+\$?[0-9]+$/;
|
|
6
|
+
function readQuotedSheet(input, start) {
|
|
7
|
+
let name = "";
|
|
8
|
+
let j = start + 1;
|
|
9
|
+
while (j < input.length && j - start <= MAX_QUOTED_SHEET_LENGTH) {
|
|
10
|
+
if (input[j] === "'") {
|
|
11
|
+
if (input[j + 1] === "'") {
|
|
12
|
+
name += "'";
|
|
13
|
+
j += 2;
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (input[j + 1] !== "!" || input[j + 2] === "=" || name === "") return null;
|
|
17
|
+
const colon = name.indexOf(":");
|
|
18
|
+
if (colon > 0 && colon < name.length - 1) {
|
|
19
|
+
return { sheet: name.slice(0, colon), sheetEnd: name.slice(colon + 1), next: j + 2 };
|
|
20
|
+
}
|
|
21
|
+
return { sheet: name, next: j + 2 };
|
|
22
|
+
}
|
|
23
|
+
name += input[j];
|
|
24
|
+
j++;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
2
28
|
function tokenizeFormula(formula) {
|
|
3
29
|
const tokens = [];
|
|
4
30
|
let input = formula.trim();
|
|
@@ -29,6 +55,13 @@ function tokenizeFormula(formula) {
|
|
|
29
55
|
});
|
|
30
56
|
continue;
|
|
31
57
|
}
|
|
58
|
+
if (char === "'") {
|
|
59
|
+
const prefix = readQuotedSheet(input, i);
|
|
60
|
+
if (prefix) {
|
|
61
|
+
i = readQualifiedReference(input, prefix.next, i, prefix.sheet, prefix.sheetEnd, offset, tokens);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
32
65
|
if (char === '"' || char === "'") {
|
|
33
66
|
const quote2 = char;
|
|
34
67
|
const start = i;
|
|
@@ -48,7 +81,8 @@ function tokenizeFormula(formula) {
|
|
|
48
81
|
tokens.push({
|
|
49
82
|
type: "STRING",
|
|
50
83
|
value: str,
|
|
51
|
-
position: start + offset
|
|
84
|
+
position: start + offset,
|
|
85
|
+
end: i + offset
|
|
52
86
|
});
|
|
53
87
|
continue;
|
|
54
88
|
}
|
|
@@ -75,7 +109,8 @@ function tokenizeFormula(formula) {
|
|
|
75
109
|
tokens.push({
|
|
76
110
|
type: "PROPERTY_REF",
|
|
77
111
|
value: prop.trim(),
|
|
78
|
-
position: start + offset
|
|
112
|
+
position: start + offset,
|
|
113
|
+
end: i + offset
|
|
79
114
|
});
|
|
80
115
|
continue;
|
|
81
116
|
}
|
|
@@ -109,65 +144,96 @@ function tokenizeFormula(formula) {
|
|
|
109
144
|
i++;
|
|
110
145
|
continue;
|
|
111
146
|
}
|
|
112
|
-
if (
|
|
147
|
+
if (isWordStart(char)) {
|
|
113
148
|
const start = i;
|
|
114
|
-
while (i < input.length &&
|
|
149
|
+
while (i < input.length && isWordChar(input[i])) {
|
|
115
150
|
i++;
|
|
116
151
|
}
|
|
117
152
|
const rawWord = input.slice(start, i);
|
|
153
|
+
if (input[i] === "!" && input[i + 1] !== "=") {
|
|
154
|
+
i = readQualifiedReference(input, i + 1, start, rawWord, void 0, offset, tokens);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
118
157
|
if (i < input.length && input[i] === ":") {
|
|
119
158
|
const colonPos = i;
|
|
120
159
|
i++;
|
|
121
160
|
const secondStart = i;
|
|
122
|
-
while (i < input.length &&
|
|
161
|
+
while (i < input.length && isWordChar(input[i])) {
|
|
123
162
|
i++;
|
|
124
163
|
}
|
|
125
164
|
const secondWord = input.slice(secondStart, i);
|
|
165
|
+
if (secondWord.length > 0 && input[i] === "!" && input[i + 1] !== "=") {
|
|
166
|
+
i = readQualifiedReference(input, i + 1, start, rawWord, secondWord, offset, tokens);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
126
169
|
if (secondWord.length > 0) {
|
|
127
170
|
tokens.push({
|
|
128
171
|
type: "RANGE",
|
|
129
172
|
value: `${rawWord}:${secondWord}`.toUpperCase(),
|
|
130
|
-
position: start + offset
|
|
173
|
+
position: start + offset,
|
|
174
|
+
end: i + offset
|
|
131
175
|
});
|
|
132
176
|
continue;
|
|
133
177
|
} else {
|
|
134
178
|
i = colonPos;
|
|
135
179
|
}
|
|
136
180
|
}
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
tokens.push({
|
|
140
|
-
type: "BOOLEAN",
|
|
141
|
-
value: upper,
|
|
142
|
-
position: start + offset
|
|
143
|
-
});
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
if (/^\$?[A-Za-z]+\$?[0-9]+$/.test(rawWord)) {
|
|
147
|
-
if (input[i] === "#") {
|
|
148
|
-
i++;
|
|
149
|
-
tokens.push({ type: "SPILL_REF", value: upper.replace(/\$/g, ""), position: start + offset });
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
tokens.push({
|
|
153
|
-
type: "CELL_REF",
|
|
154
|
-
value: upper.replace(/\$/g, ""),
|
|
155
|
-
position: start + offset
|
|
156
|
-
});
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
tokens.push({
|
|
160
|
-
type: "IDENTIFIER",
|
|
161
|
-
value: upper,
|
|
162
|
-
position: start + offset
|
|
163
|
-
});
|
|
181
|
+
pushWord(input, rawWord, start, i, offset, tokens);
|
|
182
|
+
if (tokens[tokens.length - 1].type === "SPILL_REF") i++;
|
|
164
183
|
continue;
|
|
165
184
|
}
|
|
166
185
|
throw new Error(`Unexpected character '${char}' at position ${i + offset}`);
|
|
167
186
|
}
|
|
168
|
-
tokens.push({ type: "EOF", value: "", position: input.length + offset });
|
|
187
|
+
tokens.push({ type: "EOF", value: "", position: input.length + offset, end: input.length + offset });
|
|
188
|
+
for (const token of tokens) {
|
|
189
|
+
if (token.end === void 0) token.end = token.position + Math.max(token.value.length, 1);
|
|
190
|
+
}
|
|
169
191
|
return tokens;
|
|
170
192
|
}
|
|
193
|
+
function pushWord(input, rawWord, start, end, offset, tokens, qualified) {
|
|
194
|
+
const upper = rawWord.toUpperCase();
|
|
195
|
+
const position = qualified?.position ?? start + offset;
|
|
196
|
+
const extra = qualified ? { sheet: qualified.sheet, ...qualified.sheetEnd ? { sheetEnd: qualified.sheetEnd } : {}, refStart: qualified.refStart } : {};
|
|
197
|
+
if (!qualified && (upper === "TRUE" || upper === "FALSE")) {
|
|
198
|
+
tokens.push({ type: "BOOLEAN", value: upper, position, end: end + offset });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (CELL_WORD.test(rawWord)) {
|
|
202
|
+
if (input[end] === "#") {
|
|
203
|
+
tokens.push({ type: "SPILL_REF", value: upper.replace(/\$/g, ""), position, end: end + 1 + offset, ...extra });
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
tokens.push({ type: "CELL_REF", value: upper.replace(/\$/g, ""), position, end: end + offset, ...extra });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
tokens.push({ type: "IDENTIFIER", value: upper, position, end: end + offset, ...extra });
|
|
210
|
+
}
|
|
211
|
+
function readQualifiedReference(input, from, prefixStart, sheet, sheetEnd, offset, tokens) {
|
|
212
|
+
let i = from;
|
|
213
|
+
while (i < input.length && isWordChar(input[i])) i++;
|
|
214
|
+
const word = input.slice(from, i);
|
|
215
|
+
if (!word) throw new Error(`Expected a reference after '!' at position ${from + offset}`);
|
|
216
|
+
const qualified = { sheet, sheetEnd, refStart: from + offset, position: prefixStart + offset };
|
|
217
|
+
if (input[i] === ":") {
|
|
218
|
+
let j = i + 1;
|
|
219
|
+
while (j < input.length && isWordChar(input[j])) j++;
|
|
220
|
+
const second = input.slice(i + 1, j);
|
|
221
|
+
if (second) {
|
|
222
|
+
tokens.push({
|
|
223
|
+
type: "RANGE",
|
|
224
|
+
value: `${word}:${second}`.toUpperCase(),
|
|
225
|
+
position: qualified.position,
|
|
226
|
+
end: j + offset,
|
|
227
|
+
sheet,
|
|
228
|
+
...sheetEnd ? { sheetEnd } : {},
|
|
229
|
+
refStart: qualified.refStart
|
|
230
|
+
});
|
|
231
|
+
return j;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
pushWord(input, word, from, i, offset, tokens, qualified);
|
|
235
|
+
return tokens[tokens.length - 1].type === "SPILL_REF" ? i + 1 : i;
|
|
236
|
+
}
|
|
171
237
|
|
|
172
238
|
// src/spreadsheet/formula/parser.ts
|
|
173
239
|
var WHOLE_COLUMN_END_ROW = 1048576;
|
|
@@ -205,6 +271,7 @@ function parseRange(rangeStr) {
|
|
|
205
271
|
endRow: endRef.row
|
|
206
272
|
};
|
|
207
273
|
}
|
|
274
|
+
var qualifier = (token) => token.sheet === void 0 ? {} : token.sheetEnd === void 0 ? { sheet: token.sheet } : { sheet: token.sheet, sheetEnd: token.sheetEnd };
|
|
208
275
|
var FormulaParser = class {
|
|
209
276
|
tokens = [];
|
|
210
277
|
current = 0;
|
|
@@ -275,7 +342,22 @@ var FormulaParser = class {
|
|
|
275
342
|
const argument = this.unary();
|
|
276
343
|
return { type: "UnaryOp", op, argument };
|
|
277
344
|
}
|
|
278
|
-
|
|
345
|
+
let expr = this.primary();
|
|
346
|
+
while (this.check("LPAREN") && (expr.type === "FunctionCall" || expr.type === "Invoke" || this.previous().type === "RPAREN")) {
|
|
347
|
+
this.advance();
|
|
348
|
+
expr = { type: "Invoke", callee: expr, args: this.argumentList() };
|
|
349
|
+
}
|
|
350
|
+
return expr;
|
|
351
|
+
}
|
|
352
|
+
/** Arguments after `(` up to and including `)`. Empty positions are `Missing`. */
|
|
353
|
+
argumentList() {
|
|
354
|
+
const args = [];
|
|
355
|
+
if (this.match("RPAREN")) return args;
|
|
356
|
+
do {
|
|
357
|
+
args.push(this.check("COMMA") || this.check("RPAREN") ? { type: "Missing" } : this.expression());
|
|
358
|
+
} while (this.match("COMMA"));
|
|
359
|
+
this.consume("RPAREN", `Expected ')' after function arguments`);
|
|
360
|
+
return args;
|
|
279
361
|
}
|
|
280
362
|
primary() {
|
|
281
363
|
const token = this.peek();
|
|
@@ -297,22 +379,27 @@ var FormulaParser = class {
|
|
|
297
379
|
}
|
|
298
380
|
if (token.type === "CELL_REF") {
|
|
299
381
|
this.advance();
|
|
382
|
+
if (token.sheet === void 0 && this.check("LPAREN")) {
|
|
383
|
+
this.advance();
|
|
384
|
+
return { type: "FunctionCall", name: token.value, args: this.argumentList() };
|
|
385
|
+
}
|
|
300
386
|
const parsed = parseCellReference(token.value);
|
|
301
387
|
return {
|
|
302
388
|
type: "CellRef",
|
|
303
389
|
ref: token.value,
|
|
304
390
|
col: parsed.col,
|
|
305
|
-
row: parsed.row
|
|
391
|
+
row: parsed.row,
|
|
392
|
+
...qualifier(token)
|
|
306
393
|
};
|
|
307
394
|
}
|
|
308
395
|
if (token.type === "RANGE") {
|
|
309
396
|
this.advance();
|
|
310
|
-
return parseRange(token.value);
|
|
397
|
+
return { ...parseRange(token.value), ...qualifier(token) };
|
|
311
398
|
}
|
|
312
399
|
if (token.type === "SPILL_REF") {
|
|
313
400
|
this.advance();
|
|
314
401
|
const parsed = parseCellReference(token.value);
|
|
315
|
-
return { type: "SpillRef", ref: token.value, col: parsed.col, row: parsed.row };
|
|
402
|
+
return { type: "SpillRef", ref: token.value, col: parsed.col, row: parsed.row, ...qualifier(token) };
|
|
316
403
|
}
|
|
317
404
|
if (token.type === "PROPERTY_REF") {
|
|
318
405
|
this.advance();
|
|
@@ -325,21 +412,20 @@ var FormulaParser = class {
|
|
|
325
412
|
const name = token.value;
|
|
326
413
|
this.advance();
|
|
327
414
|
if (this.check("LPAREN")) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (!this.check("RPAREN")) {
|
|
331
|
-
do {
|
|
332
|
-
args.push(this.expression());
|
|
333
|
-
} while (this.match("COMMA"));
|
|
415
|
+
if (token.sheet !== void 0) {
|
|
416
|
+
throw new Error(`Unexpected '(' after ${token.sheet}!${name} at position ${this.peek().position}`);
|
|
334
417
|
}
|
|
335
|
-
this.consume("
|
|
418
|
+
this.consume("LPAREN", `Expected '(' after function ${name}`);
|
|
336
419
|
return {
|
|
337
420
|
type: "FunctionCall",
|
|
338
421
|
name,
|
|
339
|
-
args
|
|
422
|
+
args: this.argumentList()
|
|
340
423
|
};
|
|
341
424
|
}
|
|
342
|
-
|
|
425
|
+
if (token.sheetEnd !== void 0) {
|
|
426
|
+
throw new Error(`A 3-D reference can't name ${name} at position ${token.position}`);
|
|
427
|
+
}
|
|
428
|
+
return token.sheet !== void 0 ? { type: "Name", name, sheet: token.sheet } : { type: "Name", name };
|
|
343
429
|
}
|
|
344
430
|
if (this.match("LPAREN")) {
|
|
345
431
|
const expr = this.expression();
|
|
@@ -392,6 +478,35 @@ function parseFormula(formula) {
|
|
|
392
478
|
return parser.parse();
|
|
393
479
|
}
|
|
394
480
|
|
|
481
|
+
// src/spreadsheet/formula/lambda.ts
|
|
482
|
+
var FormulaLambda = class {
|
|
483
|
+
constructor(params, body, env, sheet, native, label = "LAMBDA") {
|
|
484
|
+
this.params = params;
|
|
485
|
+
this.body = body;
|
|
486
|
+
this.env = env;
|
|
487
|
+
this.sheet = sheet;
|
|
488
|
+
this.native = native;
|
|
489
|
+
this.label = label;
|
|
490
|
+
}
|
|
491
|
+
params;
|
|
492
|
+
body;
|
|
493
|
+
env;
|
|
494
|
+
sheet;
|
|
495
|
+
native;
|
|
496
|
+
label;
|
|
497
|
+
toString() {
|
|
498
|
+
return `[${this.label}]`;
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
var isFormulaLambda = (value) => value instanceof FormulaLambda;
|
|
502
|
+
var MAX_LAMBDA_DEPTH = 1e3;
|
|
503
|
+
function lookupEnvironment(env, name) {
|
|
504
|
+
for (let e = env; e; e = e.parent) {
|
|
505
|
+
if (e.vars.has(name)) return e;
|
|
506
|
+
}
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
|
|
395
510
|
// src/spreadsheet/formula/values.ts
|
|
396
511
|
var FORMULA_ERRORS = /* @__PURE__ */ new Set([
|
|
397
512
|
"#DIV/0!",
|
|
@@ -4122,6 +4237,88 @@ var ELEMENTWISE_BUILTINS = {
|
|
|
4122
4237
|
IFNA: [0]
|
|
4123
4238
|
};
|
|
4124
4239
|
|
|
4240
|
+
// src/spreadsheet/formula/functions/lambda.ts
|
|
4241
|
+
var special = (name) => (ctx) => ctx.error("#ERROR!", `${name} must be evaluated by the formula evaluator.`);
|
|
4242
|
+
var LAMBDA_ARG = arg("lambda", "A LAMBDA function, e.g. LAMBDA(x, x*2), or a function name such as SUM.");
|
|
4243
|
+
var LAMBDA_FUNCTIONS = [
|
|
4244
|
+
{
|
|
4245
|
+
name: "LAMBDA",
|
|
4246
|
+
category: "logical",
|
|
4247
|
+
description: "Creates a reusable function: LAMBDA(x, y, x+y). Call it directly, LAMBDA(x, x+1)(2), store it in LET or a defined name, or pass it to MAP, REDUCE, SCAN, BYROW, BYCOL or MAKEARRAY.",
|
|
4248
|
+
args: [
|
|
4249
|
+
repeating(
|
|
4250
|
+
"parameter_or_calculation",
|
|
4251
|
+
"Parameter names, then the calculation that uses them (always the last argument)."
|
|
4252
|
+
)
|
|
4253
|
+
],
|
|
4254
|
+
evaluate: special("LAMBDA")
|
|
4255
|
+
},
|
|
4256
|
+
{
|
|
4257
|
+
name: "ISOMITTED",
|
|
4258
|
+
category: "logical",
|
|
4259
|
+
description: "Checks whether a LAMBDA parameter was left out of the call, e.g. LAMBDA(x, y, IF(ISOMITTED(y), x, x+y))(1,).",
|
|
4260
|
+
args: [arg("argument", "A LAMBDA parameter name.")],
|
|
4261
|
+
evaluate: special("ISOMITTED")
|
|
4262
|
+
},
|
|
4263
|
+
{
|
|
4264
|
+
name: "MAP",
|
|
4265
|
+
category: "logical",
|
|
4266
|
+
description: "Applies a LAMBDA to each value of one or more arrays: MAP(A1:A3, LAMBDA(v, v*2)).",
|
|
4267
|
+
args: [
|
|
4268
|
+
arg("array1", "An array to map."),
|
|
4269
|
+
repeating("array_or_lambda", "More arrays, then the LAMBDA taking one parameter per array.")
|
|
4270
|
+
],
|
|
4271
|
+
evaluate: special("MAP")
|
|
4272
|
+
},
|
|
4273
|
+
{
|
|
4274
|
+
name: "REDUCE",
|
|
4275
|
+
category: "logical",
|
|
4276
|
+
description: "Reduces an array to one value by applying LAMBDA(accumulator, value) to each value, row by row.",
|
|
4277
|
+
args: [
|
|
4278
|
+
arg("initial_value", "The starting accumulator (may be left empty)."),
|
|
4279
|
+
arg("array", "The array to reduce."),
|
|
4280
|
+
LAMBDA_ARG
|
|
4281
|
+
],
|
|
4282
|
+
evaluate: special("REDUCE")
|
|
4283
|
+
},
|
|
4284
|
+
{
|
|
4285
|
+
name: "SCAN",
|
|
4286
|
+
category: "logical",
|
|
4287
|
+
description: "Like REDUCE, but returns every intermediate accumulator value (running totals).",
|
|
4288
|
+
args: [
|
|
4289
|
+
arg("initial_value", "The starting accumulator (may be left empty)."),
|
|
4290
|
+
arg("array", "The array to scan."),
|
|
4291
|
+
LAMBDA_ARG
|
|
4292
|
+
],
|
|
4293
|
+
evaluate: special("SCAN")
|
|
4294
|
+
},
|
|
4295
|
+
{
|
|
4296
|
+
name: "BYROW",
|
|
4297
|
+
category: "logical",
|
|
4298
|
+
description: "Applies a LAMBDA to each row of an array and returns one value per row: BYROW(A1:C3, LAMBDA(r, SUM(r))).",
|
|
4299
|
+
args: [arg("array", "The array whose rows are passed to the LAMBDA."), LAMBDA_ARG],
|
|
4300
|
+
evaluate: special("BYROW")
|
|
4301
|
+
},
|
|
4302
|
+
{
|
|
4303
|
+
name: "BYCOL",
|
|
4304
|
+
category: "logical",
|
|
4305
|
+
description: "Applies a LAMBDA to each column of an array and returns one value per column.",
|
|
4306
|
+
args: [arg("array", "The array whose columns are passed to the LAMBDA."), LAMBDA_ARG],
|
|
4307
|
+
evaluate: special("BYCOL")
|
|
4308
|
+
},
|
|
4309
|
+
{
|
|
4310
|
+
name: "MAKEARRAY",
|
|
4311
|
+
category: "logical",
|
|
4312
|
+
description: "Builds an array of the given size from LAMBDA(row, column): MAKEARRAY(3, 3, LAMBDA(r, c, r*c)).",
|
|
4313
|
+
args: [
|
|
4314
|
+
arg("rows", "Number of rows.", "number"),
|
|
4315
|
+
arg("columns", "Number of columns.", "number"),
|
|
4316
|
+
LAMBDA_ARG
|
|
4317
|
+
],
|
|
4318
|
+
evaluate: special("MAKEARRAY")
|
|
4319
|
+
}
|
|
4320
|
+
];
|
|
4321
|
+
|
|
4125
4322
|
// src/spreadsheet/formula/functions/index.ts
|
|
4126
4323
|
var BUILTIN_FORMULA_FUNCTIONS = [
|
|
4127
4324
|
...MATH_FUNCTIONS,
|
|
@@ -4132,7 +4329,8 @@ var BUILTIN_FORMULA_FUNCTIONS = [
|
|
|
4132
4329
|
...LOOKUP_FUNCTIONS,
|
|
4133
4330
|
...DATE_FUNCTIONS,
|
|
4134
4331
|
...INFORMATION_FUNCTIONS,
|
|
4135
|
-
...ARRAY_FUNCTIONS
|
|
4332
|
+
...ARRAY_FUNCTIONS,
|
|
4333
|
+
...LAMBDA_FUNCTIONS
|
|
4136
4334
|
].map((def) => {
|
|
4137
4335
|
const elementwise = ELEMENTWISE_BUILTINS[def.name];
|
|
4138
4336
|
return elementwise && def.elementwise === void 0 ? { ...def, elementwise } : def;
|
|
@@ -4250,6 +4448,7 @@ function getFormulaSignature(nameOrDef) {
|
|
|
4250
4448
|
}
|
|
4251
4449
|
|
|
4252
4450
|
// src/spreadsheet/formula/evaluator.ts
|
|
4451
|
+
var BLANK_IGNORING_FUNCTIONS = /* @__PURE__ */ new Set(["SUM", "PRODUCT", "AVERAGE", "COUNT", "COUNTA", "MIN", "MAX"]);
|
|
4253
4452
|
var DEFAULT_MESSAGES = {
|
|
4254
4453
|
"#DIV/0!": "The formula divides by zero.",
|
|
4255
4454
|
"#VALUE!": "A value in the formula has the wrong type.",
|
|
@@ -4289,11 +4488,30 @@ function buildArray(rows, cols, fn) {
|
|
|
4289
4488
|
}
|
|
4290
4489
|
var isMultiArray = (value) => Array.isArray(value) && value.length > 1;
|
|
4291
4490
|
var firstValue = (value) => Array.isArray(value) ? value.length ? value[0] : "" : value;
|
|
4491
|
+
var sheetLabel = (node) => node.sheet === void 0 ? "" : `${node.sheetEnd === void 0 ? node.sheet : `${node.sheet}:${node.sheetEnd}`}!`;
|
|
4492
|
+
var SPECIAL_FORMS = /* @__PURE__ */ new Set(["LET", "LAMBDA", "ISOMITTED", "MAP", "REDUCE", "SCAN", "BYROW", "BYCOL", "MAKEARRAY"]);
|
|
4493
|
+
function elementValue(value) {
|
|
4494
|
+
if (isFormulaLambda(value)) return "#CALC!";
|
|
4495
|
+
if (Array.isArray(value)) return value.length === 1 ? elementValue(value[0]) : "#CALC!";
|
|
4496
|
+
return value;
|
|
4497
|
+
}
|
|
4498
|
+
function asRange(value) {
|
|
4499
|
+
if (Array.isArray(value)) return toFormulaRange(value);
|
|
4500
|
+
return { rows: 1, cols: 1, values: [value] };
|
|
4501
|
+
}
|
|
4502
|
+
var LITERAL_BLANK = { type: "Literal", value: "" };
|
|
4292
4503
|
var FormulaEvaluator = class {
|
|
4293
4504
|
context;
|
|
4294
4505
|
diagnostics = [];
|
|
4295
|
-
// LET variables
|
|
4296
|
-
|
|
4506
|
+
// LET and LAMBDA variables (innermost first, chained to outer scopes).
|
|
4507
|
+
env = null;
|
|
4508
|
+
// Sheet that unqualified references refer to while evaluating a
|
|
4509
|
+
// sheet-scoped defined name (undefined: the formula's own sheet).
|
|
4510
|
+
defaultSheet = void 0;
|
|
4511
|
+
lambdaDepth = 0;
|
|
4512
|
+
// Defined names being evaluated, to stop names that refer to themselves.
|
|
4513
|
+
nameStack = [];
|
|
4514
|
+
parsedNames = /* @__PURE__ */ new Map();
|
|
4297
4515
|
constructor(context = {}) {
|
|
4298
4516
|
this.context = context;
|
|
4299
4517
|
}
|
|
@@ -4303,7 +4521,14 @@ var FormulaEvaluator = class {
|
|
|
4303
4521
|
*/
|
|
4304
4522
|
evaluate(astOrFormula) {
|
|
4305
4523
|
const ast = typeof astOrFormula === "string" ? parseFormula(astOrFormula) : astOrFormula;
|
|
4306
|
-
|
|
4524
|
+
const value = this.evaluateNode(ast);
|
|
4525
|
+
if (isFormulaLambda(value)) {
|
|
4526
|
+
return this.fail(
|
|
4527
|
+
"#CALC!",
|
|
4528
|
+
"The formula returns a LAMBDA function without calling it. Add its arguments in parentheses, e.g. LAMBDA(x, x+1)(2)."
|
|
4529
|
+
);
|
|
4530
|
+
}
|
|
4531
|
+
return value;
|
|
4307
4532
|
}
|
|
4308
4533
|
/**
|
|
4309
4534
|
* Evaluates a formula and explains errors. Never throws: malformed formulas
|
|
@@ -4313,9 +4538,15 @@ var FormulaEvaluator = class {
|
|
|
4313
4538
|
*/
|
|
4314
4539
|
evaluateWithDiagnostics(astOrFormula) {
|
|
4315
4540
|
const saved = this.diagnostics;
|
|
4316
|
-
const
|
|
4541
|
+
const savedEnv = this.env;
|
|
4542
|
+
const savedSheet = this.defaultSheet;
|
|
4543
|
+
const savedDepth = this.lambdaDepth;
|
|
4544
|
+
const savedNames = this.nameStack;
|
|
4317
4545
|
this.diagnostics = [];
|
|
4318
|
-
this.
|
|
4546
|
+
this.env = null;
|
|
4547
|
+
this.defaultSheet = void 0;
|
|
4548
|
+
this.lambdaDepth = 0;
|
|
4549
|
+
this.nameStack = [];
|
|
4319
4550
|
try {
|
|
4320
4551
|
let value = this.evaluate(astOrFormula);
|
|
4321
4552
|
let array;
|
|
@@ -4336,10 +4567,16 @@ var FormulaEvaluator = class {
|
|
|
4336
4567
|
}
|
|
4337
4568
|
return array ? { value, array } : { value };
|
|
4338
4569
|
} catch (err) {
|
|
4570
|
+
if (err instanceof RangeError) {
|
|
4571
|
+
return { value: "#NUM!", error: "#NUM!", message: "The calculation is nested too deeply (recursion limit)." };
|
|
4572
|
+
}
|
|
4339
4573
|
return { value: "#ERROR!", error: "#ERROR!", message: err?.message || "Formula error" };
|
|
4340
4574
|
} finally {
|
|
4341
4575
|
this.diagnostics = saved;
|
|
4342
|
-
this.
|
|
4576
|
+
this.env = savedEnv;
|
|
4577
|
+
this.defaultSheet = savedSheet;
|
|
4578
|
+
this.lambdaDepth = savedDepth;
|
|
4579
|
+
this.nameStack = savedNames;
|
|
4343
4580
|
}
|
|
4344
4581
|
}
|
|
4345
4582
|
/** Records why an error happened and returns the error code. */
|
|
@@ -4357,11 +4594,16 @@ var FormulaEvaluator = class {
|
|
|
4357
4594
|
if (!this.context.resolveCell) {
|
|
4358
4595
|
throw new Error(`Cannot resolve cell reference ${node.ref}: no cell resolver provided`);
|
|
4359
4596
|
}
|
|
4360
|
-
|
|
4597
|
+
if (node.sheetEnd !== void 0) return this.evaluate3d(node);
|
|
4598
|
+
const value = this.context.resolveCell(node.col, node.row, node.sheet ?? this.defaultSheet);
|
|
4361
4599
|
if (isFormulaError(value)) {
|
|
4600
|
+
const label = `${sheetLabel(node)}${node.ref}`;
|
|
4601
|
+
if (value === "#REF!" && node.sheet !== void 0) {
|
|
4602
|
+
return this.fail("#REF!", `${label} refers to a sheet that doesn't exist.`);
|
|
4603
|
+
}
|
|
4362
4604
|
this.fail(
|
|
4363
4605
|
value,
|
|
4364
|
-
value === "#CYCLE!" ? `Cell ${
|
|
4606
|
+
value === "#CYCLE!" ? `Cell ${label} is part of a circular reference.` : `Cell ${label} contains an error (${value}).`
|
|
4365
4607
|
);
|
|
4366
4608
|
}
|
|
4367
4609
|
return value;
|
|
@@ -4370,11 +4612,15 @@ var FormulaEvaluator = class {
|
|
|
4370
4612
|
return this.evaluateRange(node);
|
|
4371
4613
|
case "SpillRef":
|
|
4372
4614
|
return this.evaluateSpillRef(node);
|
|
4373
|
-
case "Name":
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4615
|
+
case "Name":
|
|
4616
|
+
return this.evaluateName(node);
|
|
4617
|
+
case "Missing":
|
|
4618
|
+
return "";
|
|
4619
|
+
case "Invoke": {
|
|
4620
|
+
const callee = this.evaluateNode(node.callee);
|
|
4621
|
+
if (isFormulaLambda(callee)) return this.invokeLambda(callee, node.args);
|
|
4622
|
+
if (isFormulaError(callee)) return callee;
|
|
4623
|
+
return this.fail("#VALUE!", "Only a LAMBDA function can be called with arguments in parentheses.");
|
|
4378
4624
|
}
|
|
4379
4625
|
case "PropertyRef": {
|
|
4380
4626
|
if (!this.context.resolveProperty) {
|
|
@@ -4396,6 +4642,9 @@ var FormulaEvaluator = class {
|
|
|
4396
4642
|
if (isFormulaError(left)) return left;
|
|
4397
4643
|
const right = this.evaluateNode(node.right);
|
|
4398
4644
|
if (isFormulaError(right)) return right;
|
|
4645
|
+
if (isFormulaLambda(left) || isFormulaLambda(right)) {
|
|
4646
|
+
return this.fail("#VALUE!", `The ${node.op} operator can't use a LAMBDA function; call it with arguments first.`);
|
|
4647
|
+
}
|
|
4399
4648
|
if (isMultiArray(left) || isMultiArray(right)) {
|
|
4400
4649
|
const a = shapeOf(left);
|
|
4401
4650
|
const b = shapeOf(right);
|
|
@@ -4423,6 +4672,7 @@ var FormulaEvaluator = class {
|
|
|
4423
4672
|
}
|
|
4424
4673
|
}
|
|
4425
4674
|
unary(op, raw) {
|
|
4675
|
+
if (isFormulaLambda(raw)) return this.fail("#VALUE!", `The ${op} operator can't use a LAMBDA function.`);
|
|
4426
4676
|
const val = toNumber(raw);
|
|
4427
4677
|
if (isFormulaError(val)) {
|
|
4428
4678
|
return isFormulaError(raw) ? raw : this.fail("#VALUE!", `The ${op} operator can't use text (${quote(raw)}).`);
|
|
@@ -4537,7 +4787,10 @@ var FormulaEvaluator = class {
|
|
|
4537
4787
|
if (!this.context.resolveSpill) {
|
|
4538
4788
|
throw new Error(`Cannot resolve spill range ${node.ref}#: no spill resolver provided`);
|
|
4539
4789
|
}
|
|
4540
|
-
|
|
4790
|
+
if (node.sheetEnd !== void 0) {
|
|
4791
|
+
return this.fail("#REF!", `A 3-D reference can't use the spill operator (${sheetLabel(node)}${node.ref}#).`);
|
|
4792
|
+
}
|
|
4793
|
+
const spill = this.context.resolveSpill(node.col, node.row, node.sheet ?? this.defaultSheet);
|
|
4541
4794
|
if (typeof spill === "string" || !spill) {
|
|
4542
4795
|
const code = isFormulaError(spill) ? spill : "#REF!";
|
|
4543
4796
|
return this.fail(
|
|
@@ -4550,12 +4803,19 @@ var FormulaEvaluator = class {
|
|
|
4550
4803
|
if (error) this.fail(error, `The spill range ${node.ref}# contains an error (${error}).`);
|
|
4551
4804
|
return values;
|
|
4552
4805
|
}
|
|
4553
|
-
evaluateRange(node) {
|
|
4806
|
+
evaluateRange(node, options) {
|
|
4554
4807
|
if (!this.context.resolveRange) {
|
|
4555
4808
|
throw new Error(`Cannot resolve range ${node.raw}: no range resolver provided`);
|
|
4556
4809
|
}
|
|
4557
|
-
|
|
4558
|
-
|
|
4810
|
+
if (node.sheetEnd !== void 0) return this.evaluate3d(node);
|
|
4811
|
+
const sheet = node.sheet ?? this.defaultSheet;
|
|
4812
|
+
const values = options ? this.context.resolveRange(node.startCol, node.startRow, node.endCol, node.endRow, sheet, options) : this.context.resolveRange(node.startCol, node.startRow, node.endCol, node.endRow, sheet);
|
|
4813
|
+
if (!Array.isArray(values)) {
|
|
4814
|
+
if (values === "#REF!" && node.sheet !== void 0) {
|
|
4815
|
+
return this.fail("#REF!", `${sheetLabel(node)}${node.raw} refers to a sheet that doesn't exist.`);
|
|
4816
|
+
}
|
|
4817
|
+
return values;
|
|
4818
|
+
}
|
|
4559
4819
|
const cols = Math.abs(colNameToIndex(node.endCol) - colNameToIndex(node.startCol)) + 1;
|
|
4560
4820
|
if (values.length % cols === 0) withRangeShape(values, values.length / cols, cols);
|
|
4561
4821
|
const error = values.find(isFormulaError);
|
|
@@ -4563,12 +4823,29 @@ var FormulaEvaluator = class {
|
|
|
4563
4823
|
return values;
|
|
4564
4824
|
}
|
|
4565
4825
|
callFunction(node) {
|
|
4826
|
+
const variables = lookupEnvironment(this.env, node.name);
|
|
4827
|
+
if (variables) {
|
|
4828
|
+
const bound = variables.vars.get(node.name);
|
|
4829
|
+
if (isFormulaLambda(bound)) return this.invokeLambda(bound, node.args);
|
|
4830
|
+
}
|
|
4566
4831
|
const def = getFormulaFunction(node.name);
|
|
4567
|
-
if (!def)
|
|
4832
|
+
if (!def) {
|
|
4833
|
+
const named = this.lookupDefinedName(node.name);
|
|
4834
|
+
if (named) {
|
|
4835
|
+
const value = this.evaluateDefinedName(named, node.name);
|
|
4836
|
+
if (isFormulaLambda(value)) return this.invokeLambda(value, node.args);
|
|
4837
|
+
if (isFormulaError(value)) return value;
|
|
4838
|
+
return this.fail("#VALUE!", `The name ${node.name} doesn't refer to a LAMBDA function, so it can't be called.`);
|
|
4839
|
+
}
|
|
4840
|
+
if (variables) {
|
|
4841
|
+
return this.fail("#VALUE!", `The variable ${node.name} doesn't hold a LAMBDA function, so it can't be called.`);
|
|
4842
|
+
}
|
|
4843
|
+
return this.fail("#NAME?", `Unknown function "${node.name.toUpperCase()}".`);
|
|
4844
|
+
}
|
|
4568
4845
|
const countError = checkFormulaArgumentCount(def, node.args.length);
|
|
4569
4846
|
if (countError) return this.fail("#ERROR!", countError);
|
|
4570
|
-
if (def.name
|
|
4571
|
-
const args = this.lazyArguments(node.args);
|
|
4847
|
+
if (SPECIAL_FORMS.has(def.name)) return this.evaluateSpecialForm(def.name, node);
|
|
4848
|
+
const args = this.lazyArguments(node.args, BLANK_IGNORING_FUNCTIONS.has(def.name));
|
|
4572
4849
|
if (def.elementwise) {
|
|
4573
4850
|
const lifted = this.liftOverArrays(def, args);
|
|
4574
4851
|
if (lifted !== void 0) return lifted;
|
|
@@ -4581,13 +4858,22 @@ var FormulaEvaluator = class {
|
|
|
4581
4858
|
}
|
|
4582
4859
|
return result === void 0 || result === null ? "" : result;
|
|
4583
4860
|
}
|
|
4584
|
-
|
|
4861
|
+
/**
|
|
4862
|
+
* Arguments evaluated on first use. With `skipBlanks` (the function ignores
|
|
4863
|
+
* blank cells), range arguments may come back without their blanks.
|
|
4864
|
+
*/
|
|
4865
|
+
lazyArguments(nodes, skipBlanks = false) {
|
|
4585
4866
|
const cache = /* @__PURE__ */ new Map();
|
|
4586
4867
|
return nodes.map((node, index) => ({
|
|
4587
4868
|
index,
|
|
4588
4869
|
node,
|
|
4589
4870
|
value: () => {
|
|
4590
|
-
if (!cache.has(index))
|
|
4871
|
+
if (!cache.has(index)) {
|
|
4872
|
+
cache.set(
|
|
4873
|
+
index,
|
|
4874
|
+
skipBlanks && node.type === "Range" && node.sheetEnd === void 0 ? this.evaluateRange(node, { skipBlanks: true }) : this.evaluateNode(node)
|
|
4875
|
+
);
|
|
4876
|
+
}
|
|
4591
4877
|
return cache.get(index);
|
|
4592
4878
|
}
|
|
4593
4879
|
}));
|
|
@@ -4620,25 +4906,319 @@ var FormulaEvaluator = class {
|
|
|
4620
4906
|
return firstValue(this.finishCall(def, def.evaluate(this.createContext(def, elementArgs))));
|
|
4621
4907
|
});
|
|
4622
4908
|
}
|
|
4909
|
+
// --- Names, LET and LAMBDA ---
|
|
4910
|
+
/** A bare name: a variable, a defined name, or #NAME?. */
|
|
4911
|
+
evaluateName(node) {
|
|
4912
|
+
if (node.sheet === void 0) {
|
|
4913
|
+
const variables = lookupEnvironment(this.env, node.name);
|
|
4914
|
+
if (variables) return variables.vars.get(node.name);
|
|
4915
|
+
}
|
|
4916
|
+
const named = this.lookupDefinedName(node.name, node.sheet);
|
|
4917
|
+
if (named) return this.evaluateDefinedName(named, `${sheetLabel(node)}${node.name}`);
|
|
4918
|
+
if (node.sheet !== void 0) {
|
|
4919
|
+
return this.fail("#NAME?", `There is no name ${node.name} on sheet ${node.sheet}.`);
|
|
4920
|
+
}
|
|
4921
|
+
return this.fail("#NAME?", `This formula contains an unknown name.`);
|
|
4922
|
+
}
|
|
4923
|
+
lookupDefinedName(name, sheet) {
|
|
4924
|
+
return this.context.resolveName?.(name, sheet ?? this.defaultSheet) ?? null;
|
|
4925
|
+
}
|
|
4926
|
+
/**
|
|
4927
|
+
* Evaluates a defined name's formula, outside any LET/LAMBDA variables, with
|
|
4928
|
+
* unqualified references pointing at the name's sheet (for sheet-scoped
|
|
4929
|
+
* names).
|
|
4930
|
+
*/
|
|
4931
|
+
evaluateDefinedName(def, label) {
|
|
4932
|
+
const key = (def.key ?? label).toUpperCase();
|
|
4933
|
+
if (this.nameStack.includes(key)) {
|
|
4934
|
+
return this.fail("#CYCLE!", `The name ${label} refers to itself.`);
|
|
4935
|
+
}
|
|
4936
|
+
let ast;
|
|
4937
|
+
if (typeof def.formula === "string") {
|
|
4938
|
+
const text = def.formula.startsWith("=") ? def.formula : `=${def.formula}`;
|
|
4939
|
+
let cached = this.parsedNames.get(text);
|
|
4940
|
+
if (!cached) {
|
|
4941
|
+
try {
|
|
4942
|
+
cached = parseFormula(text);
|
|
4943
|
+
} catch {
|
|
4944
|
+
return this.fail("#NAME?", `The name ${label} has a formula that can't be read.`);
|
|
4945
|
+
}
|
|
4946
|
+
if (this.parsedNames.size > 500) this.parsedNames.clear();
|
|
4947
|
+
this.parsedNames.set(text, cached);
|
|
4948
|
+
}
|
|
4949
|
+
ast = cached;
|
|
4950
|
+
} else {
|
|
4951
|
+
ast = def.formula;
|
|
4952
|
+
}
|
|
4953
|
+
const savedEnv = this.env;
|
|
4954
|
+
const savedSheet = this.defaultSheet;
|
|
4955
|
+
this.nameStack.push(key);
|
|
4956
|
+
this.env = null;
|
|
4957
|
+
if (def.sheet !== void 0) this.defaultSheet = def.sheet;
|
|
4958
|
+
try {
|
|
4959
|
+
return this.evaluateNode(ast);
|
|
4960
|
+
} finally {
|
|
4961
|
+
this.env = savedEnv;
|
|
4962
|
+
this.defaultSheet = savedSheet;
|
|
4963
|
+
this.nameStack.pop();
|
|
4964
|
+
}
|
|
4965
|
+
}
|
|
4966
|
+
/** A 3-D reference (`Sheet1:Sheet3!A1`, `Sheet1:Sheet3!A1:B2`): every sheet's values, in tab order. */
|
|
4967
|
+
evaluate3d(node) {
|
|
4968
|
+
const label = node.type === "CellRef" ? `${sheetLabel(node)}${node.ref}` : `${sheetLabel(node)}${node.raw}`;
|
|
4969
|
+
const sheets = this.context.resolveSheetSpan?.(node.sheet, node.sheetEnd);
|
|
4970
|
+
if (!Array.isArray(sheets)) {
|
|
4971
|
+
return this.fail("#REF!", `${label} refers to a sheet that doesn't exist.`);
|
|
4972
|
+
}
|
|
4973
|
+
const values = [];
|
|
4974
|
+
for (const sheet of sheets) {
|
|
4975
|
+
const part = node.type === "CellRef" ? [this.context.resolveCell(node.col, node.row, sheet)] : this.context.resolveRange(node.startCol, node.startRow, node.endCol, node.endRow, sheet);
|
|
4976
|
+
if (!Array.isArray(part)) return this.fail("#REF!", `${label} refers to a sheet that doesn't exist.`);
|
|
4977
|
+
for (const value of part) values.push(value ?? "");
|
|
4978
|
+
}
|
|
4979
|
+
const error = values.find(isFormulaError);
|
|
4980
|
+
if (error) this.fail(error, `The 3-D reference ${label} contains an error (${error}).`);
|
|
4981
|
+
return withRangeShape(values, values.length, 1);
|
|
4982
|
+
}
|
|
4983
|
+
/** Calls a lambda with argument expressions, evaluated in the caller's scope. */
|
|
4984
|
+
invokeLambda(lambda, argNodes) {
|
|
4985
|
+
const params = lambda.params;
|
|
4986
|
+
const positionsOk = argNodes.length === params.length || argNodes.length === 0 && params.length === 1;
|
|
4987
|
+
if (!positionsOk) {
|
|
4988
|
+
return this.fail(
|
|
4989
|
+
"#VALUE!",
|
|
4990
|
+
`${lambda.label} expects ${params.length} argument${params.length === 1 ? "" : "s"}, but ${argNodes.length} ${argNodes.length === 1 ? "was" : "were"} given.`
|
|
4991
|
+
);
|
|
4992
|
+
}
|
|
4993
|
+
const values = [];
|
|
4994
|
+
const omitted = /* @__PURE__ */ new Set();
|
|
4995
|
+
for (let i = 0; i < params.length; i++) {
|
|
4996
|
+
const node = argNodes[i];
|
|
4997
|
+
if (!node || node.type === "Missing") {
|
|
4998
|
+
omitted.add(i);
|
|
4999
|
+
values.push("");
|
|
5000
|
+
} else {
|
|
5001
|
+
values.push(this.evaluateNode(node));
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
return this.invokeWithValues(lambda, values, omitted);
|
|
5005
|
+
}
|
|
5006
|
+
/** Calls a lambda with argument values (`omitted`: positions left out, for ISOMITTED). */
|
|
5007
|
+
invokeWithValues(lambda, values, omitted) {
|
|
5008
|
+
if (this.lambdaDepth >= MAX_LAMBDA_DEPTH) {
|
|
5009
|
+
return this.fail("#NUM!", `LAMBDA calls are nested more than ${MAX_LAMBDA_DEPTH} deep (runaway recursion?).`);
|
|
5010
|
+
}
|
|
5011
|
+
if (lambda.native) return lambda.native(values);
|
|
5012
|
+
const vars = /* @__PURE__ */ new Map();
|
|
5013
|
+
let omittedNames;
|
|
5014
|
+
lambda.params.forEach((param, i) => {
|
|
5015
|
+
vars.set(param, values[i] ?? "");
|
|
5016
|
+
if (omitted?.has(i)) (omittedNames ??= /* @__PURE__ */ new Set()).add(param);
|
|
5017
|
+
});
|
|
5018
|
+
const savedEnv = this.env;
|
|
5019
|
+
const savedSheet = this.defaultSheet;
|
|
5020
|
+
this.env = { vars, omitted: omittedNames, parent: lambda.env };
|
|
5021
|
+
this.defaultSheet = lambda.sheet;
|
|
5022
|
+
this.lambdaDepth++;
|
|
5023
|
+
try {
|
|
5024
|
+
return this.evaluateNode(lambda.body);
|
|
5025
|
+
} finally {
|
|
5026
|
+
this.lambdaDepth--;
|
|
5027
|
+
this.env = savedEnv;
|
|
5028
|
+
this.defaultSheet = savedSheet;
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
evaluateSpecialForm(name, node) {
|
|
5032
|
+
switch (name) {
|
|
5033
|
+
case "LET":
|
|
5034
|
+
return this.evaluateLet(node);
|
|
5035
|
+
case "LAMBDA":
|
|
5036
|
+
return this.evaluateLambda(node);
|
|
5037
|
+
case "ISOMITTED": {
|
|
5038
|
+
const arg2 = node.args[0];
|
|
5039
|
+
if (arg2?.type !== "Name" || arg2.sheet !== void 0) return false;
|
|
5040
|
+
const variables = lookupEnvironment(this.env, arg2.name);
|
|
5041
|
+
return !!variables?.omitted?.has(arg2.name);
|
|
5042
|
+
}
|
|
5043
|
+
case "MAP":
|
|
5044
|
+
return this.evaluateMap(node);
|
|
5045
|
+
case "REDUCE":
|
|
5046
|
+
case "SCAN":
|
|
5047
|
+
return this.evaluateReduce(node, name === "SCAN");
|
|
5048
|
+
case "BYROW":
|
|
5049
|
+
case "BYCOL":
|
|
5050
|
+
return this.evaluateByLine(node, name === "BYROW");
|
|
5051
|
+
default:
|
|
5052
|
+
return this.evaluateMakeArray(node);
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
4623
5055
|
/** LET(name1, value1, [name2, value2, ...], calculation). */
|
|
4624
5056
|
evaluateLet(node) {
|
|
4625
5057
|
if (node.args.length % 2 === 0) {
|
|
4626
5058
|
return this.fail("#ERROR!", "LET needs name/value pairs followed by a calculation.");
|
|
4627
5059
|
}
|
|
4628
|
-
const
|
|
4629
|
-
this.
|
|
5060
|
+
const vars = /* @__PURE__ */ new Map();
|
|
5061
|
+
const savedEnv = this.env;
|
|
5062
|
+
this.env = { vars, parent: savedEnv };
|
|
4630
5063
|
try {
|
|
4631
5064
|
for (let i = 0; i < node.args.length - 1; i += 2) {
|
|
4632
5065
|
const nameNode = node.args[i];
|
|
4633
|
-
if (nameNode.type !== "Name") {
|
|
5066
|
+
if (nameNode.type !== "Name" || nameNode.sheet !== void 0) {
|
|
4634
5067
|
return this.fail("#ERROR!", `Argument ${i + 1} of LET must be a name (names can't look like cell references).`);
|
|
4635
5068
|
}
|
|
4636
|
-
|
|
5069
|
+
vars.set(nameNode.name, this.evaluateNode(node.args[i + 1]));
|
|
4637
5070
|
}
|
|
4638
5071
|
return this.evaluateNode(node.args[node.args.length - 1]);
|
|
4639
5072
|
} finally {
|
|
4640
|
-
this.
|
|
5073
|
+
this.env = savedEnv;
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
/** LAMBDA([parameter1, ...], calculation): a function value closing over the current variables. */
|
|
5077
|
+
evaluateLambda(node) {
|
|
5078
|
+
const params = [];
|
|
5079
|
+
for (let i = 0; i < node.args.length - 1; i++) {
|
|
5080
|
+
const param = node.args[i];
|
|
5081
|
+
if (param.type !== "Name" || param.sheet !== void 0) {
|
|
5082
|
+
return this.fail("#VALUE!", `Parameter ${i + 1} of LAMBDA must be a name (names can't look like cell references).`);
|
|
5083
|
+
}
|
|
5084
|
+
if (params.includes(param.name)) {
|
|
5085
|
+
return this.fail("#VALUE!", `LAMBDA parameter ${param.name} is listed twice.`);
|
|
5086
|
+
}
|
|
5087
|
+
params.push(param.name);
|
|
5088
|
+
}
|
|
5089
|
+
const body = node.args[node.args.length - 1];
|
|
5090
|
+
if (body.type === "Missing") return this.fail("#VALUE!", "LAMBDA needs a calculation as its last argument.");
|
|
5091
|
+
return new FormulaLambda(params, body, this.env, this.defaultSheet);
|
|
5092
|
+
}
|
|
5093
|
+
/**
|
|
5094
|
+
* The function argument of a LAMBDA helper: a lambda, or a built-in
|
|
5095
|
+
* function given by name (`BYROW(A1:C3, SUM)`) unless a variable or defined
|
|
5096
|
+
* name has that name. Returns an error string when it is neither.
|
|
5097
|
+
*/
|
|
5098
|
+
lambdaArgument(fn, node, index) {
|
|
5099
|
+
if (!node) return this.fail("#VALUE!", `${fn} needs a LAMBDA function.`);
|
|
5100
|
+
if (node.type === "Name" && node.sheet === void 0 && !lookupEnvironment(this.env, node.name) && !this.lookupDefinedName(node.name)) {
|
|
5101
|
+
const def = getFormulaFunction(node.name);
|
|
5102
|
+
if (def && !SPECIAL_FORMS.has(def.name)) {
|
|
5103
|
+
return new FormulaLambda([], null, null, void 0, (values) => this.callBuiltinWithValues(def, values), def.name);
|
|
5104
|
+
}
|
|
5105
|
+
}
|
|
5106
|
+
const value = this.evaluateNode(node);
|
|
5107
|
+
if (isFormulaLambda(value)) return value;
|
|
5108
|
+
if (isFormulaError(value)) return value;
|
|
5109
|
+
return this.fail("#VALUE!", `Argument ${index + 1} of ${fn} must be a LAMBDA function.`);
|
|
5110
|
+
}
|
|
5111
|
+
/** Calls a built-in function with already evaluated arguments. */
|
|
5112
|
+
callBuiltinWithValues(def, values) {
|
|
5113
|
+
const countError = checkFormulaArgumentCount(def, values.length);
|
|
5114
|
+
if (countError) return this.fail("#VALUE!", countError);
|
|
5115
|
+
const args = values.map((value, index) => ({ index, node: LITERAL_BLANK, value: () => value }));
|
|
5116
|
+
if (def.elementwise) {
|
|
5117
|
+
const lifted = this.liftOverArrays(def, args);
|
|
5118
|
+
if (lifted !== void 0) return lifted;
|
|
5119
|
+
}
|
|
5120
|
+
return this.finishCall(def, def.evaluate(this.createContext(def, args)));
|
|
5121
|
+
}
|
|
5122
|
+
/** Checks a lambda takes `count` parameters (native functions take any count). */
|
|
5123
|
+
checkArity(fn, lambda, count) {
|
|
5124
|
+
if (lambda.native || lambda.params.length === count) return null;
|
|
5125
|
+
return this.fail(
|
|
5126
|
+
"#VALUE!",
|
|
5127
|
+
`The LAMBDA passed to ${fn} must take ${count} parameter${count === 1 ? "" : "s"}, but it takes ${lambda.params.length}.`
|
|
5128
|
+
);
|
|
5129
|
+
}
|
|
5130
|
+
/** MAP(array1, [array2, ...], lambda): calls the lambda with each element. */
|
|
5131
|
+
evaluateMap(node) {
|
|
5132
|
+
const lambda = this.lambdaArgument("MAP", node.args[node.args.length - 1], node.args.length - 1);
|
|
5133
|
+
if (typeof lambda === "string") return lambda;
|
|
5134
|
+
const arrays = [];
|
|
5135
|
+
for (let i = 0; i < node.args.length - 1; i++) {
|
|
5136
|
+
const value = this.evaluateNode(node.args[i]);
|
|
5137
|
+
if (isFormulaError(value)) return value;
|
|
5138
|
+
arrays.push(value);
|
|
5139
|
+
}
|
|
5140
|
+
const arity = this.checkArity("MAP", lambda, arrays.length);
|
|
5141
|
+
if (arity) return arity;
|
|
5142
|
+
let rows = 1;
|
|
5143
|
+
let cols = 1;
|
|
5144
|
+
for (const array of arrays) {
|
|
5145
|
+
const shape = shapeOf(array);
|
|
5146
|
+
rows = Math.max(rows, shape.rows);
|
|
5147
|
+
cols = Math.max(cols, shape.cols);
|
|
4641
5148
|
}
|
|
5149
|
+
if (rows * cols > MAX_FORMULA_ARRAY_SIZE) return this.fail("#NUM!", "The array result is too large.");
|
|
5150
|
+
return buildArray(
|
|
5151
|
+
rows,
|
|
5152
|
+
cols,
|
|
5153
|
+
(r, c) => elementValue(this.invokeWithValues(lambda, arrays.map((array) => broadcastGet(array, r, c))))
|
|
5154
|
+
);
|
|
5155
|
+
}
|
|
5156
|
+
/**
|
|
5157
|
+
* REDUCE([initial_value], array, lambda) / SCAN(...): folds the array row
|
|
5158
|
+
* by row with lambda(accumulator, value). SCAN returns every intermediate
|
|
5159
|
+
* value, in the array's shape.
|
|
5160
|
+
*/
|
|
5161
|
+
evaluateReduce(node, scan) {
|
|
5162
|
+
const fn = scan ? "SCAN" : "REDUCE";
|
|
5163
|
+
let acc = this.evaluateNode(node.args[0]);
|
|
5164
|
+
const arrayValue = this.evaluateNode(node.args[1]);
|
|
5165
|
+
if (isFormulaError(arrayValue)) return arrayValue;
|
|
5166
|
+
const lambda = this.lambdaArgument(fn, node.args[2], 2);
|
|
5167
|
+
if (typeof lambda === "string") return lambda;
|
|
5168
|
+
const arity = this.checkArity(fn, lambda, 2);
|
|
5169
|
+
if (arity) return arity;
|
|
5170
|
+
const range = asRange(arrayValue);
|
|
5171
|
+
const results = scan ? new Array(range.rows * range.cols) : [];
|
|
5172
|
+
for (let r = 0; r < range.rows; r++) {
|
|
5173
|
+
for (let c = 0; c < range.cols; c++) {
|
|
5174
|
+
acc = this.invokeWithValues(lambda, [acc, rangeGet(range, r, c)]);
|
|
5175
|
+
if (scan) results[c * range.rows + r] = elementValue(acc);
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5178
|
+
if (scan) return withRangeShape(results, range.rows, range.cols);
|
|
5179
|
+
return acc;
|
|
5180
|
+
}
|
|
5181
|
+
/** BYROW(array, lambda) / BYCOL(array, lambda): one result per row (a column) or per column (a row). */
|
|
5182
|
+
evaluateByLine(node, byRow) {
|
|
5183
|
+
const fn = byRow ? "BYROW" : "BYCOL";
|
|
5184
|
+
const arrayValue = this.evaluateNode(node.args[0]);
|
|
5185
|
+
if (isFormulaError(arrayValue)) return arrayValue;
|
|
5186
|
+
const lambda = this.lambdaArgument(fn, node.args[1], 1);
|
|
5187
|
+
if (typeof lambda === "string") return lambda;
|
|
5188
|
+
const arity = this.checkArity(fn, lambda, 1);
|
|
5189
|
+
if (arity) return arity;
|
|
5190
|
+
const range = asRange(arrayValue);
|
|
5191
|
+
const count = byRow ? range.rows : range.cols;
|
|
5192
|
+
const out = [];
|
|
5193
|
+
for (let i = 0; i < count; i++) {
|
|
5194
|
+
const line = [];
|
|
5195
|
+
const length = byRow ? range.cols : range.rows;
|
|
5196
|
+
for (let j = 0; j < length; j++) line.push(byRow ? rangeGet(range, i, j) : rangeGet(range, j, i));
|
|
5197
|
+
const slice = byRow ? withRangeShape(line, 1, length) : withRangeShape(line, length, 1);
|
|
5198
|
+
out.push(elementValue(this.invokeWithValues(lambda, [slice])));
|
|
5199
|
+
}
|
|
5200
|
+
return byRow ? withRangeShape(out, count, 1) : withRangeShape(out, 1, count);
|
|
5201
|
+
}
|
|
5202
|
+
/** MAKEARRAY(rows, columns, lambda): lambda(row, column) for every 1-based position. */
|
|
5203
|
+
evaluateMakeArray(node) {
|
|
5204
|
+
const size = (index) => {
|
|
5205
|
+
const value = firstValue(this.evaluateNode(node.args[index]));
|
|
5206
|
+
if (isFormulaError(value)) return value;
|
|
5207
|
+
const num = toNumber(value);
|
|
5208
|
+
if (isFormulaError(num)) return this.fail("#VALUE!", `Argument ${index + 1} of MAKEARRAY must be a number.`);
|
|
5209
|
+
return Math.trunc(num);
|
|
5210
|
+
};
|
|
5211
|
+
const rows = size(0);
|
|
5212
|
+
if (typeof rows === "string") return rows;
|
|
5213
|
+
const cols = size(1);
|
|
5214
|
+
if (typeof cols === "string") return cols;
|
|
5215
|
+
if (rows < 1 || cols < 1) return this.fail("#VALUE!", "MAKEARRAY needs at least one row and one column.");
|
|
5216
|
+
if (rows * cols > MAX_FORMULA_ARRAY_SIZE) return this.fail("#NUM!", "The array result is too large.");
|
|
5217
|
+
const lambda = this.lambdaArgument("MAKEARRAY", node.args[2], 2);
|
|
5218
|
+
if (typeof lambda === "string") return lambda;
|
|
5219
|
+
const arity = this.checkArity("MAKEARRAY", lambda, 2);
|
|
5220
|
+
if (arity) return arity;
|
|
5221
|
+
return buildArray(rows, cols, (r, c) => elementValue(this.invokeWithValues(lambda, [r + 1, c + 1])));
|
|
4642
5222
|
}
|
|
4643
5223
|
createContext(def, args) {
|
|
4644
5224
|
const has = (index) => index >= 0 && index < args.length;
|
|
@@ -4728,12 +5308,16 @@ export {
|
|
|
4728
5308
|
parseRange,
|
|
4729
5309
|
FormulaParser,
|
|
4730
5310
|
parseFormula,
|
|
5311
|
+
FormulaLambda,
|
|
5312
|
+
isFormulaLambda,
|
|
5313
|
+
MAX_LAMBDA_DEPTH,
|
|
4731
5314
|
serialFromYmd,
|
|
4732
5315
|
parseCellInput,
|
|
4733
5316
|
parseNumberText,
|
|
4734
5317
|
FORMULA_ERRORS,
|
|
4735
5318
|
isFormulaError,
|
|
4736
5319
|
isBlank,
|
|
5320
|
+
toBoolean,
|
|
4737
5321
|
toText,
|
|
4738
5322
|
numberToText,
|
|
4739
5323
|
numericValue,
|
|
@@ -4743,6 +5327,7 @@ export {
|
|
|
4743
5327
|
colNameToIndex,
|
|
4744
5328
|
indexToColName,
|
|
4745
5329
|
parseIsoDate,
|
|
5330
|
+
serialToDate,
|
|
4746
5331
|
wildcardToRegExp,
|
|
4747
5332
|
parseCriteria,
|
|
4748
5333
|
formatGeneralFit,
|
|
@@ -4768,4 +5353,4 @@ export {
|
|
|
4768
5353
|
MAX_FORMULA_ARRAY_SIZE,
|
|
4769
5354
|
FormulaEvaluator
|
|
4770
5355
|
};
|
|
4771
|
-
//# sourceMappingURL=chunk-
|
|
5356
|
+
//# sourceMappingURL=chunk-ZRNHGAQT.js.map
|