@temperlang/core 0.0.3 → 0.0.5

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 CHANGED
@@ -1,16 +1,16 @@
1
- export * from './interface-types.js';
2
- export * from './regex.js';
1
+ export * from "./interface-types.js";
2
+ export * from "./regex.js";
3
3
 
4
- export const noResultException = new (
5
- class NoResultException extends Error {
6
- constructor() {
7
- super("NoResult");
8
- }
4
+ export const noResultException = new (class NoResultException extends Error {
5
+ constructor() {
6
+ super("NoResult");
9
7
  }
10
- );
8
+ })();
11
9
 
12
10
  // Implements extension method String::isEmpty
13
- export function stringIsEmpty(s) { return s === ''; }
11
+ export function stringIsEmpty(s) {
12
+ return s === "";
13
+ }
14
14
  // Implements extension method String::split
15
15
  export function stringSplit(s, separator) {
16
16
  return s.split(separator).map((s) => s);
@@ -18,18 +18,16 @@ export function stringSplit(s, separator) {
18
18
 
19
19
  /** Specifically for utf8 and utf32, since utf16 is simpler in js. */
20
20
  class TrickyStringSlice {
21
- #length;
22
-
23
21
  hasAtLeast(count) {
24
22
  // Repeated calls to hasAtLeast are potentially expensive.
25
- return (this.#length ?? this._lengthUntil(count)) >= count;
23
+ return (this.length ?? this._lengthUntil(count)) >= count;
26
24
  }
27
25
 
28
26
  get length() {
29
- if (this.#length === undefined) {
30
- this.#length = this._lengthUntil();
27
+ if (this._length === undefined) {
28
+ this._length = this._lengthUntil();
31
29
  }
32
- return this.#length;
30
+ return this._length;
33
31
  }
34
32
 
35
33
  _lengthUntil(stop = undefined) {
@@ -39,62 +37,58 @@ class TrickyStringSlice {
39
37
  }
40
38
 
41
39
  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
40
  constructor(content, left = 0, right = content.length * 4) {
55
41
  super();
56
- this.#content = content;
57
- this.#left = left;
58
- this.#right = right;
42
+ this.content = content;
43
+ this.left = left;
44
+ this.right = right;
59
45
  }
60
46
 
61
47
  toString() {
62
- let left = this.#left;
63
- let right = this.#right;
64
- let content = this.#content;
48
+ let left = this.left;
49
+ let right = this.right;
50
+ let content = this.content;
65
51
 
66
- if (left === right) { return ""; }
52
+ if (left === right) {
53
+ return "";
54
+ }
67
55
 
68
56
  // If we're only using some bytes on the left or right, replace that codepoint with U+FFFD.
69
57
  let leftPartial = left & 3; // Do we have an incomplete code-point on the left?
70
58
  let leftIndex = (left + 3) >> 2;
71
59
  let rightIndex = right >> 2;
72
- let rightPartial = (right & 3);
60
+ let rightPartial = right & 3;
73
61
 
74
62
  // If leftIndex is in the middle of a surrogate pair, advance over the tail.
75
63
  if (leftIndex < rightIndex) {
76
64
  let leftCodeUnitUtf16 = content.charCodeAt(leftIndex);
77
- if (0xDC00 < leftCodeUnitUtf16 && leftCodeUnitUtf16 <= 0xDFFF) {
65
+ if (0xdc00 < leftCodeUnitUtf16 && leftCodeUnitUtf16 <= 0xdfff) {
78
66
  leftIndex += 1;
79
67
  }
80
68
  }
81
69
 
82
- if (leftIndex > rightIndex) { return "\uFFFD"; }
70
+ if (leftIndex > rightIndex) {
71
+ return "\uFFFD";
72
+ }
83
73
 
84
74
  let sub = content.substring(leftIndex, rightIndex);
85
75
  if (leftPartial || rightPartial) {
86
- return `${leftPartial ? "\uFFFD" : ""}${sub}${rightPartial ? "\uFFFD" : ""}`
76
+ return `${leftPartial ? "\uFFFD" : ""}${sub}${
77
+ rightPartial ? "\uFFFD" : ""
78
+ }`;
87
79
  } else {
88
80
  return sub;
89
81
  }
90
82
  }
91
83
 
92
- valueOf() { return this.toString(); }
84
+ valueOf() {
85
+ return this.toString();
86
+ }
93
87
 
94
88
  _lengthUntil(stop) {
95
- const left = this.#left;
96
- const right = this.#right;
97
- const content = this.#content;
89
+ const left = this.left;
90
+ const right = this.right;
91
+ const content = this.content;
98
92
  let i = left >> 2;
99
93
  // Add bytes between codePointBoundaryBeforeLimit and right
100
94
  // Subtract bytes past zeroth in codepoint for left
@@ -113,17 +107,17 @@ class Utf8StringSlice extends TrickyStringSlice {
113
107
  }
114
108
 
115
109
  get isEmpty() {
116
- return this.#left >= this.#right;
110
+ return this.left >= this.right;
117
111
  }
118
112
 
119
113
  read() {
120
- let left = this.#left;
121
- let right = this.#right;
114
+ let left = this.left;
115
+ let right = this.right;
122
116
  if (left >= right) {
123
117
  throw noResultException;
124
118
  }
125
119
 
126
- let content = this.#content;
120
+ let content = this.content;
127
121
  let cp = content.codePointAt(left >> 2);
128
122
  if (cp < 0x80) {
129
123
  return cp;
@@ -131,7 +125,8 @@ class Utf8StringSlice extends TrickyStringSlice {
131
125
  let byteOffset = left & 3;
132
126
  let nBytes = nUtf8BytesInChar(cp);
133
127
  let byteInfo = byteInfos[(nBytes - 1) * 4 + byteOffset];
134
- let codeUnit = ((cp >>> byteInfo.shift) & byteInfo.andMask) | byteInfo.orMask;
128
+ let codeUnit =
129
+ ((cp >>> byteInfo.shift) & byteInfo.andMask) | byteInfo.orMask;
135
130
  return codeUnit;
136
131
  }
137
132
  }
@@ -140,12 +135,12 @@ class Utf8StringSlice extends TrickyStringSlice {
140
135
  if (count <= 0) {
141
136
  return this;
142
137
  } else if (count === 1) {
143
- let left = this.#left;
144
- let right = this.#right;
138
+ let left = this.left;
139
+ let right = this.right;
145
140
  if (left >= right) {
146
141
  return this;
147
142
  }
148
- let content = this.#content;
143
+ let content = this.content;
149
144
  let cp = content.codePointAt(left >> 2);
150
145
  let newLeft;
151
146
  if (cp < 0x80) {
@@ -153,7 +148,10 @@ class Utf8StringSlice extends TrickyStringSlice {
153
148
  } else {
154
149
  let byteOffset = left & 3;
155
150
  let nBytes = nUtf8BytesInChar(cp);
156
- newLeft = (byteOffset + 1 < nBytes) ? left + 1 : (left & ~3) + ((nBytes + 4) & ~3);
151
+ newLeft =
152
+ byteOffset + 1 < nBytes
153
+ ? left + 1
154
+ : (left & ~3) + ((nBytes + 4) & ~3);
157
155
  }
158
156
  return new Utf8StringSlice(content, newLeft, right);
159
157
  } else {
@@ -162,7 +160,7 @@ class Utf8StringSlice extends TrickyStringSlice {
162
160
  }
163
161
 
164
162
  [Symbol.iterator]() {
165
- function *bytes(content, left, limit) {
163
+ function* bytes(content, left, limit) {
166
164
  let i = left;
167
165
  while (i < limit) {
168
166
  let cp = content.codePointAt(i >> 2);
@@ -173,127 +171,115 @@ class Utf8StringSlice extends TrickyStringSlice {
173
171
  let byteOffset = i & 3;
174
172
  let nBytes = nUtf8BytesInChar(cp);
175
173
  let byteInfo = byteInfos[(nBytes - 1) * 4 + byteOffset];
176
- let codeUnit = ((cp >>> byteInfo.shift) & byteInfo.andMask) | byteInfo.orMask;
174
+ let codeUnit =
175
+ ((cp >>> byteInfo.shift) & byteInfo.andMask) | byteInfo.orMask;
177
176
  yield codeUnit;
178
- i = (byteOffset + 1 < nBytes) ? i + 1 : (i & ~3) + ((nBytes + 4) & ~3);
177
+ i = byteOffset + 1 < nBytes ? i + 1 : (i & ~3) + ((nBytes + 4) & ~3);
179
178
  }
180
179
  }
181
180
  }
182
- return bytes(this.#content, this.#left, this.#right);
181
+ return bytes(this.content, this.left, this.right);
183
182
  }
184
183
 
185
184
  toJSON() {
186
- return { content: this.#content, left: this.#left, right: this.#right };
185
+ return { content: this.content, left: this.left, right: this.right };
187
186
  }
188
187
  }
189
188
 
190
189
  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
190
  constructor(content, left = 0, right = content.length) {
201
- this.#content = content;
202
- this.#left = left;
203
- this.#right = right;
191
+ this.content = content;
192
+ this.left = left;
193
+ this.right = right;
204
194
  }
205
195
 
206
196
  toString() {
207
- return this.#content.substring(this.#left, this.#right);
197
+ return this.content.substring(this.left, this.right);
208
198
  }
209
199
 
210
- valueOf() { return this.toString(); }
200
+ valueOf() {
201
+ return this.toString();
202
+ }
211
203
 
212
204
  hasAtLeast(count) {
213
205
  return this.length >= count;
214
206
  }
215
207
 
216
208
  get length() {
217
- return this.#right - this.#left;
209
+ return this.right - this.left;
218
210
  }
219
211
 
220
212
  get isEmpty() {
221
- return this.#left >= this.#right;
213
+ return this.left >= this.right;
222
214
  }
223
215
 
224
216
  read() {
225
- let left = this.#left;
226
- let right = this.#right;
227
- if (left >= right) { throw noResultException; }
228
- return this.#content.charCodeAt(left);
217
+ let left = this.left;
218
+ let right = this.right;
219
+ if (left >= right) {
220
+ throw noResultException;
221
+ }
222
+ return this.content.charCodeAt(left);
229
223
  }
230
224
 
231
225
  advance(count) {
232
226
  if (count <= 0) {
233
227
  return this;
234
228
  } else {
235
- let left = this.#left;
236
- let right = this.#right;
229
+ let left = this.left;
230
+ let right = this.right;
237
231
  if (left >= right) {
238
232
  return this;
239
233
  }
240
234
  let newLeft = left + count;
241
- if (newLeft >= right) { newLeft = right; }
242
- return new Utf16StringSlice(this.#content, newLeft, right);
235
+ if (newLeft >= right) {
236
+ newLeft = right;
237
+ }
238
+ return new Utf16StringSlice(this.content, newLeft, right);
243
239
  }
244
240
  }
245
241
 
246
242
  [Symbol.iterator]() {
247
- function *chars(content, left, limit) {
243
+ function* chars(content, left, limit) {
248
244
  let i = left;
249
245
  while (i < limit) {
250
- yield content.charCodeAt(i)
251
- i += 1
246
+ yield content.charCodeAt(i);
247
+ i += 1;
252
248
  }
253
249
  }
254
- return chars(this.#content, this.#left, this.#right);
250
+ return chars(this.content, this.left, this.right);
255
251
  }
256
252
 
257
253
  toJSON() {
258
- return { content: this.#content, left: this.#left, right: this.#right };
254
+ return { content: this.content, left: this.left, right: this.right };
259
255
  }
260
256
  }
261
257
 
262
258
  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
259
  constructor(content, left = 0, right = content.length) {
273
260
  super();
274
- this.#content = content;
275
- this.#left = left;
276
- this.#right = right;
261
+ this.content = content;
262
+ this.left = left;
263
+ this.right = right;
277
264
  }
278
265
 
279
266
  toString() {
280
- return this.#content.substring(this.#left, this.#right);
267
+ return this.content.substring(this.left, this.right);
281
268
  }
282
269
 
283
- valueOf() { return this.toString(); }
270
+ valueOf() {
271
+ return this.toString();
272
+ }
284
273
 
285
274
  _lengthUntil(stop) {
286
- const left = this.#left;
287
- const right = this.#right;
288
- const content = this.#content;
289
- let i = left;
275
+ let i = this.left;
290
276
  let len = 0;
291
- while (i < right) {
277
+ while (i < this.right) {
292
278
  if (stop !== undefined && len >= stop) {
293
279
  break;
294
280
  }
295
- let cp = content.codePointAt(i);
296
- if (cp > 0xFFFF) {
281
+ let cp = this.content.codePointAt(i);
282
+ if (cp > 0xffff) {
297
283
  i += 2;
298
284
  } else {
299
285
  i += 1;
@@ -304,58 +290,59 @@ class CodePointsStringSlice extends TrickyStringSlice {
304
290
  }
305
291
 
306
292
  get isEmpty() {
307
- return this.#left >= this.#right;
293
+ return this.left >= this.right;
308
294
  }
309
295
 
310
296
  read() {
311
- let left = this.#left;
312
- let right = this.#right;
313
- if (left >= right) { throw noResultException; }
314
- return this.#content.codePointAt(left);
297
+ if (this.left >= this.right) {
298
+ throw noResultException;
299
+ }
300
+ return this.content.codePointAt(this.left);
315
301
  }
316
302
 
317
303
  advance(count) {
318
304
  if (count <= 0) {
319
305
  return this;
320
306
  } else {
321
- let left = this.#left;
322
- let right = this.#right;
323
- let content = this.#content;
307
+ let left = this.left;
308
+ let right = this.right;
309
+ let content = this.content;
324
310
  if (left >= right) {
325
311
  return this;
326
312
  }
327
313
  let newLeft = left;
328
314
  for (let i = count; i && newLeft < right; --i) {
329
315
  let cp = content.codePointAt(newLeft);
330
- if (cp > 0xFFFF) {
316
+ if (cp > 0xffff) {
331
317
  newLeft += 2;
332
318
  } else {
333
319
  newLeft += 1;
334
320
  }
335
321
  }
336
- if (newLeft >= right) { newLeft = right; }
337
- return new CodePointsStringSlice(this.#content, newLeft, right);
322
+ if (newLeft >= right) {
323
+ newLeft = right;
324
+ }
325
+ return new CodePointsStringSlice(this.content, newLeft, right);
338
326
  }
339
327
  }
340
328
 
341
329
  [Symbol.iterator]() {
342
- function *chars(content, left, limit) {
330
+ function* chars(content, left, limit) {
343
331
  let i = left;
344
332
  while (i < limit) {
345
333
  let cp = content.codePointAt(i);
346
334
  yield cp;
347
- i += (cp > 0xFFFF) ? 2 : 1;
335
+ i += cp > 0xffff ? 2 : 1;
348
336
  }
349
337
  }
350
- return chars(this.#content, this.#left, this.#right);
338
+ return chars(this.content, this.left, this.right);
351
339
  }
352
340
 
353
341
  toJSON() {
354
- return { content: this.#content, left: this.#left, right: this.#right };
342
+ return { content: this.content, left: this.left, right: this.right };
355
343
  }
356
344
  }
357
345
 
358
-
359
346
  // Implements extension method String::utf8
360
347
  export function stringUtf8(string) {
361
348
  return new Utf8StringSlice(string);
@@ -376,33 +363,57 @@ export function intToString(i, radix) {
376
363
  return i.toString(radix);
377
364
  }
378
365
 
366
+ // Implements extension method Float64::toInt
367
+ export function float64ToInt(n) {
368
+ const i = float64ToIntUnsafe(n);
369
+ if (Math.abs(n - i) < 1) {
370
+ return i;
371
+ } else {
372
+ throw noResultException;
373
+ }
374
+ }
375
+
376
+ // Implements extension method Float64::toIntUnsafe
377
+ export function float64ToIntUnsafe(n) {
378
+ // We are free to do whatever with NaN here.
379
+ return isNaN(n)
380
+ ? 0
381
+ : Math.max(
382
+ Number.MIN_SAFE_INTEGER,
383
+ Math.min(Math.trunc(n), Number.MAX_SAFE_INTEGER)
384
+ );
385
+ }
386
+
379
387
  // Implements extension method Float64::toString
380
388
  export function float64ToString(n) {
381
389
  // TODO(mikesamuel, issue#579): need functional test to nail down
382
390
  // double formatting threshholds.
383
391
  switch (n) {
384
392
  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"
393
+ if (Object.is(n, -0)) {
394
+ return "-0.0";
395
+ } else {
396
+ return "0.0";
392
397
  }
393
398
  case Infinity:
394
- return "∞"
399
+ return "∞";
395
400
  case Number.NEGATIVE_INFINITY:
396
- return "-∞"
401
+ return "-∞";
397
402
  default:
398
- return n.toString();
399
- };
403
+ let result = n.toString();
404
+ // Rely on eagerness and js number formatting rules here.
405
+ const groups = /(-?[0-9]+)(\.[0-9]+)?(.+)?/.exec(result);
406
+ if (groups === null) {
407
+ return result;
408
+ } else {
409
+ // Guarantee a decimal point for floats.
410
+ return `${groups[1]}${groups[2] || ".0"}${groups[3] || ""}`;
411
+ }
412
+ }
400
413
  }
401
414
 
402
415
  function nUtf8BytesInChar(cp) {
403
- return (cp < 0x0800)
404
- ? ((cp < 0x80) ? 1 : 2)
405
- : ((cp < 0x10000) ? 3 : 4)
416
+ return cp < 0x0800 ? (cp < 0x80 ? 1 : 2) : cp < 0x10000 ? 3 : 4;
406
417
  }
407
418
 
408
419
  /*
@@ -451,9 +462,7 @@ export function listBuilderAdd(ls, newItem, at) {
451
462
  }
452
463
  ls.splice(at, 0, newItem);
453
464
  }
454
- return; // of type tVoid
455
465
  }
456
-
457
466
  // Implements extension method ListBuilder::addAll
458
467
  export function listBuilderAddAll(ls, newItems, at) {
459
468
  if (at === undefined) {
@@ -464,7 +473,6 @@ export function listBuilderAddAll(ls, newItems, at) {
464
473
  }
465
474
  ls.splice(at, 0, ...newItems);
466
475
  }
467
- return; // of type tVoid
468
476
  }
469
477
  // Implements extension method List::filter
470
478
  export function listFilter(ls, predicate) {
@@ -494,13 +502,15 @@ export function listFilter(ls, predicate) {
494
502
  // Implements extension method List::get
495
503
  export function listGet(ls, i) {
496
504
  let { length } = ls;
497
- if (0 <= i && i < length) { return ls[i]; }
505
+ if (0 <= i && i < length) {
506
+ return ls[i];
507
+ }
498
508
  throw noResultException;
499
509
  }
500
510
  // Implements extension method List::getOr
501
511
  export function listGetOr(ls, i, fallback) {
502
512
  let { length } = ls;
503
- return (0 <= i && i < length) ? ls[i] : fallback;
513
+ return 0 <= i && i < length ? ls[i] : fallback;
504
514
  }
505
515
  // Implements extension method List::isEmpty
506
516
  export function listIsEmpty(ls) {
@@ -508,7 +518,7 @@ export function listIsEmpty(ls) {
508
518
  }
509
519
  // Implements extension method List::join
510
520
  export function listJoin(ls, separator, elementStringifier) {
511
- let joined = '';
521
+ let joined = "";
512
522
  let { length } = ls;
513
523
  for (let i = 0; i < length; ++i) {
514
524
  if (i) {
@@ -521,9 +531,13 @@ export function listJoin(ls, separator, elementStringifier) {
521
531
  return joined;
522
532
  }
523
533
  // Implements extension method List::length
524
- export function listLength(ls) { return ls.length; }
534
+ export function listLength(ls) {
535
+ return ls.length;
536
+ }
525
537
  // Implements extension method List::mapDropping
526
- export function listMapDropping() { throw new Error("TODO List::mapDropping"); }
538
+ export function listMapDropping() {
539
+ throw new Error("TODO List::mapDropping");
540
+ }
527
541
  // Implements extension method List::map
528
542
  export function listMap(ls, transform) {
529
543
  let mapped = [];
@@ -534,6 +548,14 @@ export function listMap(ls, transform) {
534
548
  }
535
549
  return freeze(mapped);
536
550
  }
551
+ // Implements extension method ListBuilder::removeLast
552
+ export function listBuilderRemoveLast(ls) {
553
+ if (ls.length) {
554
+ return ls.pop();
555
+ } else {
556
+ throw noResultException;
557
+ }
558
+ }
537
559
  // Implements extension method ListBuilder::reverse
538
560
  export function listBuilderReverse(ls) {
539
561
  let { length } = ls;
@@ -548,11 +570,17 @@ export function listBuilderReverse(ls) {
548
570
  }
549
571
  // Implements extension method ListBuilder::set
550
572
  export function listBuilderSet(ls, i, newValue) {
551
- let { length } = ls;
552
- if (0 <= i && i <= length) {
573
+ if (0 <= i && i <= ls.length) {
553
574
  ls[i] = newValue;
554
575
  }
555
- return; // of type tVoid
576
+ }
577
+ // Implements extension method ListBuilder::removeLast
578
+ export function listBuilderSplice(ls, index, removeCount, newValues) {
579
+ // Missing count is all, but explicit undefined is 0, so give explicit length.
580
+ if (removeCount === undefined) {
581
+ removeCount = ls.length;
582
+ }
583
+ return freeze(ls.splice(index, removeCount, ...(newValues || [])));
556
584
  }
557
585
  // Implements extension method ListBuilder::toList
558
586
  export function listBuilderToList(ls) {
@@ -560,11 +588,87 @@ export function listBuilderToList(ls) {
560
588
  }
561
589
  // Implements extension method List::slice
562
590
  export function listSlice(ls, startInclusive, endExclusive) {
563
- if (startInclusive < 0) { startInclusive = 0; }
564
- if (endExclusive < 0) { endExclusive = 0; }
591
+ if (startInclusive < 0) {
592
+ startInclusive = 0;
593
+ }
594
+ if (endExclusive < 0) {
595
+ endExclusive = 0;
596
+ }
565
597
  return freeze(ls.slice(startInclusive, endExclusive));
566
598
  }
567
599
 
600
+ // Map
601
+ class FreezeMap extends Map {
602
+ // TODO Don't worry to freeze? Or worry more by wrapping private map?
603
+ // TODO Wrapping/Object.proxy presumably pays an extra cost when wrapped.
604
+ clear() {
605
+ throw new TypeError();
606
+ }
607
+ delete() {
608
+ throw new TypeError();
609
+ }
610
+ set(key, value) {
611
+ if (Object.isFrozen(this)) {
612
+ // Crash only after frozen because constructor calls set.
613
+ throw new TypeError();
614
+ }
615
+ return super.set(key, value);
616
+ }
617
+ }
618
+ export function mapConstructor(entries) {
619
+ return Object.freeze(new FreezeMap(entries));
620
+ }
621
+ // MapBuilder
622
+ export function mapBuilderConstructor(entries) {
623
+ return new Map();
624
+ }
625
+ export function mapBuilderRemove(builder, key) {
626
+ const result = builder.get(key);
627
+ if (builder.delete(key)) {
628
+ return result;
629
+ } else {
630
+ throw noResultException;
631
+ }
632
+ }
633
+ export function mapBuilderSet(builder, key, value) {
634
+ builder.set(key, value);
635
+ }
636
+ export function mapBuilderToMap(builder) {
637
+ return Object.freeze(new FreezeMap(builder));
638
+ }
639
+ // Pair
640
+ class Pair {
641
+ constructor(key, value) {
642
+ this.key = key;
643
+ this.value = value;
644
+ }
645
+ get [0]() {
646
+ return this.key;
647
+ }
648
+ get [1]() {
649
+ return this.value;
650
+ }
651
+ get length() {
652
+ return 2;
653
+ }
654
+ }
655
+ export function pairConstructor(key, value) {
656
+ return Object.freeze(new Pair(key, value));
657
+ }
658
+ // Mapped
659
+ export function mappedGet(map, key) {
660
+ const result = map.get(key);
661
+ // TODO Under compiler-error-free Temper, could undefined values get set?
662
+ // TODO Would Map<?, Void> be impossible to feed once we get checks in place?
663
+ if (result === undefined) {
664
+ throw noResultException;
665
+ }
666
+ return result;
667
+ }
668
+ export function mappedToList(map) {
669
+ return Array.from(map, ([key, value]) => new Pair(key, value));
670
+ }
671
+
568
672
  // Implements extension method Deque::constructor
569
673
  const DEQUE_NTAKEN = Symbol("Deque::nTaken");
570
674
  export function dequeConstructor() {
@@ -592,7 +696,9 @@ export function dequeRemoveFirst(deque) {
592
696
  }
593
697
  let item = deque[nTaken];
594
698
  let nShiftThreshhold = (length / 2) | 0;
595
- if (nShiftThreshhold < 32) { nShiftThreshhold = 32; }
699
+ if (nShiftThreshhold < 32) {
700
+ nShiftThreshhold = 32;
701
+ }
596
702
  if (nTaken >= nShiftThreshhold) {
597
703
  deque.splice(0, nTaken + 1);
598
704
  deque[DEQUE_NTAKEN] = 0;
@@ -603,36 +709,24 @@ export function dequeRemoveFirst(deque) {
603
709
  return item;
604
710
  }
605
711
 
606
- const ZERO_N = BigInt(0);
607
- const ONE_N = BigInt(1);
608
712
  class DenseBitVector {
609
- #bits /* : BigInt */;
610
-
611
713
  constructor() {
612
- this.#bits = ZERO_N;
714
+ this.bits = [];
613
715
  }
614
716
 
615
717
  get(index) {
616
- return (this.#bits & (ONE_N << BigInt(index))) !== ZERO_N;
718
+ return this.bits[index] == null ? false : this.bits[index];
617
719
  }
618
720
 
619
721
  set(index, newBitValue) {
620
- let mask = ONE_N << BigInt(index);
621
- this.#bits = newBitValue
622
- ? (this.#bits | mask)
623
- : (this.#bits & ~mask);
722
+ this.bits[index] = Boolean(newBitValue);
624
723
  }
625
724
 
626
725
  toString() {
627
- return `${this.#bits}`;
628
- }
629
-
630
- valueOf() {
631
- return this.#bits;
726
+ return `0b${this.bits.map(Number).join("")}`;
632
727
  }
633
-
634
728
  toJSON() {
635
- return this.#bits;
729
+ return this.bits;
636
730
  }
637
731
  }
638
732
 
@@ -651,7 +745,7 @@ export function denseBitVectorSet(denseBitVector, index, newBitValue) {
651
745
 
652
746
  // Implements extension method Boolean::toString
653
747
  export function booleanToString(b) {
654
- return b ? 'true' : 'false';
748
+ return b ? "true" : "false";
655
749
  }
656
750
 
657
751
  // Implements Symbol construction.
@@ -660,7 +754,7 @@ export function symbolFor(text) {
660
754
  }
661
755
 
662
756
  // Stubs out static property verification
663
- export function getStatic(typeGuard, symbol) {
757
+ export function getStatic(reifiedType, symbol) {
664
758
  return undefined; // TODO(bps, #780)
665
759
  }
666
760
 
@@ -679,116 +773,197 @@ export {
679
773
  // Export runtime value type checks used for safe casting
680
774
 
681
775
  export function requireIsArray(x) {
682
- if (!isArray) { throw noResultException; }
776
+ if (!isArray(x)) {
777
+ throw noResultException;
778
+ }
683
779
  return x;
684
780
  }
685
781
 
686
782
  export function requireInstanceOf(x, typeRequirement) {
687
- if (!(x instanceof typeRequirement)) { throw noResultException; }
783
+ if (!(x instanceof typeRequirement)) {
784
+ throw noResultException;
785
+ }
688
786
  return x;
689
787
  }
690
788
 
691
789
  export function requireIsSafeInteger(x) {
692
- if (!isSafeInteger(x)) { throw noResultException; }
790
+ if (!isSafeInteger(x)) {
791
+ throw noResultException;
792
+ }
693
793
  return x;
694
794
  }
695
795
 
696
796
  export function requireSame(x, y) {
697
- if (x !== y) { throw noResultException; }
797
+ if (x !== y) {
798
+ throw noResultException;
799
+ }
698
800
  return x;
699
801
  }
700
802
 
701
803
  export function requireTypeOf(x, typeOfString) {
702
- if (typeof x !== typeOfString) { throw noResultException; }
804
+ if (typeof x !== typeOfString) {
805
+ throw noResultException;
806
+ }
703
807
  return x;
704
808
  }
705
809
 
706
810
  // 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; }
811
+ export function bitwiseAnd(a, b) {
812
+ return a & b;
813
+ }
814
+ export function bitwiseOr(a, b) {
815
+ return a | b;
816
+ }
817
+ export function booleanNegation(b) {
818
+ return !b;
819
+ }
820
+ export function divDubDub(x, y) {
821
+ return x / y;
822
+ }
711
823
  export function divIntInt(x, y) {
712
824
  const result = trunc(x / y);
713
- if (!isSafeInteger(result)) { throw noResultException; }
825
+ if (!isSafeInteger(result)) {
826
+ throw noResultException;
827
+ }
714
828
  /* not NaN or infinite */
715
829
  return result;
716
830
  }
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; }
831
+ export function modIntInt(x, y) {
832
+ const result = trunc(x % y);
833
+ if (!isSafeInteger(result)) {
834
+ throw noResultException;
835
+ }
836
+ /* not NaN or infinite */
837
+ return result;
838
+ }
839
+ export function minusDub(x) {
840
+ return -x;
841
+ }
842
+ export function minusInt(x) {
843
+ return -x;
844
+ }
845
+ export function minusDubDub(x, y) {
846
+ return x - y;
847
+ }
848
+ export function minusIntInt(x, y) {
849
+ return x - y;
850
+ }
851
+ export function plusDub(x) {
852
+ return +x;
853
+ }
854
+ export function plusInt(x) {
855
+ return +x;
856
+ }
857
+ export function plusDubDub(x, y) {
858
+ return x + y;
859
+ }
860
+ export function plusIntInt(x, y) {
861
+ return x + y;
862
+ }
863
+ export function timesDubDub(x, y) {
864
+ return x * y;
865
+ }
866
+ export function timesIntInt(x, y) {
867
+ return x * y;
868
+ }
727
869
  export function strCat(...args) {
728
- let s = '';
870
+ let s = "";
729
871
  for (let arg of args) {
730
872
  s += String(arg); // Does not throw on Symbols
731
873
  }
732
- return s
874
+ return s;
733
875
  }
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;
876
+ export function listify(...args) {
877
+ return freeze(args);
878
+ }
879
+ export function cmpGeneric(a, b) {
880
+ if (typeof a === "string" && typeof b === "string") {
881
+ if (Object.is(a, b)) {
882
+ return 0;
742
883
  }
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;
884
+ const aLen = a.length;
885
+ const bLen = b.length;
886
+ const minLen = aLen < bLen ? aLen : bLen;
887
+ for (let i = 0; i < minLen; ) {
888
+ const ca = a.codePointAt(i);
889
+ const cb = b.codePointAt(i);
890
+ const d = ca - cb;
891
+ if (d !== 0) {
892
+ return d;
760
893
  }
761
- // TODO: handle user-defined comparison
894
+ i += ca < 0x10000 ? 1 : 2;
895
+ }
896
+ return aLen - bLen;
897
+ }
898
+ if (typeof a === "number" && typeof b === "number") {
899
+ if (Object.is(a, b)) {
900
+ return 0;
762
901
  }
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);
902
+ if (a === b) {
903
+ return Object.is(a, 0) - Object.is(b, 0);
766
904
  }
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; }
905
+ return a - b;
906
+ }
907
+ if (typeof a === "boolean" && typeof b === "boolean") {
908
+ return a - b;
909
+ }
910
+ throw noResultException;
911
+ };
912
+ export function ltGeneric(a, b) {
913
+ if (typeof a === "number" && typeof b === "number") {
914
+ return a < b || (a === 0 && b === 0 && Object.is(a, 0) < Object.is(b, 0));
915
+ }
916
+ if (typeof a == "boolean" && typeof b === "boolean") {
917
+ return a < b;
918
+ }
919
+ return cmpGeneric(a, b) < 0;
920
+ }
921
+ export function leGeneric(a, b) {
922
+ if (typeof a === "number" && typeof b === "number") {
923
+ return a <= b && (a !== 0 || b !== 0 || Object.is(a, 0) <= Object.is(b, 0));
924
+ }
925
+ if (typeof a == "boolean" && typeof b === "boolean") {
926
+ return a <= b;
927
+ }
928
+ return cmpGeneric(a, b) <= 0;
929
+ }
930
+ export function gtGeneric(a, b) {
931
+ if (typeof a === "number" && typeof b === "number") {
932
+ return a > b || (a === 0 && b === 0 && Object.is(a, 0) > Object.is(b, 0));
933
+ }
934
+ if (typeof a == "boolean" && typeof b === "boolean") {
935
+ return a > b;
936
+ }
937
+ return cmpGeneric(a, b) > 0;
938
+ }
939
+ export function geGeneric(a, b) {
940
+ if (typeof a === "number" && typeof b === "number") {
941
+ return a >= b && (a !== 0 || b !== 0 || Object.is(a, 0) >= Object.is(b, 0));
942
+ }
943
+ if (typeof a == "boolean" && typeof b === "boolean") {
944
+ return a >= b;
945
+ }
946
+ return cmpGeneric(a, b) >= 0;
947
+ }
948
+ export function eqGeneric(a, b) {
949
+ return Object.is(a, b);
950
+ }
951
+ export function neGeneric(a, b) {
952
+ return !Object.is(a, b);
953
+ }
954
+ export function fail() {
955
+ throw noResultException;
956
+ }
788
957
  export function print(a) {
789
958
  console.log("%s", a);
790
959
  return void 0;
791
960
  }
792
961
  export function nexter(f) {
793
- return ((generator) => () => generator.next())(f());
962
+ return (
963
+ (generator) => () =>
964
+ generator.next()
965
+ )(f());
794
966
  }
967
+
968
+ // We might customize this in the future, but actual global console works today.
969
+ export const globalConsole = console;
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temperlang/core",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Runtime support for JS generated by Temper",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/regex.js CHANGED
@@ -1,10 +1,18 @@
1
1
  import {noResultException} from "./index.js";
2
2
 
3
- export function compiledRegexCompiledFoundIn(_, compiled, text) {
3
+ export function compiledRegexCompiledFound(_, compiled, text) {
4
4
  return compiled.test(text);
5
5
  }
6
6
 
7
7
  export function compiledRegexCompiledFind(_, compiled, text, regexRefs) {
8
+ return compiledRegexCompiledFindEx(_, compiled, text, regexRefs).groups;
9
+ }
10
+
11
+ /**
12
+ * @param {RegExp} compiled
13
+ * @param {string} text
14
+ */
15
+ function compiledRegexCompiledFindEx(_, compiled, text, regexRefs) {
8
16
  const match = compiled.exec(text);
9
17
  if (match === null) {
10
18
  throw noResultException;
@@ -24,16 +32,16 @@ export function compiledRegexCompiledFind(_, compiled, text, regexRefs) {
24
32
  }
25
33
  const begins = codePointIndices(text, rawBegins);
26
34
  // Form the matches.
27
- const Group = regexRefs.match.groups[0].constructor;
28
- const resultGroups = [];
35
+ const Group = regexRefs.group.constructor;
36
+ const resultGroups = new Map();
29
37
  if (defaultFull) {
30
- resultGroups.push(new Group("full", match[0], begins.full));
38
+ resultGroups.set("full", new Group("full", match[0], begins.full));
31
39
  }
32
40
  for (const entry of Object.entries(groups)) {
33
41
  const [name, value] = entry;
34
- resultGroups.push(new Group(name, value ?? "", begins[name] ?? -1));
42
+ resultGroups.set(name, new Group(name, value ?? "", begins[name] ?? -1));
35
43
  }
36
- return new regexRefs.match.constructor(Object.freeze(resultGroups));
44
+ return { groups: resultGroups, index: match.index, length: match[0].length };
37
45
  }
38
46
 
39
47
  function codePointIndices(text, unitNameIndexArray) {
@@ -55,6 +63,41 @@ function codePointIndices(text, unitNameIndexArray) {
55
63
  return codeIndices;
56
64
  }
57
65
 
66
+ /**
67
+ * @param {RegExp} compiled
68
+ * @param {string} text
69
+ * @param {(groups: Map<string, any>) => string} format
70
+ * @returns {string}
71
+ */
72
+ export function compiledRegexCompiledReplace(
73
+ _,
74
+ compiled,
75
+ text,
76
+ format,
77
+ regexRefs
78
+ ) {
79
+ // Simple string replace doesn't provide all match group details if we want to
80
+ // make our interface consistent, so we have to do this manually here.
81
+ // The hope is that we can optimize a bunch out when we have compile-time
82
+ // contant patterns and customized match result types.
83
+ let match;
84
+ try {
85
+ match = compiledRegexCompiledFindEx(_, compiled, text, regexRefs);
86
+ } catch (e) {
87
+ if (e === noResultException) {
88
+ // Manually handle no match case for our manual replace logic.
89
+ return text;
90
+ } else {
91
+ // This shouldn't happen if we don't have bugs, but forward it in case.
92
+ throw e;
93
+ }
94
+ }
95
+ // Index and length in js space should save some processing.
96
+ const { groups, index, length } = match;
97
+ const content = format(groups);
98
+ return text.slice(0, index) + content + text.slice(index + length);
99
+ }
100
+
58
101
  export function compiledRegexCompileFormatted(_, formatted) {
59
102
  return new RegExp(formatted, "du"); // d:hasIndices, u:unicode
60
103
  }