@elaraai/east 0.0.1-beta.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.
Files changed (251) hide show
  1. package/LICENSE.md +682 -0
  2. package/README.md +276 -0
  3. package/dist/src/analyze.d.ts +95 -0
  4. package/dist/src/analyze.d.ts.map +1 -0
  5. package/dist/src/analyze.js +1110 -0
  6. package/dist/src/analyze.js.map +1 -0
  7. package/dist/src/ast.d.ts +263 -0
  8. package/dist/src/ast.d.ts.map +1 -0
  9. package/dist/src/ast.js +151 -0
  10. package/dist/src/ast.js.map +1 -0
  11. package/dist/src/ast_to_ir.d.ts +24 -0
  12. package/dist/src/ast_to_ir.d.ts.map +1 -0
  13. package/dist/src/ast_to_ir.js +834 -0
  14. package/dist/src/ast_to_ir.js.map +1 -0
  15. package/dist/src/builtins.d.ts +18 -0
  16. package/dist/src/builtins.d.ts.map +1 -0
  17. package/dist/src/builtins.js +1105 -0
  18. package/dist/src/builtins.js.map +1 -0
  19. package/dist/src/comparison.d.ts +28 -0
  20. package/dist/src/comparison.d.ts.map +1 -0
  21. package/dist/src/comparison.js +1017 -0
  22. package/dist/src/comparison.js.map +1 -0
  23. package/dist/src/compile.d.ts +22 -0
  24. package/dist/src/compile.d.ts.map +1 -0
  25. package/dist/src/compile.js +3260 -0
  26. package/dist/src/compile.js.map +1 -0
  27. package/dist/src/containers/ref.d.ts +106 -0
  28. package/dist/src/containers/ref.d.ts.map +1 -0
  29. package/dist/src/containers/ref.js +100 -0
  30. package/dist/src/containers/ref.js.map +1 -0
  31. package/dist/src/containers/sortedmap.d.ts +165 -0
  32. package/dist/src/containers/sortedmap.d.ts.map +1 -0
  33. package/dist/src/containers/sortedmap.js +237 -0
  34. package/dist/src/containers/sortedmap.js.map +1 -0
  35. package/dist/src/containers/sortedset.d.ts +185 -0
  36. package/dist/src/containers/sortedset.d.ts.map +1 -0
  37. package/dist/src/containers/sortedset.js +312 -0
  38. package/dist/src/containers/sortedset.js.map +1 -0
  39. package/dist/src/containers/variant.d.ts +131 -0
  40. package/dist/src/containers/variant.d.ts.map +1 -0
  41. package/dist/src/containers/variant.js +68 -0
  42. package/dist/src/containers/variant.js.map +1 -0
  43. package/dist/src/datetime_format/parse.d.ts +50 -0
  44. package/dist/src/datetime_format/parse.d.ts.map +1 -0
  45. package/dist/src/datetime_format/parse.js +908 -0
  46. package/dist/src/datetime_format/parse.js.map +1 -0
  47. package/dist/src/datetime_format/print.d.ts +35 -0
  48. package/dist/src/datetime_format/print.d.ts.map +1 -0
  49. package/dist/src/datetime_format/print.js +157 -0
  50. package/dist/src/datetime_format/print.js.map +1 -0
  51. package/dist/src/datetime_format/tokenize.d.ts +76 -0
  52. package/dist/src/datetime_format/tokenize.d.ts.map +1 -0
  53. package/dist/src/datetime_format/tokenize.js +271 -0
  54. package/dist/src/datetime_format/tokenize.js.map +1 -0
  55. package/dist/src/datetime_format/types.d.ts +99 -0
  56. package/dist/src/datetime_format/types.d.ts.map +1 -0
  57. package/dist/src/datetime_format/types.js +103 -0
  58. package/dist/src/datetime_format/types.js.map +1 -0
  59. package/dist/src/datetime_format/validate.d.ts +51 -0
  60. package/dist/src/datetime_format/validate.d.ts.map +1 -0
  61. package/dist/src/datetime_format/validate.js +208 -0
  62. package/dist/src/datetime_format/validate.js.map +1 -0
  63. package/dist/src/default.d.ts +21 -0
  64. package/dist/src/default.d.ts.map +1 -0
  65. package/dist/src/default.js +82 -0
  66. package/dist/src/default.js.map +1 -0
  67. package/dist/src/eastir.d.ts +33 -0
  68. package/dist/src/eastir.d.ts.map +1 -0
  69. package/dist/src/eastir.js +92 -0
  70. package/dist/src/eastir.js.map +1 -0
  71. package/dist/src/error.d.ts +13 -0
  72. package/dist/src/error.d.ts.map +1 -0
  73. package/dist/src/error.js +8 -0
  74. package/dist/src/error.js.map +1 -0
  75. package/dist/src/expr/array.d.ts +1711 -0
  76. package/dist/src/expr/array.d.ts.map +1 -0
  77. package/dist/src/expr/array.js +1805 -0
  78. package/dist/src/expr/array.js.map +1 -0
  79. package/dist/src/expr/ast.d.ts +17 -0
  80. package/dist/src/expr/ast.d.ts.map +1 -0
  81. package/dist/src/expr/ast.js +302 -0
  82. package/dist/src/expr/ast.js.map +1 -0
  83. package/dist/src/expr/blob.d.ts +141 -0
  84. package/dist/src/expr/blob.d.ts.map +1 -0
  85. package/dist/src/expr/blob.js +198 -0
  86. package/dist/src/expr/blob.js.map +1 -0
  87. package/dist/src/expr/block.d.ts +201 -0
  88. package/dist/src/expr/block.d.ts.map +1 -0
  89. package/dist/src/expr/block.js +1505 -0
  90. package/dist/src/expr/block.js.map +1 -0
  91. package/dist/src/expr/boolean.d.ts +207 -0
  92. package/dist/src/expr/boolean.d.ts.map +1 -0
  93. package/dist/src/expr/boolean.js +261 -0
  94. package/dist/src/expr/boolean.js.map +1 -0
  95. package/dist/src/expr/datetime.d.ts +544 -0
  96. package/dist/src/expr/datetime.d.ts.map +1 -0
  97. package/dist/src/expr/datetime.js +980 -0
  98. package/dist/src/expr/datetime.js.map +1 -0
  99. package/dist/src/expr/dict.d.ts +1242 -0
  100. package/dist/src/expr/dict.d.ts.map +1 -0
  101. package/dist/src/expr/dict.js +1492 -0
  102. package/dist/src/expr/dict.js.map +1 -0
  103. package/dist/src/expr/expr.d.ts +95 -0
  104. package/dist/src/expr/expr.d.ts.map +1 -0
  105. package/dist/src/expr/expr.js +171 -0
  106. package/dist/src/expr/expr.js.map +1 -0
  107. package/dist/src/expr/float.d.ts +357 -0
  108. package/dist/src/expr/float.d.ts.map +1 -0
  109. package/dist/src/expr/float.js +637 -0
  110. package/dist/src/expr/float.js.map +1 -0
  111. package/dist/src/expr/function.d.ts +46 -0
  112. package/dist/src/expr/function.d.ts.map +1 -0
  113. package/dist/src/expr/function.js +58 -0
  114. package/dist/src/expr/function.js.map +1 -0
  115. package/dist/src/expr/index.d.ts +450 -0
  116. package/dist/src/expr/index.d.ts.map +1 -0
  117. package/dist/src/expr/index.js +423 -0
  118. package/dist/src/expr/index.js.map +1 -0
  119. package/dist/src/expr/integer.d.ts +256 -0
  120. package/dist/src/expr/integer.d.ts.map +1 -0
  121. package/dist/src/expr/integer.js +311 -0
  122. package/dist/src/expr/integer.js.map +1 -0
  123. package/dist/src/expr/libs/array.d.ts +106 -0
  124. package/dist/src/expr/libs/array.d.ts.map +1 -0
  125. package/dist/src/expr/libs/array.js +140 -0
  126. package/dist/src/expr/libs/array.js.map +1 -0
  127. package/dist/src/expr/libs/blob.d.ts +42 -0
  128. package/dist/src/expr/libs/blob.d.ts.map +1 -0
  129. package/dist/src/expr/libs/blob.js +70 -0
  130. package/dist/src/expr/libs/blob.js.map +1 -0
  131. package/dist/src/expr/libs/datetime.d.ts +479 -0
  132. package/dist/src/expr/libs/datetime.d.ts.map +1 -0
  133. package/dist/src/expr/libs/datetime.js +624 -0
  134. package/dist/src/expr/libs/datetime.js.map +1 -0
  135. package/dist/src/expr/libs/dict.d.ts +66 -0
  136. package/dist/src/expr/libs/dict.d.ts.map +1 -0
  137. package/dist/src/expr/libs/dict.js +77 -0
  138. package/dist/src/expr/libs/dict.js.map +1 -0
  139. package/dist/src/expr/libs/float.d.ts +299 -0
  140. package/dist/src/expr/libs/float.d.ts.map +1 -0
  141. package/dist/src/expr/libs/float.js +564 -0
  142. package/dist/src/expr/libs/float.js.map +1 -0
  143. package/dist/src/expr/libs/integer.d.ts +228 -0
  144. package/dist/src/expr/libs/integer.d.ts.map +1 -0
  145. package/dist/src/expr/libs/integer.js +398 -0
  146. package/dist/src/expr/libs/integer.js.map +1 -0
  147. package/dist/src/expr/libs/set.d.ts +59 -0
  148. package/dist/src/expr/libs/set.d.ts.map +1 -0
  149. package/dist/src/expr/libs/set.js +69 -0
  150. package/dist/src/expr/libs/set.js.map +1 -0
  151. package/dist/src/expr/libs/string.d.ts +71 -0
  152. package/dist/src/expr/libs/string.d.ts.map +1 -0
  153. package/dist/src/expr/libs/string.js +75 -0
  154. package/dist/src/expr/libs/string.js.map +1 -0
  155. package/dist/src/expr/never.d.ts +15 -0
  156. package/dist/src/expr/never.d.ts.map +1 -0
  157. package/dist/src/expr/never.js +12 -0
  158. package/dist/src/expr/never.js.map +1 -0
  159. package/dist/src/expr/null.d.ts +15 -0
  160. package/dist/src/expr/null.d.ts.map +1 -0
  161. package/dist/src/expr/null.js +12 -0
  162. package/dist/src/expr/null.js.map +1 -0
  163. package/dist/src/expr/ref.d.ts +103 -0
  164. package/dist/src/expr/ref.d.ts.map +1 -0
  165. package/dist/src/expr/ref.js +131 -0
  166. package/dist/src/expr/ref.js.map +1 -0
  167. package/dist/src/expr/regex_validation.d.ts +25 -0
  168. package/dist/src/expr/regex_validation.d.ts.map +1 -0
  169. package/dist/src/expr/regex_validation.js +130 -0
  170. package/dist/src/expr/regex_validation.js.map +1 -0
  171. package/dist/src/expr/set.d.ts +1071 -0
  172. package/dist/src/expr/set.d.ts.map +1 -0
  173. package/dist/src/expr/set.js +1137 -0
  174. package/dist/src/expr/set.js.map +1 -0
  175. package/dist/src/expr/string.d.ts +414 -0
  176. package/dist/src/expr/string.d.ts.map +1 -0
  177. package/dist/src/expr/string.js +683 -0
  178. package/dist/src/expr/string.js.map +1 -0
  179. package/dist/src/expr/struct.d.ts +48 -0
  180. package/dist/src/expr/struct.d.ts.map +1 -0
  181. package/dist/src/expr/struct.js +65 -0
  182. package/dist/src/expr/struct.js.map +1 -0
  183. package/dist/src/expr/types.d.ts +68 -0
  184. package/dist/src/expr/types.d.ts.map +1 -0
  185. package/dist/src/expr/types.js +6 -0
  186. package/dist/src/expr/types.js.map +1 -0
  187. package/dist/src/expr/variant.d.ts +137 -0
  188. package/dist/src/expr/variant.d.ts.map +1 -0
  189. package/dist/src/expr/variant.js +105 -0
  190. package/dist/src/expr/variant.js.map +1 -0
  191. package/dist/src/fuzz.d.ts +80 -0
  192. package/dist/src/fuzz.d.ts.map +1 -0
  193. package/dist/src/fuzz.js +300 -0
  194. package/dist/src/fuzz.js.map +1 -0
  195. package/dist/src/index.d.ts +21 -0
  196. package/dist/src/index.d.ts.map +1 -0
  197. package/dist/src/index.js +21 -0
  198. package/dist/src/index.js.map +1 -0
  199. package/dist/src/internal.d.ts +36 -0
  200. package/dist/src/internal.d.ts.map +1 -0
  201. package/dist/src/internal.js +11 -0
  202. package/dist/src/internal.js.map +1 -0
  203. package/dist/src/ir.d.ts +1571 -0
  204. package/dist/src/ir.d.ts.map +1 -0
  205. package/dist/src/ir.js +56 -0
  206. package/dist/src/ir.js.map +1 -0
  207. package/dist/src/location.d.ts +48 -0
  208. package/dist/src/location.d.ts.map +1 -0
  209. package/dist/src/location.js +62 -0
  210. package/dist/src/location.js.map +1 -0
  211. package/dist/src/platform.d.ts +21 -0
  212. package/dist/src/platform.d.ts.map +1 -0
  213. package/dist/src/platform.js +8 -0
  214. package/dist/src/platform.js.map +1 -0
  215. package/dist/src/serialization/beast.d.ts +39 -0
  216. package/dist/src/serialization/beast.d.ts.map +1 -0
  217. package/dist/src/serialization/beast.js +555 -0
  218. package/dist/src/serialization/beast.js.map +1 -0
  219. package/dist/src/serialization/beast2-stream.d.ts +38 -0
  220. package/dist/src/serialization/beast2-stream.d.ts.map +1 -0
  221. package/dist/src/serialization/beast2-stream.js +665 -0
  222. package/dist/src/serialization/beast2-stream.js.map +1 -0
  223. package/dist/src/serialization/beast2.d.ts +41 -0
  224. package/dist/src/serialization/beast2.d.ts.map +1 -0
  225. package/dist/src/serialization/beast2.js +489 -0
  226. package/dist/src/serialization/beast2.js.map +1 -0
  227. package/dist/src/serialization/binary-utils.d.ts +151 -0
  228. package/dist/src/serialization/binary-utils.d.ts.map +1 -0
  229. package/dist/src/serialization/binary-utils.js +929 -0
  230. package/dist/src/serialization/binary-utils.js.map +1 -0
  231. package/dist/src/serialization/east.d.ts +84 -0
  232. package/dist/src/serialization/east.d.ts.map +1 -0
  233. package/dist/src/serialization/east.js +1802 -0
  234. package/dist/src/serialization/east.js.map +1 -0
  235. package/dist/src/serialization/index.d.ts +11 -0
  236. package/dist/src/serialization/index.d.ts.map +1 -0
  237. package/dist/src/serialization/index.js +12 -0
  238. package/dist/src/serialization/index.js.map +1 -0
  239. package/dist/src/serialization/json.d.ts +36 -0
  240. package/dist/src/serialization/json.d.ts.map +1 -0
  241. package/dist/src/serialization/json.js +849 -0
  242. package/dist/src/serialization/json.js.map +1 -0
  243. package/dist/src/type_of_type.d.ts +115 -0
  244. package/dist/src/type_of_type.d.ts.map +1 -0
  245. package/dist/src/type_of_type.js +362 -0
  246. package/dist/src/type_of_type.js.map +1 -0
  247. package/dist/src/types.d.ts +648 -0
  248. package/dist/src/types.d.ts.map +1 -0
  249. package/dist/src/types.js +1631 -0
  250. package/dist/src/types.js.map +1 -0
  251. package/package.json +87 -0
