@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.
Files changed (101) hide show
  1. package/README.md +39 -10
  2. package/dist/src/analyze.d.ts +4 -4
  3. package/dist/src/analyze.d.ts.map +1 -1
  4. package/dist/src/analyze.js +142 -44
  5. package/dist/src/analyze.js.map +1 -1
  6. package/dist/src/ast.d.ts +18 -7
  7. package/dist/src/ast.d.ts.map +1 -1
  8. package/dist/src/ast.js +1 -146
  9. package/dist/src/ast.js.map +1 -1
  10. package/dist/src/ast_to_ir.d.ts +1 -0
  11. package/dist/src/ast_to_ir.d.ts.map +1 -1
  12. package/dist/src/ast_to_ir.js +71 -10
  13. package/dist/src/ast_to_ir.js.map +1 -1
  14. package/dist/src/builtins.d.ts.map +1 -1
  15. package/dist/src/builtins.js +79 -64
  16. package/dist/src/builtins.js.map +1 -1
  17. package/dist/src/comparison.d.ts.map +1 -1
  18. package/dist/src/comparison.js +21 -0
  19. package/dist/src/comparison.js.map +1 -1
  20. package/dist/src/compile.d.ts.map +1 -1
  21. package/dist/src/compile.js +114 -220
  22. package/dist/src/compile.js.map +1 -1
  23. package/dist/src/default.d.ts.map +1 -1
  24. package/dist/src/default.js +2 -0
  25. package/dist/src/default.js.map +1 -1
  26. package/dist/src/eastir.d.ts +12 -10
  27. package/dist/src/eastir.d.ts.map +1 -1
  28. package/dist/src/eastir.js +20 -19
  29. package/dist/src/eastir.js.map +1 -1
  30. package/dist/src/expr/array.js +51 -51
  31. package/dist/src/expr/array.js.map +1 -1
  32. package/dist/src/expr/ast.d.ts.map +1 -1
  33. package/dist/src/expr/ast.js +6 -0
  34. package/dist/src/expr/ast.js.map +1 -1
  35. package/dist/src/expr/asyncfunction.d.ts +49 -0
  36. package/dist/src/expr/asyncfunction.d.ts.map +1 -0
  37. package/dist/src/expr/asyncfunction.js +60 -0
  38. package/dist/src/expr/asyncfunction.js.map +1 -0
  39. package/dist/src/expr/block.d.ts +60 -6
  40. package/dist/src/expr/block.d.ts.map +1 -1
  41. package/dist/src/expr/block.js +251 -14
  42. package/dist/src/expr/block.js.map +1 -1
  43. package/dist/src/expr/dict.js +47 -47
  44. package/dist/src/expr/dict.js.map +1 -1
  45. package/dist/src/expr/expr.d.ts +9 -0
  46. package/dist/src/expr/expr.d.ts.map +1 -1
  47. package/dist/src/expr/expr.js +5 -1
  48. package/dist/src/expr/expr.js.map +1 -1
  49. package/dist/src/expr/index.d.ts +73 -2
  50. package/dist/src/expr/index.d.ts.map +1 -1
  51. package/dist/src/expr/index.js +72 -8
  52. package/dist/src/expr/index.js.map +1 -1
  53. package/dist/src/expr/libs/array.js +1 -1
  54. package/dist/src/expr/libs/array.js.map +1 -1
  55. package/dist/src/expr/libs/dict.js +3 -3
  56. package/dist/src/expr/libs/dict.js.map +1 -1
  57. package/dist/src/expr/libs/set.js +2 -2
  58. package/dist/src/expr/libs/set.js.map +1 -1
  59. package/dist/src/expr/ref.js +1 -1
  60. package/dist/src/expr/ref.js.map +1 -1
  61. package/dist/src/expr/set.js +38 -38
  62. package/dist/src/expr/set.js.map +1 -1
  63. package/dist/src/expr/struct.d.ts +2 -0
  64. package/dist/src/expr/struct.d.ts.map +1 -1
  65. package/dist/src/expr/types.d.ts +8 -5
  66. package/dist/src/expr/types.d.ts.map +1 -1
  67. package/dist/src/fuzz.d.ts.map +1 -1
  68. package/dist/src/fuzz.js +5 -0
  69. package/dist/src/fuzz.js.map +1 -1
  70. package/dist/src/internal.d.ts +11 -9
  71. package/dist/src/internal.d.ts.map +1 -1
  72. package/dist/src/internal.js +15 -5
  73. package/dist/src/internal.js.map +1 -1
  74. package/dist/src/ir.d.ts +231 -33
  75. package/dist/src/ir.d.ts.map +1 -1
  76. package/dist/src/ir.js +3 -1
  77. package/dist/src/ir.js.map +1 -1
  78. package/dist/src/serialization/beast.d.ts.map +1 -1
  79. package/dist/src/serialization/beast.js +6 -0
  80. package/dist/src/serialization/beast.js.map +1 -1
  81. package/dist/src/serialization/beast2-stream.d.ts.map +1 -1
  82. package/dist/src/serialization/beast2-stream.js +5 -0
  83. package/dist/src/serialization/beast2-stream.js.map +1 -1
  84. package/dist/src/serialization/beast2.d.ts.map +1 -1
  85. package/dist/src/serialization/beast2.js +6 -0
  86. package/dist/src/serialization/beast2.js.map +1 -1
  87. package/dist/src/serialization/east.d.ts.map +1 -1
  88. package/dist/src/serialization/east.js +9 -1
  89. package/dist/src/serialization/east.js.map +1 -1
  90. package/dist/src/serialization/json.d.ts.map +1 -1
  91. package/dist/src/serialization/json.js +8 -2
  92. package/dist/src/serialization/json.js.map +1 -1
  93. package/dist/src/type_of_type.d.ts +19 -32
  94. package/dist/src/type_of_type.d.ts.map +1 -1
  95. package/dist/src/type_of_type.js +126 -14
  96. package/dist/src/type_of_type.js.map +1 -1
  97. package/dist/src/types.d.ts +59 -44
  98. package/dist/src/types.d.ts.map +1 -1
  99. package/dist/src/types.js +191 -91
  100. package/dist/src/types.js.map +1 -1
  101. package/package.json +3 -3
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) 2025 Elara AI Pty Ltd
3
3
  * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
