@bilig/formula 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/binder.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { BuiltinId, FormulaMode, MAX_WASM_RANGE_CELLS } from "@bilig/protocol";
2
+ import { getNativeAxisAggregateCode, getNativeRunningFoldCode, isNativeMakearraySumLambda, isWasmSafeBuiltinArity, isWasmSafeBuiltinArgs as checkWasmSafeBuiltinArgs, } from "./binder-wasm-rules.js";
2
3
  import { builtinWasmEnabledNames } from "./builtin-capabilities.js";
3
4
  import { formatRangeAddress, parseRangeAddress } from "./addressing.js";
4
5
  import { hasBuiltin } from "./builtins.js";
@@ -6,681 +7,6 @@ import { rewriteSpecialCall } from "./special-call-rewrites.js";
6
7
  function assertNever(value) {
7
8
  throw new Error(`Unexpected formula node: ${JSON.stringify(value)}`);
8
9
  }
9
- const RANGE_SAFE_BUILTINS = new Set([
10
- "SUM",
11
- "AVG",
12
- "AVERAGE",
13
- "MIN",
14
- "MAX",
15
- "COUNT",
16
- "COUNTA",
17
- "COUNTBLANK",
18
- "PRODUCT",
19
- "GEOMEAN",
20
- "HARMEAN",
21
- "SUMSQ",
22
- "GCD",
23
- "LCM",
24
- "MODE",
25
- "MODE.SNGL",
26
- "STDEV",
27
- "STDEV.P",
28
- "STDEV.S",
29
- "STDEVA",
30
- "STDEVP",
31
- "STDEVPA",
32
- "VAR",
33
- "VAR.P",
34
- "VAR.S",
35
- "VARA",
36
- "VARP",
37
- "VARPA",
38
- "SKEW",
39
- "SKEW.P",
40
- "KURT",
41
- ]);
42
- const AXIS_AGGREGATE_CODES = new Map([
43
- ["SUM", 1],
44
- ["AVERAGE", 2],
45
- ["AVG", 2],
46
- ["MIN", 3],
47
- ["MAX", 4],
48
- ["COUNT", 5],
49
- ["COUNTA", 6],
50
- ]);
51
- function isCellRangeNode(node) {
52
- if (node.kind !== "RangeRef") {
53
- return false;
54
- }
55
- try {
56
- const sheetPrefix = node.sheetName ? `${node.sheetName}!` : "";
57
- return parseRangeAddress(`${sheetPrefix}${node.start}:${node.end}`).kind === "cells";
58
- }
59
- catch {
60
- return false;
61
- }
62
- }
63
- function getNativeAxisAggregateCode(node) {
64
- if (node.kind !== "CallExpr" ||
65
- node.callee.toUpperCase() !== "LAMBDA" ||
66
- node.args.length !== 2) {
67
- return null;
68
- }
69
- const [param, body] = node.args;
70
- if (param?.kind !== "NameRef" || body?.kind !== "CallExpr" || body.args.length !== 1) {
71
- return null;
72
- }
73
- const aggregateCode = AXIS_AGGREGATE_CODES.get(body.callee.toUpperCase());
74
- if (aggregateCode === undefined) {
75
- return null;
76
- }
77
- return body.args[0]?.kind === "NameRef" &&
78
- body.args[0].name.trim().toUpperCase() === param.name.trim().toUpperCase()
79
- ? aggregateCode
80
- : null;
81
- }
82
- function getNativeRunningFoldCode(node) {
83
- if (node.kind !== "CallExpr" ||
84
- node.callee.toUpperCase() !== "LAMBDA" ||
85
- node.args.length !== 3) {
86
- return null;
87
- }
88
- const [acc, value, body] = node.args;
89
- if (acc?.kind !== "NameRef" || value?.kind !== "NameRef" || body?.kind !== "BinaryExpr") {
90
- return null;
91
- }
92
- const foldCode = body.operator === "+" ? 1 : body.operator === "*" ? 2 : null;
93
- if (foldCode === null) {
94
- return null;
95
- }
96
- const left = body.left;
97
- const right = body.right;
98
- const accName = acc.name.trim().toUpperCase();
99
- const valueName = value.name.trim().toUpperCase();
100
- return left.kind === "NameRef" &&
101
- right.kind === "NameRef" &&
102
- ((left.name.trim().toUpperCase() === accName &&
103
- right.name.trim().toUpperCase() === valueName) ||
104
- (left.name.trim().toUpperCase() === valueName && right.name.trim().toUpperCase() === accName))
105
- ? foldCode
106
- : null;
107
- }
108
- function isNativeMakearraySumLambda(node) {
109
- if (node.kind !== "CallExpr" ||
110
- node.callee.toUpperCase() !== "LAMBDA" ||
111
- node.args.length !== 3) {
112
- return false;
113
- }
114
- const [rowParam, colParam, body] = node.args;
115
- if (rowParam?.kind !== "NameRef" || colParam?.kind !== "NameRef" || body?.kind !== "BinaryExpr") {
116
- return false;
117
- }
118
- if (body.operator !== "+") {
119
- return false;
120
- }
121
- const left = body.left;
122
- const right = body.right;
123
- const rowName = rowParam.name.trim().toUpperCase();
124
- const colName = colParam.name.trim().toUpperCase();
125
- return (left.kind === "NameRef" &&
126
- right.kind === "NameRef" &&
127
- ((left.name.trim().toUpperCase() === rowName && right.name.trim().toUpperCase() === colName) ||
128
- (left.name.trim().toUpperCase() === colName && right.name.trim().toUpperCase() === rowName)));
129
- }
130
- function isCellVectorNode(node) {
131
- if (node.kind !== "RangeRef") {
132
- return false;
133
- }
134
- try {
135
- const sheetPrefix = node.sheetName ? `${node.sheetName}!` : "";
136
- const range = parseRangeAddress(`${sheetPrefix}${node.start}:${node.end}`);
137
- return (range.kind === "cells" &&
138
- (range.start.row === range.end.row || range.start.col === range.end.col));
139
- }
140
- catch {
141
- return false;
142
- }
143
- }
144
- function isWasmSafeBuiltinArity(callee, argc) {
145
- switch (callee) {
146
- case "NOT":
147
- case "LEN":
148
- case "YEAR":
149
- case "MONTH":
150
- case "DAY":
151
- case "HOUR":
152
- case "MINUTE":
153
- case "SECOND":
154
- case "INT":
155
- case "SIN":
156
- case "COS":
157
- case "TAN":
158
- case "ASIN":
159
- case "ACOS":
160
- case "ATAN":
161
- case "DEGREES":
162
- case "RADIANS":
163
- case "EXP":
164
- case "LN":
165
- case "LOG10":
166
- case "SQRT":
167
- return argc === 1;
168
- case "TODAY":
169
- case "NOW":
170
- case "RAND":
171
- return argc === 0;
172
- case "NA":
173
- return argc === 0;
174
- case "IF":
175
- return argc === 3;
176
- case "IFS":
177
- return argc >= 2 && argc % 2 === 0;
178
- case "IFERROR":
179
- case "IFNA":
180
- return argc === 2;
181
- case "WEEKDAY":
182
- return argc === 1 || argc === 2;
183
- case "DAYS":
184
- return argc === 2;
185
- case "COUNTBLANK":
186
- return argc >= 1;
187
- case "CHOOSE":
188
- return argc >= 2;
189
- case "DAYS360":
190
- case "YEARFRAC":
191
- return argc === 2 || argc === 3;
192
- case "DISC":
193
- case "INTRATE":
194
- case "RECEIVED":
195
- case "PRICEDISC":
196
- case "YIELDDISC":
197
- return argc === 4 || argc === 5;
198
- case "COUPDAYBS":
199
- case "COUPDAYS":
200
- case "COUPDAYSNC":
201
- case "COUPNCD":
202
- case "COUPNUM":
203
- case "COUPPCD":
204
- return argc === 3 || argc === 4;
205
- case "PRICEMAT":
206
- case "YIELDMAT":
207
- case "DURATION":
208
- case "MDURATION":
209
- return argc === 5 || argc === 6;
210
- case "ODDFPRICE":
211
- case "ODDFYIELD":
212
- case "ODDLPRICE":
213
- case "ODDLYIELD":
214
- return argc === 7 || argc === 8;
215
- case "TBILLPRICE":
216
- case "TBILLYIELD":
217
- case "TBILLEQ":
218
- return argc === 3;
219
- case "IRR":
220
- return argc === 1 || argc === 2;
221
- case "MIRR":
222
- return argc === 3;
223
- case "XNPV":
224
- return argc === 3;
225
- case "XIRR":
226
- return argc === 2 || argc === 3;
227
- case "PRICE":
228
- case "YIELD":
229
- return argc === 6 || argc === 7;
230
- case "ISOWEEKNUM":
231
- case "TIMEVALUE":
232
- return argc === 1;
233
- case "WEEKNUM":
234
- return argc === 1 || argc === 2;
235
- case "WORKDAY":
236
- case "NETWORKDAYS":
237
- return argc === 2 || argc === 3;
238
- case "WORKDAY.INTL":
239
- case "NETWORKDAYS.INTL":
240
- return argc >= 2 && argc <= 4;
241
- case "COUNTIF":
242
- case "USE.THE.COUNTIF":
243
- return argc === 2;
244
- case "COUNTIFS":
245
- return argc >= 2 && argc % 2 === 0;
246
- case "DAVERAGE":
247
- case "DCOUNT":
248
- case "DCOUNTA":
249
- case "DGET":
250
- case "DMAX":
251
- case "DMIN":
252
- case "DPRODUCT":
253
- case "DSTDEV":
254
- case "DSTDEVP":
255
- case "DSUM":
256
- case "DVAR":
257
- case "DVARP":
258
- return argc === 3;
259
- case "ADDRESS":
260
- return argc >= 2 && argc <= 5;
261
- case "SUMIF":
262
- case "AVERAGEIF":
263
- return argc === 2 || argc === 3;
264
- case "SUMIFS":
265
- case "AVERAGEIFS":
266
- return argc >= 3 && argc % 2 === 1;
267
- case "REPLACE":
268
- return argc === 4;
269
- case "SUBSTITUTE":
270
- return argc === 3 || argc === 4;
271
- case "REPT":
272
- return argc === 2;
273
- case "TEXT":
274
- return argc === 2;
275
- case "PHONETIC":
276
- return argc === 1;
277
- case "TEXTBEFORE":
278
- case "TEXTAFTER":
279
- return argc >= 2 && argc <= 6;
280
- case "TEXTSPLIT":
281
- return argc >= 2 && argc <= 6;
282
- case "TEXTJOIN":
283
- return argc >= 3;
284
- case "POWER":
285
- case "CONVERT":
286
- return argc === 3;
287
- case "EXACT":
288
- case "ATAN2":
289
- return argc === 2;
290
- case "BESSELI":
291
- case "BESSELJ":
292
- case "BESSELK":
293
- case "BESSELY":
294
- return argc === 2;
295
- case "EUROCONVERT":
296
- return argc >= 3 && argc <= 5;
297
- case "UPPER":
298
- case "LOWER":
299
- case "TRIM":
300
- case "VALUE":
301
- case "CHAR":
302
- case "CODE":
303
- case "UNICODE":
304
- case "UNICHAR":
305
- case "CLEAN":
306
- case "ASC":
307
- case "JIS":
308
- case "DBCS":
309
- case "BAHTTEXT":
310
- case "LENB":
311
- case "SINH":
312
- case "COSH":
313
- case "TANH":
314
- case "ASINH":
315
- case "ACOSH":
316
- case "ATANH":
317
- case "ACOT":
318
- case "ACOTH":
319
- case "COT":
320
- case "COTH":
321
- case "CSC":
322
- case "CSCH":
323
- case "SEC":
324
- case "SECH":
325
- case "SIGN":
326
- case "EVEN":
327
- case "ODD":
328
- case "FACT":
329
- case "FACTDOUBLE":
330
- return argc === 1;
331
- case "NUMBERVALUE":
332
- return argc >= 1 && argc <= 3;
333
- case "VALUETOTEXT":
334
- return argc === 1 || argc === 2;
335
- case "DOLLAR":
336
- return argc >= 1 && argc <= 3;
337
- case "DOLLARDE":
338
- case "DOLLARFR":
339
- case "COMBIN":
340
- case "COMBINA":
341
- case "QUOTIENT":
342
- return argc === 2;
343
- case "BASE":
344
- return argc === 2 || argc === 3;
345
- case "DECIMAL":
346
- return argc === 2;
347
- case "BIN2DEC":
348
- case "HEX2DEC":
349
- case "OCT2DEC":
350
- return argc === 1;
351
- case "BIN2HEX":
352
- case "BIN2OCT":
353
- case "DEC2BIN":
354
- case "DEC2HEX":
355
- case "DEC2OCT":
356
- case "HEX2BIN":
357
- case "HEX2OCT":
358
- case "OCT2BIN":
359
- case "OCT2HEX":
360
- return argc === 1 || argc === 2;
361
- case "BITAND":
362
- case "BITOR":
363
- case "BITXOR":
364
- return argc >= 2;
365
- case "BITLSHIFT":
366
- case "BITRSHIFT":
367
- return argc === 2;
368
- case "MATCH":
369
- return argc === 2 || argc === 3;
370
- case "CORREL":
371
- case "COVAR":
372
- case "PEARSON":
373
- case "COVARIANCE.P":
374
- case "COVARIANCE.S":
375
- case "PERCENTRANK":
376
- case "PERCENTRANK.INC":
377
- case "PERCENTRANK.EXC":
378
- case "SMALL":
379
- case "LARGE":
380
- case "PERCENTILE":
381
- case "PERCENTILE.INC":
382
- case "PERCENTILE.EXC":
383
- case "QUARTILE":
384
- case "QUARTILE.INC":
385
- case "QUARTILE.EXC":
386
- case "RANK":
387
- case "RANK.EQ":
388
- case "RANK.AVG":
389
- case "INTERCEPT":
390
- case "RSQ":
391
- case "SLOPE":
392
- case "STEYX":
393
- return argc === 2 || argc === 3;
394
- case "MEDIAN":
395
- case "MODE.MULT":
396
- case "GCD":
397
- case "LCM":
398
- case "PRODUCT":
399
- case "GEOMEAN":
400
- case "HARMEAN":
401
- case "SUMSQ":
402
- return argc >= 1;
403
- case "FREQUENCY":
404
- return argc === 2;
405
- case "PROB":
406
- return argc === 3 || argc === 4;
407
- case "TRIMMEAN":
408
- return argc === 2;
409
- case "FORECAST":
410
- case "FORECAST.LINEAR":
411
- return argc === 3;
412
- case "TREND":
413
- case "GROWTH":
414
- case "LINEST":
415
- case "LOGEST":
416
- return argc >= 1 && argc <= 4;
417
- case "XMATCH":
418
- return argc >= 2 && argc <= 4;
419
- case "XLOOKUP":
420
- return argc >= 3 && argc <= 6;
421
- case "INDEX":
422
- return argc === 2 || argc === 3;
423
- case "VLOOKUP":
424
- case "HLOOKUP":
425
- return argc === 3 || argc === 4;
426
- case "LEFT":
427
- case "RIGHT":
428
- case "LEFTB":
429
- case "RIGHTB":
430
- return argc === 1 || argc === 2;
431
- case "MID":
432
- case "MIDB":
433
- return argc === 3;
434
- case "FIND":
435
- case "SEARCH":
436
- case "FINDB":
437
- case "SEARCHB":
438
- return argc === 2 || argc === 3;
439
- case "REPLACEB":
440
- return argc === 4;
441
- case "ISBLANK":
442
- case "ISNUMBER":
443
- case "ISTEXT":
444
- return argc === 0 || argc === 1;
445
- case "ROUND":
446
- case "ROUNDUP":
447
- case "ROUNDDOWN":
448
- case "FLOOR":
449
- case "CEILING":
450
- case "LOG":
451
- return argc === 1 || argc === 2;
452
- case "T":
453
- case "N":
454
- case "TYPE":
455
- case "GAUSS":
456
- case "PHI":
457
- case "NORMSDIST":
458
- case "NORMSINV":
459
- return argc === 1 || (argc === 0 && (callee === "T" || callee === "N" || callee === "TYPE"));
460
- case "DELTA":
461
- case "GESTEP":
462
- case "LOGNORMDIST":
463
- case "EFFECT":
464
- case "NOMINAL":
465
- case "RRI":
466
- case "PERMUT":
467
- case "PERMUTATIONA":
468
- return argc === 2;
469
- case "STANDARDIZE":
470
- case "NORMINV":
471
- case "LOGINV":
472
- case "PDURATION":
473
- case "CONFIDENCE.NORM":
474
- case "CONFIDENCE":
475
- case "CONFIDENCE.T":
476
- case "CRITBINOM":
477
- case "BINOM.INV":
478
- return argc === 3;
479
- case "ERF":
480
- return argc === 1 || argc === 2;
481
- case "ERF.PRECISE":
482
- case "ERFC":
483
- case "ERFC.PRECISE":
484
- case "FISHER":
485
- case "FISHERINV":
486
- case "GAMMALN":
487
- case "GAMMALN.PRECISE":
488
- case "GAMMA":
489
- return argc === 1;
490
- case "GAMMA.INV":
491
- case "GAMMAINV":
492
- return argc === 3;
493
- case "CHIDIST":
494
- case "LEGACY.CHIDIST":
495
- case "CHIINV":
496
- case "CHISQ.DIST.RT":
497
- case "CHISQ.INV.RT":
498
- case "CHISQDIST":
499
- case "CHISQINV":
500
- case "LEGACY.CHIINV":
501
- case "CHISQ.TEST":
502
- case "CHITEST":
503
- case "LEGACY.CHITEST":
504
- case "F.TEST":
505
- case "FTEST":
506
- return argc === 2;
507
- case "Z.TEST":
508
- case "ZTEST":
509
- return argc === 2 || argc === 3;
510
- case "F.DIST.RT":
511
- case "FDIST":
512
- case "LEGACY.FDIST":
513
- return argc === 3;
514
- case "CHISQ.INV":
515
- return argc === 2;
516
- case "CHISQ.DIST":
517
- return argc === 3;
518
- case "BETA.INV":
519
- case "BETAINV":
520
- return argc >= 3 && argc <= 5;
521
- case "BETA.DIST":
522
- return argc >= 4 && argc <= 6;
523
- case "BETADIST":
524
- return argc >= 3 && argc <= 5;
525
- case "F.DIST":
526
- return argc === 4;
527
- case "T.DIST":
528
- return argc === 3;
529
- case "T.DIST.RT":
530
- case "T.DIST.2T":
531
- case "T.INV":
532
- case "T.INV.2T":
533
- case "TINV":
534
- return argc === 2;
535
- case "TDIST":
536
- return argc === 3;
537
- case "T.TEST":
538
- case "TTEST":
539
- return argc === 4;
540
- case "F.INV":
541
- case "F.INV.RT":
542
- case "FINV":
543
- case "LEGACY.FINV":
544
- return argc === 3;
545
- case "WEIBULL":
546
- case "WEIBULL.DIST":
547
- case "GAMMADIST":
548
- case "GAMMA.DIST":
549
- case "BINOMDIST":
550
- case "BINOM.DIST":
551
- case "NEGBINOM.DIST":
552
- return argc === 4;
553
- case "EXPONDIST":
554
- case "EXPON.DIST":
555
- case "POISSON":
556
- case "POISSON.DIST":
557
- case "NEGBINOMDIST":
558
- return argc === 3;
559
- case "BINOM.DIST.RANGE":
560
- return argc === 3 || argc === 4;
561
- case "HYPGEOMDIST":
562
- return argc === 4;
563
- case "HYPGEOM.DIST":
564
- return argc === 5;
565
- case "NORMDIST":
566
- return argc === 4;
567
- case "NORM.DIST":
568
- return argc === 4;
569
- case "NORM.INV":
570
- return argc === 3;
571
- case "NORM.S.DIST":
572
- return argc === 1 || argc === 2;
573
- case "NORM.S.INV":
574
- return argc === 1;
575
- case "LOGNORM.DIST":
576
- return argc === 3 || argc === 4;
577
- case "LOGNORM.INV":
578
- return argc === 3;
579
- case "MODE":
580
- case "MODE.SNGL":
581
- case "STDEV":
582
- case "STDEV.P":
583
- case "STDEV.S":
584
- case "STDEVA":
585
- case "STDEVP":
586
- case "STDEVPA":
587
- case "VAR":
588
- case "VAR.P":
589
- case "VAR.S":
590
- case "VARA":
591
- case "VARP":
592
- case "VARPA":
593
- case "SKEW":
594
- case "SKEW.P":
595
- case "KURT":
596
- case "NPV":
597
- return argc >= 1;
598
- case "FV":
599
- case "PV":
600
- case "PMT":
601
- case "NPER":
602
- return argc >= 3 && argc <= 5;
603
- case "RATE":
604
- return argc >= 3 && argc <= 6;
605
- case "IPMT":
606
- case "PPMT":
607
- return argc >= 4 && argc <= 6;
608
- case "ISPMT":
609
- return argc === 4;
610
- case "CUMIPMT":
611
- case "CUMPRINC":
612
- return argc === 6;
613
- case "DATE":
614
- case "TIME":
615
- case "DATEDIF":
616
- return argc === 3;
617
- case "FVSCHEDULE":
618
- return argc >= 2;
619
- case "SLN":
620
- return argc === 3;
621
- case "DB":
622
- case "DDB":
623
- return argc === 4 || argc === 5;
624
- case "SYD":
625
- return argc === 4;
626
- case "VDB":
627
- return argc >= 5 && argc <= 7;
628
- case "EDATE":
629
- case "EOMONTH":
630
- return argc === 2;
631
- case "AND":
632
- case "OR":
633
- case "XOR":
634
- return argc >= 1;
635
- case "SWITCH":
636
- return argc >= 3;
637
- case "SEQUENCE":
638
- return argc >= 1 && argc <= 4;
639
- case "EXPAND":
640
- return argc >= 2 && argc <= 4;
641
- case "FILTER":
642
- return argc === 2 || argc === 3;
643
- case "UNIQUE":
644
- return argc >= 1 && argc <= 3;
645
- case "TRIMRANGE":
646
- return argc >= 1 && argc <= 3;
647
- case "OFFSET":
648
- return argc >= 3 && argc <= 5;
649
- case "TAKE":
650
- case "DROP":
651
- return argc >= 1 && argc <= 3;
652
- case "CHOOSECOLS":
653
- case "CHOOSEROWS":
654
- return argc >= 2;
655
- case "SORT":
656
- return argc >= 1 && argc <= 4;
657
- case "SORTBY":
658
- return argc >= 2;
659
- case "TOCOL":
660
- case "TOROW":
661
- return argc >= 1 && argc <= 3;
662
- case "WRAPROWS":
663
- case "WRAPCOLS":
664
- return argc >= 2 && argc <= 4;
665
- case "LOOKUP":
666
- return argc === 2 || argc === 3;
667
- case "AREAS":
668
- case "COLUMNS":
669
- case "ROWS":
670
- case "TRANSPOSE":
671
- return argc === 1;
672
- case "HSTACK":
673
- case "VSTACK":
674
- return argc >= 1;
675
- case "ARRAYTOTEXT":
676
- return argc === 1 || argc === 2;
677
- case "MINIFS":
678
- case "MAXIFS":
679
- return argc >= 3 && argc % 2 === 1;
680
- default:
681
- return true;
682
- }
683
- }
684
10
  export function bindFormula(ast) {
685
11
  const deps = new Set();
686
12
  const symbolicNames = new Set();
@@ -867,376 +193,7 @@ export function bindFormula(ast) {
867
193
  }
868
194
  }
