@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.
Files changed (162) hide show
  1. package/README.md +101 -1
  2. package/THIRD_PARTY_NOTICES.md +40 -0
  3. package/dist/agent/index.cjs +175 -11
  4. package/dist/agent/index.cjs.map +1 -1
  5. package/dist/agent/index.d.cts +18 -4
  6. package/dist/agent/index.d.ts +18 -4
  7. package/dist/agent/index.js +168 -4
  8. package/dist/agent/index.js.map +1 -1
  9. package/dist/{arrangeShortcuts-B8O3f8-P.d.cts → arrange-BICutYPk.d.cts} +243 -29
  10. package/dist/{arrangeShortcuts-sLlD2rSX.d.ts → arrange-BICutYPk.d.ts} +243 -29
  11. package/dist/builtins-CdPcLwwF.d.cts +176 -0
  12. package/dist/builtins-Chj6zv_O.d.ts +176 -0
  13. package/dist/canvas/index.cjs +8588 -1857
  14. package/dist/canvas/index.cjs.map +1 -1
  15. package/dist/canvas/index.d.cts +1847 -9
  16. package/dist/canvas/index.d.ts +1847 -9
  17. package/dist/canvas/index.js +8577 -1846
  18. package/dist/canvas/index.js.map +1 -1
  19. package/dist/chunk-3CC7OPUQ.cjs +831 -0
  20. package/dist/chunk-3CC7OPUQ.cjs.map +1 -0
  21. package/dist/chunk-4BVP64JJ.cjs +1406 -0
  22. package/dist/chunk-4BVP64JJ.cjs.map +1 -0
  23. package/dist/chunk-BVP3TSLH.cjs +69 -0
  24. package/dist/chunk-BVP3TSLH.cjs.map +1 -0
  25. package/dist/{chunk-OLZJXL4R.cjs → chunk-CS6NZWWA.cjs} +59 -25
  26. package/dist/chunk-CS6NZWWA.cjs.map +1 -0
  27. package/dist/{chunk-MCSJFWAO.cjs → chunk-EWCOFSCQ.cjs} +685 -100
  28. package/dist/chunk-EWCOFSCQ.cjs.map +1 -0
  29. package/dist/chunk-HDYMLS3X.cjs +409 -0
  30. package/dist/chunk-HDYMLS3X.cjs.map +1 -0
  31. package/dist/chunk-HRMEDT2L.js +1406 -0
  32. package/dist/chunk-HRMEDT2L.js.map +1 -0
  33. package/dist/{chunk-D27YFC46.cjs → chunk-IFHKKVXH.cjs} +382 -63
  34. package/dist/chunk-IFHKKVXH.cjs.map +1 -0
  35. package/dist/{chunk-PGNZKFQS.js → chunk-JOIREKL7.js} +137 -58
  36. package/dist/chunk-JOIREKL7.js.map +1 -0
  37. package/dist/chunk-KRGXUQ2Y.js +1696 -0
  38. package/dist/chunk-KRGXUQ2Y.js.map +1 -0
  39. package/dist/{chunk-3LV6RRPH.js → chunk-MHEPLBFB.js} +2 -2
  40. package/dist/chunk-MR2764X2.js +831 -0
  41. package/dist/chunk-MR2764X2.js.map +1 -0
  42. package/dist/{chunk-XNORY7M6.cjs → chunk-NFVBT3UZ.cjs} +166 -87
  43. package/dist/chunk-NFVBT3UZ.cjs.map +1 -0
  44. package/dist/chunk-NQF32I6Z.cjs +6478 -0
  45. package/dist/chunk-NQF32I6Z.cjs.map +1 -0
  46. package/dist/chunk-O5RYH2PG.js +69 -0
  47. package/dist/chunk-O5RYH2PG.js.map +1 -0
  48. package/dist/{chunk-ITBSKI26.js → chunk-Q2OHWD2A.js} +1064 -360
  49. package/dist/chunk-Q2OHWD2A.js.map +1 -0
  50. package/dist/{chunk-FODNLX5N.js → chunk-QJSOZFCZ.js} +38 -4
  51. package/dist/chunk-QJSOZFCZ.js.map +1 -0
  52. package/dist/{chunk-SML64VJG.cjs → chunk-RRREAOVM.cjs} +4 -4
  53. package/dist/{chunk-SML64VJG.cjs.map → chunk-RRREAOVM.cjs.map} +1 -1
  54. package/dist/{chunk-5S3Z4PPF.js → chunk-SWE3NPOU.js} +347 -28
  55. package/dist/chunk-SWE3NPOU.js.map +1 -0
  56. package/dist/chunk-TXXNBDQX.cjs +1696 -0
  57. package/dist/chunk-TXXNBDQX.cjs.map +1 -0
  58. package/dist/chunk-UBCFIABU.js +409 -0
  59. package/dist/chunk-UBCFIABU.js.map +1 -0
  60. package/dist/chunk-X7HXL2SB.js +6478 -0
  61. package/dist/chunk-X7HXL2SB.js.map +1 -0
  62. package/dist/{chunk-SBCZ6RN5.cjs → chunk-YOMYSFJU.cjs} +1163 -459
  63. package/dist/chunk-YOMYSFJU.cjs.map +1 -0
  64. package/dist/{chunk-3CN2WD3X.js → chunk-ZRNHGAQT.js} +661 -76
  65. package/dist/chunk-ZRNHGAQT.js.map +1 -0
  66. package/dist/commands/index.cjs +425 -4
  67. package/dist/commands/index.cjs.map +1 -1
  68. package/dist/commands/index.d.cts +39 -77
  69. package/dist/commands/index.d.ts +39 -77
  70. package/dist/commands/index.js +424 -3
  71. package/dist/commands/index.js.map +1 -1
  72. package/dist/compare/index.cjs +3 -3
  73. package/dist/compare/index.d.cts +1 -1
  74. package/dist/compare/index.d.ts +1 -1
  75. package/dist/compare/index.js +2 -2
  76. package/dist/controller-CxXGOEmy.d.cts +2038 -0
  77. package/dist/controller-CxXGOEmy.d.ts +2038 -0
  78. package/dist/{documents-BQjzJNe2.d.ts → documents-CHT1U5Vs.d.ts} +10 -0
  79. package/dist/{documents-KRTPhvst.d.cts → documents-Gp7wb0Me.d.cts} +10 -0
  80. package/dist/export/index.cjs +36 -0
  81. package/dist/export/index.cjs.map +1 -0
  82. package/dist/export/index.d.cts +243 -0
  83. package/dist/export/index.d.ts +243 -0
  84. package/dist/export/index.js +36 -0
  85. package/dist/export/index.js.map +1 -0
  86. package/dist/extensions/index.cjs +101 -6
  87. package/dist/extensions/index.cjs.map +1 -1
  88. package/dist/extensions/index.d.cts +18 -5
  89. package/dist/extensions/index.d.ts +18 -5
  90. package/dist/extensions/index.js +101 -6
  91. package/dist/generation/index.cjs +1013 -69
  92. package/dist/generation/index.cjs.map +1 -1
  93. package/dist/generation/index.d.cts +474 -13
  94. package/dist/generation/index.d.ts +474 -13
  95. package/dist/generation/index.js +976 -32
  96. package/dist/generation/index.js.map +1 -1
  97. package/dist/image-editor/index.cjs +3385 -1275
  98. package/dist/image-editor/index.cjs.map +1 -1
  99. package/dist/image-editor/index.d.cts +693 -128
  100. package/dist/image-editor/index.d.ts +693 -128
  101. package/dist/image-editor/index.js +3325 -1215
  102. package/dist/image-editor/index.js.map +1 -1
  103. package/dist/{index-fJrlkJCd.d.ts → index-B273Zllk.d.ts} +500 -7
  104. package/dist/{index-BhJBSm2d.d.cts → index-jcJwmBYt.d.cts} +500 -7
  105. package/dist/index.cjs +1603 -45
  106. package/dist/index.cjs.map +1 -1
  107. package/dist/index.d.cts +154 -27
  108. package/dist/index.d.ts +154 -27
  109. package/dist/index.js +1584 -26
  110. package/dist/index.js.map +1 -1
  111. package/dist/mention-text-CDnCFd3c.d.cts +53 -0
  112. package/dist/mention-text-CDnCFd3c.d.ts +53 -0
  113. package/dist/model-C1zApQM8.d.cts +219 -0
  114. package/dist/model-C1zApQM8.d.ts +219 -0
  115. package/dist/persistence/index.d.cts +3 -120
  116. package/dist/persistence/index.d.ts +3 -120
  117. package/dist/registry-CED5C0lA.d.cts +204 -0
  118. package/dist/registry-CED5C0lA.d.ts +204 -0
  119. package/dist/{registry-GJI4gToU.d.cts → registry-CEgwXED1.d.cts} +12 -1
  120. package/dist/{registry-GJI4gToU.d.ts → registry-CEgwXED1.d.ts} +12 -1
  121. package/dist/{registry-D32LpIc4.d.cts → registry-D0GADKc7.d.cts} +1 -1
  122. package/dist/{registry-evKTP4ls.d.ts → registry-DhVUZioj.d.ts} +1 -1
  123. package/dist/serializers/index.cjs +12 -4
  124. package/dist/serializers/index.cjs.map +1 -1
  125. package/dist/serializers/index.d.cts +41 -13
  126. package/dist/serializers/index.d.ts +41 -13
  127. package/dist/serializers/index.js +12 -4
  128. package/dist/spreadsheet/index.cjs +157 -3
  129. package/dist/spreadsheet/index.cjs.map +1 -1
  130. package/dist/spreadsheet/index.d.cts +534 -11
  131. package/dist/spreadsheet/index.d.ts +534 -11
  132. package/dist/spreadsheet/index.js +157 -3
  133. package/dist/storage-BpFfspHm.d.cts +120 -0
  134. package/dist/storage-BpFfspHm.d.ts +120 -0
  135. package/dist/tokens/index.cjs +115 -0
  136. package/dist/tokens/index.cjs.map +1 -0
  137. package/dist/tokens/index.d.cts +212 -0
  138. package/dist/tokens/index.d.ts +212 -0
  139. package/dist/tokens/index.js +115 -0
  140. package/dist/tokens/index.js.map +1 -0
  141. package/dist/{track-changes-BK5sVf4_.d.cts → track-changes-D_VAPELi.d.cts} +95 -11
  142. package/dist/{track-changes-CDjCrwKe.d.ts → track-changes-Db_fGEvx.d.ts} +95 -11
  143. package/package.json +15 -1
  144. package/dist/chunk-3CN2WD3X.js.map +0 -1
  145. package/dist/chunk-5S3Z4PPF.js.map +0 -1
  146. package/dist/chunk-D27YFC46.cjs.map +0 -1
  147. package/dist/chunk-FODNLX5N.js.map +0 -1
  148. package/dist/chunk-ITBSKI26.js.map +0 -1
  149. package/dist/chunk-MA2F5KHF.cjs +0 -3223
  150. package/dist/chunk-MA2F5KHF.cjs.map +0 -1
  151. package/dist/chunk-MCSJFWAO.cjs.map +0 -1
  152. package/dist/chunk-OLZJXL4R.cjs.map +0 -1
  153. package/dist/chunk-PGNZKFQS.js.map +0 -1
  154. package/dist/chunk-SBCZ6RN5.cjs.map +0 -1
  155. package/dist/chunk-U5KWZU4X.js +0 -3223
  156. package/dist/chunk-U5KWZU4X.js.map +0 -1
  157. package/dist/chunk-XNORY7M6.cjs.map +0 -1
  158. package/dist/controller-BhLLAoi8.d.cts +0 -1000
  159. package/dist/controller-BhLLAoi8.d.ts +0 -1000
  160. package/dist/types-DX4Mh4SL.d.cts +0 -217
  161. package/dist/types-DX4Mh4SL.d.ts +0 -217
  162. /package/dist/{chunk-3LV6RRPH.js.map → chunk-MHEPLBFB.js.map} +0 -0