4
  */
5
- import { Builtins } from "./builtins.js";
5
+ import {} from "./builtins.js";
6
6
  import { printLocationValue } from "./ir.js";
7
7
  import { compareFor, equalFor, greaterEqualFor, greaterFor, isFor, lessEqualFor, lessFor, notEqualFor } from "./comparison.js";
8
8
  import { printFor, parseFor } from "./serialization/east.js";
@@ -14,7 +14,7 @@ import { BufferWriter } from "./serialization/binary-utils.js";
14
14
  import { decodeBeast2For, decodeBeastFor, encodeBeast2For, encodeBeastFor, fromJSONFor, toJSONFor } from "./serialization/index.js";
15
15
  import { formatDateTime } from "./datetime_format/print.js";
16
16
  import { parseDateTimeFormatted } from "./datetime_format/parse.js";
17
- import { EastTypeValueType, isTypeValueEqual, isSubtypeValue, toEastTypeValue, expandTypeValue } from "./type_of_type.js";
17
+ import { EastTypeValueType, isTypeValueEqual, expandTypeValue } from "./type_of_type.js";
18
18
  import { ref } from "./containers/ref.js";
19
19
  export { isTypeValueEqual };
20
20
  export const printTypeValue = printFor(EastTypeValueType);
@@ -64,25 +64,11 @@ class BreakException {
64
64
  * @internal
65
65
  */
66
66
  export function compile_internal(ir, ctx, platform, asyncPlatformFns, fresh_ctx = true, compilingNodes = new Set()) {
67
- // Detect circular IR
68
- if (compilingNodes.has(ir)) {
69
- throw new Error(`Circular IR reference detected at ${printLocationValue(ir.value?.location || { file: "unknown", line: 0, column: 0 })}. `);
70
- }
71
- // Track this node (exception-safe with try-finally)
72
- compilingNodes.add(ir);
73
- try {
74
- return compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, compilingNodes);
75
- }
76
- finally {
77
- compilingNodes.delete(ir);
78
- }
79
- }
80
- /** Internal helper that does the actual compilation work */
81
- function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, compilingNodes = new Set()) {
67
+ // The IR is checked prior to compilation, so we can assume it's valid here.
68
+ // The compiler needs to take care that Promises are properly awaited, so most IR nodes need both sync and async implementations.
69
+ // We assume unnecessary `async` functions degrade performance but unnecessary `await`s are not too bad, so while we could be more "specific" in our awaits we do not bother
70
+ // TODO if function calls accepted the call Location, we could probably simplify the call site code generation
82
71
  if (ir.type === "Value") {
83
- if (ir.value.type.type !== ir.value.value.type) {
84
- throw new Error(`Value node expected value of type .${ir.value.type.type} but got .${ir.value.value.type} at ${printLocationValue(ir.value.location)}`);
85
- }
86
72
  const v = ir.value.value.value;
87
73
  return (_ctx) => v;
88
74
  }
@@ -191,13 +177,6 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
191
177
  }
