@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 +794 -0
- package/interface-types.js +121 -0
- package/package.json +9 -0
- package/regex.js +95 -0
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
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;
|