@temperlang/core 0.2.1 → 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/interface.js ADDED
@@ -0,0 +1,56 @@
1
+
2
+ /**
3
+ * A temper Super-Type.
4
+ *
5
+ * Returns a `Union`.
6
+ * Temper types always extend a Union made by this function.
7
+ * Only takes temper types as inputs.
8
+ *
9
+ * Examples:
10
+ *
11
+ * Basic usage with multiple inheritance.
12
+ * ```js
13
+ * class Plant extends type() {}
14
+ * class Potato extends type(Plant) {}
15
+ * class Snack extends type() {}
16
+ * class Fry extends type(Potato, Snack) {}
17
+ * ```
18
+ *
19
+ * The following will not work because Basic does not extend `type()`.
20
+ * ```
21
+ * class Basic {}
22
+ * class Sub extends type(Basic) {}
23
+ * ```
24
+ *
25
+ * The following will work.
26
+ * ```
27
+ * class Pet extends type() {}
28
+ * class Dog extends Pet {}
29
+ * ```
30
+ *
31
+ * type()
32
+ * @param {typeof Union} superTypes
33
+ * @return {Union}
34
+ */
35
+ export const type = (...superTypes) => {
36
+ const key = Symbol();
37
+
38
+ class Union {
39
+ static [Symbol.hasInstance] = (instance) => {
40
+ return key in instance;
41
+ };
42
+ }
43
+
44
+ Union.prototype[key] = null;
45
+
46
+ for (const superType of superTypes.reverse()) {
47
+ let proto = Object.getPrototypeOf(superType.prototype);
48
+ for (const sym of Object.getOwnPropertySymbols(proto)) {
49
+ Union.prototype[sym] = null;
50
+ }
51
+ Object.defineProperties(Union.prototype, Object.getOwnPropertyDescriptors(proto));
52
+ Object.defineProperties(Union.prototype, Object.getOwnPropertyDescriptors(superType.prototype));
53
+ }
54
+
55
+ return Union;
56
+ };
package/listed.js ADDED
@@ -0,0 +1,289 @@
1
+
2
+ // Implements extension method Listed::mapDropping
3
+ import {bubble} from "./core.js";
4
+
5
+ export const listedMapDropping = () => {
6
+ throw new Error("TODO List::mapDropping");
7
+ }
8
+
9
+ /**
10
+ * Implements extension method Listed::map
11
+ * @template T
12
+ * @template R
13
+ * @param {T[]} ls
14
+ * @param {(prev: T) => R} transform
15
+ * @returns {Readonly<R[]>}
16
+ */
17
+ export const listedMap = (ls, transform) => {
18
+ let mapped = [];
19
+ let { length } = ls;
20
+ for (let i = 0; i < length; ++i) {
21
+ mapped[i] = transform(ls[i]);
22
+ }
23
+ return Object.freeze(mapped);
24
+ }
25
+
26
+ /**
27
+ * Implements extension method Listed::reduceFrom
28
+ * @template T, R
29
+ * @param {T[]} ls
30
+ * @param {R} initial
31
+ * @param {(prev: T, cur: R) => R}accumulate
32
+ * @returns {R}
33
+ */
34
+ export const listedReduceFrom = (ls, initial, accumulate) => {
35
+ return ls.reduce(accumulate, initial);
36
+ }
37
+
38
+ /**
39
+ * Implements extension method Listed::sort
40
+ * @template T
41
+ * @param {T[]} ls
42
+ * @param {(lhs: T, rhs: T) => number} compare
43
+ */
44
+ export const listedSorted = (ls, compare) => {
45
+ return Object.freeze(ls.slice().sort(compare));
46
+ }
47
+
48
+ /**
49
+ * Implements extension method Listed::toList
50
+ * @template T
51
+ * @param {T[] | Readonly<T[]>} ls
52
+ * @returns {Readonly<T[]>}
53
+ */
54
+ export const listedToList = (ls) => {
55
+ if (Object.isFrozen(ls)) {
56
+ return ls;
57
+ } else {
58
+ return listBuilderToList(ls);
59
+ }
60
+ }
61
+
62
+
63
+ /**
64
+ * Implements extension method ListBuilder::add
65
+ * @template T
66
+ * @param {T[]} ls
67
+ * @param {T} newItem
68
+ * @param {number | null} [at]
69
+ */
70
+ export const listBuilderAdd = (ls, newItem, at) => {
71
+ if (at == null) {
72
+ // Technically, we could also use splice instead of push for this case.
73
+ // Which is better might depend on minifiers and/or execution speed.
74
+ ls.push(newItem);
75
+ } else {
76
+ if (at < 0 || at > ls.length) {
77
+ bubble();
78
+ }
79
+ ls.splice(at, 0, newItem);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Implements extension method ListBuilder::addAll
85
+ * @template T
86
+ * @param {T[]} ls
87
+ * @param {T[]} newItems
88
+ * @param {number | null} [at]
89
+ */
90
+ export const listBuilderAddAll = (ls, newItems, at) => {
91
+ if (at == null) {
92
+ ls.push(...newItems);
93
+ } else {
94
+ if (at < 0 || at > ls.length) {
95
+ bubble();
96
+ }
97
+ ls.splice(at, 0, ...newItems);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Implements extension method Listed::filter
103
+ * @template T
104
+ * @param {T[]} ls
105
+ * @param {(T) => boolean} predicate
106
+ * @returns {Readonly<T[]>}
107
+ */
108
+ export const listedFilter = (ls, predicate) => {
109
+ let filtered = null;
110
+ let nFiltered = 0; // Just past index of last element of ls filtered onto filtered
111
+ let { length } = ls;
112
+ for (let i = 0; i < length; ++i) {
113
+ let element = ls[i];
114
+ let ok = predicate(element);
115
+ if (!ok) {
116
+ if (!filtered) {
117
+ filtered = [];
118
+ }
119
+ filtered.push(...ls.slice(nFiltered, i));
120
+ nFiltered = i + 1;
121
+ }
122
+ }
123
+ let fullyFiltered;
124
+ if (filtered) {
125
+ filtered.push(...ls.slice(nFiltered, length));
126
+ fullyFiltered = filtered;
127
+ } else {
128
+ fullyFiltered = ls;
129
+ }
130
+ return Object.freeze(fullyFiltered);
131
+ }
132
+
133
+ /**
134
+ * Implements extension method Listed::get
135
+ * @template T
136
+ * @param {T[]} ls
137
+ * @param {number} i
138
+ * @returns {T}
139
+ */
140
+ export const listedGet = (ls, i) => {
141
+ let { length } = ls;
142
+ if (0 <= i && i < length) {
143
+ return ls[i];
144
+ }
145
+ bubble();
146
+ }
147
+
148
+ /**
149
+ * Implements extension method Listed::getOr
150
+ * @template T
151
+ * @param {T[]} ls
152
+ * @param {number} i
153
+ * @param {T} fallback
154
+ * @returns {T}
155
+ */
156
+ export const listedGetOr = (ls, i, fallback) => {
157
+ let { length } = ls;
158
+ return 0 <= i && i < length ? ls[i] : fallback;
159
+ }
160
+
161
+ /**
162
+ * Implements extension method List::isEmpty
163
+ * @template T
164
+ * @param {T[]} ls
165
+ * @returns {boolean}
166
+ */
167
+ export const listIsEmpty = (ls) => {
168
+ return !ls.length;
169
+ }
170
+
171
+ /**
172
+ * Implements extension method Listed::join
173
+ * @template T
174
+ * @param {T[]} ls
175
+ * @param {string} separator
176
+ * @param {(T) => string} elementStringifier
177
+ * @returns {string}
178
+ */
179
+ export const listedJoin = (ls, separator, elementStringifier) => {
180
+ let joined = "";
181
+ let { length } = ls;
182
+ for (let i = 0; i < length; ++i) {
183
+ if (i) {
184
+ joined += separator;
185
+ }
186
+ let element = ls[i];
187
+ let stringifiedElement = elementStringifier(element);
188
+ joined += stringifiedElement;
189
+ }
190
+ return joined;
191
+ }
192
+
193
+ /**
194
+ * @template T
195
+ * @param {T[]} ls
196
+ */
197
+ // Implements extension method ListBuilder::clear
198
+ export const listBuilderClear = (ls) => {
199
+ ls.length = 0;
200
+ }
201
+
202
+ /**
203
+ * Implements extension method ListBuilder::removeLast
204
+ * @template T
205
+ * @param {T[]} ls
206
+ * @returns {T}
207
+ */
208
+ export const listBuilderRemoveLast = (ls) => {
209
+ if (ls.length) {
210
+ return ls.pop();
211
+ } else {
212
+ bubble();
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Implements extension method ListBuilder::reverse
218
+ * @template T
219
+ * @param {T[]} ls
220
+ */
221
+ export const listBuilderReverse = (ls) => {
222
+ let { length } = ls;
223
+ let lastIndex = length - 1;
224
+ let mid = length >> 1;
225
+ for (let i = 0; i < mid; ++i) {
226
+ let j = lastIndex - i;
227
+ let a = ls[i];
228
+ ls[i] = ls[j];
229
+ ls[j] = a;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Implements extension method ListBuilder::set
235
+ * @template T
236
+ * @param {T[]} ls
237
+ * @param {number} i
238
+ * @param {T} newValue
239
+ */
240
+ export const listBuilderSet = (ls, i, newValue) => {
241
+ if (0 <= i && i <= ls.length) {
242
+ ls[i] = newValue;
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Implements extension method ListBuilder::removeLast
248
+ * @template T
249
+ * @param {T[]} ls
250
+ * @param {number} index
251
+ * @param {number | null} [removeCount]
252
+ * @param {T[] | null} [newValues]
253
+ * @returns {Readonly<T[]>}
254
+ */
255
+ export const listBuilderSplice = (ls, index, removeCount, newValues) => {
256
+ // Missing count is all, but explicit undefined is 0, so give explicit length.
257
+ if (removeCount == null) {
258
+ removeCount = ls.length;
259
+ }
260
+ return Object.freeze(ls.splice(index, removeCount, ...(newValues || [])));
261
+ }
262
+
263
+ /**
264
+ * Implements extension method ListBuilder::toList
265
+ * @template T
266
+ * @param {T[]} ls
267
+ * @returns {Readonly<T[]>}
268
+ */
269
+ export const listBuilderToList = (ls) => {
270
+ return Object.freeze(ls.slice());
271
+ }
272
+
273
+ /**
274
+ * Implements extension method Listed::slice
275
+ * @template T
276
+ * @param {T[]} ls
277
+ * @param {number} startInclusive
278
+ * @param {number} endExclusive
279
+ * @returns {Readonly<T[]>}
280
+ */
281
+ export const listedSlice = (ls, startInclusive, endExclusive) => {
282
+ if (startInclusive < 0) {
283
+ startInclusive = 0;
284
+ }
285
+ if (endExclusive < 0) {
286
+ endExclusive = 0;
287
+ }
288
+ return Object.freeze(ls.slice(startInclusive, endExclusive));
289
+ }
package/mapped.js ADDED
@@ -0,0 +1,216 @@
1
+ import { pairConstructor } from "./pair.js";
2
+ import { bubble } from "./core.js";
3
+
4
+ /**
5
+ * @typedef {import("./pair.js").Pair} Pair
6
+ */
7
+
8
+ /**
9
+ * @template K, V
10
+ * @extends {Map<K, V>}
11
+ */
12
+ export class FreezeMap extends Map {
13
+ // TODO Don't worry to freeze? Or worry more by wrapping private map?
14
+ // TODO Wrapping/Object.proxy presumably pays an extra cost when wrapped.
15
+ clear() {
16
+ throw new TypeError();
17
+ }
18
+ delete() {
19
+ throw new TypeError();
20
+ }
21
+ set(key, value) {
22
+ if (Object.isFrozen(this)) {
23
+ // Crash only after frozen because constructor calls set.
24
+ throw new TypeError();
25
+ }
26
+ return super.set(key, value);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * @template K, V
32
+ * @param {Pair<K, V>} entries
33
+ * @returns {Map<K, V>}
34
+ */
35
+ export const mapConstructor = (entries) => {
36
+ return Object.freeze(new FreezeMap(entries));
37
+ }
38
+
39
+ /**
40
+ * @template K, V
41
+ * @param {Map<K, V>} entries
42
+ * @returns {Map<K, V>}
43
+ */
44
+ export const mapBuilderConstructor = (entries) => {
45
+ return new Map();
46
+ }
47
+
48
+ /**
49
+ * @template K, V
50
+ * @param {Map<K, V>} builder
51
+ * @param {K} key
52
+ * @returns {V}
53
+ */
54
+ export const mapBuilderRemove = (builder, key) => {
55
+ const result = builder.get(key);
56
+ if (builder.delete(key)) {
57
+ return result;
58
+ } else {
59
+ bubble();
60
+ }
61
+ }
62
+
63
+ /**
64
+ * @template K, V
65
+ * @param {Map<K, V>} builder
66
+ * @param {K} key
67
+ * @param {V} value
68
+ */
69
+ export const mapBuilderSet = (builder, key, value) => {
70
+ builder.set(key, value);
71
+ }
72
+
73
+ /**
74
+ * @template K, V
75
+ * @param {Map<K, V>} builder
76
+ * @returns {FreezeMap<K, V>}
77
+ */
78
+ export const mappedToMap = (mapped) => {
79
+ if (mapped instanceof FreezeMap) {
80
+ return mapped;
81
+ }
82
+ return Object.freeze(new FreezeMap(mapped));
83
+ }
84
+
85
+ /**
86
+ * @template K, V
87
+ * @param {Map<K, V>} mapped
88
+ * @returns {Readonly<FreezeMap<K, V>>}
89
+ */
90
+ export const mapBuilderToMap = (mapped) => {
91
+ return Object.freeze(new FreezeMap(mapped));
92
+ }
93
+
94
+ /**
95
+ * @template K, V
96
+ * @param {Map<K, V>} mapped
97
+ * @returns {Map<K, V>}
98
+ */
99
+ export const mappedToMapBuilder = (mapped) => {
100
+ return new Map(mapped);
101
+ }
102
+
103
+ ////////////
104
+ // Mapped //
105
+ ////////////
106
+
107
+ /**
108
+ * @template K, V
109
+ * @param {Map<K, V>} map
110
+ * @returns {number}
111
+ */
112
+ export const mappedLength = (map) => {
113
+ return map.size;
114
+ }
115
+
116
+ /**
117
+ * @template K, V
118
+ * @param {Map<K, V>} map
119
+ * @param {K} key
120
+ * @returns {V}
121
+ */
122
+ export const mappedGet = (map, key) => {
123
+ const result = map.get(key);
124
+ // TODO Under compiler-error-free Temper, could undefined values get set?
125
+ // TODO Would Map<?, Void> be impossible to feed once we get checks in place?
126
+ if (result === undefined) {
127
+ bubble();
128
+ }
129
+ return result;
130
+ }
131
+
132
+ /**
133
+ * @template K, V
134
+ * @param {Map<K, V>} map
135
+ * @param {K} key
136
+ * @param {V} fallback
137
+ * @returns {V}
138
+ */
139
+ export const mappedGetOr = (map, key, fallback) => {
140
+ return map.get(key) ?? fallback;
141
+ }
142
+
143
+ /**
144
+ * @template K, V
145
+ * @param {Map<K, V>} map
146
+ * @param {K} key
147
+ * @returns {boolean}
148
+ */
149
+ export const mappedHas = (map, key) => {
150
+ return map.has(key);
151
+ }
152
+
153
+ /**
154
+ * @template K, V
155
+ * @param {Map<K, V>} map
156
+ * @returns {Readonly<K[]>}
157
+ */
158
+ export const mappedKeys = (map) => {
159
+ return Object.freeze(Array.prototype.slice.call(map.keys()));
160
+ }
161
+
162
+ /**
163
+ * @template K, V
164
+ * @param {Map<K, V>} map
165
+ * @returns {Readonly<V[]>}
166
+ */
167
+ export const mappedValues = (map) => {
168
+ return Object.freeze(Array.prototype.slice.call(map.values()));
169
+ }
170
+
171
+ /**
172
+ * @template K, V
173
+ * @param {Map<K, V>} map
174
+ * @returns {Readonly<Readonly<Pair<K, V>>[]>}
175
+ */
176
+ export const mappedToList = (map) => {
177
+ return Object.freeze(Array.from(map, ([key, value]) => pairConstructor(key, value)));
178
+ }
179
+
180
+ /**
181
+ * @template K, V, R
182
+ * @param {Map<K, V>} map
183
+ * @param {(value: K) => R} func
184
+ * @returns {Readonly<R[]>}
185
+ */
186
+ export const mappedToListWith = (map, func) => {
187
+ return Object.freeze(Array.from(map, ([key, value]) => func(key, value)));
188
+ }
189
+
190
+ /**
191
+ * @template K, V, R
192
+ * @param {Map<K, V>} map
193
+ * @returns {Readonly<Pair<K, V>>[]}
194
+ */
195
+ export const mappedToListBuilder = (map) => {
196
+ return Object.freeze(Array.from(map, ([key, value]) => pairConstructor(key, value)));
197
+ }
198
+
199
+ /**
200
+ * @template K, V, R
201
+ * @param {Map<K, V>} map
202
+ * @param {(value: K) => R} func
203
+ * @returns {Readonly<R[]>}
204
+ */
205
+ export const mappedToListBuilderWith = (map, func) => {
206
+ return Object.freeze(Array.from(map, ([key, value]) => func(key, value)));
207
+ }
208
+
209
+ /**
210
+ * @template K, V
211
+ * @param {Map<K, V>} map
212
+ * @param {(key: K, value: V) => void} func
213
+ */
214
+ export const mappedForEach = (map, func) => {
215
+ map.forEach((v, k) => func(k, v));
216
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temperlang/core",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Runtime support for JS generated by Temper",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/pair.js ADDED
@@ -0,0 +1,69 @@
1
+
2
+ /**
3
+ * @template K, V
4
+ */
5
+ export class Pair {
6
+ /** @type {K} */
7
+ key;
8
+
9
+ /** @type {V} */
10
+ value;
11
+
12
+ /**
13
+ * @param {K} key
14
+ * @param {V} value
15
+ */
16
+ constructor(key, value) {
17
+ this.key = key;
18
+ this.value = value;
19
+ }
20
+
21
+ /**
22
+ * The key held by this pair.
23
+ * @returns {K}
24
+ */
25
+ get key() {
26
+ return this.key;
27
+ }
28
+
29
+ /**
30
+ * The value held by this pair.
31
+ * @returns {V}
32
+ */
33
+ get value() {
34
+ return this.value;
35
+ }
36
+
37
+ /**
38
+ * The first index is the same as the key.
39
+ * @returns {K}
40
+ */
41
+ get [0]() {
42
+ return this.key;
43
+ }
44
+
45
+ /**
46
+ * The second index is the same as the value.
47
+ * @returns {V}
48
+ */
49
+ get [1]() {
50
+ return this.value;
51
+ }
52
+
53
+ /**
54
+ * @returns {2}
55
+ */
56
+ get length() {
57
+ return 2;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * @template K, V
63
+ * @param {K} key
64
+ * @param {V} value
65
+ * @returns {Readonly<Pair<K, V>>}
66
+ */
67
+ export const pairConstructor = (key, value) => {
68
+ return Object.freeze(new Pair(key, value));
69
+ }