192
178
  else if (ir.type === "Variable") {
193
179
  const name = ir.value.name;
194
- const ctx_type = ctx[name];
195
- if (ctx_type === undefined) {
196
- throw new Error(`Variable defined at ${printLocationValue(ir.value.location)} is not in scope at ${printLocationValue(ir.value.location)}`);
197
- }
198
- if (!isTypeValueEqual(ctx_type, ir.value.type)) {
199
- throw new Error(`Variable defined at ${printLocationValue(ir.value.location)} has type ${printTypeValue(ctx_type)} but expected ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
200
- }
201
180
  if (ir.value.mutable && ir.value.captured) {
202
181
  return (ctx) => ctx[name].x;
203
182
  }
@@ -206,9 +185,6 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
206
185
  }
207
186
  }
208
187
  else if (ir.type === "Let") {
209
- if (!isSubtypeValue(ir.value.value.value.type, ir.value.variable.value.type)) {
210
- throw new Error(`Cannot initialize a variable defined at ${printLocationValue(ir.value.variable.value.location)} of type ${printTypeValue(ir.value.variable.value.type)} to a value of type ${printTypeValue(ir.value.value.value.type)} at ${printLocationValue(ir.value.location)}`);
211
- }
212
188
  const compiled_statement = compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, false, compilingNodes);
213
189
  const name = ir.value.variable.value.name;
214
190
  ctx[name] = ir.value.variable.value.type;
@@ -243,15 +219,6 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
243
219
  }
244
220
  else if (ir.type === "Assign") {
245
221
  const name = ir.value.variable.value.name;
246
- if (ctx[name] === undefined) {
247
- throw new Error(`Attempting to reassign to a variable defined at ${printLocationValue(ir.value.variable.value.location)} which is not in scope at ${printLocationValue(ir.value.location)} at ${printLocationValue(ir.value.location)}`);
248
- }
249
- if (!isSubtypeValue(ir.value.value.value.type, ctx[name])) {
250
- throw new Error(`Cannot reassign to a variable defined at ${printLocationValue(ir.value.variable.value.location)} of type ${printTypeValue(ir.value.variable.value.type)} to a value of type ${printTypeValue(ir.value.value.value.type)} at ${printLocationValue(ir.value.location)}`);
251
- }
252
- if (!ir.value.variable.value.mutable) {
253
- throw new Error(`Cannot reassign const variable ${name} at ${printLocationValue(ir.value.location)}`);
254
- }
255
222
  const compiled_statement = compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, false, compilingNodes);
256
223
  if (ir.value.variable.value.mutable && ir.value.variable.value.captured) {
257
224
  if (ir.value.isAsync) {
@@ -293,58 +260,30 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
293
260
  }
294
261
  }
295
262
  else if (ir.type === "As") {
296
- if (!isSubtypeValue(ir.value.value.value.type, ir.value.type)) {
297
- throw new Error(`Cannot cast value of type ${printTypeValue(ir.value.value.value.type)} to type ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
298
- }
299
263
  // in dynamically typed runtimes like Javascript, this is a no-op
300
264
  // (for statically typed runtimes this assists in unifying types in branches)
301
265
  return compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, fresh_ctx, compilingNodes);
302
266
  }
303
267
  else if (ir.type === "UnwrapRecursive") {
304
- // TODO there seems to be cases of Wrap / Unwrap that could be optimized away during compilation
305
- if (!isTypeValueEqual(ir.value.value.value.type, ir.value.type)) {
306
- throw new Error(`Cannot unwrap recursive value of type ${printTypeValue(ir.value.value.value.type)} to type ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
307
- }
308
268
  // in dynamically typed runtimes like Javascript, this is a no-op
309
269
  // (for statically typed runtimes this assists in e.g. typing a reference or pointer)
310
270
  return compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, fresh_ctx, compilingNodes);
311
271
  }
312
272
  else if (ir.type === "WrapRecursive") {
313
- // TODO there seems to be cases of Wrap / Unwrap that could be optimized away during compilation
314
- if (!isSubtypeValue(ir.value.value.value.type, ir.value.type)) {
315
- throw new Error(`Cannot wrap value of type ${printTypeValue(ir.value.value.value.type)} to recursive type ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
316
- }
317
273
  // in dynamically typed runtimes like Javascript, this is a no-op
318
274
  // (for statically typed runtimes this assists in e.g. typing a heap allocation)
319
275
  return compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, false, compilingNodes);
320
276
  }