@@ -1,4 +1,30 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// 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 (/[a-zA-Z_$]/.test(char)) {
147
+ if (isWordStart(char)) {
113
148
  const start = i;
114
- while (i < input.length && /[a-zA-Z0-9_$.]/.test(input[i])) {
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 && /[a-zA-Z0-9_$.]/.test(input[i])) {
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
- const upper = rawWord.toUpperCase();
138
- if (upper === "TRUE" || upper === "FALSE") {
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 = _nullishCoalesce(_optionalChain([qualified, 'optionalAccess', _2 => _2.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 = class {
209
276
  __init() {this.tokens = []}
210
277
  __init2() {this.current = 0}
@@ -275,7 +342,22 @@ var FormulaParser = (_class = class {
275
342
  const argument = this.unary();
276
343
  return { type: "UnaryOp", op, argument };
277
344
  }
278
- return this.primary();
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 = 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 = class {
325
412
  const name = token.value;
326
413
  this.advance();
327
414
  if (this.check("LPAREN")) {
328
- this.consume("LPAREN", `Expected '(' after function ${name}`);
329
- const args = [];
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("RPAREN", `Expected ')' after function arguments`);
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
- return { type: "Name", name };
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
+
492
+
493
+
494
+
495
+
496
+
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!",
@@ -597,7 +712,7 @@ function parseIsoDate(value) {
597
712
  var isoDateHasTime = (value) => /[ T]\d/.test(value.trim());
598
713
  function isoDateTime(value) {
599
714
  if (value.length < 8 || value.charCodeAt(4) !== 45) return null;
600
- return _nullishCoalesce(_optionalChain([parseIsoDate, 'call', _2 => _2(value), 'optionalAccess', _3 => _3.getTime, 'call', _4 => _4()]), () => ( null));
715
+ return _nullishCoalesce(_optionalChain([parseIsoDate, 'call', _3 => _3(value), 'optionalAccess', _4 => _4.getTime, 'call', _5 => _5()]), () => ( null));
601
716
  }
602
717
  function dateToSerial(date) {
603
718
  const utc = Date.UTC(
@@ -1170,14 +1285,14 @@ var BUILTINS = [
1170
1285
  [49, "@"]
1171
1286
  ];
1172
1287
  function builtinNumberFormat(id) {
1173
- return _optionalChain([BUILTINS, 'access', _5 => _5.find, 'call', _6 => _6(([i]) => i === id), 'optionalAccess', _7 => _7[1]]);
1288
+ return _optionalChain([BUILTINS, 'access', _6 => _6.find, 'call', _7 => _7(([i]) => i === id), 'optionalAccess', _8 => _8[1]]);
1174
1289
  }
1175
1290
  function builtinNumberFormatId(code) {
1176
1291
  const exact = BUILTINS.find(([, c]) => c === code);
1177
1292
  if (exact) return exact[0];
1178
1293
  const norm = (s) => s.replace(/\\/g, "").toLowerCase();
1179
1294
  const n = norm(code);
1180
- return _optionalChain([BUILTINS, 'access', _8 => _8.find, 'call', _9 => _9(([, c]) => norm(c) === n), 'optionalAccess', _10 => _10[0]]);
1295
+ return _optionalChain([BUILTINS, 'access', _9 => _9.find, 'call', _10 => _10(([, c]) => norm(c) === n), 'optionalAccess', _11 => _11[0]]);
1181
1296
  }
1182
1297
 
1183
1298
  // src/spreadsheet/numfmt/parse.ts
@@ -1220,7 +1335,7 @@ function testCond(c, v) {
1220
1335
  return v >= c.value;
1221
1336
  }
1222
1337
  }
1223
- var isDigitTok = (t, c) => _optionalChain([t, 'optionalAccess', _11 => _11.t]) === "digit" && (c === void 0 || t.c === c);
1338
+ var isDigitTok = (t, c) => _optionalChain([t, 'optionalAccess', _12 => _12.t]) === "digit" && (c === void 0 || t.c === c);
1224
1339
  function splitSections(code) {
1225
1340
  const cs = Array.from(code);
1226
1341
  const lastClose = cs.lastIndexOf("]");
@@ -1272,7 +1387,7 @@ function startsWithCi(cs, i, pat) {
1272
1387
  function pushLit(toks, s) {
1273
1388
  if (s === "") return;
1274
1389
  const last = toks[toks.length - 1];
1275
- if (_optionalChain([last, 'optionalAccess', _12 => _12.t]) === "lit") last.s += s;
1390
+ if (_optionalChain([last, 'optionalAccess', _13 => _13.t]) === "lit") last.s += s;
1276
1391
  else toks.push({ t: "lit", s });
1277
1392
  }
1278
1393
  var CURRENCY_CHARS = /* @__PURE__ */ new Set(["$", "\u20AC", "\xA3", "\xA5", "\u20B9", "\u20A9", "\u20BD", "\xA2", "\u20AA", "\u20AB", "\u0E3F"]);
@@ -1430,7 +1545,7 @@ function tokenize(src) {
1430
1545
  }
1431
1546
  return raw;
1432
1547
  }
1433
- var datePartOf = (t) => _optionalChain([t, 'optionalAccess', _13 => _13.t]) === "date" ? t.d : void 0;
1548
+ var datePartOf = (t) => _optionalChain([t, 'optionalAccess', _14 => _14.t]) === "date" ? t.d : void 0;
1434
1549
  function analyze(raw) {
1435
1550
  const toks = raw.toks;
1436
1551
  const isDate = toks.some((t) => t.t === "date");
@@ -1471,8 +1586,8 @@ function analyze(raw) {
1471
1586
  if (t.t !== "date" || t.d.k !== "month" || t.d.n > 2) continue;
1472
1587
  const prev = prevPart[i2];
1473
1588
  const nxt = nextPart[i2];
1474
- const afterH = _optionalChain([prev, 'optionalAccess', _14 => _14.k]) === "hour" || _optionalChain([prev, 'optionalAccess', _15 => _15.k]) === "eh";
1475
- const beforeS = _optionalChain([nxt, 'optionalAccess', _16 => _16.k]) === "second" || _optionalChain([nxt, 'optionalAccess', _17 => _17.k]) === "es";
1589
+ const afterH = _optionalChain([prev, 'optionalAccess', _15 => _15.k]) === "hour" || _optionalChain([prev, 'optionalAccess', _16 => _16.k]) === "eh";
1590
+ const beforeS = _optionalChain([nxt, 'optionalAccess', _17 => _17.k]) === "second" || _optionalChain([nxt, 'optionalAccess', _18 => _18.k]) === "es";
1476
1591
  if (afterH || beforeS) toks[i2] = { t: "date", d: { k: "minute", n: t.d.n } };
1477
1592
  }
1478
1593
  const out2 = [];
@@ -1575,7 +1690,7 @@ function analyze(raw) {
1575
1690
  break;
1576
1691
  }
1577
1692
  case "slash": {
1578
- const nextOk = _optionalChain([toks, 'access', _18 => _18[i + 1], 'optionalAccess', _19 => _19.t]) === "denom" || _optionalChain([firstNonLitAfter, 'access', _20 => _20[i], 'optionalAccess', _21 => _21.t]) === "digit";
1693
+ const nextOk = _optionalChain([toks, 'access', _19 => _19[i + 1], 'optionalAccess', _20 => _20.t]) === "denom" || _optionalChain([firstNonLitAfter, 'access', _21 => _21[i], 'optionalAccess', _22 => _22.t]) === "digit";
1579
1694
  const hasExp = seenExp || lastExp > i;
1580
1695
  if (!fractionSlash && seenDigit && nextOk && !seenPoint && !hasExp) {
1581
1696
  fractionSlash = true;
@@ -1586,7 +1701,7 @@ function analyze(raw) {
1586
1701
  break;
1587
1702
  }
1588
1703
  case "denom":
1589
- if (!(_optionalChain([toks, 'access', _22 => _22[i - 1], 'optionalAccess', _23 => _23.t]) === "slash" && sec.fraction)) replace = { t: "lit", s: t.s };
1704
+ if (!(_optionalChain([toks, 'access', _23 => _23[i - 1], 'optionalAccess', _24 => _24.t]) === "slash" && sec.fraction)) replace = { t: "lit", s: t.s };
1590
1705
  break;
1591
1706
  default:
1592
1707
  break;
@@ -1748,7 +1863,7 @@ function renderDate(sec, v) {
1748
1863
  }
1749
1864
  var digitChar = (toks, i) => {
1750
1865
  const t = toks[i];
1751
- return _optionalChain([t, 'optionalAccess', _24 => _24.t]) === "digit" ? t.c : "0";
1866
+ return _optionalChain([t, 'optionalAccess', _25 => _25.t]) === "digit" ? t.c : "0";
1752
1867
  };
1753
1868
  function fillInt(toks, idxs, digits, thousands, forceZero, pieces) {
1754
1869
  let rem = digits.length;
@@ -1807,7 +1922,7 @@ function fillFrac(toks, idxs, frac, pieces) {
1807
1922
  }
1808
1923
  function digitIndices(toks, start, end) {
1809
1924
  const out = [];
1810
- for (let i = start; i < end; i++) if (_optionalChain([toks, 'access', _25 => _25[i], 'optionalAccess', _26 => _26.t]) === "digit") out.push(i);
1925
+ for (let i = start; i < end; i++) if (_optionalChain([toks, 'access', _26 => _26[i], 'optionalAccess', _27 => _27.t]) === "digit") out.push(i);
1811
1926
  return out;
1812
1927
  }
1813
1928
  function renderNumber(sec, v) {
@@ -1926,7 +2041,7 @@ function blankRange(toks, start, end, spaces, pieces, lits) {
1926
2041
  }
1927
2042
  function renderFraction(sec, x, pieces, lits) {
1928
2043
  const toks = sec.toks;
1929
- const isDigit = (i) => _optionalChain([toks, 'access', _27 => _27[i], 'optionalAccess', _28 => _28.t]) === "digit";
2044
+ const isDigit = (i) => _optionalChain([toks, 'access', _28 => _28[i], 'optionalAccess', _29 => _29.t]) === "digit";
1930
2045
  const slash = toks.findIndex((t) => t.t === "slash");
1931
2046
  if (slash === -1) return;
1932
2047
  let numLast = -1;
@@ -1945,13 +2060,13 @@ function renderFraction(sec, x, pieces, lits) {
1945
2060
  let fixedDen;
1946
2061
  let denStart;
1947
2062
  const afterSlash = toks[slash + 1];
1948
- if (_optionalChain([afterSlash, 'optionalAccess', _29 => _29.t]) === "denom") {
2063
+ if (_optionalChain([afterSlash, 'optionalAccess', _30 => _30.t]) === "denom") {
1949
2064
  const parsed = Number(afterSlash.s);
1950
2065
  fixedDen = Math.min(Math.max(Number.isFinite(parsed) ? parsed : 1e9, 1), 1e9);
1951
2066
  denStart = slash + 1;
1952
2067
  } else {
1953
2068
  denStart = slash + 1;
1954
- while (_optionalChain([toks, 'access', _30 => _30[denStart], 'optionalAccess', _31 => _31.t]) === "lit") denStart += 1;
2069
+ while (_optionalChain([toks, 'access', _31 => _31[denStart], 'optionalAccess', _32 => _32.t]) === "lit") denStart += 1;
1955
2070
  }
1956
2071
  let denEnd = denStart;
1957
2072
  if (fixedDen !== void 0) denEnd += 1;
@@ -2009,7 +2124,7 @@ function renderFraction(sec, x, pieces, lits) {
2009
2124
  }
2010
2125
  }
2011
2126
  function isDigitTok2(t, c) {
2012
- return _optionalChain([t, 'optionalAccess', _32 => _32.t]) === "digit" && t.c === c;
2127
+ return _optionalChain([t, 'optionalAccess', _33 => _33.t]) === "digit" && t.c === c;
2013
2128
  }
2014
2129
 
2015
2130
  // src/spreadsheet/numfmt/index.ts
@@ -2041,7 +2156,7 @@ var NumberFormat = class _NumberFormat {
2041
2156
  this.general = false;
2042
2157
  this.sections = parseSections(code);
2043
2158
  const len = this.sections.length;
2044
- const lastIsText = _optionalChain([this, 'access', _33 => _33.sections, 'access', _34 => _34[len - 1], 'optionalAccess', _35 => _35.kind]) === "text";
2159
+ const lastIsText = _optionalChain([this, 'access', _34 => _34.sections, 'access', _35 => _35[len - 1], 'optionalAccess', _36 => _36.kind]) === "text";
2045
2160
  this.textSection = len >= 4 ? 3 : lastIsText ? len - 1 : void 0;
2046
2161
  this.numSections = len >= 4 ? 3 : lastIsText ? len - 1 : len;
2047
2162
  }
@@ -2118,7 +2233,7 @@ var NumberFormat = class _NumberFormat {
2118
2233
  const s0 = secs[0];
2119
2234
  if (!s0) return null;
2120
2235
  const c0 = s0.cond;
2121
- const c1 = _optionalChain([secs, 'access', _36 => _36[1], 'optionalAccess', _37 => _37.cond]);
2236
+ const c1 = _optionalChain([secs, 'access', _37 => _37[1], 'optionalAccess', _38 => _38.cond]);
2122
2237
  if (c0 || c1) {
2123
2238
  let idx;
2124
2239
  if (c0 && testCond(c0, v)) idx = 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,12 +4488,31 @@ 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 = (_class3 = class {
4293
4504
 
4294
4505
  __init5() {this.diagnostics = []}
4295
- // LET variables, innermost scope last.
4296
- __init6() {this.scopes = []}
4297
- constructor(context = {}) {;_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);
4506
+ // LET and LAMBDA variables (innermost first, chained to outer scopes).
4507
+ __init6() {this.env = null}
4508
+ // Sheet that unqualified references refer to while evaluating a
4509
+ // sheet-scoped defined name (undefined: the formula's own sheet).
4510
+ __init7() {this.defaultSheet = void 0}
4511
+ __init8() {this.lambdaDepth = 0}
4512
+ // Defined names being evaluated, to stop names that refer to themselves.
4513
+ __init9() {this.nameStack = []}
4514
+ __init10() {this.parsedNames = /* @__PURE__ */ new Map()}
4515
+ constructor(context = {}) {;_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);
4298
4516
  this.context = context;
4299
4517
  }
4300
4518
  /**
@@ -4303,7 +4521,14 @@ var FormulaEvaluator = (_class3 = class {
4303
4521
  */
4304
4522
  evaluate(astOrFormula) {
4305
4523
  const ast = typeof astOrFormula === "string" ? parseFormula(astOrFormula) : astOrFormula;
4306
- return this.evaluateNode(ast);
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 = (_class3 = class {
4313
4538
  */
4314
4539
  evaluateWithDiagnostics(astOrFormula) {
4315
4540
  const saved = this.diagnostics;
4316
- const savedScopes = this.scopes;
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.scopes = [];
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;
@@ -4329,17 +4560,23 @@ var FormulaEvaluator = (_class3 = class {
4329
4560
  const result = {
4330
4561
  value,
4331
4562
  error: code,
4332
- message: _nullishCoalesce(_optionalChain([diagnostic, 'optionalAccess', _38 => _38.message]), () => ( describeFormulaError(code)))
4563
+ message: _nullishCoalesce(_optionalChain([diagnostic, 'optionalAccess', _39 => _39.message]), () => ( describeFormulaError(code)))
4333
4564
  };
4334
4565
  if (array) result.array = array;
4335
4566
  return result;
4336
4567
  }
4337
4568
  return array ? { value, array } : { value };
4338
4569
  } catch (err) {
4339
- return { value: "#ERROR!", error: "#ERROR!", message: _optionalChain([err, 'optionalAccess', _39 => _39.message]) || "Formula error" };
4570
+ if (err instanceof RangeError) {
4571
+ return { value: "#NUM!", error: "#NUM!", message: "The calculation is nested too deeply (recursion limit)." };
4572
+ }
4573
+ return { value: "#ERROR!", error: "#ERROR!", message: _optionalChain([err, 'optionalAccess', _40 => _40.message]) || "Formula error" };
4340
4574
  } finally {
4341
4575
  this.diagnostics = saved;
4342
- this.scopes = savedScopes;
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 = (_class3 = 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
- const value = this.context.resolveCell(node.col, node.row);
4597
+ if (node.sheetEnd !== void 0) return this.evaluate3d(node);
4598
+ const value = this.context.resolveCell(node.col, node.row, _nullishCoalesce(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 ${node.ref} is part of a circular reference.` : `Cell ${node.ref} contains an error (${value}).`
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 = (_class3 = class {
4370
4612
  return this.evaluateRange(node);
4371
4613
  case "SpillRef":
4372
4614
  return this.evaluateSpillRef(node);
4373
- case "Name": {
4374
- for (let i = this.scopes.length - 1; i >= 0; i--) {
4375
- if (this.scopes[i].has(node.name)) return this.scopes[i].get(node.name);
4376
- }
4377
- return this.fail("#NAME?", "This formula contains an unknown name.");
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 = (_class3 = 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 = (_class3 = 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)}).`);
@@ -4517,7 +4767,7 @@ var FormulaEvaluator = (_class3 = class {
4517
4767
  const value = this.evaluateNode(argument);
4518
4768
  if (!Array.isArray(value)) return value;
4519
4769
  if (value.length <= 1) return firstValue(value);
4520
- const cell = _optionalChain([this, 'access', _40 => _40.context, 'access', _41 => _41.getFormulaCell, 'optionalCall', _42 => _42()]);
4770
+ const cell = _optionalChain([this, 'access', _41 => _41.context, 'access', _42 => _42.getFormulaCell, 'optionalCall', _43 => _43()]);
4521
4771
  if (argument.type === "Range" && cell) {
4522
4772
  const range = toFormulaRange(value);
4523
4773
  const top = Math.min(argument.startRow, argument.endRow);
@@ -4537,7 +4787,10 @@ var FormulaEvaluator = (_class3 = 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
- const spill = this.context.resolveSpill(node.col, node.row);
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, _nullishCoalesce(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 = (_class3 = 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
- const values = this.context.resolveRange(node.startCol, node.startRow, node.endCol, node.endRow);
4558
- if (!Array.isArray(values)) return values;
4810
+ if (node.sheetEnd !== void 0) return this.evaluate3d(node);
4811
+ const sheet = _nullishCoalesce(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 = (_class3 = 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) return this.fail("#NAME?", `Unknown function "${node.name.toUpperCase()}".`);
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 === "LET") return this.evaluateLet(node);
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 = (_class3 = class {
4581
4858
  }
4582
4859
  return result === void 0 || result === null ? "" : result;
4583
4860
  }
4584
- lazyArguments(nodes) {
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)) cache.set(index, this.evaluateNode(node));
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
  }));
@@ -4599,7 +4885,7 @@ var FormulaEvaluator = (_class3 = class {
4599
4885
  * multi-value array.
4600
4886
  */
4601
4887
  liftOverArrays(def, args) {
4602
- const liftable = (index) => _optionalChain([def, 'access', _43 => _43.args, 'access', _44 => _44[getFormulaArgumentSpecIndex(def, index)], 'optionalAccess', _45 => _45.type]) !== "range";
4888
+ const liftable = (index) => _optionalChain([def, 'access', _44 => _44.args, 'access', _45 => _45[getFormulaArgumentSpecIndex(def, index)], 'optionalAccess', _46 => _46.type]) !== "range";
4603
4889
  const triggers = def.elementwise === true ? args.map((a) => a.index) : def.elementwise;
4604
4890
  if (!triggers.some((i) => i < args.length && liftable(i) && isMultiArray(args[i].value()))) return void 0;
4605
4891
  let rows = 1;
@@ -4620,25 +4906,319 @@ var FormulaEvaluator = (_class3 = 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 _nullishCoalesce(_optionalChain([this, 'access', _47 => _47.context, 'access', _48 => _48.resolveName, 'optionalCall', _49 => _49(name, _nullishCoalesce(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 = (_nullishCoalesce(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 (e2) {
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 = _optionalChain([this, 'access', _50 => _50.context, 'access', _51 => _51.resolveSheetSpan, 'optionalCall', _52 => _52(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(_nullishCoalesce(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, _nullishCoalesce(values[i], () => ( "")));
5016
+ if (_optionalChain([omitted, 'optionalAccess', _53 => _53.has, 'call', _54 => _54(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 (_optionalChain([arg2, 'optionalAccess', _55 => _55.type]) !== "Name" || arg2.sheet !== void 0) return false;
5040
+ const variables = lookupEnvironment(this.env, arg2.name);
5041
+ return !!_optionalChain([variables, 'optionalAccess', _56 => _56.omitted, 'optionalAccess', _57 => _57.has, 'call', _58 => _58(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 scope = /* @__PURE__ */ new Map();
4629
- this.scopes.push(scope);
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
- scope.set(nameNode.name, this.evaluateNode(node.args[i + 1]));
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.scopes.pop();
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
+ }
4641
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);
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;
@@ -4767,5 +5347,10 @@ var FormulaEvaluator = (_class3 = class {
4767
5347
 
4768
5348
 
4769
5349
 
4770
- exports.tokenizeFormula = tokenizeFormula; exports.WHOLE_COLUMN_END_ROW = WHOLE_COLUMN_END_ROW; exports.parseCellReference = parseCellReference; exports.parseRange = parseRange; exports.FormulaParser = FormulaParser; exports.parseFormula = parseFormula; exports.serialFromYmd = serialFromYmd; exports.parseCellInput = parseCellInput; exports.parseNumberText = parseNumberText; exports.FORMULA_ERRORS = FORMULA_ERRORS; exports.isFormulaError = isFormulaError; exports.isBlank = isBlank; exports.toText = toText; exports.numberToText = numberToText; exports.numericValue = numericValue; exports.compareNumbers = compareNumbers; exports.compareValues = compareValues; exports.sortCompareValues = sortCompareValues; exports.colNameToIndex = colNameToIndex; exports.indexToColName = indexToColName; exports.parseIsoDate = parseIsoDate; exports.wildcardToRegExp = wildcardToRegExp; exports.parseCriteria = parseCriteria; exports.formatGeneralFit = formatGeneralFit; exports.builtinNumberFormat = builtinNumberFormat; exports.builtinNumberFormatId = builtinNumberFormatId; exports.MAX_NUMBER_FORMAT_CODE_CHARS = MAX_NUMBER_FORMAT_CODE_CHARS; exports.NumberFormat = NumberFormat; exports.clearNumberFormatCache = clearNumberFormatCache; exports.dateValueToSerial = dateValueToSerial; exports.formatNumberFormatValue = formatNumberFormatValue; exports.formatTextFunction = formatTextFunction; exports.numberFormatColorToCss = numberFormatColorToCss; exports.registerFormulaFunction = registerFormulaFunction; exports.unregisterFormulaFunction = unregisterFormulaFunction; exports.getFormulaFunction = getFormulaFunction; exports.listFormulaFunctions = listFormulaFunctions; exports.searchFormulaFunctions = searchFormulaFunctions; exports.getFormulaArgumentSpecIndex = getFormulaArgumentSpecIndex; exports.checkFormulaArgumentCount = checkFormulaArgumentCount; exports.getFormulaSignatureParts = getFormulaSignatureParts; exports.getFormulaSignature = getFormulaSignature; exports.describeFormulaError = describeFormulaError; exports.MAX_FORMULA_ARRAY_SIZE = MAX_FORMULA_ARRAY_SIZE; exports.FormulaEvaluator = FormulaEvaluator;
4771
- //# sourceMappingURL=chunk-MCSJFWAO.cjs.map
5350
+
5351
+
5352
+
5353
+
5354
+
5355
+ exports.tokenizeFormula = tokenizeFormula; exports.WHOLE_COLUMN_END_ROW = WHOLE_COLUMN_END_ROW; exports.parseCellReference = parseCellReference; exports.parseRange = parseRange; exports.FormulaParser = FormulaParser; exports.parseFormula = parseFormula; exports.FormulaLambda = FormulaLambda; exports.isFormulaLambda = isFormulaLambda; exports.MAX_LAMBDA_DEPTH = MAX_LAMBDA_DEPTH; exports.serialFromYmd = serialFromYmd; exports.parseCellInput = parseCellInput; exports.parseNumberText = parseNumberText; exports.FORMULA_ERRORS = FORMULA_ERRORS; exports.isFormulaError = isFormulaError; exports.isBlank = isBlank; exports.toBoolean = toBoolean; exports.toText = toText; exports.numberToText = numberToText; exports.numericValue = numericValue; exports.compareNumbers = compareNumbers; exports.compareValues = compareValues; exports.sortCompareValues = sortCompareValues; exports.colNameToIndex = colNameToIndex; exports.indexToColName = indexToColName; exports.parseIsoDate = parseIsoDate; exports.serialToDate = serialToDate; exports.wildcardToRegExp = wildcardToRegExp; exports.parseCriteria = parseCriteria; exports.formatGeneralFit = formatGeneralFit; exports.builtinNumberFormat = builtinNumberFormat; exports.builtinNumberFormatId = builtinNumberFormatId; exports.MAX_NUMBER_FORMAT_CODE_CHARS = MAX_NUMBER_FORMAT_CODE_CHARS; exports.NumberFormat = NumberFormat; exports.clearNumberFormatCache = clearNumberFormatCache; exports.dateValueToSerial = dateValueToSerial; exports.formatNumberFormatValue = formatNumberFormatValue; exports.formatTextFunction = formatTextFunction; exports.numberFormatColorToCss = numberFormatColorToCss; exports.registerFormulaFunction = registerFormulaFunction; exports.unregisterFormulaFunction = unregisterFormulaFunction; exports.getFormulaFunction = getFormulaFunction; exports.listFormulaFunctions = listFormulaFunctions; exports.searchFormulaFunctions = searchFormulaFunctions; exports.getFormulaArgumentSpecIndex = getFormulaArgumentSpecIndex; exports.checkFormulaArgumentCount = checkFormulaArgumentCount; exports.getFormulaSignatureParts = getFormulaSignatureParts; exports.getFormulaSignature = getFormulaSignature; exports.describeFormulaError = describeFormulaError; exports.MAX_FORMULA_ARRAY_SIZE = MAX_FORMULA_ARRAY_SIZE; exports.FormulaEvaluator = FormulaEvaluator;
5356
+ //# sourceMappingURL=chunk-EWCOFSCQ.cjs.map