-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlang.mjs
645 lines (521 loc) · 28.1 KB
/
lang.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
/* Type */
export function isNil(val) {return val == null}
export function reqNil(val) {return isNil(val) ? val : throwErrFun(val, isNil)}
export function optNil(val) {return isNil(val) ? val : reqNil(val)}
export function onlyNil(val) {return isNil(val) ? val : undefined}
export function isSome(val) {return val != null}
export function reqSome(val) {return isSome(val) ? val : throwErrFun(val, isSome)}
export function optSome(val) {return isNil(val) ? val : reqSome(val)}
export function onlySome(val) {return isSome(val) ? val : undefined}
export function isBool(val) {return typeof val === `boolean`}
export function reqBool(val) {return isBool(val) ? val : throwErrFun(val, isBool)}
export function optBool(val) {return isNil(val) ? val : reqBool(val)}
export function onlyBool(val) {return isBool(val) ? val : undefined}
export function laxBool(val) {return isNil(val) ? false : reqBool(val)}
export function isNum(val) {return typeof val === `number`}
export function reqNum(val) {return isNum(val) ? val : throwErrFun(val, isNum)}
export function optNum(val) {return isNil(val) ? val : reqNum(val)}
export function onlyNum(val) {return isNum(val) ? val : undefined}
export function laxNum(val) {return isNil(val) ? 0 : reqNum(val)}
export function isFin(val) {return Number.isFinite(val)}
export function reqFin(val) {return isFin(val) ? val : throwErrFun(val, isFin)}
export function optFin(val) {return isNil(val) ? val : reqFin(val)}
export function onlyFin(val) {return isFin(val) ? val : undefined}
export function laxFin(val) {return isNil(val) ? 0 : reqFin(val)}
export function isFinNeg(val) {return isNum(val) && val < 0 && val > -Infinity}
export function reqFinNeg(val) {return isFinNeg(val) ? val : throwErrFun(val, isFinNeg)}
export function optFinNeg(val) {return isNil(val) ? val : reqFinNeg(val)}
export function onlyFinNeg(val) {return isFinNeg(val) ? val : undefined}
export function isFinPos(val) {return isNum(val) && val > 0 && val < Infinity}
export function reqFinPos(val) {return isFinPos(val) ? val : throwErrFun(val, isFinPos)}
export function optFinPos(val) {return isNil(val) ? val : reqFinPos(val)}
export function onlyFinPos(val) {return isFinPos(val) ? val : undefined}
// TODO: simply use `Number.isSafeInteger`.
export function isInt(val) {return isNum(val) && ((val % 1) === 0)}
export function reqInt(val) {return isInt(val) ? val : throwErrFun(val, isInt)}
export function optInt(val) {return isNil(val) ? val : reqInt(val)}
export function onlyInt(val) {return isInt(val) ? val : undefined}
export function laxInt(val) {return isNil(val) ? 0 : reqInt(val)}
export function isNat(val) {return isInt(val) && val >= 0}
export function reqNat(val) {return isNat(val) ? val : throwErrFun(val, isNat)}
export function optNat(val) {return isNil(val) ? val : reqNat(val)}
export function onlyNat(val) {return isNat(val) ? val : undefined}
export function laxNat(val) {return isNil(val) ? 0 : reqNat(val)}
export function isIntNeg(val) {return isInt(val) && val < 0}
export function reqIntNeg(val) {return isIntNeg(val) ? val : throwErrFun(val, isIntNeg)}
export function optIntNeg(val) {return isNil(val) ? val : reqIntNeg(val)}
export function onlyIntNeg(val) {return isIntNeg(val) ? val : undefined}
export function isIntPos(val) {return isInt(val) && val > 0}
export function reqIntPos(val) {return isIntPos(val) ? val : throwErrFun(val, isIntPos)}
export function optIntPos(val) {return isNil(val) ? val : reqIntPos(val)}
export function onlyIntPos(val) {return isIntPos(val) ? val : undefined}
export function isNaN(val) {return val !== val}
export function reqNaN(val) {return isNaN(val) ? val : throwErrFun(val, isNaN)}
export function optNaN(val) {return isNil(val) ? val : reqNaN(val)}
export function onlyNaN(val) {return isNaN(val) ? val : undefined}
export function isInf(val) {return val === Infinity || val === -Infinity}
export function reqInf(val) {return isInf(val) ? val : throwErrFun(val, isInf)}
export function optInf(val) {return isNil(val) ? val : reqInf(val)}
export function onlyInf(val) {return isInf(val) ? val : undefined}
export function isBigInt(val) {return typeof val === `bigint`}
export function reqBigInt(val) {return isBigInt(val) ? val : throwErrFun(val, isBigInt)}
export function optBigInt(val) {return isNil(val) ? val : reqBigInt(val)}
export function onlyBigInt(val) {return isBigInt(val) ? val : undefined}
export function laxBigInt(val) {return isNil(val) ? BigInt(0) : reqBigInt(val)}
export function isStr(val) {return typeof val === `string`}
export function reqStr(val) {return isStr(val) ? val : throwErrFun(val, isStr)}
export function optStr(val) {return isNil(val) ? val : reqStr(val)}
export function onlyStr(val) {return isStr(val) ? val : undefined}
export function laxStr(val) {return isNil(val) ? `` : reqStr(val)}
// TODO shorter name.
export function isValidStr(val) {return isStr(val) && !!val}
export function reqValidStr(val) {return isValidStr(val) ? val : throwErrFun(val, isValidStr)}
export function optValidStr(val) {return isNil(val) ? val : reqValidStr(val)}
export function onlyValidStr(val) {return isValidStr(val) ? val : undefined}
export function isSym(val) {return typeof val === `symbol`}
export function reqSym(val) {return isSym(val) ? val : throwErrFun(val, isSym)}
export function optSym(val) {return isNil(val) ? val : reqSym(val)}
export function onlySym(val) {return isSym(val) ? val : undefined}
// TODO tune perf.
export function isKey(val) {return isPrim(val) && !isJunk(val)}
export function reqKey(val) {return isKey(val) ? val : throwErrFun(val, isKey)}
export function optKey(val) {return isNil(val) ? val : reqKey(val)}
export function onlyKey(val) {return isKey(val) ? val : undefined}
export function isStructKey(val) {return isStr(val) || isSym(val)}
export function reqStructKey(val) {return isStructKey(val) ? val : throwErrFun(val, isKey)}
export function optStructKey(val) {return isNil(val) ? val : reqStructKey(val)}
export function onlyStructKey(val) {return isStructKey(val) ? val : undefined}
export function isPk(val) {return isValidStr(val) || isIntPos(val)}
export function reqPk(val) {return isPk(val) ? val : throwErrFun(val, isPk)}
export function optPk(val) {return isNil(val) ? val : reqPk(val)}
export function onlyPk(val) {return isPk(val) ? val : undefined}
export function isJunk(val) {return isNil(val) || isNaN(val) || isInf(val)}
export function reqJunk(val) {return isJunk(val) ? val : throwErrFun(val, isJunk)}
export function optJunk(val) {return isNil(val) ? val : reqJunk(val)}
export function onlyJunk(val) {return isJunk(val) ? val : undefined}
export function isComp(val) {return isObj(val) || isFun(val)}
export function reqComp(val) {return isComp(val) ? val : throwErrFun(val, isComp)}
export function optComp(val) {return isNil(val) ? val : reqComp(val)}
export function onlyComp(val) {return isComp(val) ? val : undefined}
export function isPrim(val) {return !isComp(val)}
export function reqPrim(val) {return isPrim(val) ? val : throwErrFun(val, isPrim)}
export function optPrim(val) {return isNil(val) ? val : reqPrim(val)}
export function onlyPrim(val) {return isPrim(val) ? val : undefined}
export function isFun(val) {return typeof val === `function`}
export function reqFun(val) {return isFun(val) ? val : throwErrFun(val, isFun)}
export function optFun(val) {return isNil(val) ? val : reqFun(val)}
export function onlyFun(val) {return isFun(val) ? val : undefined}
export function isFunSync(val) {return isFunType(val, `Function`)}
export function reqFunSync(val) {return isFunSync(val) ? val : throwErrFun(val, isFunSync)}
export function optFunSync(val) {return isNil(val) ? val : reqFunSync(val)}
export function onlyFunSync(val) {return isFunSync(val) ? val : undefined}
export function isFunGen(val) {return isFunType(val, `GeneratorFunction`)}
export function reqFunGen(val) {return isFunGen(val) ? val : throwErrFun(val, isFunGen)}
export function optFunGen(val) {return isNil(val) ? val : reqFunGen(val)}
export function onlyFunGen(val) {return isFunGen(val) ? val : undefined}
export function isFunAsync(val) {return isFunType(val, `AsyncFunction`)}
export function reqFunAsync(val) {return isFunAsync(val) ? val : throwErrFun(val, isFunAsync)}
export function optFunAsync(val) {return isNil(val) ? val : reqFunAsync(val)}
export function onlyFunAsync(val) {return isFunAsync(val) ? val : undefined}
export function isFunAsyncGen(val) {return isFunType(val, `AsyncGeneratorFunction`)}
export function reqFunAsyncGen(val) {return isFunAsyncGen(val) ? val : throwErrFun(val, isFunAsyncGen)}
export function optFunAsyncGen(val) {return isNil(val) ? val : reqFunAsyncGen(val)}
export function onlyFunAsyncGen(val) {return isFunAsyncGen(val) ? val : undefined}
export function isObj(val) {return isSome(val) && typeof val === `object`}
export function reqObj(val) {return isObj(val) ? val : throwErrFun(val, isObj)}
export function optObj(val) {return isNil(val) ? val : reqObj(val)}
export function onlyObj(val) {return isObj(val) ? val : undefined}
export function isNpo(val) {return isObj(val) && isNil(Object.getPrototypeOf(val))}
export function reqNpo(val) {return isNpo(val) ? val : throwErrFun(val, isNpo)}
export function optNpo(val) {return isNil(val) ? val : reqNpo(val)}
export function onlyNpo(val) {return isNpo(val) ? val : undefined}
export function laxNpo(val) {return isNil(val) ? Emp() : reqNpo(val)}
export function isDict(val) {
if (!isObj(val)) return false
const pro = Object.getPrototypeOf(val)
return isNil(pro) || (
pro === Object.prototype
&& (!own.call(val, `constructor`) || enu.call(val, `constructor`))
)
}
export function reqDict(val) {return isDict(val) ? val : throwErrFun(val, isDict)}
export function optDict(val) {return isNil(val) ? val : reqDict(val)}
export function onlyDict(val) {return isDict(val) ? val : undefined}
export function laxDict(val) {return isNil(val) ? Emp() : reqDict(val)}
export function isStruct(val) {return isObj(val) && !(Symbol.iterator in val)}
export function reqStruct(val) {return isStruct(val) ? val : throwErrFun(val, isStruct)}
export function optStruct(val) {return isNil(val) ? val : reqStruct(val)}
export function onlyStruct(val) {return isStruct(val) ? val : undefined}
export function laxStruct(val) {return isNil(val) ? Emp() : reqStruct(val)}
export function isArr(val) {return Array.isArray(val)}
export function reqArr(val) {return isArr(val) ? val : throwErrFun(val, isArr)}
export function optArr(val) {return isNil(val) ? val : reqArr(val)}
export function onlyArr(val) {return isArr(val) ? val : undefined}
export function laxArr(val) {return isNil(val) ? [] : reqArr(val)}
/*
At the time of writing, in V8, array subclasses perform MUCH worse than true
arrays. In some hotspots we enforce true arrays for consistent performance.
*/
export function isTrueArr(val) {return isArr(val) && val.constructor === Array}
export function reqTrueArr(val) {return isTrueArr(val) ? val : throwErrFun(val, isTrueArr)}
export function optTrueArr(val) {return isNil(val) ? val : reqTrueArr(val)}
export function onlyTrueArr(val) {return isTrueArr(val) ? val : undefined}
export function laxTrueArr(val) {return isNil(val) ? [] : reqTrueArr(val)}
export function isReg(val) {return isInst(val, RegExp)}
export function reqReg(val) {return isReg(val) ? val : throwErrFun(val, isReg)}
export function optReg(val) {return isNil(val) ? val : reqReg(val)}
export function onlyReg(val) {return isReg(val) ? val : undefined}
export function isDate(val) {return isInst(val, Date)}
export function reqDate(val) {return isDate(val) ? val : throwErrFun(val, isDate)}
export function optDate(val) {return isNil(val) ? val : reqDate(val)}
export function onlyDate(val) {return isDate(val) ? val : undefined}
export function isValidDate(val) {return isDate(val) && isFin(val.valueOf())}
export function reqValidDate(val) {return isValidDate(val) ? val : throwErrFun(val, isValidDate)}
export function optValidDate(val) {return isNil(val) ? val : reqValidDate(val)}
export function onlyValidDate(val) {return isValidDate(val) ? val : undefined}
export function isInvalidDate(val) {return isDate(val) && !isValidDate(val)}
export function reqInvalidDate(val) {return isInvalidDate(val) ? val : throwErrFun(val, isInvalidDate)}
export function optInvalidDate(val) {return isNil(val) ? val : reqInvalidDate(val)}
export function onlyInvalidDate(val) {return isInvalidDate(val) ? val : undefined}
export function isSet(val) {return isInst(val, Set)}
export function reqSet(val) {return isSet(val) ? val : throwErrFun(val, isSet)}
export function optSet(val) {return isNil(val) ? val : reqSet(val)}
export function onlySet(val) {return isSet(val) ? val : undefined}
export function laxSet(val) {return isNil(val) ? new Set() : reqSet(val)}
export function isMap(val) {return isInst(val, Map)}
export function reqMap(val) {return isMap(val) ? val : throwErrFun(val, isMap)}
export function optMap(val) {return isNil(val) ? val : reqMap(val)}
export function onlyMap(val) {return isMap(val) ? val : undefined}
export function laxMap(val) {return isNil(val) ? new Map() : reqMap(val)}
export function isPromise(val) {return isComp(val) && `then` in val && isFun(val.then)}
export function reqPromise(val) {return isPromise(val) ? val : throwErrFun(val, isPromise)}
export function optPromise(val) {return isNil(val) ? val : reqPromise(val)}
export function onlyPromise(val) {return isPromise(val) ? val : undefined}
export function isIter(val) {return isObj(val) && Symbol.iterator in val}
export function reqIter(val) {return isIter(val) ? val : throwErrFun(val, isIter)}
export function optIter(val) {return isNil(val) ? val : reqIter(val)}
export function onlyIter(val) {return isIter(val) ? val : undefined}
export function isIterAsync(val) {return isObj(val) && Symbol.asyncIterator in val}
export function reqIterAsync(val) {return isIterAsync(val) ? val : throwErrFun(val, isIterAsync)}
export function optIterAsync(val) {return isNil(val) ? val : reqIterAsync(val)}
export function onlyIterAsync(val) {return isIterAsync(val) ? val : undefined}
export function isIterator(val) {return isIter(val) && hasNext(val)}
export function reqIterator(val) {return isIterator(val) ? val : throwErrFun(val, isIterator)}
export function optIterator(val) {return isNil(val) ? val : reqIterator(val)}
export function onlyIterator(val) {return isIterator(val) ? val : undefined}
export function isIteratorAsync(val) {return isIterAsync(val) && hasNext(val)}
export function reqIteratorAsync(val) {return isIteratorAsync(val) ? val : throwErrFun(val, isIteratorAsync)}
export function optIteratorAsync(val) {return isNil(val) ? val : reqIteratorAsync(val)}
export function onlyIteratorAsync(val) {return isIteratorAsync(val) ? val : undefined}
export function isGen(val) {return isIterator(val) && hasMeth(val, `return`) && hasMeth(val, `throw`)}
export function reqGen(val) {return isGen(val) ? val : throwErrFun(val, isGen)}
export function optGen(val) {return isNil(val) ? val : reqGen(val)}
export function onlyGen(val) {return isGen(val) ? val : undefined}
// TODO add `isErrCls`.
export function isCls(val) {return isFun(val) && !!val.prototype}
export function reqCls(val) {return isCls(val) ? val : throwErrFun(val, isCls)}
export function optCls(val) {return isNil(val) ? val : reqCls(val)}
export function onlyCls(val) {return isCls(val) ? val : undefined}
// TODO tests.
export function isSubCls(sub, sup) {return isCls(sub) && (sub === sup || isInst(sub.prototype, sup))}
// TODO tests.
export function reqSubCls(sub, sup) {
if (isSubCls(sub, sup)) return sub
throw TypeError(`expected subclass of ${show(sup)}, found ${show(sub)}`)
}
export function isList(val) {return isSome(val) && (isArr(val) || (isIter(val) && isNat(getLength(val))))}
export function reqList(val) {return isList(val) ? val : throwErrFun(val, isList)}
export function optList(val) {return isNil(val) ? val : reqList(val)}
export function onlyList(val) {return isList(val) ? val : undefined}
export function laxList(val) {return isNil(val) ? [] : reqList(val)}
export function isSeq(val) {return isArr(val) || isSet(val) || isList(val) || isIterator(val)}
export function reqSeq(val) {return isSeq(val) ? val : throwErrFun(val, isSeq)}
export function optSeq(val) {return isNil(val) ? val : reqSeq(val)}
export function onlySeq(val) {return isSeq(val) ? val : undefined}
export function isVac(val) {return !val || (isArr(val) && (!val.length || val.every(isVac)))}
export function reqVac(val) {return isVac(val) ? val : throwErrFun(val, isVac)}
export function optVac(val) {return isNil(val) ? val : reqVac(val)}
export function onlyVac(val) {return isVac(val) ? val : undefined}
export function isScalar(val) {
if (isObj(val)) {
const fun = get(val, `toString`)
return isFun(fun) && fun !== Object.prototype.toString && fun !== Array.prototype.toString
}
return !(isNil(val) || isSym(val) || isFun(val))
}
export function reqScalar(val) {return isScalar(val) ? val : throwErrFun(val, isScalar)}
export function optScalar(val) {return isNil(val) ? val : reqScalar(val)}
export function onlyScalar(val) {return isScalar(val) ? val : undefined}
export function isScalarOpt(val) {return isNil(val) || isScalar(val)}
export function reqScalarOpt(val) {return isScalarOpt(val) ? val : throwErrFun(val, isScalarOpt)}
export function optScalarOpt(val) {return isNil(val) ? val : reqScalarOpt(val)}
export function onlyScalarOpt(val) {return isScalarOpt(val) ? val : undefined}
export function isArrble(val) {return isIter(val) && `toArray` in val && isFun(val.toArray)}
export function reqArrble(val) {return isArrble(val) ? val : throwErrFun(val, isArrble)}
export function optArrble(val) {return isNil(val) ? val : reqArrble(val)}
export function onlyArrble(val) {return isArrble(val) ? val : undefined}
export function isEqable(val) {return isObj(val) && `eq` in val && isFun(val.eq)}
export function reqEqable(val) {return isEqable(val) ? val : throwErrFun(val, isEqable)}
export function optEqable(val) {return isNil(val) ? val : reqEqable(val)}
export function onlyEqable(val) {return isEqable(val) ? val : undefined}
export function isClearable(val) {return isObj(val) && `clear` in val && isFun(val.clear)}
export function reqClearable(val) {return isClearable(val) ? val : throwErrFun(val, isClearable)}
export function optClearable(val) {return isNil(val) ? val : reqClearable(val)}
export function onlyClearable(val) {return isClearable(val) ? val : undefined}
export function isErr(val) {return isInst(val, Error)}
export function reqErr(val) {return isErr(val) ? val : throwErrFun(val, isErr)}
export function optErr(val) {return isNil(val) ? val : reqErr(val)}
export function onlyErr(val) {return isErr(val) ? val : undefined}
export function isArrOf(val, fun) {
reqFun(fun)
return isArr(val) && val.every(fun)
}
// TODO improve error message.
export function reqArrOf(val, fun) {
for (const elem of reqArr(val)) req(elem, fun)
return val
}
export function optArrOf(val, fun) {return isNil(val) ? val : reqArrOf(val, fun)}
// TODO consolidate with `hasLen`.
// The two functions must be exactly inverse.
export function isEmpty(val) {
if (!isObj(val)) return true
if (isList(val)) return val.length === 0
if (isIter(val)) return getSize(val) === 0
return false
}
export function isInst(val, cls) {return isObj(val) && val instanceof cls}
/* Assert */
export function req(val, fun) {
if (reqValidator(fun)(val)) return val
throw errFun(val, fun)
}
export function reqOneOf(val, funs) {
for (const fun of reqArr(funs)) if (fun(val)) return val
throw errOneOf(val, funs)
}
export function opt(val, fun) {
reqValidator(fun)
return isNil(val) ? val : req(val, fun)
}
export function optOneOf(val, funs) {
return isNil(val) ? val : reqOneOf(val, funs)
}
export function reqInst(val, cls) {
if (isInst(val, cls)) return val
throw errInst(val, cls)
}
export function optInst(val, cls) {
reqCls(cls)
return isNil(val) ? val : reqInst(val, cls)
}
export function only(val, fun) {return reqValidator(fun)(val) ? val : undefined}
export function onlyInst(val, cls) {return isInst(val, cls) ? val : undefined}
/* Cast */
export function toInst(val, cls) {return isInst(val, cls) ? val : new cls(val)}
export function toInstOpt(val, cls) {return isNil(val) ? val : toInst(val, cls)}
export function render(src) {
const out = renderOpt(src)
if (isSome(out)) return out
throw errConv(src, `string`)
}
export function renderOpt(val) {
if (isStr(val)) return val
if (isDate(val)) return renderDate(val)
if (isScalar(val)) return String(val)
return undefined
}
export function renderLax(val) {return isNil(val) ? `` : render(val)}
export function show(val, vis = new Map()) {
if (isStr(val)) return JSON.stringify(val)
if (isSym(val)) return val.toString()
if (isFun(val)) return showFun(val)
if (isObj(val)) return showObj(val, vis)
return String(val)
}
export function toTrueArr(val) {
if (isNil(val)) return []
if (isTrueArr(val)) return val
return [...reqIter(val)]
}
/* Misc */
export function is(one, two) {return one === two || (isNaN(one) && isNaN(two))}
export function truthy(val) {return !!val}
export function falsy(val) {return !val}
export function nop() {}
export function id(val) {return val}
export function val(src) {return function val() {return src}}
export function panic(val) {if (isSome(val)) throw val}
export function True() {return true}
export function False() {return false}
export function vac(val) {return isVac(val) ? undefined : val}
export function bind(fun, ...args) {return reqFun(fun).bind(this, ...args)}
export function not(fun) {
reqFun(fun)
return function not() {return !fun.apply(this, arguments)}
}
export function hasOwn(val, key) {return isComp(val) && own.call(val, key)}
export function hasOwnEnum(val, key) {return isComp(val) && enu.call(val, key)}
export function hasInherited(val, key) {return isComp(val) && key in val && !own.call(val, key)}
export function hasMeth(val, key) {return isComp(val) && key in val && isFun(val[key])}
export function eq(one, two) {
if (is(one, two)) return true
if (!isEqable(one)) return false
const con = getCon(two)
return !!con && (con === getCon(one)) && one.eq(two)
}
/* Cls */
export function setProto(tar, cls) {
if (Object.getPrototypeOf(tar) !== cls.prototype) {
Object.setPrototypeOf(tar, cls.prototype)
}
}
export function Emp() {return new.target && new.target !== Emp ? this : Object.create(null)}
Emp.prototype = null
/* Op */
// TODO add type checking. `+` must require consistent types.
export function add(a, b) {return a + b}
export function sub(a, b) {return a - b}
export function mul(a, b) {return a * b}
export function div(a, b) {return a / b}
export function rem(a, b) {return a % b}
export function lt(a, b) {return a < b}
export function gt(a, b) {return a > b}
export function lte(a, b) {return a <= b}
export function gte(a, b) {return a >= b}
export function neg(val) {return -val}
export function inc(val) {return val + 1}
export function dec(val) {return val - 1}
/* Internal */
const own = Object.prototype.hasOwnProperty
const enu = Object.prototype.propertyIsEnumerable
function isFunType(val, name) {return isFun(val) && val.constructor.name === name}
function instDesc(val) {return isFun(val) ? `instance of ${showFunName(val)} ` : ``}
function hasNext(val) {return `next` in val && isFun(val.next)}
function reqValidator(fun) {
if (!isFun(fun)) {
throw TypeError(`expected validator function, got ${show(fun)}`)
}
return fun
}
export function errType(val, msg) {return TypeError(msgType(val, msg))}
export function msgType(val, msg) {return `expected variant of ${msg}, got ${show(val)}`}
export function errFun(val, fun) {return TypeError(msgFun(val, fun))}
export function msgFun(val, fun) {return msgType(val, showFunName(fun))}
export function throwErrFun(val, fun) {throw errFun(val, fun)}
export function errConv(val, msg) {return TypeError(msgConv(val, msg))}
export function errSynt(val, msg) {return SyntaxError(msgConv(val, msg))}
export function msgConv(val, msg) {return `unable to convert ${show(val)} to ${msg}`}
export function errConvInst(val, inst) {return TypeError(msgConvInst(val, inst))}
export function msgConvInst(val, inst) {return msgConv(val, inst.constructor.name)}
export function errInst(val, cls) {return TypeError(msgInst(val, cls))}
export function msgInst(val, cls) {return `expected instance of ${showFunName(cls)}, got ${instDesc(getCon(val))}${show(val)}`}
export function errIn(val, key) {return TypeError(msgIn(val, key))}
export function msgIn(val, key) {return `unable to find ${show(key)} in ${show(val)}`}
export function errImpl() {return TypeError(msgImpl())}
export function msgImpl() {return `not implemented`}
export function errTrans(err, cls, msg) {
err = toInst(err, cls)
const pre = renderLax(msg)
const suf = renderLax(err.message)
err.message = pre && suf ? pre + `: ` + suf : pre || suf
return err
}
export function errWrap(err, cls, msg) {
if (isErrorCauseSupported()) return new cls(msg, {cause: err})
return errTrans(err, cls, msg)
}
export function errCause(val) {
if (isErrorCauseSupported()) while (isErr(val)) val = val.cause
return val
}
/*
The feature is standard, but at the time of writing, has limited engine support.
Many engines, including Chrome, do not report causes unless explicitly asked
for, and Safari <15 does not support causes at all.
*/
function isErrorCauseSupported() {
return ERROR_CAUSE ??= `cause` in Error(``, {cause: undefined})
}
let ERROR_CAUSE
function errOneOf(val, funs) {return TypeError(msgType(val, `[` + showFuns(funs) + `]`))}
export function convType(tar, src, msg) {
if (isSome(tar)) return tar
throw errConv(src, msg)
}
export function convSynt(tar, src, msg) {
if (isSome(tar)) return tar
throw errSynt(src, msg)
}
function showFun(val) {return `[function ${val.name || val}]`}
function showFuns(funs) {return funs.map(showFunName).join(`, `)}
export function showFunName(fun) {return fun.name || showFun(fun)}
function showObj(src, vis) {
const ind = vis?.get(src)
if (ind) return `[cyclic ` + ind + `]`
vis?.set(src, vis.size + 1)
if (isErr(src)) return String(src)
if (isArr(src)) return showArr(src, vis)
const con = getCon(src)
if (!con || con === Object) return showDictOpt(src, vis) || `{}`
const name = getName(con) || getTag(src)
if (isInst(src, Boolean)) return `[${name || `Boolean`}: ${show(src.valueOf())}]`
if (isInst(src, Number)) return `[${name || `Number`}: ${show(src.valueOf())}]`
if (isInst(src, BigInt)) return `[${name || `BigInt`}: ${show(src.valueOf())}n]`
if (isInst(src, String)) return `[${name || `String`}: ${show(src.valueOf())}]`
if (isInst(src, Symbol)) return `[${name || `Symbol`}: ${show(src.valueOf())}]`
const dict = showDictOpt(src, vis)
if (!name) return dict || `{}`
return `[object ${name}${dict && `: `}${dict}]`
}
function showArr(src, vis) {
const tar = []
for (src of src) tar.push(show(src, vis))
return `[` + tar.join(`, `) + `]`
}
function showDictOpt(src, vis) {
const buf = []
for (const key of Object.getOwnPropertySymbols(src)) {
buf.push(showDictEntry(key, src[key], vis))
}
for (const key of Object.getOwnPropertyNames(src)) {
buf.push(showDictEntry(key, src[key], vis))
}
const out = buf.join(`, `)
return out && (`{` + out + `}`)
}
function showDictEntry(key, val, vis) {
return showDictKey(key, vis) + `: ` + show(val, vis)
}
function showDictKey(val, vis) {
if (isSym(val)) return `[` + show(val, vis) + `]`
if (/^(?:\d+|\d+\.\d+|\d+n|[A-Za-z_$][\w$]*)$/.test(reqStr(val))) {
return val
}
return show(val, vis)
}
/*
Like `val?.[key]` but with sanity checks: works only on composite values and
avoids accessing the property unless it satisfies the `in` check.
*/
export function get(val, key) {
return isComp(val) && key in val ? val[key] : undefined
}
export function getOwn(val, key) {return hasOwn(val, key) ? val[key] : undefined}
export function reqGet(val, key) {
if (isComp(val) && key in val) return val[key]
throw errIn(val, key)
}
function getName(val) {return get(val, `name`)}
function getLength(val) {return get(val, `length`)}
function getSize(val) {return get(val, `size`)}
function getCon(val) {return get(val, `constructor`)}
function getTag(val) {return get(val, Symbol.toStringTag)}
// This is actually faster than default rendering.
function renderDate(val) {
if (val.toString === Date.prototype.toString) return val.toISOString()
return val.toString()
}
export function structKeys(val) {
return isNil(val) ? [] : Object.keys(reqStruct(val))
}