@temperlang/core 0.3.0 → 0.5.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 +71 -0
- package/bitvector.js +103 -0
- package/check-type.js +65 -0
- package/core.js +186 -0
- package/date.js +45 -0
- package/deque.js +67 -0
- package/float.js +74 -0
- package/index.js +13 -1254
- package/interface.js +56 -0
- package/listed.js +289 -0
- package/mapped.js +216 -0
- package/net.js +41 -0
- package/package.json +1 -1
- package/pair.js +69 -0
- package/regex.js +98 -77
- package/string.js +221 -0
- package/interface-types.js +0 -121
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,186 @@
|
|
|
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
|
+
* TODO Distinguish panic from bubble.
|
|
103
|
+
*
|
|
104
|
+
* @returns {never}
|
|
105
|
+
*/
|
|
106
|
+
export const panic = bubble;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @template T
|
|
110
|
+
* @param {T} a
|
|
111
|
+
*/
|
|
112
|
+
export const print = (a) => {
|
|
113
|
+
console.log("%s", a);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Takes a JSON adapter and a value that it can adapt.
|
|
118
|
+
* This is called when JavaScript code calls JSON.stringify on a Temper type instance
|
|
119
|
+
* that has a zero argument jsonAdapter static method.
|
|
120
|
+
*
|
|
121
|
+
* @return any
|
|
122
|
+
*/
|
|
123
|
+
export let marshalToJsonObject = (jsonAdapter, value) => {
|
|
124
|
+
let stack = [[]];
|
|
125
|
+
let pendingKey = null;
|
|
126
|
+
function store(value) {
|
|
127
|
+
let top = stack[stack.length - 1];
|
|
128
|
+
if (pendingKey !== null) {
|
|
129
|
+
top[pendingKey] = value;
|
|
130
|
+
pendingKey = null;
|
|
131
|
+
} else if (Array.isArray(top)) {
|
|
132
|
+
top.push(value);
|
|
133
|
+
} else {
|
|
134
|
+
throw new Error();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
let jsonProducer = {
|
|
138
|
+
interchangeContext: { getHeader() { return null; } },
|
|
139
|
+
|
|
140
|
+
startObject() {
|
|
141
|
+
let o = {};
|
|
142
|
+
store(o);
|
|
143
|
+
stack.push(o);
|
|
144
|
+
},
|
|
145
|
+
endObject() { stack.pop(); },
|
|
146
|
+
objectKey(key) { pendingKey = String(key); },
|
|
147
|
+
|
|
148
|
+
startArray() {
|
|
149
|
+
let a = [];
|
|
150
|
+
store(a);
|
|
151
|
+
stack.push(a);
|
|
152
|
+
},
|
|
153
|
+
endArray() { stack.pop(); },
|
|
154
|
+
|
|
155
|
+
nullValue() { store(null); },
|
|
156
|
+
booleanValue(b) { store(!!b); },
|
|
157
|
+
intValue(v) { store(Math.trunc(v)); },
|
|
158
|
+
float64Value(v) { store(+v); },
|
|
159
|
+
numericTokenValue(s) { store(+s); },
|
|
160
|
+
stringValue(s) { store(`${s}`); },
|
|
161
|
+
|
|
162
|
+
parseErrorReceiver: null,
|
|
163
|
+
}
|
|
164
|
+
jsonAdapter.encodeToJson(value, jsonProducer);
|
|
165
|
+
return stack[0][0];
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/** @type {{}} */
|
|
169
|
+
let emptySingleton = Object.freeze(
|
|
170
|
+
// Prototype for empty
|
|
171
|
+
Object.create(
|
|
172
|
+
Object.freeze(
|
|
173
|
+
Object.create(
|
|
174
|
+
null,
|
|
175
|
+
{
|
|
176
|
+
toString: {
|
|
177
|
+
value: function toString() { return "(empty)" }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
/** @return {{}} */
|
|
186
|
+
export function empty() { return emptySingleton }
|
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
|
+
}
|