@@ -0,0 +1,1505 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ import { getPlatforms } from "../ast.js";
6
+ import { get_location, printLocation } from "../location.js";
7
+ import { FunctionType, isSubtype, NullType, printType, isTypeEqual, StringType, NeverType, VariantType, BooleanType, TypeUnion, IntegerType, StructType, ArrayType } from "../types.js";
8
+ import { AstSymbol, Expr } from "./expr.js";
9
+ import { NeverExpr } from "./never.js";
10
+ import { NullExpr } from "./null.js";
11
+ import { BooleanExpr } from "./boolean.js";
12
+ import { IntegerExpr } from "./integer.js";
13
+ import { FloatExpr } from "./float.js";
14
+ import { StringExpr } from "./string.js";
15
+ import { DateTimeExpr } from "./datetime.js";
16
+ import { BlobExpr } from "./blob.js";
17
+ import { ArrayExpr } from "./array.js";
18
+ import { SetExpr } from "./set.js";
19
+ import { DictExpr } from "./dict.js";
20
+ import { StructExpr } from "./struct.js";
21
+ import { VariantExpr } from "./variant.js";
22
+ import { createFunctionExpr } from "./function.js";
23
+ import { valueOrExprToAst, valueOrExprToAstTyped } from "./ast.js";
24
+ import { toEastTypeValue } from "../type_of_type.js";
25
+ import { RefExpr } from "./ref.js";
26
+ /** A factory function to help build `Expr` from AST.
27
+ * We inject this into each concrete `Expr` type so they can create new expressions recursively, without having circular dependencies between JavaScript modules.
28
+ */
29
+ export function fromAst(ast) {
30
+ const type = ast.type.type;
31
+ if (type === "Never") {
32
+ return new NeverExpr(ast, fromAst);
33
+ }
34
+ else if (type === "Null") {
35
+ return new NullExpr(ast, fromAst);
36
+ }
37
+ else if (type === "Boolean") {
38
+ return new BooleanExpr(ast, fromAst);
39
+ }
40
+ else if (type === "Integer") {
41
+ return new IntegerExpr(ast, fromAst);
42
+ }
43
+ else if (type === "Float") {
44
+ return new FloatExpr(ast, fromAst);
45
+ }
46
+ else if (type === "String") {
47
+ return new StringExpr(ast, fromAst);
48
+ }
49
+ else if (type === "DateTime") {
50
+ return new DateTimeExpr(ast, fromAst);
51
+ }
52
+ else if (type === "Blob") {
53
+ return new BlobExpr(ast, fromAst);
54
+ }
55
+ else if (type === "Ref") {
56
+ return new RefExpr(ast.type.value, ast, fromAst);
57
+ }
58
+ else if (type === "Array") {
59
+ return new ArrayExpr(ast.type.value, ast, fromAst);
60
+ }
61
+ else if (type === "Set") {
62
+ return new SetExpr(ast.type.key, ast, fromAst);
63
+ }
64
+ else if (type === "Dict") {
65
+ return new DictExpr(ast.type.key, ast.type.value, ast, fromAst);
66
+ }
67
+ else if (type === "Struct") {
68
+ return new StructExpr(ast.type.fields, ast, fromAst);
69
+ }
70
+ else if (type === "Variant") {
71
+ return new VariantExpr(ast.type.cases, ast, fromAst);
72
+ }
73
+ else if (type === "Recursive") {
74
+ // Automatically unwrap a recursive type to give access to the node data
75
+ const as_ast = {
76
+ ast_type: "UnwrapRecursive",
77
+ type: ast.type.node,
78
+ location: ast.location,
79
+ value: ast,
80
+ };
81
+ return fromAst(as_ast);
82
+ }
83
+ else if (type === "Function") {
84
+ return createFunctionExpr(ast.type.inputs, ast.type.output, ast, fromAst);
85
+ }
86
+ else {
87
+ throw new Error(`fromAst not implemented for type ${printType(ast.type)} at ${printLocation(ast.location)}`);
88
+ }
89
+ }
90
+ export function from(value, type) {
91
+ if (value instanceof Expr) {
92
+ if (type) {
93
+ const t = Expr.type(value);
94
+ if (!isSubtype(t, type)) {
95
+ throw new Error(`Expression expected to have type ${printType(type)} but got type ${printType(t)}`);
96
+ }
97
+ }
98
+ return value;
99
+ }
100
+ // Handle function case specially since it requires BlockBuilder
101
+ if (type && type.type === "Function") {
102
+ if (typeof value !== "function") {
103
+ throw new Error(`Expected function but got ${typeof value}`);
104
+ }
105
+ const { inputs, output } = type;
106
+ const input_variables = inputs.map(i => ({
107
+ ast_type: "Variable",
108
+ type: i,
109
+ location: get_location(3),
110
+ mutable: false,
111
+ }));
112
+ const $ = BlockBuilder(output);
113
+ const input_exprs = input_variables.map(fromAst);
114
+ const ret = value($, ...input_exprs);
115
+ const statements = $.statements;
116
+ if (ret !== undefined) {
117
+ if (ret instanceof Expr) {
118
+ // It is possible this expression is already the last statement.
119
+ // Input of the form `$ => $.xxx()` or `$ => { ...; return $.xxx(); }` will cause this
120
+ // So we filter out this case to avoid unintended repeated statements (and if users write an identical expression twice, they won't be `===`)
121
+ if (statements.length === 0 || statements[statements.length - 1] !== ret[AstSymbol]) {
122
+ statements.push(Expr.ast(ret));
123
+ }
124
+ }
125
+ else {
126
+ const retAst = valueOrExprToAstTyped(ret, output);
127
+ statements.push(retAst);
128
+ }
129
+ }
130
+ let body_ast;
131
+ if (isTypeEqual(output, NullType)) {
132
+ if (statements.length === 0) {
133
+ body_ast = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
134
+ }
135
+ else if (statements.length === 1 && isSubtype(statements[0].type, NullType)) {
136
+ body_ast = statements[0];
137
+ }
138
+ else {
139
+ if (!isSubtype(statements[statements.length - 1].type, NullType)) {
140
+ statements.push({ ast_type: "Value", type: NullType, location: get_location(3), value: null });
141
+ }
142
+ body_ast = {
143
+ ast_type: "Block",
144
+ type: statements[statements.length - 1].type,
145
+ location: get_location(3),
146
+ statements: statements,
147
+ };
148
+ }
149
+ }
150
+ else {
151
+ if (statements.length === 0) {
152
+ throw new Error(`Function expected output of type ${printType(output)}, but no function body or statements were provided`);
153
+ }
154
+ else if (statements.length === 1) {
155
+ if (!isSubtype(statements[0].type, output)) {
156
+ throw new Error(`Function expected output of type ${printType(output)}, but function body has type ${printType(statements[0].type)}`);
157
+ }
158
+ body_ast = statements[0];
159
+ }
160
+ else {
161
+ if (!isSubtype(statements[statements.length - 1].type, output)) {
162
+ throw new Error(`Function expected output of type ${printType(output)}, but function body has type ${printType(statements[statements.length - 1].type)}`);
163
+ }
164
+ body_ast = {
165
+ ast_type: "Block",
166
+ type: statements[statements.length - 1].type,
167
+ location: get_location(3),
168
+ statements: statements,
169
+ };
170
+ }
171
+ }
172
+ if (!isSubtype(body_ast.type, output)) {
173
+ throw new Error(`Expected function definition to return type ${printType(output)}, got ${printType(body_ast.type)}`);
174
+ }
175
+ const platforms = getPlatforms(body_ast);
176
+ if (type.platforms !== null) {
177
+ if (!platforms.every(p => type.platforms.includes(p))) {
178
+ throw new Error(`Function is not allowed to call platform functions: ${platforms.filter(p => !type.platforms.includes(p)).join(", ")}`);
179
+ }
180
+ }
181
+ const ast = {
182
+ ast_type: "Function",
183
+ type: FunctionType(inputs, output, platforms),
184
+ location: get_location(3),
185
+ parameters: input_variables,
186
+ body: body_ast,
187
+ };
188
+ return fromAst(ast);
189
+ }
190
+ const ast = type ? valueOrExprToAstTyped(value, type) : valueOrExprToAst(value);
191
+ return fromAst(ast);
192
+ }
193
+ export function func(input_types, output_type, body) {
194
+ const parameters = input_types.map(i => ({
195
+ ast_type: "Variable",
196
+ type: i,
197
+ location: get_location(2),
198
+ mutable: false,
199
+ }));
200
+ const $ = BlockBuilder(output_type ?? NeverType);
201
+ let ret = body($, ...parameters.map(fromAst));
202
+ const statements = $.statements;
203
+ if (output_type === undefined) {
204
+ if (ret === undefined) {
205
+ throw new Error(`When output_type is not specified, function body must return a value`);
206
+ }
207
+ const ret_ast = ret instanceof Expr ? Expr.ast(ret) : valueOrExprToAst(ret);
208
+ if (statements.length === 0 || statements[statements.length - 1] !== ret_ast) { // avoid duplicate statements
209
+ statements.push(ret_ast);
210
+ }
211
+ const ret_type = ret_ast.type;
212
+ let body_ast;
213
+ if (statements.length === 0) {
214
+ body_ast = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
215
+ }
216
+ else if (statements.length === 1) {
217
+ body_ast = statements[0];
218
+ }
219
+ else {
220
+ body_ast = {
221
+ ast_type: "Block",
222
+ type: statements[statements.length - 1].type,
223
+ location: get_location(2),
224
+ statements: statements,
225
+ };
226
+ }
227
+ if (!isSubtype(body_ast.type, ret_type)) {
228
+ throw new Error(`function expected to return a value of type ${printType(ret_type)}, got ${printType(body_ast.type)}`);
229
+ }
230
+ const ast = {
231
+ ast_type: "Function",
232
+ type: FunctionType(input_types, ret_type, getPlatforms(body_ast)),
233
+ location: get_location(2),
234
+ parameters: parameters,
235
+ body: body_ast,
236
+ };
237
+ return fromAst(ast);
238
+ }
239
+ else {
240
+ if (ret !== undefined) {
241
+ const ret_ast = valueOrExprToAstTyped(ret, output_type);
242
+ if (statements.length === 0 || statements[statements.length - 1] !== ret_ast) { // avoid duplicate statements
243
+ statements.push(ret_ast);
244
+ }
245
+ }
246
+ let body_ast;
247
+ if (statements.length === 0) {
248
+ body_ast = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
249
+ }
250
+ else if (statements.length === 1) {
251
+ body_ast = statements[0];
252
+ }
253
+ else {
254
+ body_ast = {
255
+ ast_type: "Block",
256
+ type: statements[statements.length - 1].type,
257
+ location: get_location(2),
258
+ statements: statements,
259
+ };
260
+ }
261
+ // This is an interesting one - do we have implicit returns? Do we need to track the absence of control flow?
262
+ // if (!isSubtype(expr.type, output_type)) {
263
+ // throw new Error(`func expected to return a value of type ${printType(output_type)}, got ${printType(expr.type)}`);
264
+ // }
265
+ const ast = {
266
+ ast_type: "Function",
267
+ type: FunctionType(input_types, output_type, getPlatforms(body_ast)),
268
+ location: get_location(2),
269
+ parameters: parameters,
270
+ body: body_ast,
271
+ };
272
+ return fromAst(ast);
273
+ }
274
+ }
275
+ /** Template string literal to create an East string dynamically from components
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * str`My name is ${name}`
280
+ * ```
281
+ */
282
+ export function str(strings, ...expressions) {
283
+ const location = get_location(2);
284
+ // For simple strings, e.g: str`abc`
285
+ if (strings.length === 1) {
286
+ return fromAst({
287
+ ast_type: "Value",
288
+ type: StringType,
289
+ location,
290
+ value: strings[0],
291
+ });
292
+ }
293
+ // In the below we optimize away components of `strings` that is empty, removing run-time overhead.
294
+ // To do so correctly we need to handle when the first component is empty
295
+ if (strings[0] === "") {
296
+ let ret;
297
+ if (typeof expressions[0] === "string") {
298
+ ret = {
299
+ ast_type: "Value",
300
+ type: StringType,
301
+ location,
302
+ value: expressions[0],
303
+ };
304
+ }
305
+ else if (isTypeEqual(Expr.type(expressions[0]), StringType)) {
306
+ ret = Expr.ast(expressions[0]);
307
+ }
308
+ else {
309
+ ret = {
310
+ ast_type: "Builtin",
311
+ type: StringType,
312
+ location,
313
+ builtin: "Print",
314
+ type_parameters: [Expr.type(expressions[0])],
315
+ arguments: [Expr.ast(expressions[0])],
316
+ };
317
+ }
318
+ if (strings[1].length > 0) {
319
+ ret = {
320
+ ast_type: "Builtin",
321
+ type: StringType,
322
+ location: ret.location,
323
+ builtin: "StringConcat",
324
+ type_parameters: [],
325
+ arguments: [ret, {
326
+ ast_type: "Value",
327
+ location: ret.location,
328
+ type: StringType,
329
+ value: strings[1],
330
+ }]
331
+ };
332
+ }
333
+ for (let i = 2; i < strings.length; i++) {
334
+ const expr = expressions[i - 1];
335
+ if (typeof expr === "string") {
336
+ ret = {
337
+ ast_type: "Builtin",
338
+ type: StringType,
339
+ location: ret.location,
340
+ builtin: "StringConcat",
341
+ type_parameters: [],
342
+ arguments: [ret, {
343
+ ast_type: "Value",
344
+ type: StringType,
345
+ location,
346
+ value: expr,
347
+ }]
348
+ };
349
+ }
350
+ else if (isTypeEqual(Expr.type(expr), StringType)) {
351
+ ret = {
352
+ ast_type: "Builtin",
353
+ type: StringType,
354
+ location: ret.location,
355
+ builtin: "StringConcat",
356
+ type_parameters: [],
357
+ arguments: [ret, Expr.ast(expr)]
358
+ };
359
+ }
360
+ else {
361
+ ret = {
362
+ ast_type: "Builtin",
363
+ type: StringType,
364
+ location: ret.location,
365
+ builtin: "StringConcat",
366
+ type_parameters: [],
367
+ arguments: [ret, {
368
+ ast_type: "Builtin",
369
+ type: StringType,
370
+ location: ret.location,
371
+ builtin: "Print",
372
+ type_parameters: [Expr.type(expr)],
373
+ arguments: [Expr.ast(expr)],
374
+ }]
375
+ };
376
+ }
377
+ if (strings[i].length > 0) {
378
+ ret = {
379
+ ast_type: "Builtin",
380
+ type: StringType,
381
+ location: ret.location,
382
+ builtin: "StringConcat",
383
+ type_parameters: [],
384
+ arguments: [ret, {
385
+ ast_type: "Value",
386
+ location: ret.location,
387
+ type: StringType,
388
+ value: strings[i],
389
+ }]
390
+ };
391
+ }
392
+ }
393
+ return fromAst(ret);
394
+ }
395
+ else {
396
+ let ret = {
397
+ ast_type: "Value",
398
+ type: StringType,
399
+ location: get_location(2),
400
+ value: strings[0],
401
+ };
402
+ for (let i = 1; i < strings.length; i++) {
403
+ const expr = expressions[i - 1];
404
+ if (typeof expr === "string") {
405
+ ret = {
406
+ ast_type: "Builtin",
407
+ type: StringType,
408
+ location: ret.location,
409
+ builtin: "StringConcat",
410
+ type_parameters: [],
411
+ arguments: [ret, {
412
+ ast_type: "Value",
413
+ type: StringType,
414
+ location,
415
+ value: expr,
416
+ }]
417
+ };
418
+ }
419
+ else if (isTypeEqual(Expr.type(expr), StringType)) {
420
+ ret = {
421
+ ast_type: "Builtin",
422
+ type: StringType,
423
+ location: ret.location,
424
+ builtin: "StringConcat",
425
+ type_parameters: [],
426
+ arguments: [ret, Expr.ast(expr)]
427
+ };
428
+ }
429
+ else {
430
+ ret = {
431
+ ast_type: "Builtin",
432
+ type: StringType,
433
+ location: ret.location,
434
+ builtin: "StringConcat",
435
+ type_parameters: [],
436
+ arguments: [ret, {
437
+ ast_type: "Builtin",
438
+ type: StringType,
439
+ location: ret.location,
440
+ builtin: "Print",
441
+ type_parameters: [Expr.type(expr)],
442
+ arguments: [Expr.ast(expr)],
443
+ }]
444
+ };
445
+ }
446
+ if (strings[i].length > 0) {
447
+ ret = {
448
+ ast_type: "Builtin",
449
+ type: StringType,
450
+ location: ret.location,
451
+ builtin: "StringConcat",
452
+ type_parameters: [],
453
+ arguments: [ret, {
454
+ ast_type: "Value",
455
+ location: ret.location,
456
+ type: StringType,
457
+ value: strings[i],
458
+ }]
459
+ };
460
+ }
461
+ }
462
+ return fromAst(ret);
463
+ }
464
+ }
465
+ /**
466
+ * Create an East block expression.
467
+ * The block may produce a value by returning an expressions.
468
+ */
469
+ export function block(body) {
470
+ // Technically, a block not included in a statement will increment the var count and loop count
471
+ const $ = BlockBuilder(NeverType);
472
+ const ret = body($);
473
+ const statements = $.statements;
474
+ let ret_type = NullType;
475
+ for (const stmt of statements) {
476
+ ret_type = stmt.type;
477
+ }
478
+ if (ret === undefined) {
479
+ // This ensures our TypeScript type inference and actual type inference concur
480
+ if (!isTypeEqual(ret_type, NeverType)) {
481
+ throw new Error(`block without return must have type ${printType(NeverType)}, got ${printType(ret_type)} - try returning the final expression or adding \`return null\``);
482
+ }
483
+ }
484
+ else {
485
+ const ret_ast = valueOrExprToAst(ret);
486
+ if (statements.length === 0 || statements[statements.length - 1] !== ret_ast) { // avoid duplicate statements
487
+ statements.push(ret_ast);
488
+ ret_type = ret_ast.type;
489
+ }
490
+ }
491
+ if (statements.length === 0) {
492
+ return fromAst({
493
+ ast_type: "Value",
494
+ location: get_location(2),
495
+ type: ret_type,
496
+ value: null,
497
+ });
498
+ }
499
+ else if (statements.length === 1) {
500
+ return fromAst(statements[0]);
501
+ }
502
+ else {
503
+ return fromAst({
504
+ ast_type: "Block",
505
+ location: get_location(2),
506
+ type: ret_type,
507
+ statements: statements,
508
+ });
509
+ }
510
+ }
511
+ /**
512
+ * Create an East error expression
513
+ */
514
+ export function error(message, location = get_location(2)) {
515
+ const messageAst = message instanceof Expr ? Expr.ast(message) : valueOrExprToAstTyped(message, StringType);
516
+ return fromAst({
517
+ ast_type: "Error",
518
+ type: NeverType,
519
+ message: messageAst,
520
+ location,
521
+ });
522
+ }
523
+ /** Create a match expression */
524
+ export function matchExpr(variant, handlers) {
525
+ if (Expr.type(variant).type !== "Variant") {
526
+ throw new Error(`match not defined over ${printType(Expr.type(variant))}`);
527
+ }
528
+ const cases_out = {};
529
+ let out_type = NeverType;
530
+ for (const [k, t] of Object.entries(Expr.type(variant).cases)) {
531
+ const handler = handlers[k];
532
+ const data_variable = {
533
+ ast_type: "Variable",
534
+ type: t,
535
+ location: get_location(2),
536
+ mutable: false,
537
+ };
538
+ const ast = handler === undefined ? valueOrExprToAstTyped(null, NullType) : block($ => handler($, fromAst(data_variable)))[AstSymbol];
539
+ out_type = TypeUnion(out_type, ast.type);
540
+ cases_out[k] = { variable: data_variable, body: ast };
541
+ }
542
+ // TODO do we care if extra case branches exist? (we don't even call the function passed in!)
543
+ return fromAst({
544
+ ast_type: "Match",
545
+ type: out_type,
546
+ location: get_location(2),
547
+ variant: Expr.ast(variant),
548
+ cases: cases_out,
549
+ });
550
+ }
551
+ /**
552
+ * Create an East try-catch expression
553
+ */
554
+ export function tryCatch(try_body, catch_body) {
555
+ const try_type = Expr.type(try_body);
556
+ // Create variables for the catch body
557
+ const message_variable = {
558
+ ast_type: "Variable",
559
+ type: StringType,
560
+ location: get_location(2),
561
+ mutable: false,
562
+ };
563
+ const stack_variable = {
564
+ ast_type: "Variable",
565
+ type: ArrayType(StructType({ filename: StringType, line: IntegerType, column: IntegerType })),
566
+ location: get_location(2),
567
+ mutable: false,
568
+ };
569
+ if (typeof catch_body !== "function") {
570
+ throw new Error(`tryCatch expected catch_body to be a function, got ${typeof catch_body}`);
571
+ }
572
+ const catch_body_expr = block($ => catch_body($, fromAst(message_variable), fromAst(stack_variable)));
573
+ const catch_body_ast = catch_body_expr[AstSymbol];
574
+ const catch_type = catch_body_ast.type;
575
+ const ret_type = TypeUnion(try_type, catch_type);
576
+ return fromAst({
577
+ ast_type: "TryCatch",
578
+ type: ret_type,
579
+ location: get_location(2),
580
+ try_body: Expr.ast(try_body),
581
+ catch_body: catch_body_ast,
582
+ message: message_variable,
583
+ stack: stack_variable,
584
+ });
585
+ }
586
+ /** Test if the first value is equal to the second. */
587
+ export function equal(left, right) {
588
+ const rightAst = valueOrExprToAstTyped(right, Expr.type(left));
589
+ return fromAst({
590
+ ast_type: "Builtin",
591
+ type: BooleanType,
592
+ location: get_location(2),
593
+ builtin: "Equal",
594
+ type_parameters: [Expr.type(left)],
595
+ arguments: [Expr.ast(left), rightAst],
596
+ });
597
+ }
598
+ /** Test if the first value is not equal to the second. */
599
+ export function notEqual(left, right) {
600
+ const rightAst = valueOrExprToAstTyped(right, Expr.type(left));
601
+ return fromAst({
602
+ ast_type: "Builtin",
603
+ type: BooleanType,
604
+ location: get_location(2),
605
+ builtin: "NotEqual",
606
+ type_parameters: [Expr.type(left)],
607
+ arguments: [Expr.ast(left), rightAst],
608
+ });
609
+ }
610
+ /** Test if the first value is less than the second. */
611
+ export function less(left, right) {
612
+ const rightAst = valueOrExprToAstTyped(right, Expr.type(left));
613
+ return fromAst({
614
+ ast_type: "Builtin",
615
+ type: BooleanType,
616
+ location: get_location(2),
617
+ builtin: "Less",
618
+ type_parameters: [Expr.type(left)],
619
+ arguments: [Expr.ast(left), rightAst],
620
+ });
621
+ }
622
+ /** Test if the first value is less than or equal to the second. */
623
+ export function lessEqual(left, right) {
624
+ const rightAst = valueOrExprToAstTyped(right, Expr.type(left));
625
+ return fromAst({
626
+ ast_type: "Builtin",
627
+ type: BooleanType,
628
+ location: get_location(2),
629
+ builtin: "LessEqual",
630
+ type_parameters: [Expr.type(left)],
631
+ arguments: [Expr.ast(left), rightAst],
632
+ });
633
+ }
634
+ /** Test if the first value is greater than the second. */
635
+ export function greater(left, right) {
636
+ const rightAst = valueOrExprToAstTyped(right, Expr.type(left));
637
+ return fromAst({
638
+ ast_type: "Builtin",
639
+ type: BooleanType,
640
+ location: get_location(2),
641
+ builtin: "Greater",
642
+ type_parameters: [Expr.type(left)],
643
+ arguments: [Expr.ast(left), rightAst],
644
+ });
645
+ }
646
+ /** Test if the first value is greater than or equal to the second. */
647
+ export function greaterEqual(left, right) {
648
+ const rightAst = valueOrExprToAstTyped(right, Expr.type(left));
649
+ return fromAst({
650
+ ast_type: "Builtin",
651
+ type: BooleanType,
652
+ location: get_location(2),
653
+ builtin: "GreaterEqual",
654
+ type_parameters: [Expr.type(left)],
655
+ arguments: [Expr.ast(left), rightAst],
656
+ });
657
+ }
658
+ /** Test if the first value is the same object the second.
659
+ * For mutable collections, such as arrays, this means that they are the same object in memory.
660
+ * For immutable data types, such as integers and strings, this is equivalent to equality.
661
+ */
662
+ export function is(left, right) {
663
+ const rightAst = valueOrExprToAstTyped(right, Expr.type(left));
664
+ return fromAst({
665
+ ast_type: "Builtin",
666
+ type: BooleanType,
667
+ location: get_location(2),
668
+ builtin: "Is",
669
+ type_parameters: [Expr.type(left)],
670
+ arguments: [Expr.ast(left), rightAst],
671
+ });
672
+ }
673
+ /** Print a value as a string. */
674
+ export function print(value) {
675
+ const valueAst = Expr.ast(value);
676
+ return fromAst({
677
+ ast_type: "Builtin",
678
+ type: StringType,
679
+ location: get_location(2),
680
+ builtin: "Print",
681
+ type_parameters: [valueAst.type],
682
+ arguments: [valueAst],
683
+ });
684
+ }
685
+ /** Create a callable helper to invoke a platform function.
686
+ *
687
+ * Platform functions provide access to external capabilities (logging, I/O, database access, etc.)
688
+ * that East code can call. They are defined by the platform when compiling and executing East IR.
689
+ *
690
+ * @param name - The name of the platform function (must match a function provided in the platform object)
691
+ * @param input_types - Array of input parameter types for the platform function
692
+ * @param output_type - The return type of the platform function
693
+ * @returns A callable function that creates Platform AST nodes when invoked
694
+ *
695
+ * @example
696
+ * ```ts
697
+ // Define a platform function helper for logging
698
+ const log = East.platform("log", [StringType], NullType);
699
+
700
+ // Use it in East code
701
+ const myFunction = East.function([], NullType, ($) => {
702
+ $(log("Hello, world!"));
703
+ $.return(null);
704
+ });
705
+
706
+ // Provide the implementation when compiling
707
+ const platform = [
708
+ log.implement(false, (message: string) => console.log(message)),
709
+ ];
710
+ const compiled = myFunction.toIR().compile(platform);
711
+ compiled(); // Logs "Hello, world!" to the console
712
+ * ```
713
+ */
714
+ export function platform(name, input_types, output_type) {
715
+ const fn = (...args) => {
716
+ if (args.length !== input_types.length) {
717
+ throw new Error(`Platform function ${name} expected ${input_types.length} arguments, got ${args.length}`);
718
+ }
719
+ const argAsts = args.map((arg, index) => {
720
+ const expectedType = input_types[index];
721
+ let ast = valueOrExprToAstTyped(arg, expectedType);
722
+ if (ast.type.type === "Never") {
723
+ throw new Error(`Platform function ${name} argument ${index} expected type ${printType(expectedType)}, got Never type`);
724
+ }
725
+ if (!isTypeEqual(ast.type, expectedType)) {
726
+ if (!isSubtype(ast.type, expectedType)) {
727
+ throw new Error(`Platform function ${name} argument ${index} expected type ${printType(expectedType)}, got ${printType(ast.type)}`);
728
+ }
729
+ // Insert implicit cast
730
+ ast = {
731
+ ast_type: "As",
732
+ type: expectedType,
733
+ location: get_location(2),
734
+ value: ast,
735
+ };
736
+ }
737
+ return ast;
738
+ });
739
+ return fromAst({
740
+ ast_type: "Platform",
741
+ type: output_type,
742
+ location: get_location(2),
743
+ name: name,
744
+ arguments: argAsts,
745
+ });
746
+ };
747
+ fn.implement = (fnImpl) => {
748
+ // Convert types from EastType to EastTypeValue once at implementation time
749
+ const inputsAsValues = input_types.map(t => toEastTypeValue(t));
750
+ const outputAsValue = toEastTypeValue(output_type);
751
+ if (output_type.type === "Null") {
752
+ return {
753
+ name,
754
+ inputs: inputsAsValues,
755
+ output: outputAsValue,
756
+ type: 'sync',
757
+ fn: (...args) => { fnImpl(...args); return null; },
758
+ };
759
+ }
760
+ else {
761
+ return {
762
+ name,
763
+ inputs: inputsAsValues,
764
+ output: outputAsValue,
765
+ type: 'sync',
766
+ fn: fnImpl,
767
+ };
768
+ }
769
+ };
770
+ fn.implementAsync = (fnImpl) => {
771
+ // Convert types from EastType to EastTypeValue once at implementation time
772
+ const inputsAsValues = input_types.map(t => toEastTypeValue(t));
773
+ const outputAsValue = toEastTypeValue(output_type);
774
+ if (output_type.type === "Null") {
775
+ return {
776
+ name,
777
+ inputs: inputsAsValues,
778
+ output: outputAsValue,
779
+ type: 'async',
780
+ fn: async (...args) => { await fnImpl(...args); return null; },
781
+ };
782
+ }
783
+ else {
784
+ return {
785
+ name,
786
+ inputs: inputsAsValues,
787
+ output: outputAsValue,
788
+ type: 'async',
789
+ fn: fnImpl,
790
+ };
791
+ }
792
+ };
793
+ return fn;
794
+ }
795
+ export const BlockBuilder = (return_type) => {
796
+ const statements = [];
797
+ const $ = ((expr) => {
798
+ const ast = Expr.ast(expr);
799
+ statements.push(ast);
800
+ return expr;
801
+ });
802
+ $.statements = statements;
803
+ $.type = () => {
804
+ if (statements.length === 0) {
805
+ return NullType;
806
+ }
807
+ else {
808
+ return statements[statements.length - 1].type;
809
+ }
810
+ };
811
+ $.const = ((value, type) => {
812
+ if (isTypeEqual($.type(), NeverType)) {
813
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
814
+ }
815
+ let ast;
816
+ if (value instanceof Expr) {
817
+ if (type && !isSubtype(Expr.type(value), type)) {
818
+ throw new Error(`Expression expected to have type ${printType(type)} but got type ${printType(Expr.type(value))}`);
819
+ }
820
+ ast = Expr.ast(value);
821
+ }
822
+ else {
823
+ ast = type ? valueOrExprToAstTyped(value, type) : valueOrExprToAst(value);
824
+ }
825
+ const variable = {
826
+ ast_type: "Variable",
827
+ type: type ?? ast.type,
828
+ location: get_location(2),
829
+ mutable: false,
830
+ };
831
+ statements.push({
832
+ ast_type: "Let",
833
+ type: NullType,
834
+ location: get_location(2),
835
+ variable,
836
+ value: ast,
837
+ });
838
+ return fromAst(variable);
839
+ });
840
+ $.let = ((value, type) => {
841
+ if (isTypeEqual($.type(), NeverType)) {
842
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
843
+ }
844
+ let ast;
845
+ if (value instanceof Expr) {
846
+ if (type && !isSubtype(Expr.type(value), type)) {
847
+ throw new Error(`Expression expected to have type ${printType(type)} but got type ${printType(Expr.type(value))}`);
848
+ }
849
+ ast = Expr.ast(value);
850
+ }
851
+ else {
852
+ ast = type ? valueOrExprToAstTyped(value, type) : valueOrExprToAst(value);
853
+ }
854
+ const variable = {
855
+ ast_type: "Variable",
856
+ type: type ?? ast.type,
857
+ location: get_location(2),
858
+ mutable: true,
859
+ };
860
+ statements.push({
861
+ ast_type: "Let",
862
+ type: NullType,
863
+ location: get_location(2),
864
+ variable,
865
+ value: ast,
866
+ });
867
+ return fromAst(variable);
868
+ });
869
+ $.assign = ((variable, value) => {
870
+ if (isTypeEqual($.type(), NeverType)) {
871
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
872
+ }
873
+ const v = Expr.ast(variable);
874
+ if (v.ast_type !== "Variable") {
875
+ throw new Error("Can only assign to a variable");
876
+ }
877
+ if (!v.mutable) {
878
+ throw new Error(`Cannot assign to variable defined at ${printLocation(v.location)} defined as const`);
879
+ }
880
+ const ast_value = value instanceof Expr ? Expr.ast(value) : valueOrExprToAstTyped(value, v.type);
881
+ const ast = {
882
+ ast_type: "Assign",
883
+ type: NullType,
884
+ location: get_location(2),
885
+ variable: v,
886
+ value: ast_value,
887
+ };
888
+ statements.push(ast);
889
+ return fromAst(ast);
890
+ });
891
+ // TODO if/when we introduce recursion, can we somehow get benefit from the typing?
892
+ $.return = (expr) => {
893
+ if (isTypeEqual($.type(), NeverType)) {
894
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
895
+ }
896
+ const expAst = expr instanceof Expr ? Expr.ast(expr) : valueOrExprToAst(expr);
897
+ if (!isSubtype(expAst.type, return_type)) {
898
+ throw new Error(`Return expected to have type ${printType(return_type)}, got ${printType(expAst.type)}`);
899
+ }
900
+ const ast = {
901
+ ast_type: "Return",
902
+ type: NeverType,
903
+ location: get_location(2),
904
+ value: expAst
905
+ };
906
+ statements.push(ast);
907
+ return fromAst(ast);
908
+ };
909
+ $.break = (label) => {
910
+ if (isTypeEqual($.type(), NeverType)) {
911
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
912
+ }
913
+ const ast = {
914
+ ast_type: "Break",
915
+ type: NeverType,
916
+ location: get_location(2),
917
+ label,
918
+ };
919
+ statements.push(ast);
920
+ return fromAst(ast);
921
+ };
922
+ $.continue = (label) => {
923
+ if (isTypeEqual($.type(), NeverType)) {
924
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
925
+ }
926
+ const ast = {
927
+ ast_type: "Continue",
928
+ type: NeverType,
929
+ location: get_location(2),
930
+ label,
931
+ };
932
+ statements.push(ast);
933
+ return fromAst(ast);
934
+ };
935
+ $.if = (predicate, true_branch) => {
936
+ if (isTypeEqual($.type(), NeverType)) {
937
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
938
+ }
939
+ const predicateAst = predicate instanceof Expr ? Expr.ast(predicate) : valueOrExprToAstTyped(predicate, BooleanType);
940
+ if (predicateAst.type.type !== "Boolean") {
941
+ throw new Error(`if predicate expected to have type Boolean, got ${printType(predicateAst.type)}`);
942
+ }
943
+ predicate = fromAst(predicateAst);
944
+ const $_true = BlockBuilder(return_type);
945
+ const ret_true = true_branch($_true);
946
+ const true_stmts = $_true.statements;
947
+ if (ret_true !== undefined) {
948
+ const ret_ast = valueOrExprToAst(ret_true);
949
+ if (true_stmts.length === 0 || true_stmts[true_stmts.length - 1] !== ret_ast) { // avoid duplicate statements
950
+ true_stmts.push(ret_ast);
951
+ }
952
+ }
953
+ let true_ast;
954
+ if (true_stmts.length === 0) {
955
+ true_ast = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
956
+ }
957
+ else if (true_stmts.length === 1 && isSubtype(true_stmts[0].type, NullType)) {
958
+ true_ast = true_stmts[0];
959
+ }
960
+ else {
961
+ if (!isSubtype(true_stmts[true_stmts.length - 1].type, NullType)) {
962
+ true_stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
963
+ }
964
+ true_ast = {
965
+ ast_type: "Block",
966
+ type: true_stmts[true_stmts.length - 1].type,
967
+ location: get_location(2),
968
+ statements: true_stmts,
969
+ };
970
+ }
971
+ const if_else_ast = {
972
+ ast_type: "IfElse",
973
+ type: NullType,
974
+ location: get_location(2),
975
+ ifs: [{
976
+ predicate: Expr.ast(predicate),
977
+ body: true_ast,
978
+ }],
979
+ else_body: { ast_type: "Value", type: NullType, location: get_location(2), value: null },
980
+ };
981
+ statements.push(if_else_ast);
982
+ return new IfElseExpr(if_else_ast, return_type);
983
+ };
984
+ $.match = (variant, cases) => {
985
+ if (isTypeEqual($.type(), NeverType)) {
986
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
987
+ }
988
+ if (Expr.type(variant).type !== "Variant") {
989
+ throw new Error(`match not defined over ${printType(Expr.type(variant))}`);
990
+ }
991
+ const cases_out = {};
992
+ let out_type = NullType;
993
+ for (const [k, t] of Object.entries(Expr.type(variant).cases)) {
994
+ const f = cases[k];
995
+ const data_variable = {
996
+ ast_type: "Variable",
997
+ type: t,
998
+ location: get_location(2),
999
+ mutable: false,
1000
+ };
1001
+ if (f === undefined) {
1002
+ cases_out[k] = { variable: data_variable, body: { ast_type: "Value", type: NullType, location: get_location(2), value: null } };
1003
+ }
1004
+ else {
1005
+ const data = fromAst(data_variable);
1006
+ const $ = BlockBuilder(return_type);
1007
+ const ret = f($, data);
1008
+ const stmts = $.statements;
1009
+ if (ret !== undefined) {
1010
+ const ret_ast = valueOrExprToAst(ret);
1011
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1012
+ stmts.push(ret_ast);
1013
+ }
1014
+ }
1015
+ let expr;
1016
+ if (stmts.length === 0) {
1017
+ expr = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
1018
+ }
1019
+ else if (stmts.length === 1 && isSubtype(stmts[0].type, NullType)) {
1020
+ expr = stmts[0];
1021
+ }
1022
+ else {
1023
+ if (!isSubtype(stmts[stmts.length - 1].type, NullType)) {
1024
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1025
+ }
1026
+ expr = {
1027
+ ast_type: "Block",
1028
+ type: stmts[stmts.length - 1].type,
1029
+ location: get_location(2),
1030
+ statements: stmts,
1031
+ };
1032
+ }
1033
+ cases_out[k] = { variable: data_variable, body: expr };
1034
+ }
1035
+ }
1036
+ // TODO do we care if extra case branches exist? (we don't even call the function passed in!)
1037
+ const ast = {
1038
+ ast_type: "Match",
1039
+ type: out_type,
1040
+ location: get_location(2),
1041
+ variant: Expr.ast(variant),
1042
+ cases: cases_out,
1043
+ };
1044
+ statements.push(ast);
1045
+ return fromAst(ast);
1046
+ };
1047
+ $.while = (predicate, body) => {
1048
+ if (isTypeEqual($.type(), NeverType)) {
1049
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
1050
+ }
1051
+ const predicateAst = predicate instanceof Expr ? Expr.ast(predicate) : valueOrExprToAstTyped(predicate, BooleanType);
1052
+ if (predicateAst.type.type !== "Boolean") {
1053
+ throw new Error(`while predicate expected to have type Boolean, got ${printType(predicateAst.type)}`);
1054
+ }
1055
+ const label = { location: get_location(2) };
1056
+ const $_body = BlockBuilder(return_type);
1057
+ const ret = body($_body, label);
1058
+ const stmts = $_body.statements;
1059
+ if (ret !== undefined) {
1060
+ const ret_ast = valueOrExprToAst(ret);
1061
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1062
+ stmts.push(ret_ast);
1063
+ }
1064
+ }
1065
+ let expr;
1066
+ if (stmts.length === 0) {
1067
+ expr = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
1068
+ }
1069
+ else if (stmts.length === 1 && isSubtype(stmts[0].type, NullType)) {
1070
+ expr = stmts[0];
1071
+ }
1072
+ else {
1073
+ if (!isSubtype(stmts[stmts.length - 1].type, NullType)) {
1074
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1075
+ }
1076
+ expr = {
1077
+ ast_type: "Block",
1078
+ type: stmts[stmts.length - 1].type,
1079
+ location: get_location(2),
1080
+ statements: stmts,
1081
+ };
1082
+ }
1083
+ // Note: we could try to look for unconditional loops with NeverType bodies and make the whole statement NeverType
1084
+ // However, this would have a false positive on a loop which breaks itself out of the loop, which would continue execution!
1085
+ // That is, `while (true) { break }` is valid and continues execution after the loop.
1086
+ const ast = {
1087
+ ast_type: "While",
1088
+ type: NullType,
1089
+ location: get_location(2),
1090
+ predicate: predicateAst,
1091
+ label,
1092
+ body: expr,
1093
+ };
1094
+ statements.push(ast);
1095
+ return fromAst(ast);
1096
+ };
1097
+ $.for = ((collection, body) => {
1098
+ if (isTypeEqual($.type(), NeverType)) {
1099
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
1100
+ }
1101
+ if (collection instanceof ArrayExpr) {
1102
+ const value_variable = {
1103
+ ast_type: "Variable",
1104
+ type: Expr.type(collection).value,
1105
+ location: get_location(2),
1106
+ mutable: false,
1107
+ };
1108
+ const value = fromAst(value_variable);
1109
+ const key_variable = {
1110
+ ast_type: "Variable",
1111
+ type: IntegerType,
1112
+ location: get_location(2),
1113
+ mutable: false,
1114
+ };
1115
+ const key = fromAst(key_variable);
1116
+ const label = { location: get_location(2) };
1117
+ const $ = BlockBuilder(return_type);
1118
+ const ret = body($, value, key, label);
1119
+ const stmts = $.statements;
1120
+ if (ret !== undefined) {
1121
+ const ret_ast = valueOrExprToAst(ret);
1122
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1123
+ stmts.push(ret_ast);
1124
+ }
1125
+ }
1126
+ let expr;
1127
+ if (stmts.length === 0) {
1128
+ expr = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
1129
+ }
1130
+ else if (stmts.length === 1 && isSubtype(stmts[0].type, NullType)) {
1131
+ expr = stmts[0];
1132
+ }
1133
+ else {
1134
+ if (!isSubtype(stmts[stmts.length - 1].type, NullType)) {
1135
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1136
+ }
1137
+ expr = {
1138
+ ast_type: "Block",
1139
+ type: stmts[stmts.length - 1].type,
1140
+ location: get_location(2),
1141
+ statements: stmts,
1142
+ };
1143
+ }
1144
+ const ast = {
1145
+ ast_type: "ForArray",
1146
+ type: NullType,
1147
+ location: get_location(2),
1148
+ label,
1149
+ array: Expr.ast(collection),
1150
+ key: key_variable,
1151
+ value: value_variable,
1152
+ body: expr,
1153
+ };
1154
+ statements.push(ast);
1155
+ return fromAst(ast);
1156
+ }
1157
+ else if (collection instanceof SetExpr) {
1158
+ const key_variable = {
1159
+ ast_type: "Variable",
1160
+ type: Expr.type(collection).key,
1161
+ location: get_location(2),
1162
+ mutable: false,
1163
+ };
1164
+ const key = fromAst(key_variable);
1165
+ const label = { location: get_location(2) };
1166
+ const $ = BlockBuilder(return_type);
1167
+ const ret = body($, key, label);
1168
+ const stmts = $.statements;
1169
+ if (ret !== undefined) {
1170
+ const ret_ast = valueOrExprToAst(ret);
1171
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1172
+ stmts.push(ret_ast);
1173
+ }
1174
+ }
1175
+ let expr;
1176
+ if (stmts.length === 0) {
1177
+ expr = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
1178
+ }
1179
+ else if (stmts.length === 1 && isSubtype(stmts[0].type, NullType)) {
1180
+ expr = stmts[0];
1181
+ }
1182
+ else {
1183
+ if (!isSubtype(stmts[stmts.length - 1].type, NullType)) {
1184
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1185
+ }
1186
+ expr = {
1187
+ ast_type: "Block",
1188
+ type: stmts[stmts.length - 1].type,
1189
+ location: get_location(2),
1190
+ statements: stmts,
1191
+ };
1192
+ }
1193
+ const ast = {
1194
+ ast_type: "ForSet",
1195
+ type: NullType,
1196
+ location: get_location(2),
1197
+ label,
1198
+ set: Expr.ast(collection),
1199
+ key: key_variable,
1200
+ body: expr,
1201
+ };
1202
+ statements.push(ast);
1203
+ return fromAst(ast);
1204
+ }
1205
+ else if (collection instanceof DictExpr) {
1206
+ const value_variable = {
1207
+ ast_type: "Variable",
1208
+ type: Expr.type(collection).value,
1209
+ location: get_location(2),
1210
+ mutable: false,
1211
+ };
1212
+ const value = fromAst(value_variable);
1213
+ const key_variable = {
1214
+ ast_type: "Variable",
1215
+ type: Expr.type(collection).key,
1216
+ location: get_location(2),
1217
+ mutable: false,
1218
+ };
1219
+ const key = fromAst(key_variable);
1220
+ const label = { location: get_location(2) };
1221
+ const $ = BlockBuilder(return_type);
1222
+ const ret = body($, value, key, label);
1223
+ const stmts = $.statements;
1224
+ if (ret !== undefined) {
1225
+ const ret_ast = valueOrExprToAst(ret);
1226
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1227
+ stmts.push(ret_ast);
1228
+ }
1229
+ }
1230
+ let expr;
1231
+ if (stmts.length === 0) {
1232
+ expr = { ast_type: "Value", type: NullType, location: get_location(2), value: null };
1233
+ }
1234
+ else if (stmts.length === 1 && isSubtype(stmts[0].type, NullType)) {
1235
+ expr = stmts[0];
1236
+ }
1237
+ else {
1238
+ if (!isSubtype(stmts[stmts.length - 1].type, NullType)) {
1239
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1240
+ }
1241
+ expr = {
1242
+ ast_type: "Block",
1243
+ type: stmts[stmts.length - 1].type,
1244
+ location: get_location(2),
1245
+ statements: stmts,
1246
+ };
1247
+ }
1248
+ const ast = {
1249
+ ast_type: "ForDict",
1250
+ type: NullType,
1251
+ location: get_location(2),
1252
+ label,
1253
+ dict: Expr.ast(collection),
1254
+ key: key_variable,
1255
+ value: value_variable,
1256
+ body: expr,
1257
+ };
1258
+ statements.push(ast);
1259
+ return fromAst(ast);
1260
+ }
1261
+ else {
1262
+ throw new Error(`for not defined over ${printType(Expr.type(collection))} - you can only loop over arrays, sets and dictionaries`);
1263
+ }
1264
+ });
1265
+ $.error = (message, location) => {
1266
+ if (isTypeEqual($.type(), NeverType)) {
1267
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
1268
+ }
1269
+ if (location === undefined) {
1270
+ location = get_location(2);
1271
+ }
1272
+ const ast = {
1273
+ ast_type: "Error",
1274
+ type: NeverType,
1275
+ message: message instanceof Expr ? Expr.ast(message) : valueOrExprToAstTyped(message, StringType),
1276
+ location,
1277
+ };
1278
+ statements.push(ast);
1279
+ return fromAst(ast);
1280
+ };
1281
+ $.try = (body) => {
1282
+ if (isTypeEqual($.type(), NeverType)) {
1283
+ throw new Error(`Unreachable statement detected at ${printLocation(get_location(2))}`);
1284
+ }
1285
+ const $try = BlockBuilder(return_type);
1286
+ const ret = body($try);
1287
+ const try_stmts = $try.statements;
1288
+ if (ret !== undefined) {
1289
+ const ret_ast = valueOrExprToAst(ret);
1290
+ if (try_stmts.length === 0 || try_stmts[try_stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1291
+ try_stmts.push(ret_ast);
1292
+ }
1293
+ }
1294
+ if (try_stmts.length === 0 || !isSubtype(try_stmts[try_stmts.length - 1].type, NullType)) {
1295
+ try_stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1296
+ }
1297
+ let try_expr;
1298
+ if (try_stmts.length === 1) {
1299
+ try_expr = try_stmts[0];
1300
+ }
1301
+ else {
1302
+ try_expr = {
1303
+ ast_type: "Block",
1304
+ type: try_stmts[try_stmts.length - 1].type,
1305
+ location: get_location(2),
1306
+ statements: try_stmts,
1307
+ };
1308
+ }
1309
+ // Now define the variables available in the catch block
1310
+ const message_variable = {
1311
+ ast_type: "Variable",
1312
+ type: StringType,
1313
+ location: get_location(2),
1314
+ mutable: false,
1315
+ };
1316
+ const stack_variable = {
1317
+ ast_type: "Variable",
1318
+ type: ArrayType(StructType({ filename: StringType, line: IntegerType, column: IntegerType })),
1319
+ location: get_location(2),
1320
+ mutable: false,
1321
+ };
1322
+ const try_catch_ast = {
1323
+ ast_type: "TryCatch",
1324
+ type: NullType,
1325
+ location: get_location(2),
1326
+ try_body: try_expr,
1327
+ catch_body: { ast_type: "Value", type: NullType, location: get_location(2), value: null }, // will be updated later
1328
+ message: message_variable,
1329
+ stack: stack_variable,
1330
+ };
1331
+ statements.push(try_catch_ast);
1332
+ return new TryCatchExpr(try_catch_ast, message_variable, stack_variable, return_type);
1333
+ };
1334
+ return $;
1335
+ };
1336
+ /** A helper class to perform if-elseif-else control flow. */
1337
+ class IfElseExpr extends NullExpr {
1338
+ return_type;
1339
+ /** @internal */
1340
+ constructor(ast, return_type) {
1341
+ super(ast, fromAst);
1342
+ this.return_type = return_type;
1343
+ // Note: the AST has been initialized to `value(null)` for the else body
1344
+ // The idea is that the if method bellow will fill them after the fact via mutation
1345
+ }
1346
+ /** Define the logic to follow when the condition is false. */
1347
+ else(body) {
1348
+ const $ = BlockBuilder(this.return_type);
1349
+ const ret = body($);
1350
+ const stmts = $.statements;
1351
+ if (ret !== undefined) {
1352
+ const ret_ast = valueOrExprToAst(ret);
1353
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1354
+ stmts.push(ret_ast);
1355
+ }
1356
+ }
1357
+ if (stmts.length === 0 || !isSubtype(stmts[stmts.length - 1].type, NullType)) {
1358
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1359
+ }
1360
+ this[AstSymbol].else_body = stmts.length === 1 ? stmts[0] : {
1361
+ ast_type: "Block",
1362
+ type: stmts[stmts.length - 1].type,
1363
+ location: get_location(2),
1364
+ statements: stmts,
1365
+ };
1366
+ // propagate unreachable information
1367
+ let can_terminate = true;
1368
+ check: {
1369
+ for (const ifStmt of this[AstSymbol].ifs) {
1370
+ if (isTypeEqual(ifStmt.predicate.type, NeverType)) {
1371
+ can_terminate = false;
1372
+ break check;
1373
+ }
1374
+ else if (!isTypeEqual(ifStmt.body.type, NeverType)) {
1375
+ break check;
1376
+ }
1377
+ }
1378
+ if (isTypeEqual(this[AstSymbol].else_body.type, NeverType)) {
1379
+ can_terminate = false;
1380
+ }
1381
+ }
1382
+ if (!can_terminate) {
1383
+ this[AstSymbol].type = NeverType;
1384
+ }
1385
+ return fromAst(this[AstSymbol]);
1386
+ }
1387
+ /** Check for another predicate, and execute logic if it is true */
1388
+ elseIf(predicate, body) {
1389
+ const predicateAst = predicate instanceof Expr ? Expr.ast(predicate) : valueOrExprToAstTyped(predicate, BooleanType);
1390
+ if (predicateAst.type.type !== "Boolean") {
1391
+ throw new Error(`elseIf predicate expected to have type Boolean, got ${printType(predicateAst.type)}`);
1392
+ }
1393
+ predicate = fromAst(predicateAst);
1394
+ const $ = BlockBuilder(this.return_type);
1395
+ const ret = body($);
1396
+ const stmts = $.statements;
1397
+ if (ret !== undefined) {
1398
+ const ret_ast = valueOrExprToAst(ret);
1399
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1400
+ stmts.push(ret_ast);
1401
+ }
1402
+ }
1403
+ if (stmts.length === 0 || !isSubtype(stmts[stmts.length - 1].type, NullType)) {
1404
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1405
+ }
1406
+ this[AstSymbol].ifs.push({
1407
+ predicate: Expr.ast(predicate),
1408
+ body: stmts.length === 1 ? stmts[0] : {
1409
+ ast_type: "Block",
1410
+ type: stmts[stmts.length - 1].type,
1411
+ location: get_location(2),
1412
+ statements: stmts,
1413
+ },
1414
+ });
1415
+ return this;
1416
+ }
1417
+ }
1418
+ /** A helper class to perform try-catch control flow. */
1419
+ class TryCatchExpr extends NullExpr {
1420
+ message_variable;
1421
+ stack_variable;
1422
+ return_type;
1423
+ catchCalled = false;
1424
+ /** @internal */
1425
+ constructor(ast, message_variable, stack_variable, return_type) {
1426
+ super(ast, fromAst);
1427
+ this.message_variable = message_variable;
1428
+ this.stack_variable = stack_variable;
1429
+ this.return_type = return_type;
1430
+ // Note: the AST has been initialized to `value(null)` for the catch body
1431
+ // The idea is that the catch method bellow will fill it after the fact via mutation
1432
+ }
1433
+ /** Define the logic to follow when an error is caught. The error message and stack trace is provided. */
1434
+ catch(body) {
1435
+ if (this.catchCalled) {
1436
+ throw new Error(`Cannot call .catch() more than once on the same try block at ${printLocation(get_location(2))}`);
1437
+ }
1438
+ this.catchCalled = true;
1439
+ const $ = BlockBuilder(this.return_type);
1440
+ const ret = body($, fromAst(this.message_variable), fromAst(this.stack_variable));
1441
+ const stmts = $.statements;
1442
+ if (ret !== undefined) {
1443
+ const ret_ast = valueOrExprToAst(ret);
1444
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1445
+ stmts.push(ret_ast);
1446
+ }
1447
+ }
1448
+ if (stmts.length === 0 || !isSubtype(stmts[stmts.length - 1].type, NullType)) {
1449
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1450
+ }
1451
+ this[AstSymbol].catch_body = stmts.length === 1 ? stmts[0] : {
1452
+ ast_type: "Block",
1453
+ type: stmts[stmts.length - 1].type,
1454
+ location: get_location(2),
1455
+ statements: stmts,
1456
+ };
1457
+ // Propagate unreachable information
1458
+ if (isTypeEqual(this[AstSymbol].try_body.type, NeverType) && isTypeEqual(this[AstSymbol].catch_body.type, NeverType)) {
1459
+ this[AstSymbol].type = NeverType;
1460
+ }
1461
+ return this;
1462
+ }
1463
+ /** Define cleanup logic that always executes, regardless of whether an error occurred. The finally block is for side effects only and does not affect the return type. */
1464
+ finally(body) {
1465
+ const $ = BlockBuilder(this.return_type);
1466
+ const ret = body($);
1467
+ const stmts = $.statements;
1468
+ if (ret !== undefined) {
1469
+ const ret_ast = valueOrExprToAst(ret);
1470
+ if (stmts.length === 0 || stmts[stmts.length - 1] !== ret_ast) { // avoid duplicate statements
1471
+ stmts.push(ret_ast);
1472
+ }
1473
+ }
1474
+ if (stmts.length === 0 || !isSubtype(stmts[stmts.length - 1].type, NullType)) {
1475
+ stmts.push({ ast_type: "Value", type: NullType, location: get_location(2), value: null });
1476
+ }
1477
+ this[AstSymbol].finally_body = stmts.length === 1 ? stmts[0] : {
1478
+ ast_type: "Block",
1479
+ type: stmts[stmts.length - 1].type,
1480
+ location: get_location(2),
1481
+ statements: stmts,
1482
+ };
1483
+ }
1484
+ }
1485
+ // Bind the static factory methods to the Expr class which were left undefined in expr.ts
1486
+ Object.assign(Expr, {
1487
+ // Static factory methods - now simple functions
1488
+ fromAst,
1489
+ from,
1490
+ block,
1491
+ error,
1492
+ tryCatch,
1493
+ function: func,
1494
+ print,
1495
+ str,
1496
+ equal,
1497
+ notEqual,
1498
+ less,
1499
+ lessEqual,
1500
+ greater,
1501
+ greaterEqual,
1502
+ is,
1503
+ match: matchExpr
1504
+ });
1505
+ //# sourceMappingURL=block.js.map