321
277
  else if (ir.type === "Function") {
322
- if (ir.value.type.type !== "Function") {
323
- throw new Error(`Expected Function type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
324
- }
325
278
  const ctx2 = {};
326
279
  for (const variable of ir.value.captures) {
327
- const ctx_type = ctx[variable.value.name];
328
- if (ctx_type === undefined) {
329
- throw new Error(`Captured variable defined at ${printLocationValue(variable.value.location)} is not in scope at ${printLocationValue(ir.value.location)}`);
330
- }
331
- if (!isTypeValueEqual(ctx_type, variable.value.type)) {
332
- throw new Error(`Captured variable defined at ${printLocationValue(variable.value.location)} has type ${printTypeValue(ctx_type)} but expected ${printTypeValue(variable.value.type)} at ${printLocationValue(ir.value.location)}`);
333
- }
334
280
  ctx2[variable.value.name] = variable.value.type;
335
281
  }
336
282
  for (const parameter of ir.value.parameters) {
337
283
  const parameter_name = parameter.value.name;
338
284
  ctx2[parameter_name] = parameter.value.type;
339
285
  }
340
- // Function return values are covariant
341
- // TODO what is the semantics here in a static language? Should we use `As` here?
342
- // Note: this requires unreachability analysis to be correct, since early terminating programs might have a final statement that produces never
343
- if (!isSubtypeValue(ir.value.body.value.type, ir.value.type.value.output)) {
344
- throw new Error(`Expected Function to return type ${printTypeValue(ir.value.type.value.output)}, got ${printTypeValue(ir.value.body.value.type)} at ${printLocationValue(ir.value.location)}`);
345
- }
346
286
  const compiled_body = compile_internal(ir.value.body, ctx2, platform, asyncPlatformFns, true, compilingNodes);
347
- const bodyIsAsync = ir.value.type.value.platforms.some(p => asyncPlatformFns.has(p));
348
287
  const capture_names = ir.value.captures.map(v => v.value.name);
349
288
  const parameter_names = ir.value.parameters.map(v => v.value.name);
350
289
  return (ctx) => {
@@ -352,47 +291,50 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
352
291
  for (const name of capture_names) {
353
292
  ctx2[name] = ctx[name];
354
293
  }
355
- // If body is async, create an async function that awaits internally
356
- if (bodyIsAsync) {
357
- return async (...args) => {
358
- const ctx3 = { ...ctx2 };
359
- parameter_names.forEach((name, i) => ctx3[name] = args[i]);
360
- return await compiled_body(ctx3);
361
- };
362
- }
363
- else {
364
- return (...args) => {
365
- const ctx3 = { ...ctx2 };
366
- parameter_names.forEach((name, i) => ctx3[name] = args[i]);
367
- return compiled_body(ctx3);
368
- };
369
- }
294
+ return (...args) => {
295
+ const ctx3 = { ...ctx2 };
296
+ parameter_names.forEach((name, i) => ctx3[name] = args[i]);
297
+ return compiled_body(ctx3);
298
+ };
370
299
  };
371
300
  }
372
- else if (ir.type === "Call") {
373
- if (ir.value.function.value.type.type !== "Function") {
374
- throw new Error(`Call expected Function, got ${printTypeValue(ir.value.function.value.type)} at ${printLocationValue(ir.value.location)}`);
301
+ else if (ir.type === "AsyncFunction") {
302
+ const ctx2 = {};
303
+ for (const variable of ir.value.captures) {
304
+ ctx2[variable.value.name] = variable.value.type;
375
305
  }
376
- if (ir.value.function.value.type.value.inputs.length !== ir.value.arguments.length) {
377
- throw new Error(`Function expected ${ir.value.function.value.type.value.inputs.length} arguments, got ${ir.value.arguments.length} at ${printLocationValue(ir.value.location)}`);
306
+ for (const parameter of ir.value.parameters) {
307
+ const parameter_name = parameter.value.name;
308
+ ctx2[parameter_name] = parameter.value.type;
378
309
  }
379
- for (const [i, t] of ir.value.function.value.type.value.inputs.entries()) {
380
- // TODO what is the semantics here in a static language? Should we use `As` here?
381
- if (!isSubtypeValue(t, ir.value.arguments[i].value.type)) {
382
- throw new Error(`Function expected argument ${i + 1} of type ${printTypeValue(t)}, got ${printTypeValue(ir.value.arguments[i].value.type)} at ${printLocationValue(ir.value.location)}`);
310
+ const compiled_body = compile_internal(ir.value.body, ctx2, platform, asyncPlatformFns, true, compilingNodes);
311
+ const capture_names = ir.value.captures.map(v => v.value.name);
312
+ const parameter_names = ir.value.parameters.map(v => v.value.name);
313
+ return (ctx) => {
314
+ const ctx2 = {};
315
+ for (const name of capture_names) {
316
+ ctx2[name] = ctx[name];
383
317
  }
384
- }
318
+ return (...args) => {
319
+ const ctx3 = { ...ctx2 };
320
+ parameter_names.forEach((name, i) => ctx3[name] = args[i]);
321
+ return compiled_body(ctx3); // Promise can pass through in tail position
322
+ };
323
+ };
324
+ }
325
+ else if (ir.type === "Call") {
385
326
  const compiled_f = compile_internal(ir.value.function, ctx, platform, asyncPlatformFns, false, compilingNodes);
386
327
  const compiled_args = ir.value.arguments.map(argument => compile_internal(argument, ctx, platform, asyncPlatformFns, false, compilingNodes));
387
328
  const location = ir.value.location;
388
- if (ir.value.function.value.type.value.isAsync) {
329
+ if (ir.value.isAsync) {
330
+ // need to await the arguments
389
331
  return async (ctx) => {
390
332
  try {
391
333
  const args = [];
392
334
  for (const compiled_arg of compiled_args) {
393
335
  args.push(await compiled_arg(ctx));
394
336
  }
395
- return (await compiled_f(ctx))(...args);
337
+ return compiled_f(ctx)(...args);
396
338
  }
397
339
  catch (e) {
398
340
  if (e instanceof ReturnException) {
@@ -440,9 +382,45 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
440
382
  };
441
383
  }
442
384
  }
385
+ else if (ir.type === "CallAsync") {
386
+ const compiled_f = compile_internal(ir.value.function, ctx, platform, asyncPlatformFns, false, compilingNodes);
387
+ const compiled_args = ir.value.arguments.map(argument => compile_internal(argument, ctx, platform, asyncPlatformFns, false, compilingNodes));
388
+ const location = ir.value.location;
389
+ return async (ctx) => {
390
+ try {
391
+ const args = [];
392
+ for (const compiled_arg of compiled_args) {
393
+ args.push(await compiled_arg(ctx));
394
+ }
395
+ return await compiled_f(ctx)(...args);
396
+ }
397
+ catch (e) {
398
+ if (e instanceof ReturnException) {
399
+ return e.value;
400
+ }
401
+ else if (e instanceof EastError) {
402
+ e.location.push(location); // The fact that we need to push the call location not the definition location means we need to handle this here
403
+ throw (e);
404
+ }
405
+ else if (e instanceof ContinueException) {
406
+ throw new Error(`continue failed to find label ${e.label} at ${printLocationValue(ir.value.location)}`);
407
+ }
408
+ else if (e instanceof BreakException) {
409
+ throw new Error(`break failed to find label ${e.label} at ${printLocationValue(ir.value.location)}`);
410
+ }
411
+ else {
412
+ throw (e);
413
+ }
414
+ }
415
+ };
416
+ }
443
417
  else if (ir.type === "IfElse") {
444
418
  const ifs = [];
419
+ let asyncPredicate = false;
445
420
  for (const { predicate, body } of ir.value.ifs) {
421
+ if (predicate.value.isAsync) {
422
+ asyncPredicate = true;
423
+ }
446
424
  ifs.push({
447
425
  predicate: compile_internal(predicate, ctx, platform, asyncPlatformFns, false, compilingNodes),
448
426
  body: compile_internal(body, Object.create(ctx), platform, asyncPlatformFns, true, compilingNodes),
@@ -450,14 +428,26 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
450
428
  }
451
429
  const else_body = compile_internal(ir.value.else_body, Object.create(ctx), platform, asyncPlatformFns, true, compilingNodes);
452
430
  if (ir.value.isAsync) {
453
- return async (ctx) => {
454
- for (const { predicate, body } of ifs) {
455
- if (await predicate(ctx)) {
456
- return body(Object.create(ctx));
431
+ if (asyncPredicate) {
432
+ return async (ctx) => {
433
+ for (const { predicate, body } of ifs) {
434
+ if (await predicate(ctx)) {
435
+ return await body(Object.create(ctx));
436
+ }
457
437
  }
458
- }
459
- return else_body(Object.create(ctx));
460
- };
438
+ return await else_body(Object.create(ctx));
439
+ };
440
+ }
441
+ else {
442
+ return async (ctx) => {
443
+ for (const { predicate, body } of ifs) {
444
+ if (predicate(ctx)) {
445
+ return await body(Object.create(ctx));
446
+ }
447
+ }
448
+ return await else_body(Object.create(ctx));
449
+ };
450
+ }
461
451
  }
462
452
  else {
463
453
  return (ctx) => {
@@ -482,12 +472,22 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
482
472
  compiled_cases[k] = compile_internal(body, ctx2, platform, asyncPlatformFns, true, compilingNodes);
483
473
  }
484
474
  if (ir.value.isAsync) {
485
- return async (ctx) => {
486
- const v = await compiled_variant(ctx);
487
- const ctx2 = Object.create(ctx);
488
- ctx2[data_names[v.type]] = v.value;
489
- return compiled_cases[v.type](ctx2);
490
- };
475
+ if (ir.value.variant.value.isAsync) {
476
+ return async (ctx) => {
477
+ const v = await compiled_variant(ctx);
478
+ const ctx2 = Object.create(ctx);
479
+ ctx2[data_names[v.type]] = v.value;
480
+ return await compiled_cases[v.type](ctx2);
481
+ };
482
+ }
483
+ else {
484
+ return async (ctx) => {
485
+ const v = compiled_variant(ctx);
486
+ const ctx2 = Object.create(ctx);
487
+ ctx2[data_names[v.type]] = v.value;
488
+ return await compiled_cases[v.type](ctx2);
489
+ };
490
+ }
491
491
  }
492
492
  else {
493
493
  return (ctx) => {
@@ -768,15 +768,10 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
768
768
  // The pattern of creating a new context (e.g. a function) and then immediately invoking a block (e.g. as the function body) is very common.
769
769
  // Here we avoid creating a second context when possible, as an optimization.
770
770
  if (fresh_ctx) {
771
- let last_type = variant("Null", null);
772
771
  const compiled_statements = [];
773
772
  for (const statement of ir.value.statements) {
774
773
  const compiled_statement = compile_internal(statement, ctx, platform, asyncPlatformFns, true, compilingNodes);
775
774
  compiled_statements.push(compiled_statement);
776
- last_type = statement.value.type;
777
- }
778
- if (!isSubtypeValue(last_type, ir.value.type)) {
779
- throw new Error(`Block evaluates to type ${printTypeValue(last_type)} but expected ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
780
775
  }
781
776
  if (ir.value.isAsync) {
782
777
  return async (ctx) => {
@@ -799,15 +794,10 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
799
794
  }
800
795
  else {
801
796
  const ctx2 = Object.create(ctx);
802
- let last_type = variant("Null", null);
803
797
  const compiled_statements = [];
804
798
  for (const statement of ir.value.statements) {
805
799
  const compiled_statement = compile_internal(statement, ctx2, platform, asyncPlatformFns, true, compilingNodes);
806
800
  compiled_statements.push(compiled_statement);
807
- last_type = statement.value.type;
808
- }
809
- if (!isSubtypeValue(last_type, ir.value.type)) {
810
- throw new Error(`Block evaluates to type ${printTypeValue(last_type)} but expected ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
811
801
  }
812
802
  if (ir.value.isAsync) {
813
803
  return async (ctx) => {
@@ -832,16 +822,6 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
832
822
  }
833
823
  }
834
824
  else if (ir.type === "GetField") {
835
- if (ir.value.struct.value.type.type !== "Struct") {
836
- throw new Error(`GetField expected Struct, got ${printTypeValue(ir.value.struct.value.type)} at ${printLocationValue(ir.value.location)}`);
837
- }
838
- const struct_field = expandTypeValue(ir.value.struct.value.type).value.find(f => f.name === ir.value.field);
839
- if (!struct_field) {
840
- throw new Error(`GetField field ${ir.value.field} not found in struct ${printTypeValue(ir.value.struct.value.type)} at ${printLocationValue(ir.value.location)}`);
841
- }
842
- if (!isTypeValueEqual(struct_field.type, ir.value.type)) {
843
- throw new Error(`GetField expected to have type ${printTypeValue(ir.value.type)}, got ${printTypeValue(struct_field.type)} at ${printLocationValue(ir.value.location)}`);
844
- }
845
825
  const struct = compile_internal(ir.value.struct, ctx, platform, asyncPlatformFns, false, compilingNodes);
846
826
  const field = ir.value.field;
847
827
  if (ir.value.isAsync) {
@@ -852,17 +832,7 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
852
832
  }
853
833
  }
854
834
  else if (ir.type === "Struct") {
855
- if (ir.value.type.type !== "Struct") {
856
- throw new Error(`Expected Struct output type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
857
- }
858
- const fields = ir.value.fields.map((f, i) => {
859
- const struct_field = expandTypeValue(ir.value.type).value[i];
860
- if (!struct_field || struct_field.name !== f.name) {
861
- throw new Error(`Struct field ${f.name} not found at position ${i} in struct type ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
862
- }
863
- if (!isSubtypeValue(f.value.value.type, struct_field.type)) {
864
- throw new Error(`Struct field ${f.name} expected to have type ${printTypeValue(struct_field.type)}, got ${printTypeValue(f.value.value.type)} at ${printLocationValue(ir.value.location)}`);
865
- }
835
+ const fields = ir.value.fields.map(f => {
866
836
  return compile_internal(f.value, ctx, platform, asyncPlatformFns, false, compilingNodes);
867
837
  });
868
838
  const keys = ir.value.fields.map(f => f.name);
@@ -880,17 +850,7 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
880
850
  }
881
851
  }
882
852
  else if (ir.type === "Variant") {
883
- if (ir.value.type.type !== "Variant") {
884
- throw new Error(`Expected Variant output type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
885
- }
886
853
  const k = ir.value.case;
887
- const variant_case = expandTypeValue(ir.value.type).value.find(c => c.name === k);
888
- if (!variant_case) {
889
- throw new Error(`Variant case ${k} not found in variant type ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
890
- }
891
- if (!isSubtypeValue(ir.value.value.value.type, variant_case.type)) {
892
- throw new Error(`Variant case ${k} expected to have type ${printTypeValue(variant_case.type)}, got ${printTypeValue(ir.value.value.value.type)} at ${printLocationValue(ir.value.location)}`);
893
- }
894
854
  const v = compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, false, compilingNodes);
895
855
  if (ir.value.isAsync) {
896
856
  return async (ctx) => variant(k, await v(ctx));
@@ -900,12 +860,6 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
900
860
  }
901
861
  }
902
862
  else if (ir.type === "NewRef") {
903
- if (ir.value.type.type !== "Ref") {
904
- throw new Error(`Expected Ref output type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
905
- }
906
- if (!isSubtypeValue(ir.value.value.value.type, expandTypeValue(ir.value.type).value)) {
907
- throw new Error(`NewRef value expected to have type ${printTypeValue(ir.value.type.value)}, got ${printTypeValue(ir.value.value.value.type)} at ${printLocationValue(ir.value.location)}`);
908
- }
909
863
  const value = compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, false, compilingNodes);
910
864
  if (ir.value.isAsync) {
911
865
  return async (ctx) => ref(await value(ctx));
@@ -915,13 +869,7 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
915
869
  }
916
870
  }
917
871
  else if (ir.type === "NewArray") {
918
- if (ir.value.type.type !== "Array") {
919
- throw new Error(`Expected Array output type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
920
- }
921
872
  const values = ir.value.values.map(a => {
922
- if (!isSubtypeValue(a.value.type, expandTypeValue(ir.value.type).value)) {
923
- throw new Error(`NewArray element expected to have type ${printTypeValue(ir.value.type.value)}, got ${printTypeValue(a.value.type)} at ${printLocationValue(ir.value.location)}`);
924
- }
925
873
  return compile_internal(a, ctx, platform, asyncPlatformFns, false, compilingNodes);
926
874
  });
927
875
  if (ir.value.isAsync) {
@@ -938,13 +886,7 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
938
886
  }
939
887
  }
940
888
  else if (ir.type === "NewSet") {
941
- if (ir.value.type.type !== "Set") {
942
- throw new Error(`Expected Set output type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
943
- }
944
889
  const values = ir.value.values.map(a => {
945
- if (!isSubtypeValue(a.value.type, expandTypeValue(ir.value.type).value)) {
946
- throw new Error(`NewSet element expected to have type ${printTypeValue(ir.value.type.value)}, got ${printTypeValue(a.value.type)} at ${printLocationValue(ir.value.location)}`);
947
- }
948
890
  return compile_internal(a, ctx, platform, asyncPlatformFns, false, compilingNodes);
949
891
  });
950
892
  const keyComparer = compareFor(ir.value.type.value);
@@ -962,16 +904,7 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
962
904
  }
963
905
  }
964
906
  else if (ir.type === "NewDict") {
965
- if (ir.value.type.type !== "Dict") {
966
- throw new Error(`Expected Dict output type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
967
- }
968
907
  const values = ir.value.values.map(({ key, value }) => {
969
- if (!isSubtypeValue(key.value.type, expandTypeValue(ir.value.type).value.key)) {
970
- throw new Error(`NewDict key expected to have type ${printTypeValue(ir.value.type.value.key)}, got ${printTypeValue(key.value.type)} at ${printLocationValue(ir.value.location)}`);
971
- }
972
- if (!isSubtypeValue(value.value.type, expandTypeValue(ir.value.type).value.value)) {
973
- throw new Error(`NewDict value expected to have type ${printTypeValue(ir.value.type.value.value)}, got ${printTypeValue(value.value.type)} at ${printLocationValue(ir.value.location)}`);
974
- }
975
908
  return [compile_internal(key, ctx, platform, asyncPlatformFns, false, compilingNodes), compile_internal(value, ctx, platform, asyncPlatformFns, false, compilingNodes)];
976
909
  });
977
910
  const keyComparer = compareFor(ir.value.type.value.key);
@@ -989,10 +922,6 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
989
922
  }
990
923
  }
991
924
  else if (ir.type === "Return") {
992
- if (ir.value.type.type !== "Never") {
993
- throw new Error(`Return must have Never type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
994
- }
995
- // TODO pipe through return type context so we can check it here
996
925
  const compiled_value = compile_internal(ir.value.value, ctx, platform, asyncPlatformFns, false, compilingNodes);
997
926
  if (ir.value.isAsync) {
998
927
  return async (ctx) => {
@@ -1006,61 +935,25 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
1006
935
  }
1007
936
  }
1008
937
  else if (ir.type === "Continue") {
1009
- if (ir.value.type.type !== "Never") {
1010
- throw new Error(`Continue must have Never type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
1011
- }
1012
938
  const label = ir.value.label;
1013
- // TODO pipe through local label context so we can check it here
1014
939
  return (_ctx) => {
1015
940
  throw new ContinueException(label.name);
1016
941
  };
1017
942
  }
1018
943
  else if (ir.type === "Break") {
1019
- if (ir.value.type.type !== "Never") {
1020
- throw new Error(`Break must have Never type, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
1021
- }
1022
944
  const label = ir.value.label;
1023
- // TODO pipe through local label context so we can check it here
1024
945
  return (_ctx) => {
1025
946
  throw new BreakException(label.name);
1026
947
  };
1027
948
  }
1028
949
  else if (ir.type === "Builtin") {
1029
- // TODO - I haven't dealt with builtins implementations yet
1030
- // check types match
1031
- let b = Builtins[ir.value.builtin];
1032
- if (b === undefined) {
1033
- throw new Error(`Builtin ${JSON.stringify(ir.value.builtin)} not found at ${printLocationValue(ir.value.location)}`);
1034
- }
1035
- if (b.type_parameters.length !== ir.value.type_parameters.length) {
1036
- throw new Error(`Builtin ${JSON.stringify(ir.value.builtin)} expected ${b.type_parameters.length} type parameters, got ${ir.value.type_parameters.length} type parameters at ${printLocationValue(ir.value.location)}`);
1037
- }
1038
- if (b.inputs.length !== ir.value.arguments.length) {
1039
- throw new Error(`Builtin ${JSON.stringify(ir.value.builtin)} expected ${b.inputs.length} arguments, got ${ir.value.arguments.length} arguments at ${printLocationValue(ir.value.location)}`);
1040
- }
1041
- const type_params = new Map(b.type_parameters.map((T, i) => [T, ir.value.type_parameters[i]]));
1042
950
  let argsAsync = false;
1043
951
  const args = ir.value.arguments.map((a, _i) => {
1044
- // TODO these checks seem to fail for some builtins - particularly comparisons with recursive types
1045
- // The issue appears to be that the type parameters in ir.value.type_parameters have been expanded too much
1046
- // The Expr machinery and UnionType are possibly places to investigate why the identity of types is getting lost (or else we might need a more structural, not identity-based, approach to toEastTypeValue)
1047
- // The failures here do not cause faults, but it might make life harder for a static compiler
1048
- // const builtin_arg_type = b.inputs[i]!;
1049
- // const expected_type = applyTypeParameters(toEastTypeValue(builtin_arg_type), type_params);
1050
- // TODO what is the semantics here in a static language? Should we use `As` here?
1051
- // if (!isSubtypeValue(a.value.type, expected_type)) {
1052
- // throw new Error(`Argument ${i+1} to builtin ${JSON.stringify(ir.value.builtin)} with type parameters [${ir.value.type_parameters.map(t => printTypeValue(t)).join(", ")}] expected to have type ${printTypeValue(expected_type)}, got ${printTypeValue(a.value.type)} at ${printLocationValue(a.value.location)}`)
1053
- // }
1054
952
  if (a.value.isAsync) {
1055
953
  argsAsync = true;
1056
954
  }
1057
955
  return compile_internal(a, ctx, platform, asyncPlatformFns, false, compilingNodes);
1058
956
  });
1059
- const expected_output_type = applyTypeParameters(typeof b.output === "string" ? b.output : toEastTypeValue(b.output), type_params);
1060
- // TODO what is the semantics here in a static language? Should we use `As` here? What about variance?
1061
- if (!isTypeValueEqual(ir.value.type, expected_output_type)) {
1062
- throw new Error(`Builtin ${JSON.stringify(ir.value.builtin)} output type expected to have type ${printTypeValue(expected_output_type)}, got ${printTypeValue(ir.value.type)} at ${printLocationValue(ir.value.location)}`);
1063
- }
1064
957
  // Special optimization for regex builtins with literal pattern/flags
1065
958
  if ((ir.value.builtin === "RegexContains" || ir.value.builtin === "RegexIndexOf" || ir.value.builtin === "RegexReplace") &&
1066
959
  ir.value.arguments[1]?.type === "Value" &&
@@ -1249,7 +1142,6 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
1249
1142
  }
1250
1143
  }
1251
1144
  else if (ir.type === "Platform") {
1252
- // TODO check types match (would require having the platform's expected types here)
1253
1145
  let argsAsync = false;
1254
1146
  const args = ir.value.arguments.map(a => {
1255
1147
  if (a.value.isAsync) {
@@ -1267,11 +1159,11 @@ function compileNodeInternal(ir, ctx, platform, asyncPlatformFns, fresh_ctx, com
1267
1159
  for (const a of args) {
1268
1160
  args_resolved.push(await a(ctx));
1269
1161
  }
1270
- return evaluator(...args_resolved);
1162
+ return evaluator(...args_resolved); // evaluator can return Promise unconditionally in tail position if the platform function is async
1271
1163
  };
1272
1164
  }
1273
1165
  else {
1274
- return (ctx) => evaluator(...args.map(a => a(ctx)));
1166
+ return (ctx) => evaluator(...args.map(a => a(ctx))); // evaluator can return Promise unconditionally in tail position if the platform function is async
1275
1167
  }
1276
1168
  }
1277
1169
  else {
@@ -3250,8 +3142,10 @@ export function applyTypeParameters(t, params) {
3250
3142
  return t;
3251
3143
  }
3252
3144
  else if (t.type === "Function") {
3253
- // TODO platforms?
3254
- return variant("Function", { inputs: t.value.inputs.map(i => applyTypeParameters(i, params)), output: applyTypeParameters(t.value.output, params), platforms: [] });
3145
+ return variant("Function", { inputs: t.value.inputs.map(i => applyTypeParameters(i, params)), output: applyTypeParameters(t.value.output, params) });
3146
+ }
3147
+ else if (t.type === "AsyncFunction") {
3148
+ return variant("AsyncFunction", { inputs: t.value.inputs.map(i => applyTypeParameters(i, params)), output: applyTypeParameters(t.value.output, params) });
3255
3149
  }
3256
3150
  else {
3257
3151
  throw new Error(`Unhandled type ${t.type}`);