869
195
  function isWasmSafeBuiltinArgs(callee, args) {
870
- const argc = args.length;
871
- const isScalarArg = (arg) => isWasmSafe(arg);
872
- const isCellRangeArg = (arg) => isWasmSafe(arg, true) && isCellRangeNode(arg);
873
- const isCellVectorArg = (arg) => isWasmSafe(arg, true) && isCellVectorNode(arg);
874
- const isCellOrScalarArg = (arg) => isCellVectorArg(arg) || isScalarArg(arg);
875
- const isNativeSequenceArg = (arg) => arg.kind === "CallExpr" &&
876
- arg.callee.toUpperCase() === "SEQUENCE" &&
877
- isWasmSafeBuiltinArity("SEQUENCE", arg.args.length) &&
878
- arg.args.every((child) => isWasmSafe(child));
879
- switch (callee) {
880
- case "SUM":
881
- case "AVG":
882
- case "MIN":
883
- case "MAX":
884
- case "COUNT":
885
- case "COUNTA":
886
- case "COUNTBLANK":
887
- return args.every((arg) => isWasmSafe(arg, true) || isNativeSequenceArg(arg));
888
- case "CHOOSE":
889
- return (argc >= 2 &&
890
- isScalarArg(args[0]) &&
891
- args.slice(1).every((arg) => isWasmSafe(arg, true) || isNativeSequenceArg(arg)));
892
- case "COUNTIF":
893
- case "USE.THE.COUNTIF":
894
- return args.length === 2 && isCellRangeArg(args[0]) && isScalarArg(args[1]);
895
- case "COUNTIFS":
896
- if (args.length === 0 || args.length % 2 !== 0) {
897
- return false;
898
- }
899
- return args.every((arg, index) => index % 2 === 0 ? isCellRangeArg(arg) : isScalarArg(arg));
900
- case "DAVERAGE":
901
- case "DCOUNT":
902
- case "DCOUNTA":
903
- case "DGET":
904
- case "DMAX":
905
- case "DMIN":
906
- case "DPRODUCT":
907
- case "DSTDEV":
908
- case "DSTDEVP":
909
- case "DSUM":
910
- case "DVAR":
911
- case "DVARP":
912
- return (argc === 3 &&
913
- isCellRangeArg(args[0]) &&
914
- isWasmSafe(args[1], true) &&
915
- isCellRangeArg(args[2]));
916
- case "CHISQ.TEST":
917
- case "CHITEST":
918
- case "LEGACY.CHITEST":
919
- case "F.TEST":
920
- case "FTEST":
921
- return argc === 2 && args.every((arg) => isWasmSafe(arg, true));
922
- case "T.TEST":
923
- case "TTEST":
924
- return (argc === 4 &&
925
- isCellRangeArg(args[0]) &&
926
- isCellRangeArg(args[1]) &&
927
- isScalarArg(args[2]) &&
928
- isScalarArg(args[3]));
929
- case "Z.TEST":
930
- case "ZTEST":
931
- return (argc === 2 || argc === 3) && args.every((arg) => isWasmSafe(arg, true));
932
- case "SUMIF":
933
- case "AVERAGEIF":
934
- if (args.length !== 2 && args.length !== 3) {
935
- return false;
936
- }
937
- return (isCellRangeArg(args[0]) &&
938
- isScalarArg(args[1]) &&
939
- (args.length === 2 || isCellRangeArg(args[2])));
940
- case "SUMIFS":
941
- case "AVERAGEIFS":
942
- if (args.length < 3 || args.length % 2 === 0) {
943
- return false;
944
- }
945
- if (!isCellRangeArg(args[0])) {
946
- return false;
947
- }
948
- return args
949
- .slice(1)
950
- .every((arg, index) => (index % 2 === 0 ? isCellRangeArg(arg) : isScalarArg(arg)));
951
- case "SUMPRODUCT":
952
- return args.length >= 1 && args.every((arg) => isCellRangeArg(arg));
953
- case "MATCH":
954
- return ((args.length === 2 || args.length === 3) &&
955
- isScalarArg(args[0]) &&
956
- isCellVectorArg(args[1]) &&
957
- (args.length === 2 || isScalarArg(args[2])));
958
- case "CORREL":
959
- case "COVAR":
960
- case "PEARSON":
961
- case "COVARIANCE.P":
962
- case "COVARIANCE.S":
963
- case "INTERCEPT":
964
- case "RSQ":
965
- case "SLOPE":
966
- case "STEYX":
967
- return args.length === 2 && args.every((arg) => isWasmSafe(arg, true));
968
- case "MEDIAN":
969
- return args.length >= 1 && args.every((arg) => isWasmSafe(arg, true));
970
- case "MODE.MULT":
971
- return args.length >= 1 && args.every((arg) => isWasmSafe(arg, true));
972
- case "FREQUENCY":
973
- return args.length === 2 && isWasmSafe(args[0], true) && isWasmSafe(args[1], true);
974
- case "BESSELI":
975
- case "BESSELJ":
976
- case "BESSELK":
977
- case "BESSELY":
978
- return args.length === 2 && isScalarArg(args[0]) && isScalarArg(args[1]);
979
- case "SMALL":
980
- case "LARGE":
981
- case "PERCENTILE":
982
- case "PERCENTILE.INC":
983
- case "PERCENTILE.EXC":
984
- case "QUARTILE":
985
- case "QUARTILE.INC":
986
- case "QUARTILE.EXC":
987
- return args.length === 2 && isWasmSafe(args[0], true) && isScalarArg(args[1]);
988
- case "PERCENTRANK":
989
- case "PERCENTRANK.INC":
990
- case "PERCENTRANK.EXC":
991
- return ((args.length === 2 || args.length === 3) &&
992
- isWasmSafe(args[0], true) &&
993
- isScalarArg(args[1]) &&
994
- (args.length === 2 || isScalarArg(args[2])));
995
- case "RANK":
996
- case "RANK.EQ":
997
- case "RANK.AVG":
998
- return ((args.length === 2 || args.length === 3) &&
999
- isScalarArg(args[0]) &&
1000
- isWasmSafe(args[1], true) &&
1001
- (args.length === 2 || isScalarArg(args[2])));
1002
- case "FORECAST":
1003
- case "FORECAST.LINEAR":
1004
- return (args.length === 3 &&
1005
- isScalarArg(args[0]) &&
1006
- isWasmSafe(args[1], true) &&
1007
- isWasmSafe(args[2], true));
1008
- case "TREND":
1009
- case "GROWTH":
1010
- case "LINEST":
1011
- case "LOGEST":
1012
- return (args.length >= 1 &&
1013
- args.length <= 4 &&
1014
- isWasmSafe(args[0], true) &&
1015
- (args.length < 2 || isWasmSafe(args[1], true)) &&
1016
- (args.length < 3 || isWasmSafe(args[2], true)) &&
1017
- (args.length < 4 || isScalarArg(args[3])));
1018
- case "XMATCH":
1019
- return (args.length >= 2 &&
1020
- args.length <= 4 &&
1021
- isScalarArg(args[0]) &&
1022
- isCellVectorArg(args[1]) &&
1023
- args.slice(2).every((arg) => isScalarArg(arg)));
1024
- case "XLOOKUP":
1025
- return (args.length >= 3 &&
1026
- args.length <= 6 &&
1027
- isScalarArg(args[0]) &&
1028
- isCellVectorArg(args[1]) &&
1029
- isCellVectorArg(args[2]) &&
1030
- args.slice(3).every((arg) => isScalarArg(arg)));
1031
- case "INDEX":
1032
- return ((args.length === 2 || args.length === 3) &&
1033
- isCellRangeArg(args[0]) &&
1034
- isScalarArg(args[1]) &&
1035
- (args.length === 2 || isScalarArg(args[2])));
1036
- case "VLOOKUP":
1037
- return ((args.length === 3 || args.length === 4) &&
1038
- isScalarArg(args[0]) &&
1039
- isCellRangeArg(args[1]) &&
1040
- isScalarArg(args[2]) &&
1041
- (args.length === 3 || isScalarArg(args[3])));
1042
- case "HLOOKUP":
1043
- return ((args.length === 3 || args.length === 4) &&
1044
- isScalarArg(args[0]) &&
1045
- isCellRangeArg(args[1]) &&
1046
- isScalarArg(args[2]) &&
1047
- (args.length === 3 || isScalarArg(args[3])));
1048
- case "DAYS":
1049
- case "DAYS360":
1050
- case "YEARFRAC":
1051
- case "DISC":
1052
- case "INTRATE":
1053
- case "RECEIVED":
1054
- case "PRICEDISC":
1055
- case "YIELDDISC":
1056
- case "COUPDAYBS":
1057
- case "COUPDAYS":
1058
- case "COUPDAYSNC":
1059
- case "COUPNCD":
1060
- case "COUPNUM":
1061
- case "COUPPCD":
1062
- case "PRICEMAT":
1063
- case "YIELDMAT":
1064
- case "ODDFPRICE":
1065
- case "ODDFYIELD":
1066
- case "ODDLPRICE":
1067
- case "ODDLYIELD":
1068
- case "PRICE":
1069
- case "YIELD":
1070
- case "DURATION":
1071
- case "MDURATION":
1072
- case "TBILLPRICE":
1073
- case "TBILLYIELD":
1074
- case "TBILLEQ":
1075
- case "ISOWEEKNUM":
1076
- case "TIMEVALUE":
1077
- case "WEEKNUM":
1078
- return args.every((arg) => isScalarArg(arg));
1079
- case "EXPAND":
1080
- return (argc >= 2 &&
1081
- argc <= 4 &&
1082
- isWasmSafe(args[0], true) &&
1083
- isScalarArg(args[1]) &&
1084
- (argc < 3 || isScalarArg(args[2])) &&
1085
- (argc < 4 || isScalarArg(args[3])));
1086
- case "WORKDAY":
1087
- return args.length === 2
1088
- ? args.every((arg) => isScalarArg(arg))
1089
- : args.every((arg) => isScalarArg(arg));
1090
- case "NETWORKDAYS":
1091
- return args.length === 2
1092
- ? args.every((arg) => isScalarArg(arg))
1093
- : args.every((arg) => isScalarArg(arg));
1094
- case "WORKDAY.INTL":
1095
- case "NETWORKDAYS.INTL":
1096
- return args.every((arg) => isScalarArg(arg));
1097
- case "NUMBERVALUE":
1098
- case "TEXT":
1099
- return args.every((arg) => isScalarArg(arg));
1100
- case "PHONETIC":
1101
- return argc === 1 && (isScalarArg(args[0]) || isCellRangeNode(args[0]));
1102
- case "VALUETOTEXT":
1103
- case "TEXTBEFORE":
1104
- case "TEXTAFTER":
1105
- case "CHAR":
1106
- case "CODE":
1107
- case "UNICODE":
1108
- case "UNICHAR":
1109
- case "CLEAN":
1110
- case "ASC":
1111
- case "JIS":
1112
- case "DBCS":
1113
- case "BAHTTEXT":
1114
- case "TEXTSPLIT":
1115
- case "CONVERT":
1116
- case "EUROCONVERT":
1117
- case "BASE":
1118
- case "DECIMAL":
1119
- case "BIN2DEC":
1120
- case "BIN2HEX":
1121
- case "BIN2OCT":
1122
- case "DEC2BIN":
1123
- case "DEC2HEX":
1124
- case "DEC2OCT":
1125
- case "HEX2BIN":
1126
- case "HEX2DEC":
1127
- case "HEX2OCT":
1128
- case "OCT2BIN":
1129
- case "OCT2DEC":
1130
- case "OCT2HEX":
1131
- case "BITAND":
1132
- case "BITOR":
1133
- case "BITXOR":
1134
- case "BITLSHIFT":
1135
- case "BITRSHIFT":
1136
- return args.every((arg) => isScalarArg(arg));
1137
- case "TEXTJOIN":
1138
- return (argc >= 3 &&
1139
- isScalarArg(args[0]) &&
1140
- isScalarArg(args[1]) &&
1141
- args.slice(2).every((arg) => isWasmSafe(arg, true) || isNativeSequenceArg(arg)));
1142
- case "REPLACE":
1143
- case "SUBSTITUTE":
1144
- case "REPT":
1145
- return args.every((arg) => isScalarArg(arg));
1146
- case "OFFSET":
1147
- case "TAKE":
1148
- case "DROP":
1149
- case "CHOOSECOLS":
1150
- case "CHOOSEROWS":
1151
- case "SORT":
1152
- case "TOCOL":
1153
- case "TOROW":
1154
- case "WRAPROWS":
1155
- case "WRAPCOLS":
1156
- if (args.length === 0) {
1157
- return false;
1158
- }
1159
- return isCellRangeArg(args[0]) && args.slice(1).every((arg) => isScalarArg(arg));
1160
- case "FILTER":
1161
- return ((argc === 2 || argc === 3) &&
1162
- isCellRangeArg(args[0]) &&
1163
- isWasmSafe(args[1], true) &&
1164
- (argc === 2 || isScalarArg(args[2])));
1165
- case "UNIQUE":
1166
- return (argc >= 1 &&
1167
- argc <= 3 &&
1168
- isCellRangeArg(args[0]) &&
1169
- args.slice(1).every((arg) => isScalarArg(arg)));
1170
- case "TRIMRANGE":
1171
- return (argc >= 1 &&
1172
- argc <= 3 &&
1173
- isWasmSafe(args[0], true) &&
1174
- args.slice(1).every((arg) => isScalarArg(arg)));
1175
- case "PROB":
1176
- return ((argc === 3 || argc === 4) &&
1177
- isWasmSafe(args[0], true) &&
1178
- isWasmSafe(args[1], true) &&
1179
- isScalarArg(args[2]) &&
1180
- (argc === 3 || isScalarArg(args[3])));
1181
- case "TRIMMEAN":
1182
- return argc === 2 && isWasmSafe(args[0], true) && isScalarArg(args[1]);
1183
- case "LOOKUP":
1184
- if (argc < 2 || argc > 3) {
1185
- return false;
1186
- }
1187
- return (isScalarArg(args[0]) &&
1188
- isCellOrScalarArg(args[1]) &&
1189
- (argc === 2 || isCellVectorArg(args[2]) || isScalarArg(args[2])));
1190
- case "TRANSPOSE":
1191
- return args.length === 1 && isWasmSafe(args[0], true);
1192
- case "HSTACK":
1193
- case "VSTACK":
1194
- return args.length >= 1 && args.every((arg) => isWasmSafe(arg, true));
1195
- case "AREAS":
1196
- case "COLUMNS":
1197
- case "ROWS":
1198
- return args.length === 1 && isCellRangeArg(args[0]);
1199
- case "ARRAYTOTEXT":
1200
- return ((argc === 1 || argc === 2) &&
1201
- (isCellRangeArg(args[0]) || isScalarArg(args[0])) &&
1202
- (argc === 1 || isScalarArg(args[1])));
1203
- case "MINIFS":
1204
- case "MAXIFS":
1205
- if (args.length < 3 || args.length % 2 === 0 || !isCellRangeArg(args[0])) {
1206
- return false;
1207
- }
1208
- return args
1209
- .slice(1)
1210
- .every((arg, index) => (index % 2 === 0 ? isCellRangeArg(arg) : isScalarArg(arg)));
1211
- case "IRR":
1212
- return ((argc === 1 || argc === 2) &&
1213
- isCellRangeArg(args[0]) &&
1214
- (argc === 1 || isScalarArg(args[1])));
1215
- case "MIRR":
1216
- return (argc === 3 && isCellRangeArg(args[0]) && isScalarArg(args[1]) && isScalarArg(args[2]));
1217
- case "XNPV":
1218
- return (argc === 3 &&
1219
- isScalarArg(args[0]) &&
1220
- isCellRangeArg(args[1]) &&
1221
- isCellRangeArg(args[2]));
1222
- case "XIRR":
1223
- return ((argc === 2 || argc === 3) &&
1224
- isCellRangeArg(args[0]) &&
1225
- isCellRangeArg(args[1]) &&
1226
- (argc === 2 || isScalarArg(args[2])));
1227
- case "SORTBY":
1228
- if (args.length < 2) {
1229
- return false;
1230
- }
1231
- return (isCellRangeArg(args[0]) &&
1232
- args
1233
- .slice(1)
1234
- .every((arg, index) => index % 2 === 0 ? isScalarArg(arg) || isWasmSafe(arg, true) : isScalarArg(arg)));
1235
- default: {
1236
- const allowRangeArgs = RANGE_SAFE_BUILTINS.has(callee);
1237
- return args.every((arg) => isWasmSafe(arg, allowRangeArgs));
1238
- }
1239
- }
196
+ return checkWasmSafeBuiltinArgs(callee, args, { isWasmSafe });
1240
197
  }
1241
198
  function isTopLevelWasmSafe(node) {
1242
199
  if (node.kind !== "CallExpr") {