@temperlang/core 0.3.0 → 0.4.0

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/async.js ADDED
@@ -0,0 +1,71 @@
1
+
2
+ /**
3
+ * @param generatorFactory {() => Generator<unknown>}
4
+ */
5
+ export const runAsync = (generatorFactory) => {
6
+ let generator = generatorFactory();
7
+ setTimeout(() => { generator.next() });
8
+ }
9
+
10
+ /**
11
+ * Adapts a generator that takes an extra doAwait function.
12
+ * This allows adapting `await p` to `yield doAwait(p)`.
13
+ *
14
+ * @template T
15
+ * @template {Array} A The argument list
16
+ * @param generatorFactory {(doAsync: ((p: Promise<unknown>) => void), ...rest: A) => Generator<T>}
17
+ * @returns {(...A) => Generator<T>}
18
+ */
19
+ export const adaptAwaiter = (generatorFactory) => {
20
+ return (...args) => {
21
+ /**
22
+ * @template R
23
+ * @param {Promise<R>} p
24
+ */
25
+ const doAwait = (p) => {
26
+ p.then(
27
+ (result) => { generator.next(result) },
28
+ (error) => { generator.throw(error) },
29
+ );
30
+ };
31
+
32
+ /** @type {Generator<T>} */
33
+ const generator = generatorFactory(doAwait, ...args);
34
+
35
+ return generator;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * @template R
41
+ */
42
+ export class PromiseBuilder {
43
+ /** @type {Promise<R>} */
44
+ promise;
45
+ /** @type {(value: (PromiseLike<R> | R)) => void} */
46
+ resolve;
47
+ /** @type {(reason?: any) => void} */
48
+ reject;
49
+
50
+ constructor() {
51
+ this.promise = new Promise(
52
+ (resolve, reject) => {
53
+ this.resolve = resolve;
54
+ this.reject = reject;
55
+ }
56
+ );
57
+ }
58
+
59
+ /** @param {R} value */
60
+ complete(value) {
61
+ this.resolve(value)
62
+ }
63
+
64
+ /** @param {any?} reason */
65
+ breakPromise(reason) {
66
+ this.reject(reason);
67
+ }
68
+ }
69
+
70
+ // We might customize this in the future, but actual global console works today.
71
+ export const globalConsole = console;
package/bitvector.js ADDED
@@ -0,0 +1,103 @@
1
+
2
+ /**
3
+ * @param {number} byte
4
+ * @returns {string}
5
+ */
6
+ const byteToString = (byte) => {
7
+ return byte.toString(2).padStart(8, '0');
8
+ };
9
+
10
+ /**
11
+ * @param {number} byte
12
+ * @returns {boolean[]}
13
+ */
14
+ const byteToBitArray = (byte) => {
15
+ const ret = [];
16
+ for (let i = 0; i < 8; i++) {
17
+ ret.push((byte & 1) !== 0);
18
+ byte >>= 1;
19
+ }
20
+ return ret;
21
+ };
22
+
23
+ export class DenseBitVector {
24
+ /**
25
+ * @type {Uint8Array}
26
+ */
27
+ values;
28
+
29
+ constructor() {
30
+ this.values = new Uint8Array(8);
31
+ }
32
+
33
+ /**
34
+ * @param {number} index
35
+ * @returns {boolean}
36
+ */
37
+ get(index) {
38
+ const number = this.values[index >> 3] || 0;
39
+ return (number & (1 << (index & 7))) !== 0;
40
+ }
41
+
42
+ /**
43
+ * @param {number} index
44
+ * @param {boolean} newBitValue
45
+ */
46
+ set(index, newBitValue) {
47
+ let values = this.values;
48
+ const len = values.byteLength;
49
+ const key = index >> 3;
50
+ const shift = index & 7;
51
+ if (len <= key) {
52
+ const next = new Uint8Array(len << 1);
53
+ next.set(values);
54
+ next[key] = next[key] & ~(1 << shift) | (newBitValue << shift);
55
+ this.values = next;
56
+ } else {
57
+ values[key] = values[key] & ~(1 << shift) | (newBitValue << shift);
58
+ }
59
+ }
60
+
61
+ /**
62
+ * @returns {string}
63
+ */
64
+ toString() {
65
+ return `0b${Array.prototype.map.call(this.values, byteToString).join('')}`;
66
+ }
67
+
68
+ /**
69
+ * @returns {boolean[]}
70
+ */
71
+ toJSON() {
72
+ return Array.prototype.flatMap.call(this.values, byteToBitArray);
73
+ }
74
+ };
75
+
76
+ /**
77
+ * Implements extension method DenseBitVector::constructor
78
+ * @param {number} capacity
79
+ * @returns {DenseBitVector}
80
+ */
81
+ export const denseBitVectorConstructor = (capacity) => {
82
+ return new DenseBitVector();
83
+ };
84
+
85
+ /**
86
+ * Implements extension method DenseBitVector::get
87
+ * @param {DenseBitVector} denseBitVector
88
+ * @param {number} index
89
+ * @returns {boolean}
90
+ */
91
+ export const denseBitVectorGet = (denseBitVector, index) => {
92
+ return denseBitVector.get(index);
93
+ };
94
+
95
+ /**
96
+ * Implements extension method DenseBitVector::set
97
+ * @param {DenseBitVector} denseBitVector
98
+ * @param {number} index
99
+ * @param {boolean} newBitValue
100
+ */
101
+ export const denseBitVectorSet = (denseBitVector, index, newBitValue) => {
102
+ denseBitVector.set(index, newBitValue);
103
+ };
package/check-type.js ADDED
@@ -0,0 +1,65 @@
1
+
2
+ // Export runtime value type checks used for safe casting
3
+ import {bubble} from "./core.js";
4
+
5
+ /**
6
+ * @typedef { "bigint" | "boolean" | "number" | "object" | "string" | "symbol" | "undefined" } TypeOf
7
+ */
8
+
9
+ /**
10
+ * @template T
11
+ * @param {any} x
12
+ * @param {new(...args: any[]) => T} typeRequirement
13
+ * @returns {T}
14
+ */
15
+ export const requireInstanceOf = (x, typeRequirement) => {
16
+ if (!(x instanceof typeRequirement)) {
17
+ bubble();
18
+ }
19
+ return x;
20
+ };
21
+
22
+ /**
23
+ * @template T
24
+ * @param {T?} x
25
+ * @returns {T}
26
+ */
27
+ export const requireNotNull = (x) =>
28
+ x == null ? bubble() : x
29
+
30
+ /**
31
+ * @param {number} x
32
+ * @returns {number}
33
+ */
34
+ export const requireIsSafeInteger = (x) => {
35
+ if (!Number.isSafeInteger(x)) {
36
+ bubble();
37
+ }
38
+ return x;
39
+ };
40
+
41
+ /**
42
+ * @template X, Y
43
+ * @param {X} x
44
+ * @param {Y} y
45
+ * @returns {boolean}
46
+ */
47
+ export const requireSame = (x, y) => {
48
+ if (x !== y) {
49
+ bubble();
50
+ }
51
+ return x;
52
+ };
53
+
54
+ /**
55
+ * @template T
56
+ * @param {T} x
57
+ * @param {TypeOf} typeOfString
58
+ * @returns {T}
59
+ */
60
+ export const requireTypeOf = (x, typeOfString) => {
61
+ if (typeof x !== typeOfString) {
62
+ bubble();
63
+ }
64
+ return x;
65
+ };
package/core.js ADDED
@@ -0,0 +1,107 @@
1
+
2
+ /**
3
+ * @param {number} x
4
+ * @param {number} y
5
+ * @returns {number}
6
+ */
7
+ export const divIntInt = (x, y) => {
8
+ const result = Math.trunc(x / y);
9
+ if (!Number.isSafeInteger(result)) {
10
+ bubble();
11
+ }
12
+ /* not NaN or infinite */
13
+ return result;
14
+ };
15
+
16
+ /**
17
+ * @param {number} x
18
+ * @param {number} y
19
+ * @returns {number}
20
+ */
21
+ export const modIntInt = (x, y) => {
22
+ const result = Math.trunc(x % y);
23
+ // TODO: is this ever false if `Number.isSafeInteger(x)`?
24
+ if (!Number.isSafeInteger(result)) {
25
+ bubble();
26
+ }
27
+ /* not NaN or infinite */
28
+ return result;
29
+ };
30
+
31
+ /**
32
+ * Compare two Strings.
33
+ * @param {string} a
34
+ * @param {string} b
35
+ * @return {number}
36
+ */
37
+ export const cmpString = (a, b) => {
38
+ if (Object.is(a, b)) {
39
+ return 0;
40
+ }
41
+ const aLen = a.length;
42
+ const bLen = b.length;
43
+ const minLen = aLen < bLen ? aLen : bLen;
44
+ for (let i = 0; i < minLen;) {
45
+ const ca = a.codePointAt(i);
46
+ const cb = b.codePointAt(i);
47
+ const d = ca - cb;
48
+ if (d !== 0) {
49
+ return d;
50
+ }
51
+ i += ca < 0x10000 ? 1 : 2;
52
+ }
53
+ return aLen - bLen;
54
+ };
55
+
56
+ /**
57
+ * Compare two Numbers, accounting for signedness of zero.
58
+ * @param {number} a
59
+ * @param {number} b
60
+ * @return {number}
61
+ */
62
+ export const cmpFloat = (a, b) => {
63
+ if (Object.is(a, b)) {
64
+ return 0;
65
+ }
66
+ if (a === b) {
67
+ return Object.is(a, 0) - Object.is(b, 0);
68
+ }
69
+ if (isNaN(a) || isNaN(b)) {
70
+ return isNaN(a) - isNaN(b);
71
+ }
72
+ return a - b;
73
+ };
74
+
75
+ /**
76
+ * @template {string | number | boolean} T
77
+ * @param {T} a
78
+ * @param {T} b
79
+ * @returns {number}
80
+ */
81
+ export const cmpGeneric = (a, b) => {
82
+ if (typeof a === "string" && typeof b === "string") {
83
+ return cmpString(a, b);
84
+ }
85
+ if (typeof a === "number" && typeof b === "number") {
86
+ return cmpFloat(a, b);
87
+ }
88
+ if (typeof a === "boolean" && typeof b === "boolean") {
89
+ return a - b;
90
+ }
91
+ bubble();
92
+ };
93
+
94
+ /**
95
+ * @returns {never}
96
+ */
97
+ export const bubble = () => {
98
+ throw Error();
99
+ };
100
+
101
+ /**
102
+ * @template T
103
+ * @param {T} a
104
+ */
105
+ export const print = (a) => {
106
+ console.log("%s", a);
107
+ };
package/date.js ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Implements Date::constructor
3
+ * @param {number} year
4
+ * @param {number} month
5
+ * @param {number} day
6
+ * @returns {Date}
7
+ */
8
+ export const dateConstructor = (year, month, day) => {
9
+ let d = new Date(0);
10
+ // If we were to pass year into `new Date`, then it would
11
+ // have 1900 added when in the range [0, 99].
12
+ // developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#year
13
+ d.setUTCFullYear(year);
14
+ d.setUTCMonth(month - 1 /* JS months are zero indexed */);
15
+ d.setUTCDate(day); // UTCDay is day of the week
16
+ return d;
17
+ };
18
+
19
+ /**
20
+ * Implements Date::today
21
+ * @returns {Date}
22
+ */
23
+ export const dateToday = () => {
24
+ let d = new Date(Date.now());
25
+ // Get rid of the time component.
26
+ d.setTime(
27
+ Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())
28
+ );
29
+ return d;
30
+ };
31
+
32
+ /**
33
+ * Implements Date::yearsBetween
34
+ * @param {Date} start
35
+ * @param {Date} end
36
+ * @returns {number}
37
+ */
38
+ export const dateYearsBetween = (start, end) => {
39
+ let yearDelta = end.getUTCFullYear() - start.getUTCFullYear();
40
+ let monthDelta = end.getUTCMonth() - start.getUTCMonth();
41
+ return yearDelta -
42
+ // If the end month/day is before the start's then we
43
+ // don't have a full year.
44
+ (monthDelta < 0 || monthDelta === 0 && end.getUTCDate() < start.getUTCDate());
45
+ };
package/deque.js ADDED
@@ -0,0 +1,67 @@
1
+
2
+ // Implements extension method Deque::add
3
+ import {bubble} from "./core.js";
4
+
5
+ const DEQUE_NTAKEN = Symbol("Deque::nTaken");
6
+
7
+ /**
8
+ * Implements extension method Deque::constructor
9
+ * @template T
10
+ * @returns {T[]}
11
+ */
12
+ export const dequeConstructor = () => {
13
+ let deque = [];
14
+ Object.defineProperty(deque, DEQUE_NTAKEN, { value: 0, writable: true });
15
+ return deque;
16
+ };
17
+
18
+ /**
19
+ * Implements extension method Deque::add
20
+ * @template T
21
+ * @param {T[]} deque
22
+ * @param {T} element
23
+ */
24
+ export const dequeAdd = (deque, element) => {
25
+ deque.push(element);
26
+ };
27
+
28
+ /**
29
+ * Implements extension method Deque::isEmpty
30
+ * @template T
31
+ * @param {T[]} deque
32
+ * @returns {boolean}
33
+ */
34
+ export const dequeIsEmpty = (deque) => {
35
+ return deque.length === (deque[DEQUE_NTAKEN] || 0);
36
+ };
37
+
38
+ /**
39
+ * Implements extension method Deque::removeFirst
40
+ * @template T
41
+ * @param {T[]} deque
42
+ * @returns {T}
43
+ */
44
+ export const dequeRemoveFirst = (deque) => {
45
+ // https://gist.github.com/mikesamuel/444258e7005e8fc9534d9cf274b1df58
46
+ let nTaken = deque[DEQUE_NTAKEN];
47
+ let length = deque.length;
48
+ if (length === nTaken) {
49
+ deque[DEQUE_NTAKEN] = 0;
50
+ deque.length = 0;
51
+ bubble();
52
+ }
53
+ let item = deque[nTaken];
54
+ let nShiftThreshold = (length / 2) | 0;
55
+ if (nShiftThreshold < 32) {
56
+ nShiftThreshold = 32;
57
+ }
58
+ if (nTaken >= nShiftThreshold) {
59
+ deque.splice(0, nTaken + 1);
60
+ deque[DEQUE_NTAKEN] = 0;
61
+ } else {
62
+ deque[nTaken] = undefined;
63
+ deque[DEQUE_NTAKEN] = nTaken + 1;
64
+ }
65
+ return item;
66
+ };
67
+
package/float.js ADDED
@@ -0,0 +1,74 @@
1
+
2
+ // Implements extension method Float64::near
3
+ import {bubble} from "./core.js";
4
+
5
+ /**
6
+ * Implements extension method Float64::near
7
+ * @param {number} x
8
+ * @param {number} y
9
+ * @param {number | null} [relTol]
10
+ * @param {number | null} [absTol]
11
+ * @returns {boolean}
12
+ */
13
+ export const float64Near = (x, y, relTol, absTol) => {
14
+ if (relTol == null) {
15
+ relTol = 1e-9;
16
+ }
17
+ if (absTol == null) {
18
+ absTol = 0;
19
+ }
20
+ const margin = Math.max(Math.max(Math.abs(x), Math.abs(y)) * relTol, absTol);
21
+ return Math.abs(x - y) < margin;
22
+ }
23
+
24
+ /**
25
+ * Implements extension method Float64::toInt
26
+ * @param {number} n
27
+ * @returns {number}
28
+ */
29
+ export const float64ToInt = (n) => {
30
+ const i = float64ToIntUnsafe(n);
31
+ if (Math.abs(n - i) < 1) {
32
+ return i;
33
+ } else {
34
+ bubble();
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Implements extension method Float64::toIntUnsafe
40
+ * @param {number} n
41
+ * @returns {number}
42
+ */
43
+ export const float64ToIntUnsafe = (n) => {
44
+ // We are free to do whatever with NaN here.
45
+ return isNaN(n)
46
+ ? 0
47
+ : Math.max(
48
+ Number.MIN_SAFE_INTEGER,
49
+ Math.min(Math.trunc(n), Number.MAX_SAFE_INTEGER)
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Implements extension method Float64::toString
55
+ * @param {number} n
56
+ * @returns
57
+ */
58
+ export const float64ToString = (n) => {
59
+ // TODO(mikesamuel, issue#579): need functional test to nail down
60
+ // double formatting thresholds.
61
+ if (n === 0) {
62
+ return Object.is(n, -0) ? "-0.0" : "0.0";
63
+ } else {
64
+ let result = n.toString();
65
+ // Rely on eagerness and js number formatting rules here.
66
+ const groups = /(-?[0-9]+)(\.[0-9]+)?(.+)?/.exec(result);
67
+ if (groups === null) {
68
+ return result;
69
+ } else {
70
+ // Guarantee a decimal point for floats.
71
+ return `${groups[1]}${groups[2] || ".0"}${groups[3] || ""}`;
72
+ }
73
+ }
74
+ }