@temperlang/core 0.0.3

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/index.js ADDED
@@ -0,0 +1,794 @@
1
+ export * from './interface-types.js';
2
+ export * from './regex.js';
3
+
4
+ export const noResultException = new (
5
+ class NoResultException extends Error {
6
+ constructor() {
7
+ super("NoResult");
8
+ }
9
+ }
10
+ );
11
+
12
+ // Implements extension method String::isEmpty
13
+ export function stringIsEmpty(s) { return s === ''; }
14
+ // Implements extension method String::split
15
+ export function stringSplit(s, separator) {
16
+ return s.split(separator).map((s) => s);
17
+ }
18
+
19
+ /** Specifically for utf8 and utf32, since utf16 is simpler in js. */
20
+ class TrickyStringSlice {
21
+ #length;
22
+
23
+ hasAtLeast(count) {
24
+ // Repeated calls to hasAtLeast are potentially expensive.
25
+ return (this.#length ?? this._lengthUntil(count)) >= count;
26
+ }
27
+
28
+ get length() {
29
+ if (this.#length === undefined) {
30
+ this.#length = this._lengthUntil();
31
+ }
32
+ return this.#length;
33
+ }
34
+
35
+ _lengthUntil(stop = undefined) {
36
+ // To be overridden in subclasses.
37
+ return 0;
38
+ }
39
+ }
40
+
41
+ class Utf8StringSlice extends TrickyStringSlice {
42
+ /** The underlying string value. */
43
+ #content;
44
+ /**
45
+ * The byte index approximation of the left side.
46
+ * A byte index approximation is an integer, i, such that:
47
+ * - (i >> 2) is the index of a codepoint in #content
48
+ * - (i & 3) is the index of a byte within the UTF-8 representation of that codepoint.
49
+ */
50
+ #left;
51
+ /** The byte index approximation of the right side. */
52
+ #right;
53
+
54
+ constructor(content, left = 0, right = content.length * 4) {
55
+ super();
56
+ this.#content = content;
57
+ this.#left = left;
58
+ this.#right = right;
59
+ }
60
+
61
+ toString() {
62
+ let left = this.#left;
63
+ let right = this.#right;
64
+ let content = this.#content;
65
+
66
+ if (left === right) { return ""; }
67
+
68
+ // If we're only using some bytes on the left or right, replace that codepoint with U+FFFD.
69
+ let leftPartial = left & 3; // Do we have an incomplete code-point on the left?
70
+ let leftIndex = (left + 3) >> 2;
71
+ let rightIndex = right >> 2;
72
+ let rightPartial = (right & 3);
73
+
74
+ // If leftIndex is in the middle of a surrogate pair, advance over the tail.
75
+ if (leftIndex < rightIndex) {
76
+ let leftCodeUnitUtf16 = content.charCodeAt(leftIndex);
77
+ if (0xDC00 < leftCodeUnitUtf16 && leftCodeUnitUtf16 <= 0xDFFF) {
78
+ leftIndex += 1;
79
+ }
80
+ }
81
+
82
+ if (leftIndex > rightIndex) { return "\uFFFD"; }
83
+
84
+ let sub = content.substring(leftIndex, rightIndex);
85
+ if (leftPartial || rightPartial) {
86
+ return `${leftPartial ? "\uFFFD" : ""}${sub}${rightPartial ? "\uFFFD" : ""}`
87
+ } else {
88
+ return sub;
89
+ }
90
+ }
91
+
92
+ valueOf() { return this.toString(); }
93
+
94
+ _lengthUntil(stop) {
95
+ const left = this.#left;
96
+ const right = this.#right;
97
+ const content = this.#content;
98
+ let i = left >> 2;
99
+ // Add bytes between codePointBoundaryBeforeLimit and right
100
+ // Subtract bytes past zeroth in codepoint for left
101
+ let len = (right & 3) - (left & 3);
102
+ const codePointBoundaryBeforeLimit = (right & ~3) >> 2;
103
+ while (i < codePointBoundaryBeforeLimit) {
104
+ if (stop !== undefined && len >= stop) {
105
+ break;
106
+ }
107
+ let cp = content.codePointAt(i);
108
+ let nBytes = nUtf8BytesInChar(cp);
109
+ len += nBytes;
110
+ i += (4 + nBytes) >> 2;
111
+ }
112
+ return len;
113
+ }
114
+
115
+ get isEmpty() {
116
+ return this.#left >= this.#right;
117
+ }
118
+
119
+ read() {
120
+ let left = this.#left;
121
+ let right = this.#right;
122
+ if (left >= right) {
123
+ throw noResultException;
124
+ }
125
+
126
+ let content = this.#content;
127
+ let cp = content.codePointAt(left >> 2);
128
+ if (cp < 0x80) {
129
+ return cp;
130
+ } else {
131
+ let byteOffset = left & 3;
132
+ let nBytes = nUtf8BytesInChar(cp);
133
+ let byteInfo = byteInfos[(nBytes - 1) * 4 + byteOffset];
134
+ let codeUnit = ((cp >>> byteInfo.shift) & byteInfo.andMask) | byteInfo.orMask;
135
+ return codeUnit;
136
+ }
137
+ }
138
+
139
+ advance(count) {
140
+ if (count <= 0) {
141
+ return this;
142
+ } else if (count === 1) {
143
+ let left = this.#left;
144
+ let right = this.#right;
145
+ if (left >= right) {
146
+ return this;
147
+ }
148
+ let content = this.#content;
149
+ let cp = content.codePointAt(left >> 2);
150
+ let newLeft;
151
+ if (cp < 0x80) {
152
+ newLeft = left + 4;
153
+ } else {
154
+ let byteOffset = left & 3;
155
+ let nBytes = nUtf8BytesInChar(cp);
156
+ newLeft = (byteOffset + 1 < nBytes) ? left + 1 : (left & ~3) + ((nBytes + 4) & ~3);
157
+ }
158
+ return new Utf8StringSlice(content, newLeft, right);
159
+ } else {
160
+ throw new Error("TODO");
161
+ }
162
+ }
163
+
164
+ [Symbol.iterator]() {
165
+ function *bytes(content, left, limit) {
166
+ let i = left;
167
+ while (i < limit) {
168
+ let cp = content.codePointAt(i >> 2);
169
+ if (cp < 0x80) {
170
+ yield cp;
171
+ i += 4;
172
+ } else {
173
+ let byteOffset = i & 3;
174
+ let nBytes = nUtf8BytesInChar(cp);
175
+ let byteInfo = byteInfos[(nBytes - 1) * 4 + byteOffset];
176
+ let codeUnit = ((cp >>> byteInfo.shift) & byteInfo.andMask) | byteInfo.orMask;
177
+ yield codeUnit;
178
+ i = (byteOffset + 1 < nBytes) ? i + 1 : (i & ~3) + ((nBytes + 4) & ~3);
179
+ }
180
+ }
181
+ }
182
+ return bytes(this.#content, this.#left, this.#right);
183
+ }
184
+
185
+ toJSON() {
186
+ return { content: this.#content, left: this.#left, right: this.#right };
187
+ }
188
+ }
189
+
190
+ class Utf16StringSlice {
191
+ /** The underlying string value. */
192
+ #content;
193
+ /**
194
+ * A regular character offset of the left of the slice, inclusive.
195
+ */
196
+ #left;
197
+ /** A regular character offset of the right of the slice, exclusive. */
198
+ #right;
199
+
200
+ constructor(content, left = 0, right = content.length) {
201
+ this.#content = content;
202
+ this.#left = left;
203
+ this.#right = right;
204
+ }
205
+
206
+ toString() {
207
+ return this.#content.substring(this.#left, this.#right);
208
+ }
209
+
210
+ valueOf() { return this.toString(); }
211
+
212
+ hasAtLeast(count) {
213
+ return this.length >= count;
214
+ }
215
+
216
+ get length() {
217
+ return this.#right - this.#left;
218
+ }
219
+
220
+ get isEmpty() {
221
+ return this.#left >= this.#right;
222
+ }
223
+
224
+ read() {
225
+ let left = this.#left;
226
+ let right = this.#right;
227
+ if (left >= right) { throw noResultException; }
228
+ return this.#content.charCodeAt(left);
229
+ }
230
+
231
+ advance(count) {
232
+ if (count <= 0) {
233
+ return this;
234
+ } else {
235
+ let left = this.#left;
236
+ let right = this.#right;
237
+ if (left >= right) {
238
+ return this;
239
+ }
240
+ let newLeft = left + count;
241
+ if (newLeft >= right) { newLeft = right; }
242
+ return new Utf16StringSlice(this.#content, newLeft, right);
243
+ }
244
+ }
245
+
246
+ [Symbol.iterator]() {
247
+ function *chars(content, left, limit) {
248
+ let i = left;
249
+ while (i < limit) {
250
+ yield content.charCodeAt(i)
251
+ i += 1
252
+ }
253
+ }
254
+ return chars(this.#content, this.#left, this.#right);
255
+ }
256
+
257
+ toJSON() {
258
+ return { content: this.#content, left: this.#left, right: this.#right };
259
+ }
260
+ }
261
+
262
+ class CodePointsStringSlice extends TrickyStringSlice {
263
+ /** The underlying string value. */
264
+ #content;
265
+ /**
266
+ * A regular character offset of the left of the slice, inclusive.
267
+ */
268
+ #left;
269
+ /** A regular character offset of the right of the slice, exclusive. */
270
+ #right;
271
+
272
+ constructor(content, left = 0, right = content.length) {
273
+ super();
274
+ this.#content = content;
275
+ this.#left = left;
276
+ this.#right = right;
277
+ }
278
+
279
+ toString() {
280
+ return this.#content.substring(this.#left, this.#right);
281
+ }
282
+
283
+ valueOf() { return this.toString(); }
284
+
285
+ _lengthUntil(stop) {
286
+ const left = this.#left;
287
+ const right = this.#right;
288
+ const content = this.#content;
289
+ let i = left;
290
+ let len = 0;
291
+ while (i < right) {
292
+ if (stop !== undefined && len >= stop) {
293
+ break;
294
+ }
295
+ let cp = content.codePointAt(i);
296
+ if (cp > 0xFFFF) {
297
+ i += 2;
298
+ } else {
299
+ i += 1;
300
+ }
301
+ len += 1;
302
+ }
303
+ return len;
304
+ }
305
+
306
+ get isEmpty() {
307
+ return this.#left >= this.#right;
308
+ }
309
+
310
+ read() {
311
+ let left = this.#left;
312
+ let right = this.#right;
313
+ if (left >= right) { throw noResultException; }
314
+ return this.#content.codePointAt(left);
315
+ }
316
+
317
+ advance(count) {
318
+ if (count <= 0) {
319
+ return this;
320
+ } else {
321
+ let left = this.#left;
322
+ let right = this.#right;
323
+ let content = this.#content;
324
+ if (left >= right) {
325
+ return this;
326
+ }
327
+ let newLeft = left;
328
+ for (let i = count; i && newLeft < right; --i) {
329
+ let cp = content.codePointAt(newLeft);
330
+ if (cp > 0xFFFF) {
331
+ newLeft += 2;
332
+ } else {
333
+ newLeft += 1;
334
+ }
335
+ }
336
+ if (newLeft >= right) { newLeft = right; }
337
+ return new CodePointsStringSlice(this.#content, newLeft, right);
338
+ }
339
+ }
340
+
341
+ [Symbol.iterator]() {
342
+ function *chars(content, left, limit) {
343
+ let i = left;
344
+ while (i < limit) {
345
+ let cp = content.codePointAt(i);
346
+ yield cp;
347
+ i += (cp > 0xFFFF) ? 2 : 1;
348
+ }
349
+ }
350
+ return chars(this.#content, this.#left, this.#right);
351
+ }
352
+
353
+ toJSON() {
354
+ return { content: this.#content, left: this.#left, right: this.#right };
355
+ }
356
+ }
357
+
358
+
359
+ // Implements extension method String::utf8
360
+ export function stringUtf8(string) {
361
+ return new Utf8StringSlice(string);
362
+ }
363
+
364
+ // Implements extension method String::utf16
365
+ export function stringUtf16(string) {
366
+ return new Utf16StringSlice(string);
367
+ }
368
+
369
+ // Implements extension method String::codePoints
370
+ export function stringCodePoints(string) {
371
+ return new CodePointsStringSlice(string);
372
+ }
373
+
374
+ // Implements extension method Int::toString
375
+ export function intToString(i, radix) {
376
+ return i.toString(radix);
377
+ }
378
+
379
+ // Implements extension method Float64::toString
380
+ export function float64ToString(n) {
381
+ // TODO(mikesamuel, issue#579): need functional test to nail down
382
+ // double formatting threshholds.
383
+ switch (n) {
384
+ case 0:
385
+ // differentiating negative zero from zero is a pain. Got this solution from
386
+ // https://dev.to/emnudge/identifying-negative-zero-2j1o
387
+ if (1 / n == Infinity) {
388
+ return "0.0"
389
+ }
390
+ else {
391
+ return "-0.0"
392
+ }
393
+ case Infinity:
394
+ return "∞"
395
+ case Number.NEGATIVE_INFINITY:
396
+ return "-∞"
397
+ default:
398
+ return n.toString();
399
+ };
400
+ }
401
+
402
+ function nUtf8BytesInChar(cp) {
403
+ return (cp < 0x0800)
404
+ ? ((cp < 0x80) ? 1 : 2)
405
+ : ((cp < 0x10000) ? 3 : 4)
406
+ }
407
+
408
+ /*
409
+ * | First code point | Last code point | Byte 0 | Byte 1 | Byte 2 | Byte 3 |
410
+ * | ---------------- | --------------- | --------- | --------- | --------- | --------- |
411
+ * | U+0000 | U+007F | 0xxx_xxxx | | | |
412
+ * | U+0080 | U+07FF | 110x_xxxx | 10xx_xxxx | | |
413
+ * | U+0800 | U+FFFF | 1110_xxxx | 10xx_xxxx | 10xx_xxxx | |
414
+ * | U+10000 | U+10FFFF | 1111_0xxx | 10xx_xxxx | 10xx_xxxx | 10xx_xxxx |
415
+ */
416
+ const byteInfos = [
417
+ { andMask: 0b0111_1111, orMask: 0, shift: 0 },
418
+ null,
419
+ null,
420
+ null,
421
+
422
+ { andMask: 0b0001_1111, orMask: 0b1100_0000, shift: 6 },
423
+ { andMask: 0b0011_1111, orMask: 0b1000_0000, shift: 0 },
424
+ null,
425
+ null,
426
+
427
+ { andMask: 0b0000_1111, orMask: 0b1110_0000, shift: 12 },
428
+ { andMask: 0b0011_1111, orMask: 0b1000_0000, shift: 6 },
429
+ { andMask: 0b0011_1111, orMask: 0b1000_0000, shift: 0 },
430
+ null,
431
+
432
+ { andMask: 0b0000_0111, orMask: 0b1111_0000, shift: 18 },
433
+ { andMask: 0b0011_1111, orMask: 0b1000_0000, shift: 12 },
434
+ { andMask: 0b0011_1111, orMask: 0b1000_0000, shift: 6 },
435
+ { andMask: 0b0011_1111, orMask: 0b1000_0000, shift: 0 },
436
+ ];
437
+
438
+ function freeze(items) {
439
+ return Object.freeze(items);
440
+ }
441
+
442
+ // Implements extension method ListBuilder::add
443
+ export function listBuilderAdd(ls, newItem, at) {
444
+ if (at === undefined) {
445
+ // Technically, we could also use splice instead of push for this case.
446
+ // Which is better might depend on minifiers and/or execution speed.
447
+ ls.push(newItem);
448
+ } else {
449
+ if (at < 0 || at > ls.length) {
450
+ throw noResultException;
451
+ }
452
+ ls.splice(at, 0, newItem);
453
+ }
454
+ return; // of type tVoid
455
+ }
456
+
457
+ // Implements extension method ListBuilder::addAll
458
+ export function listBuilderAddAll(ls, newItems, at) {
459
+ if (at === undefined) {
460
+ ls.push(...newItems);
461
+ } else {
462
+ if (at < 0 || at > ls.length) {
463
+ throw noResultException;
464
+ }
465
+ ls.splice(at, 0, ...newItems);
466
+ }
467
+ return; // of type tVoid
468
+ }
469
+ // Implements extension method List::filter
470
+ export function listFilter(ls, predicate) {
471
+ let filtered = null;
472
+ let nFiltered = 0; // Just past index of last element of ls filtered onto filtered
473
+ let { length } = ls;
474
+ for (let i = 0; i < length; ++i) {
475
+ let element = ls[i];
476
+ let ok = predicate(element);
477
+ if (!ok) {
478
+ if (!filtered) {
479
+ filtered = [];
480
+ }
481
+ filtered.push(...ls.slice(nFiltered, i));
482
+ nFiltered = i + 1;
483
+ }
484
+ }
485
+ let fullyFiltered;
486
+ if (filtered) {
487
+ filtered.push(...ls.slice(nFiltered, length));
488
+ fullyFiltered = filtered;
489
+ } else {
490
+ fullyFiltered = ls;
491
+ }
492
+ return freeze(fullyFiltered);
493
+ }
494
+ // Implements extension method List::get
495
+ export function listGet(ls, i) {
496
+ let { length } = ls;
497
+ if (0 <= i && i < length) { return ls[i]; }
498
+ throw noResultException;
499
+ }
500
+ // Implements extension method List::getOr
501
+ export function listGetOr(ls, i, fallback) {
502
+ let { length } = ls;
503
+ return (0 <= i && i < length) ? ls[i] : fallback;
504
+ }
505
+ // Implements extension method List::isEmpty
506
+ export function listIsEmpty(ls) {
507
+ return !ls.length;
508
+ }
509
+ // Implements extension method List::join
510
+ export function listJoin(ls, separator, elementStringifier) {
511
+ let joined = '';
512
+ let { length } = ls;
513
+ for (let i = 0; i < length; ++i) {
514
+ if (i) {
515
+ joined += separator;
516
+ }
517
+ let element = ls[i];
518
+ let stringifiedElement = elementStringifier(element);
519
+ joined += stringifiedElement;
520
+ }
521
+ return joined;
522
+ }
523
+ // Implements extension method List::length
524
+ export function listLength(ls) { return ls.length; }
525
+ // Implements extension method List::mapDropping
526
+ export function listMapDropping() { throw new Error("TODO List::mapDropping"); }
527
+ // Implements extension method List::map
528
+ export function listMap(ls, transform) {
529
+ let mapped = [];
530
+ let { length } = ls;
531
+ for (let i = 0; i < length; ++i) {
532
+ let transformed = transform(ls[i]);
533
+ mapped[i] = transformed;
534
+ }
535
+ return freeze(mapped);
536
+ }
537
+ // Implements extension method ListBuilder::reverse
538
+ export function listBuilderReverse(ls) {
539
+ let { length } = ls;
540
+ let lastIndex = length - 1;
541
+ let mid = length >> 1;
542
+ for (let i = 0; i < mid; ++i) {
543
+ let j = lastIndex - i;
544
+ let a = ls[i];
545
+ ls[i] = ls[j];
546
+ ls[j] = a;
547
+ }
548
+ }
549
+ // Implements extension method ListBuilder::set
550
+ export function listBuilderSet(ls, i, newValue) {
551
+ let { length } = ls;
552
+ if (0 <= i && i <= length) {
553
+ ls[i] = newValue;
554
+ }
555
+ return; // of type tVoid
556
+ }
557
+ // Implements extension method ListBuilder::toList
558
+ export function listBuilderToList(ls) {
559
+ return freeze(ls.slice());
560
+ }
561
+ // Implements extension method List::slice
562
+ export function listSlice(ls, startInclusive, endExclusive) {
563
+ if (startInclusive < 0) { startInclusive = 0; }
564
+ if (endExclusive < 0) { endExclusive = 0; }
565
+ return freeze(ls.slice(startInclusive, endExclusive));
566
+ }
567
+
568
+ // Implements extension method Deque::constructor
569
+ const DEQUE_NTAKEN = Symbol("Deque::nTaken");
570
+ export function dequeConstructor() {
571
+ let deque = [];
572
+ Object.defineProperty(deque, DEQUE_NTAKEN, { value: 0, writable: true });
573
+ return deque;
574
+ }
575
+ // Implements extension method Deque::add
576
+ export function dequeAdd(deque, element) {
577
+ deque.push(element);
578
+ }
579
+ // Implements extension method Deque::isEmpty
580
+ export function dequeIsEmpty(deque) {
581
+ return deque.length === (deque[DEQUE_NTAKEN] || 0);
582
+ }
583
+ // Implements extension method Deque::removeFirst
584
+ export function dequeRemoveFirst(deque) {
585
+ // https://gist.github.com/mikesamuel/444258e7005e8fc9534d9cf274b1df58
586
+ let nTaken = deque[DEQUE_NTAKEN];
587
+ let length = deque.length;
588
+ if (length === nTaken) {
589
+ deque[DEQUE_NTAKEN] = 0;
590
+ deque.length = 0;
591
+ throw noResultException;
592
+ }
593
+ let item = deque[nTaken];
594
+ let nShiftThreshhold = (length / 2) | 0;
595
+ if (nShiftThreshhold < 32) { nShiftThreshhold = 32; }
596
+ if (nTaken >= nShiftThreshhold) {
597
+ deque.splice(0, nTaken + 1);
598
+ deque[DEQUE_NTAKEN] = 0;
599
+ } else {
600
+ deque[nTaken] = undefined;
601
+ deque[DEQUE_NTAKEN] = nTaken + 1;
602
+ }
603
+ return item;
604
+ }
605
+
606
+ const ZERO_N = BigInt(0);
607
+ const ONE_N = BigInt(1);
608
+ class DenseBitVector {
609
+ #bits /* : BigInt */;
610
+
611
+ constructor() {
612
+ this.#bits = ZERO_N;
613
+ }
614
+
615
+ get(index) {
616
+ return (this.#bits & (ONE_N << BigInt(index))) !== ZERO_N;
617
+ }
618
+
619
+ set(index, newBitValue) {
620
+ let mask = ONE_N << BigInt(index);
621
+ this.#bits = newBitValue
622
+ ? (this.#bits | mask)
623
+ : (this.#bits & ~mask);
624
+ }
625
+
626
+ toString() {
627
+ return `${this.#bits}`;
628
+ }
629
+
630
+ valueOf() {
631
+ return this.#bits;
632
+ }
633
+
634
+ toJSON() {
635
+ return this.#bits;
636
+ }
637
+ }
638
+
639
+ // Implements extension method DenseBitVector::constructor
640
+ export function denseBitVectorConstructor(capacity) {
641
+ return new DenseBitVector();
642
+ }
643
+ // Implements extension method DenseBitVector::get
644
+ export function denseBitVectorGet(denseBitVector, index) {
645
+ return denseBitVector.get(index);
646
+ }
647
+ // Implements extension method DenseBitVector::set
648
+ export function denseBitVectorSet(denseBitVector, index, newBitValue) {
649
+ return denseBitVector.set(index, newBitValue);
650
+ }
651
+
652
+ // Implements extension method Boolean::toString
653
+ export function booleanToString(b) {
654
+ return b ? 'true' : 'false';
655
+ }
656
+
657
+ // Implements Symbol construction.
658
+ export function symbolFor(text) {
659
+ return Symbol.for(text);
660
+ }
661
+
662
+ // Stubs out static property verification
663
+ export function getStatic(typeGuard, symbol) {
664
+ return undefined; // TODO(bps, #780)
665
+ }
666
+
667
+ const String = "".constructor;
668
+ const { isArray } = Array;
669
+ const { isSafeInteger } = Number;
670
+ const { trunc } = Math;
671
+
672
+ export {
673
+ // Export reliable paths to JS builtins, so they can import them
674
+ // via an unambiguous name locally and not worry about masking.
675
+ isArray,
676
+ isSafeInteger,
677
+ };
678
+
679
+ // Export runtime value type checks used for safe casting
680
+
681
+ export function requireIsArray(x) {
682
+ if (!isArray) { throw noResultException; }
683
+ return x;
684
+ }
685
+
686
+ export function requireInstanceOf(x, typeRequirement) {
687
+ if (!(x instanceof typeRequirement)) { throw noResultException; }
688
+ return x;
689
+ }
690
+
691
+ export function requireIsSafeInteger(x) {
692
+ if (!isSafeInteger(x)) { throw noResultException; }
693
+ return x;
694
+ }
695
+
696
+ export function requireSame(x, y) {
697
+ if (x !== y) { throw noResultException; }
698
+ return x;
699
+ }
700
+
701
+ export function requireTypeOf(x, typeOfString) {
702
+ if (typeof x !== typeOfString) { throw noResultException; }
703
+ return x;
704
+ }
705
+
706
+ // When we need a reference to builtin that normally we inline
707
+ export function bitwiseAnd(a, b) { return a & b; }
708
+ export function bitwiseOr(a, b) { return a | b; }
709
+ export function booleanNegation(b) { return !b; }
710
+ export function divDubDub(x, y) { return x / y; }
711
+ export function divIntInt(x, y) {
712
+ const result = trunc(x / y);
713
+ if (!isSafeInteger(result)) { throw noResultException; }
714
+ /* not NaN or infinite */
715
+ return result;
716
+ }
717
+ export function minusDub(x) { return -x; }
718
+ export function minusInt(x) { return -x; }
719
+ export function minusDubDub(x, y) { return x - y; }
720
+ export function minusIntInt(x, y) { return x - y; }
721
+ export function plusDub(x) { return +x; }
722
+ export function plusInt(x) { return +x; }
723
+ export function plusDubDub(x, y) { return x + y; }
724
+ export function plusIntInt(x, y) { return x + y; }
725
+ export function timesDubDub(x, y) { return x * y; }
726
+ export function timesIntInt(x, y) { return x * y; }
727
+ export function strCat(...args) {
728
+ let s = '';
729
+ for (let arg of args) {
730
+ s += String(arg); // Does not throw on Symbols
731
+ }
732
+ return s
733
+ }
734
+ export function listify(...args) { return freeze(args); }
735
+ export const genericCmp =
736
+ (a, b) => {
737
+ let d = 0;
738
+ let ta = typeof a;
739
+ let tb = typeof b;
740
+ if (ta !== tb) {
741
+ throw noResultException;
742
+ }
743
+ if (a !== b) {
744
+ if (ta === 'string') { // UTF-8 order for strings
745
+ const aLen = a.length,
746
+ bLen = b.length,
747
+ minLen = aLen < bLen ? aLen : bLen;
748
+ for (let i = 0; i < minLen;) {
749
+ let ca = a.codePointAt(i);
750
+ let cb = b.codePointAt(i);
751
+ d = ca - cb;
752
+ if (d) { break; }
753
+ i += ca < 0x10000 ? 1 : 2;
754
+ }
755
+ if (!d) {
756
+ d = aLen - bLen;
757
+ }
758
+ } else {
759
+ d = (a < b) ? -1 : 1;
760
+ }
761
+ // TODO: handle user-defined comparison
762
+ }
763
+ if (ta === 'number' && a === 0 && b === 0) {
764
+ // Need to sort -0 before 0, which requires converting to signed infinities
765
+ return genericCmp(1/a, 1/b);
766
+ }
767
+ return (d > 0) - (d < 0);
768
+ };
769
+ export function genericLt(a, b) { return genericCmp(a, b) < 0; }
770
+ export function genericLe(a, b) { return genericCmp(a, b) <= 0; }
771
+ export function genericGt(a, b) { return genericCmp(a, b) > 0; }
772
+ export function genericGe(a, b) { return genericCmp(a, b) >= 0; }
773
+ export function genericEq(a, b) {
774
+ if (a === 0 && b === 0) {
775
+ // This sorts out the 0.0 neq to -0.0
776
+ return 1/a === 1/b
777
+ }
778
+ return a === b;
779
+ }
780
+ export function genericNe(a, b) {
781
+ if (a === 0 && b === 0) {
782
+ // This sorts out the 0.0 neq to -0.0
783
+ return 1/a !== 1/b
784
+ }
785
+ return a !== b;
786
+ }
787
+ export function fail() { throw noResultException; }
788
+ export function print(a) {
789
+ console.log("%s", a);
790
+ return void 0;
791
+ }
792
+ export function nexter(f) {
793
+ return ((generator) => () => generator.next())(f());
794
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @fileoverview
3
+ * Declares *InterfaceType* which allows exposing a Temper `interface` type
4
+ * as a type that user types can extend and which inter-operates with
5
+ * `instanceof`.
6
+ *
7
+ * User code may do
8
+ *
9
+ * MyClass.implementedBy(AnInterface);
10
+ *
11
+ * to declare that `class MyClass implements AnInterface { ... }`.
12
+ *
13
+ * After that, members defined on *AnInterface* will be available via
14
+ * *MyClass*'s prototype and `new MyClass(...) instanceof AnInterface` will
15
+ * be true.
16
+ */
17
+
18
+ export class InterfaceType {
19
+ constructor(name, members, supers, inheritanceDepth) {
20
+ /** Diagnostic string. The name of the interface. */
21
+ this.name = name;
22
+ /**
23
+ * A list of [kind, propertyKey, value] triples that should be added to
24
+ * classes that implement this modulo overriding.
25
+ * Kind has type ("m" | "g" | "s") where "m" indicates a normal method
26
+ * whose name is key, "g" a getter for the property named key,
27
+ * and "s" a setter.
28
+ */
29
+ this.members = [...members];
30
+ /** interface types that are super-types of this */
31
+ this.supers = [...supers];
32
+ /**
33
+ * Computed by Temper compiler so that sub-type members can clobber
34
+ * super-type members but not otherwise.
35
+ */
36
+ this.inheritanceDepth = inheritanceDepth;
37
+ /** Presence `in` a value indicates sub-type. */
38
+ this.interfaceTag = Symbol(this.toString());
39
+ }
40
+
41
+ toString() { return `interface ${this.name}`; }
42
+
43
+ /** Invoked when user code does `value instanceof anInterfaceType`. */
44
+ [Symbol.hasInstance](x) {
45
+ return x && typeof x === 'object' && this.interfaceTag in x;
46
+ }
47
+
48
+ /** Called to declare a class implements this InterfaceType. */
49
+ implementedBy(classType) {
50
+ const memberMaps = InterfaceType.#memberMapsFor(classType);
51
+ classType.prototype[this.interfaceTag] = void 0;
52
+ const inheritanceDepth = this.inheritanceDepth;
53
+ for (const [kind, key, value] of this.members) {
54
+ const memberMap = memberMaps[kind];
55
+ const depth = memberMap.get(key);
56
+ if (typeof depth === 'undefined' || inheritanceDepth > depth) {
57
+ memberMap.set(key, inheritanceDepth);
58
+ const proto = classType.prototype;
59
+ const descriptor = Object.getOwnPropertyDescriptor(proto, key)
60
+ || { configurable: true };
61
+ switch (kind) {
62
+ case 'm':
63
+ descriptor.value = value;
64
+ break;
65
+ case 'g':
66
+ descriptor.get = value;
67
+ break;
68
+ case 's':
69
+ descriptor.set = value;
70
+ break;
71
+ }
72
+ Object.defineProperty(proto, key, descriptor);
73
+ }
74
+ }
75
+ for (const superType of this.supers) {
76
+ superType.implementedBy(classType);
77
+ }
78
+ }
79
+
80
+ static #memberMapsKey = Symbol('memberMaps');
81
+
82
+ static #memberMapsFor(classType) {
83
+ const memberMapsKey = InterfaceType.#memberMapsKey;
84
+ // Do not reuse super-class's member map
85
+ let maps = Object.hasOwnProperty.call(classType, memberMapsKey)
86
+ ? classType[memberMapsKey] : null;
87
+ if (!maps) {
88
+ maps = { m: new Map(), g: new Map(), s: new Map() };
89
+ classType[memberMapsKey] = maps;
90
+ // Walk prototypes. Even though Temper does not allow class
91
+ // inheritance, this code may be used by JavaScript types that
92
+ // do, so flatten class members so that interfaces don't clobber
93
+ // class members.
94
+ // Except we treat interfaces as sub-types of Object, so stop
95
+ // before Object.prototype. This allows an interface to override
96
+ // Object members like toString, valueOf, etc.
97
+ let prototype = classType.prototype;
98
+ while (prototype && prototype !== Object.prototype) {
99
+ for (const keys of [
100
+ Object.getOwnPropertyNames(prototype),
101
+ Object.getOwnPropertySymbols(prototype),
102
+ ]) {
103
+ for (const key of keys) {
104
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
105
+ if ('value' in descriptor) {
106
+ maps.m.set(key, Number.MAX_SAFE_INTEGER);
107
+ }
108
+ if (descriptor.get) {
109
+ maps.g.set(key, Number.MAX_SAFE_INTEGER);
110
+ }
111
+ if (descriptor.set) {
112
+ maps.s.set(key, Number.MAX_SAFE_INTEGER);
113
+ }
114
+ }
115
+ }
116
+ prototype = Object.getPrototypeOf(prototype);
117
+ }
118
+ }
119
+ return maps;
120
+ }
121
+ }
package/package.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@temperlang/core",
3
+ "version": "0.0.3",
4
+ "description": "Runtime support for JS generated by Temper",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "author": "https://github.com/mikesamuel",
8
+ "license": "Apache-2.0"
9
+ }
package/regex.js ADDED
@@ -0,0 +1,95 @@
1
+ import {noResultException} from "./index.js";
2
+
3
+ export function compiledRegexCompiledFoundIn(_, compiled, text) {
4
+ return compiled.test(text);
5
+ }
6
+
7
+ export function compiledRegexCompiledFind(_, compiled, text, regexRefs) {
8
+ const match = compiled.exec(text);
9
+ if (match === null) {
10
+ throw noResultException;
11
+ }
12
+ const { groups, indices: { groups: indexGroups } } = match;
13
+ // Find the begin indices in code points for all matched groups.
14
+ const rawBegins = [];
15
+ const defaultFull = !("full" in groups);
16
+ if (defaultFull) {
17
+ rawBegins.push({ name: "full", index: match.index });
18
+ }
19
+ for (const entry of Object.entries(indexGroups)) {
20
+ const [name, indices] = entry;
21
+ if (indices !== undefined) {
22
+ rawBegins.push({ name, index: indices[0] });
23
+ }
24
+ }
25
+ const begins = codePointIndices(text, rawBegins);
26
+ // Form the matches.
27
+ const Group = regexRefs.match.groups[0].constructor;
28
+ const resultGroups = [];
29
+ if (defaultFull) {
30
+ resultGroups.push(new Group("full", match[0], begins.full));
31
+ }
32
+ for (const entry of Object.entries(groups)) {
33
+ const [name, value] = entry;
34
+ resultGroups.push(new Group(name, value ?? "", begins[name] ?? -1));
35
+ }
36
+ return new regexRefs.match.constructor(Object.freeze(resultGroups));
37
+ }
38
+
39
+ function codePointIndices(text, unitNameIndexArray) {
40
+ // Follows logic from CodePointsStringSlice but simpler.
41
+ // Sort first for single pass O(m log m) + O(n) rather than O(m * n).
42
+ // TODO(tjp, regex): Can we presume we often receive them in sorted order?
43
+ unitNameIndexArray.sort((a, b) => a.index - b.index);
44
+ let unitCount = 0;
45
+ let codeCount = 0;
46
+ const codeIndices = {};
47
+ for (const unitNameIndex of unitNameIndexArray) {
48
+ const { name, index: unitIndex } = unitNameIndex;
49
+ while (unitCount < unitIndex) {
50
+ unitCount += text.codePointAt(unitCount) > 0xFFFF ? 2 : 1;
51
+ codeCount += 1;
52
+ }
53
+ codeIndices[name] = codeCount;
54
+ }
55
+ return codeIndices;
56
+ }
57
+
58
+ export function compiledRegexCompileFormatted(_, formatted) {
59
+ return new RegExp(formatted, "du"); // d:hasIndices, u:unicode
60
+ }
61
+
62
+ export function regexFormatterAdjustCodeSet(self, codeSet, regexRefs) {
63
+ if (codeSet.negated) {
64
+ let maxCode = codeSet.items.reduce(
65
+ (maxCode, item) => Math.max(maxCode, self.maxCode(item)) ?? 0, 0
66
+ );
67
+ if (maxCode < MIN_SUPPLEMENTAL_CP) {
68
+ // Add a bonus explicit surrogate pair to encourage js code points.
69
+ // Alternatively, we could construct the inverse positive set, but
70
+ // this ends up looking a bit more like the provided form.
71
+ if (codeSetBonus === null) {
72
+ // Any surrogate pair will do.
73
+ codeSetBonus = new regexRefs.codePoints.constructor("🌐");
74
+ }
75
+ return new regexRefs.orObject.constructor([
76
+ codeSetBonus,
77
+ new codeSet.constructor(
78
+ [codeSetBonus].concat(codeSet.items), true
79
+ ),
80
+ ]);
81
+ }
82
+ }
83
+ return codeSet;
84
+ }
85
+
86
+ export function regexFormatterPushCodeTo(_, out, code, insideCodeSet) {
87
+ // Ignore insideCodeSet for now.
88
+ // TODO(tjp, regex): Get fancier, including with work in Temper.
89
+ out.push(`\\u{${code.toString(16)}}`);
90
+ }
91
+
92
+ // Cached later for some approximate efficiency.
93
+ let codeSetBonus = null;
94
+
95
+ const MIN_SUPPLEMENTAL_CP = 0x10000;