@elaraai/east 0.0.1-beta.0 → 0.0.1-beta.2
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/README.md +39 -10
- package/dist/src/analyze.d.ts +4 -4
- package/dist/src/analyze.d.ts.map +1 -1
- package/dist/src/analyze.js +142 -44
- package/dist/src/analyze.js.map +1 -1
- package/dist/src/ast.d.ts +18 -7
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast.js +1 -146
- package/dist/src/ast.js.map +1 -1
- package/dist/src/ast_to_ir.d.ts +1 -0
- package/dist/src/ast_to_ir.d.ts.map +1 -1
- package/dist/src/ast_to_ir.js +71 -10
- package/dist/src/ast_to_ir.js.map +1 -1
- package/dist/src/builtins.d.ts.map +1 -1
- package/dist/src/builtins.js +79 -64
- package/dist/src/builtins.js.map +1 -1
- package/dist/src/comparison.d.ts.map +1 -1
- package/dist/src/comparison.js +21 -0
- package/dist/src/comparison.js.map +1 -1
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +114 -220
- package/dist/src/compile.js.map +1 -1
- package/dist/src/default.d.ts.map +1 -1
- package/dist/src/default.js +2 -0
- package/dist/src/default.js.map +1 -1
- package/dist/src/eastir.d.ts +12 -10
- package/dist/src/eastir.d.ts.map +1 -1
- package/dist/src/eastir.js +20 -19
- package/dist/src/eastir.js.map +1 -1
- package/dist/src/expr/array.js +51 -51
- package/dist/src/expr/array.js.map +1 -1
- package/dist/src/expr/ast.d.ts.map +1 -1
- package/dist/src/expr/ast.js +6 -0
- package/dist/src/expr/ast.js.map +1 -1
- package/dist/src/expr/asyncfunction.d.ts +49 -0
- package/dist/src/expr/asyncfunction.d.ts.map +1 -0
- package/dist/src/expr/asyncfunction.js +60 -0
- package/dist/src/expr/asyncfunction.js.map +1 -0
- package/dist/src/expr/block.d.ts +60 -6
- package/dist/src/expr/block.d.ts.map +1 -1
- package/dist/src/expr/block.js +251 -14
- package/dist/src/expr/block.js.map +1 -1
- package/dist/src/expr/dict.js +47 -47
- package/dist/src/expr/dict.js.map +1 -1
- package/dist/src/expr/expr.d.ts +9 -0
- package/dist/src/expr/expr.d.ts.map +1 -1
- package/dist/src/expr/expr.js +5 -1
- package/dist/src/expr/expr.js.map +1 -1
- package/dist/src/expr/index.d.ts +73 -2
- package/dist/src/expr/index.d.ts.map +1 -1
- package/dist/src/expr/index.js +72 -8
- package/dist/src/expr/index.js.map +1 -1
- package/dist/src/expr/libs/array.js +1 -1
- package/dist/src/expr/libs/array.js.map +1 -1
- package/dist/src/expr/libs/dict.js +3 -3
- package/dist/src/expr/libs/dict.js.map +1 -1
- package/dist/src/expr/libs/set.js +2 -2
- package/dist/src/expr/libs/set.js.map +1 -1
- package/dist/src/expr/ref.js +1 -1
- package/dist/src/expr/ref.js.map +1 -1
- package/dist/src/expr/set.js +38 -38
- package/dist/src/expr/set.js.map +1 -1
- package/dist/src/expr/struct.d.ts +2 -0
- package/dist/src/expr/struct.d.ts.map +1 -1
- package/dist/src/expr/types.d.ts +8 -5
- package/dist/src/expr/types.d.ts.map +1 -1
- package/dist/src/fuzz.d.ts.map +1 -1
- package/dist/src/fuzz.js +5 -0
- package/dist/src/fuzz.js.map +1 -1
- package/dist/src/internal.d.ts +11 -9
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +15 -5
- package/dist/src/internal.js.map +1 -1
- package/dist/src/ir.d.ts +231 -33
- package/dist/src/ir.d.ts.map +1 -1
- package/dist/src/ir.js +3 -1
- package/dist/src/ir.js.map +1 -1
- package/dist/src/serialization/beast.d.ts.map +1 -1
- package/dist/src/serialization/beast.js +6 -0
- package/dist/src/serialization/beast.js.map +1 -1
- package/dist/src/serialization/beast2-stream.d.ts.map +1 -1
- package/dist/src/serialization/beast2-stream.js +5 -0
- package/dist/src/serialization/beast2-stream.js.map +1 -1
- package/dist/src/serialization/beast2.d.ts.map +1 -1
- package/dist/src/serialization/beast2.js +6 -0
- package/dist/src/serialization/beast2.js.map +1 -1
- package/dist/src/serialization/east.d.ts.map +1 -1
- package/dist/src/serialization/east.js +9 -1
- package/dist/src/serialization/east.js.map +1 -1
- package/dist/src/serialization/json.d.ts.map +1 -1
- package/dist/src/serialization/json.js +8 -2
- package/dist/src/serialization/json.js.map +1 -1
- package/dist/src/type_of_type.d.ts +19 -32
- package/dist/src/type_of_type.d.ts.map +1 -1
- package/dist/src/type_of_type.js +126 -14
- package/dist/src/type_of_type.js.map +1 -1
- package/dist/src/types.d.ts +59 -44
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +191 -91
- package/dist/src/types.js.map +1 -1
- package/package.json +3 -3
package/dist/src/types.js
CHANGED
|
@@ -48,9 +48,6 @@ export const BlobType = { type: "Blob" };
|
|
|
48
48
|
* @throws When the element type contains functions
|
|
49
49
|
*/
|
|
50
50
|
export function RefType(type) {
|
|
51
|
-
if (typeof type !== "string" && !isDataType(type)) {
|
|
52
|
-
throw new Error(`Ref value type must be a (non-function) data type, got ${printType(type)}`);
|
|
53
|
-
}
|
|
54
51
|
return { type: "Ref", value: type };
|
|
55
52
|
}
|
|
56
53
|
;
|
|
@@ -63,9 +60,6 @@ export function RefType(type) {
|
|
|
63
60
|
* @throws When the element type contains functions
|
|
64
61
|
*/
|
|
65
62
|
export function ArrayType(type) {
|
|
66
|
-
if (typeof type !== "string" && !isDataType(type)) {
|
|
67
|
-
throw new Error(`Array value type must be a (non-function) data type, got ${printType(type)}`);
|
|
68
|
-
}
|
|
69
63
|
return { type: "Array", value: type };
|
|
70
64
|
}
|
|
71
65
|
;
|
|
@@ -98,9 +92,6 @@ export function DictType(key, value) {
|
|
|
98
92
|
if (typeof key !== "string" && !isImmutableType(key)) {
|
|
99
93
|
throw new Error(`Dict key type must be an immutable type, got ${printType(key)}`);
|
|
100
94
|
}
|
|
101
|
-
if (typeof value !== "string" && !isDataType(value)) {
|
|
102
|
-
throw new Error(`Dict value type must be a (non-function) data type, got ${printType(value)}`);
|
|
103
|
-
}
|
|
104
95
|
return { type: "Dict", key, value };
|
|
105
96
|
}
|
|
106
97
|
;
|
|
@@ -112,12 +103,6 @@ export function DictType(key, value) {
|
|
|
112
103
|
* @returns A Struct type
|
|
113
104
|
*/
|
|
114
105
|
export function StructType(field_types) {
|
|
115
|
-
// Validate all field types are data types (no functions)
|
|
116
|
-
for (const [field_name, field_type] of Object.entries(field_types)) {
|
|
117
|
-
if (typeof field_type !== "string" && !isDataType(field_type)) {
|
|
118
|
-
throw new Error(`Struct field ${field_name} must be a (non-function) data type, got ${printType(field_type)}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
106
|
return { type: "Struct", fields: field_types };
|
|
122
107
|
}
|
|
123
108
|
;
|
|
@@ -129,12 +114,6 @@ export function StructType(field_types) {
|
|
|
129
114
|
* @returns A Variant type with cases sorted alphabetically
|
|
130
115
|
*/
|
|
131
116
|
export function VariantType(case_types) {
|
|
132
|
-
// Validate all case types are data types (no functions)
|
|
133
|
-
for (const [case_name, case_type] of Object.entries(case_types)) {
|
|
134
|
-
if (typeof case_type !== "string" && !isDataType(case_type)) {
|
|
135
|
-
throw new Error(`Variant case ${case_name} must be a (non-function) data type, got ${printType(case_type)}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
117
|
// Cases are sorted alphabetically by their name
|
|
139
118
|
const cases_sorted = Object.fromEntries(Object.entries(case_types).sort((x, y) => x[0] < y[0] ? -1 : x[0] === y[0] ? 0 : 1));
|
|
140
119
|
return { type: "Variant", cases: cases_sorted };
|
|
@@ -154,7 +133,7 @@ export function VariantType(case_types) {
|
|
|
154
133
|
*/
|
|
155
134
|
function validateNotMutuallyRecursive(type, allowedMarker) {
|
|
156
135
|
const visited = new Set();
|
|
157
|
-
function check(t) {
|
|
136
|
+
function check(t, allowed) {
|
|
158
137
|
// Skip string placeholders (used for generic builtins)
|
|
159
138
|
if (typeof t === "string")
|
|
160
139
|
return;
|
|
@@ -167,8 +146,12 @@ function validateNotMutuallyRecursive(type, allowedMarker) {
|
|
|
167
146
|
throw new Error(`Invalid type encountered during recursion validation: ${t}${allowedMarkerString === null ? "" : ` (allowed marker: ${allowedMarkerString})`}`);
|
|
168
147
|
}
|
|
169
148
|
// Skip allowedMarker
|
|
170
|
-
if (t === allowedMarker)
|
|
149
|
+
if (t === allowedMarker) {
|
|
150
|
+
if (!allowed) {
|
|
151
|
+
throw new Error("RecursiveType cannot pass into set keys, dictionary keys, or function input/output types");
|
|
152
|
+
}
|
|
171
153
|
return;
|
|
154
|
+
}
|
|
172
155
|
// Avoid infinite loops
|
|
173
156
|
if (visited.has(t))
|
|
174
157
|
return;
|
|
@@ -185,31 +168,38 @@ function validateNotMutuallyRecursive(type, allowedMarker) {
|
|
|
185
168
|
}
|
|
186
169
|
else if (t.type === "Struct") {
|
|
187
170
|
for (const [_, field_type] of Object.entries(t.fields)) {
|
|
188
|
-
check(field_type);
|
|
171
|
+
check(field_type, allowed);
|
|
189
172
|
}
|
|
190
173
|
}
|
|
191
174
|
else if (t.type === "Variant") {
|
|
192
175
|
for (const [_, case_type] of Object.entries(t.cases)) {
|
|
193
|
-
check(case_type);
|
|
176
|
+
check(case_type, allowed);
|
|
194
177
|
}
|
|
195
178
|
}
|
|
179
|
+
else if (t.type === "Ref") {
|
|
180
|
+
check(t.value, allowed);
|
|
181
|
+
}
|
|
196
182
|
else if (t.type === "Array") {
|
|
197
|
-
check(t.value);
|
|
183
|
+
check(t.value, allowed);
|
|
198
184
|
}
|
|
199
185
|
else if (t.type === "Set") {
|
|
200
|
-
check(t.key);
|
|
186
|
+
check(t.key, false);
|
|
201
187
|
}
|
|
202
188
|
else if (t.type === "Dict") {
|
|
203
|
-
check(t.key);
|
|
204
|
-
check(t.value);
|
|
189
|
+
check(t.key, false);
|
|
190
|
+
check(t.value, allowed);
|
|
205
191
|
}
|
|
206
192
|
else if (t.type === "Function") {
|
|
207
|
-
t.inputs.forEach(check);
|
|
208
|
-
check(t.output);
|
|
193
|
+
t.inputs.forEach(input => check(input, false));
|
|
194
|
+
check(t.output, false);
|
|
195
|
+
}
|
|
196
|
+
else if (t.type === "AsyncFunction") {
|
|
197
|
+
t.inputs.forEach(input => check(input, false));
|
|
198
|
+
check(t.output, false);
|
|
209
199
|
}
|
|
210
200
|
// Primitive types don't need recursion
|
|
211
201
|
}
|
|
212
|
-
check(type);
|
|
202
|
+
check(type, true);
|
|
213
203
|
}
|
|
214
204
|
/**
|
|
215
205
|
* Constructs a recursive type with the specified node structure.
|
|
@@ -252,25 +242,33 @@ export function RecursiveType(f) {
|
|
|
252
242
|
ret.node = type;
|
|
253
243
|
// Validate SCC size 1 (no nested RecursiveTypes with cross-references)
|
|
254
244
|
validateNotMutuallyRecursive(type, ret);
|
|
255
|
-
if (typeof type !== "string" && !isDataType(type)) {
|
|
256
|
-
throw new Error(`Recursive node type must be a (non-function) data type, got ${printType(type)}`);
|
|
257
|
-
}
|
|
258
245
|
return ret;
|
|
259
246
|
}
|
|
260
247
|
;
|
|
261
248
|
/**
|
|
262
249
|
* Constructs a Function type with the specified input and output types.
|
|
263
250
|
*
|
|
264
|
-
* @typeParam I - Tuple type of input parameter types
|
|
265
|
-
* @typeParam O - The output/return type
|
|
266
251
|
* @param inputs - Array of {@link EastType} instances for each parameter
|
|
267
252
|
* @param output - The {@link EastType} of the return value
|
|
268
253
|
* @returns A Function type
|
|
254
|
+
*
|
|
255
|
+
* @see {@link AsyncFunctionType} for asynchronous functions.
|
|
269
256
|
*/
|
|
270
|
-
export function FunctionType(inputs, output
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
257
|
+
export function FunctionType(inputs, output) {
|
|
258
|
+
return { type: "Function", inputs, output };
|
|
259
|
+
}
|
|
260
|
+
;
|
|
261
|
+
/**
|
|
262
|
+
* Constructs an AsyncFunction type with the specified input and output types.
|
|
263
|
+
*
|
|
264
|
+
* @param inputs - Array of {@link EastType} instances for each parameter
|
|
265
|
+
* @param output - The {@link EastType} of the return value
|
|
266
|
+
* @returns An AsyncFunction type
|
|
267
|
+
*
|
|
268
|
+
* @see {@link AsyncFunctionType} for asynchronous functions.
|
|
269
|
+
*/
|
|
270
|
+
export function AsyncFunctionType(inputs, output) {
|
|
271
|
+
return { type: "AsyncFunction", inputs, output };
|
|
274
272
|
}
|
|
275
273
|
;
|
|
276
274
|
/**
|
|
@@ -281,8 +279,7 @@ export function FunctionType(inputs, output, platforms) {
|
|
|
281
279
|
* @returns `true` if the type is a pure data type, `false` if it contains functions
|
|
282
280
|
*
|
|
283
281
|
* @remarks
|
|
284
|
-
* Data types can be fully serialized and transmitted between runtimes.
|
|
285
|
-
* This recursively checks {@link StructType}, {@link VariantType}, and {@link RecursiveType} fields.
|
|
282
|
+
* Data types have a total ordering, can be fully serialized and transmitted between runtimes.
|
|
286
283
|
*/
|
|
287
284
|
export function isDataType(type, recursive_type) {
|
|
288
285
|
// Avoid infinite loops
|
|
@@ -290,20 +287,18 @@ export function isDataType(type, recursive_type) {
|
|
|
290
287
|
return true;
|
|
291
288
|
}
|
|
292
289
|
if (type.type === "Ref") {
|
|
293
|
-
|
|
294
|
-
return true;
|
|
290
|
+
return isDataType(type.value, recursive_type);
|
|
295
291
|
}
|
|
296
292
|
else if (type.type === "Array") {
|
|
297
|
-
|
|
298
|
-
return true;
|
|
293
|
+
return isDataType(type.value, recursive_type);
|
|
299
294
|
}
|
|
300
295
|
else if (type.type === "Set") {
|
|
301
|
-
// Set constructors check their
|
|
296
|
+
// Set constructors check their key type is an (immutable) data type
|
|
302
297
|
return true;
|
|
303
298
|
}
|
|
304
299
|
else if (type.type === "Dict") {
|
|
305
|
-
// Dict constructors check their key
|
|
306
|
-
return
|
|
300
|
+
// Dict constructors check their key type is an (immutable) data type
|
|
301
|
+
return isDataType(type.value, recursive_type);
|
|
307
302
|
}
|
|
308
303
|
else if (type.type === "Struct") {
|
|
309
304
|
for (const field_type of Object.values(type.fields)) {
|
|
@@ -327,6 +322,9 @@ export function isDataType(type, recursive_type) {
|
|
|
327
322
|
else if (type.type === "Function") {
|
|
328
323
|
return false;
|
|
329
324
|
}
|
|
325
|
+
else if (type.type === "AsyncFunction") {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
330
328
|
else {
|
|
331
329
|
// Primitive types are data types
|
|
332
330
|
return true;
|
|
@@ -340,8 +338,7 @@ export function isDataType(type, recursive_type) {
|
|
|
340
338
|
* @returns `true` if the type is immutable, `false` if it contains mutable collections or functions
|
|
341
339
|
*
|
|
342
340
|
* @remarks
|
|
343
|
-
* Immutable types exclude {@link ArrayType}, {@link SetType}, {@link DictType}, and {@link
|
|
344
|
-
* This recursively checks {@link StructType}, {@link VariantType}, and {@link RecursiveType} fields.
|
|
341
|
+
* Immutable types exclude {@link ArrayType}, {@link SetType}, {@link DictType}, {@link FunctionType} and {@link AsyncFunctionType}.
|
|
345
342
|
*/
|
|
346
343
|
export function isImmutableType(type, recursive_type) {
|
|
347
344
|
// Avoid infinite loops
|
|
@@ -387,6 +384,10 @@ export function isImmutableType(type, recursive_type) {
|
|
|
387
384
|
// Functions are not data types, and "immutable data" is a subset of "data"
|
|
388
385
|
return false;
|
|
389
386
|
}
|
|
387
|
+
else if (type.type === "AsyncFunction") {
|
|
388
|
+
// Functions are not data types, and "immutable data" is a subset of "data"
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
390
391
|
else {
|
|
391
392
|
// Primitive types are immutable
|
|
392
393
|
return true;
|
|
@@ -470,7 +471,34 @@ export function EastTypeOf(value) {
|
|
|
470
471
|
* For {@link StructType} and {@link VariantType}, field/case order matters.
|
|
471
472
|
* For {@link RecursiveType}, uses cycle tracking to handle recursive references.
|
|
472
473
|
*/
|
|
474
|
+
// Cache for memoizing isTypeEqual results (top-level calls only)
|
|
475
|
+
const typeEqualCache = new WeakMap();
|
|
473
476
|
export function isTypeEqual(t1, t2, r1 = t1, r2 = t2) {
|
|
477
|
+
// Fast path: reference equality
|
|
478
|
+
if (t1 === t2)
|
|
479
|
+
return true;
|
|
480
|
+
// Memoization for top-level calls only (r1/r2 are just for internal cycle detection)
|
|
481
|
+
const isTopLevel = r1 === t1 && r2 === t2;
|
|
482
|
+
if (isTopLevel) {
|
|
483
|
+
const innerCache = typeEqualCache.get(t1);
|
|
484
|
+
if (innerCache) {
|
|
485
|
+
const cached = innerCache.get(t2);
|
|
486
|
+
if (cached !== undefined)
|
|
487
|
+
return cached;
|
|
488
|
+
}
|
|
489
|
+
const result = isTypeEqualImpl(t1, t2, r1, r2);
|
|
490
|
+
// Store result in cache
|
|
491
|
+
let cache = typeEqualCache.get(t1);
|
|
492
|
+
if (!cache) {
|
|
493
|
+
cache = new WeakMap();
|
|
494
|
+
typeEqualCache.set(t1, cache);
|
|
495
|
+
}
|
|
496
|
+
cache.set(t2, result);
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
499
|
+
return isTypeEqualImpl(t1, t2, r1, r2);
|
|
500
|
+
}
|
|
501
|
+
function isTypeEqualImpl(t1, t2, r1, r2) {
|
|
474
502
|
if (t1.type === "Recursive") {
|
|
475
503
|
if (t2.type === "Recursive") {
|
|
476
504
|
if (t1.node === r1) {
|
|
@@ -607,21 +635,25 @@ export function isTypeEqual(t1, t2, r1 = t1, r2 = t2) {
|
|
|
607
635
|
if (!isTypeEqual(t1.output, t2.output, r1, r2)) {
|
|
608
636
|
return false;
|
|
609
637
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
if (t1.platforms === null || t2.platforms === null) {
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
640
|
+
else if (t1.type === "AsyncFunction") {
|
|
641
|
+
if (t2.type !== "AsyncFunction") {
|
|
615
642
|
return false;
|
|
616
643
|
}
|
|
617
|
-
|
|
644
|
+
// Check input types match
|
|
645
|
+
if (t1.inputs.length !== t2.inputs.length) {
|
|
618
646
|
return false;
|
|
619
647
|
}
|
|
620
|
-
for (let i = 0; i < t1.
|
|
621
|
-
if (t1.
|
|
648
|
+
for (let i = 0; i < t1.inputs.length; i++) {
|
|
649
|
+
if (!isTypeEqual(t1.inputs[i], t2.inputs[i], r1, r2)) {
|
|
622
650
|
return false;
|
|
623
651
|
}
|
|
624
652
|
}
|
|
653
|
+
// Check output type matches
|
|
654
|
+
if (!isTypeEqual(t1.output, t2.output, r1, r2)) {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
625
657
|
return true;
|
|
626
658
|
}
|
|
627
659
|
else {
|
|
@@ -750,6 +782,9 @@ export function isValueOf(value, type, node_type, nodes_visited) {
|
|
|
750
782
|
else if (type.type === "Function") {
|
|
751
783
|
throw new Error('Javascript functions cannot be converted to East functions');
|
|
752
784
|
}
|
|
785
|
+
else if (type.type === "AsyncFunction") {
|
|
786
|
+
throw new Error('Javascript functions cannot be converted to East async functions');
|
|
787
|
+
}
|
|
753
788
|
else {
|
|
754
789
|
throw new Error(`Unknown type encountered during value type check: ${type.type}`);
|
|
755
790
|
}
|
|
@@ -843,7 +878,14 @@ export function printType(type, stack = []) {
|
|
|
843
878
|
else if (type.type === "Function") {
|
|
844
879
|
// Note: functions can't be inside recursive types
|
|
845
880
|
stack.push(type);
|
|
846
|
-
const ret = `.Function (inputs=[${type.inputs.map(t => printType(t, stack)).join(", ")}], output=${printType(type.output, stack)}
|
|
881
|
+
const ret = `.Function (inputs=[${type.inputs.map(t => printType(t, stack)).join(", ")}], output=${printType(type.output, stack)})`;
|
|
882
|
+
stack.pop();
|
|
883
|
+
return ret;
|
|
884
|
+
}
|
|
885
|
+
else if (type.type === "AsyncFunction") {
|
|
886
|
+
// Note: functions can't be inside recursive types
|
|
887
|
+
stack.push(type);
|
|
888
|
+
const ret = `.AsyncFunction (inputs=[${type.inputs.map(t => printType(t, stack)).join(", ")}], output=${printType(type.output, stack)})`;
|
|
847
889
|
stack.pop();
|
|
848
890
|
return ret;
|
|
849
891
|
}
|
|
@@ -865,7 +907,30 @@ export function printType(type, stack = []) {
|
|
|
865
907
|
* - {@link VariantType} supports width subtyping (more cases → fewer cases)
|
|
866
908
|
* - {@link FunctionType} uses contravariant inputs and covariant outputs
|
|
867
909
|
*/
|
|
910
|
+
// Cache for memoizing isSubtype results
|
|
911
|
+
const isSubtypeCache = new WeakMap();
|
|
868
912
|
export function isSubtype(t1, t2) {
|
|
913
|
+
// Fast path: reference equality (reflexivity)
|
|
914
|
+
if (t1 === t2)
|
|
915
|
+
return true;
|
|
916
|
+
// Check cache
|
|
917
|
+
const innerCache = isSubtypeCache.get(t1);
|
|
918
|
+
if (innerCache) {
|
|
919
|
+
const cached = innerCache.get(t2);
|
|
920
|
+
if (cached !== undefined)
|
|
921
|
+
return cached;
|
|
922
|
+
}
|
|
923
|
+
const result = isSubtypeImpl(t1, t2);
|
|
924
|
+
// Store in cache
|
|
925
|
+
let cache = isSubtypeCache.get(t1);
|
|
926
|
+
if (!cache) {
|
|
927
|
+
cache = new WeakMap();
|
|
928
|
+
isSubtypeCache.set(t1, cache);
|
|
929
|
+
}
|
|
930
|
+
cache.set(t2, result);
|
|
931
|
+
return result;
|
|
932
|
+
}
|
|
933
|
+
function isSubtypeImpl(t1, t2) {
|
|
869
934
|
// Equi-recursive type subtyping: unfold recursive types when only one side is recursive
|
|
870
935
|
if (t1.type === "Recursive") {
|
|
871
936
|
if (t2.type === "Recursive") {
|
|
@@ -975,7 +1040,15 @@ export function isSubtype(t1, t2) {
|
|
|
975
1040
|
}
|
|
976
1041
|
}
|
|
977
1042
|
else if (t1.type === "Function") {
|
|
978
|
-
if (t2.type === "Function") {
|
|
1043
|
+
if (t2.type === "Function" || t2.type === "AsyncFunction") {
|
|
1044
|
+
return t1.inputs.length === t2.inputs.length && t1.inputs.every((t, i) => isSubtype(t2.inputs[i], t)) && isSubtype(t1.output, t2.output); // contravariant inputs and covariant output
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
else if (t1.type === "AsyncFunction") {
|
|
1051
|
+
if (t2.type === "AsyncFunction") {
|
|
979
1052
|
return t1.inputs.length === t2.inputs.length && t1.inputs.every((t, i) => isSubtype(t2.inputs[i], t)) && isSubtype(t1.output, t2.output); // contravariant inputs and covariant output
|
|
980
1053
|
}
|
|
981
1054
|
else {
|
|
@@ -1112,17 +1185,30 @@ export function TypeUnion(t1, t2) {
|
|
|
1112
1185
|
if (t1.inputs.length !== t2.inputs.length) {
|
|
1113
1186
|
throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
|
|
1114
1187
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1188
|
+
return FunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
|
|
1189
|
+
}
|
|
1190
|
+
else if (t2.type === "AsyncFunction") {
|
|
1191
|
+
if (t1.inputs.length !== t2.inputs.length) {
|
|
1192
|
+
throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
|
|
1118
1193
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1194
|
+
return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
else if (t1.type === "AsyncFunction") {
|
|
1201
|
+
if (t2.type === "Function") {
|
|
1202
|
+
if (t1.inputs.length !== t2.inputs.length) {
|
|
1203
|
+
throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
|
|
1121
1204
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1205
|
+
return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
|
|
1206
|
+
}
|
|
1207
|
+
else if (t2.type === "AsyncFunction") {
|
|
1208
|
+
if (t1.inputs.length !== t2.inputs.length) {
|
|
1209
|
+
throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
|
|
1124
1210
|
}
|
|
1125
|
-
return
|
|
1211
|
+
return AsyncFunctionType(t1.inputs.map((t, i) => TypeIntersect(t, t2.inputs[i])), TypeUnion(t1.output, t2.output));
|
|
1126
1212
|
}
|
|
1127
1213
|
else {
|
|
1128
1214
|
throw new TypeMismatchError(`Cannot union ${printType(t1)} with ${printType(t2)}: incompatible types`);
|
|
@@ -1265,17 +1351,30 @@ export function TypeIntersect(t1, t2) {
|
|
|
1265
1351
|
if (t1.inputs.length !== t2.inputs.length) {
|
|
1266
1352
|
throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
|
|
1267
1353
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1354
|
+
return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
|
|
1355
|
+
}
|
|
1356
|
+
else if (t2.type === "AsyncFunction") {
|
|
1357
|
+
if (t1.inputs.length !== t2.inputs.length) {
|
|
1358
|
+
throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
|
|
1271
1359
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1360
|
+
return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
else if (t1.type === "AsyncFunction") {
|
|
1367
|
+
if (t2.type === "Function") {
|
|
1368
|
+
if (t1.inputs.length !== t2.inputs.length) {
|
|
1369
|
+
throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
|
|
1274
1370
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1371
|
+
return FunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
|
|
1372
|
+
}
|
|
1373
|
+
else if (t2.type === "AsyncFunction") {
|
|
1374
|
+
if (t1.inputs.length !== t2.inputs.length) {
|
|
1375
|
+
throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: functions take different number of arguments`);
|
|
1277
1376
|
}
|
|
1278
|
-
return
|
|
1377
|
+
return AsyncFunctionType(t1.inputs.map((t, i) => TypeUnion(t, t2.inputs[i])), TypeIntersect(t1.output, t2.output));
|
|
1279
1378
|
}
|
|
1280
1379
|
else {
|
|
1281
1380
|
throw new TypeMismatchError(`Cannot intersect ${printType(t1)} with ${printType(t2)}: incompatible types`);
|
|
@@ -1423,20 +1522,18 @@ export function TypeEqual(t1, t2, r1 = t1, r2 = t2) {
|
|
|
1423
1522
|
if (t1.inputs.length !== t2.inputs.length) {
|
|
1424
1523
|
throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions take different number of arguments`);
|
|
1425
1524
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions have different platform effects`);
|
|
1437
|
-
}
|
|
1525
|
+
return FunctionType(t1.inputs.map((t, i) => TypeEqual(t, t2.inputs[i], r1, r2)), TypeEqual(t1.output, t2.output, r1, r2));
|
|
1526
|
+
}
|
|
1527
|
+
else {
|
|
1528
|
+
throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
else if (t1.type === "AsyncFunction") {
|
|
1532
|
+
if (t2.type === "AsyncFunction") {
|
|
1533
|
+
if (t1.inputs.length !== t2.inputs.length) {
|
|
1534
|
+
throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: functions take different number of arguments`);
|
|
1438
1535
|
}
|
|
1439
|
-
return
|
|
1536
|
+
return AsyncFunctionType(t1.inputs.map((t, i) => TypeEqual(t, t2.inputs[i], r1, r2)), TypeEqual(t1.output, t2.output, r1, r2));
|
|
1440
1537
|
}
|
|
1441
1538
|
else {
|
|
1442
1539
|
throw new TypeMismatchError(`${printType(t1)} is not equal to ${printType(t2)}: incompatible types`);
|
|
@@ -1565,6 +1662,9 @@ export function TypeWiden(t1, t2) {
|
|
|
1565
1662
|
else if (t1.type === "Function") {
|
|
1566
1663
|
throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: functions not supported`);
|
|
1567
1664
|
}
|
|
1665
|
+
else if (t1.type === "AsyncFunction") {
|
|
1666
|
+
throw new TypeMismatchError(`Cannot widen ${printType(t1)} with ${printType(t2)}: functions not supported`);
|
|
1667
|
+
}
|
|
1568
1668
|
else {
|
|
1569
1669
|
if (t1.type === t2.type) {
|
|
1570
1670
|
return t1;
|