@hyperscale0/hsx 1.0.0-beta.1 → 1.0.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 (255) hide show
  1. package/CHANGELOG.md +58 -9
  2. package/LICENSING.md +3 -3
  3. package/README.md +39 -149
  4. package/TRADEMARKS.md +4 -4
  5. package/bin/hsx.ts +2 -0
  6. package/dist/bin/hsx.js +2 -0
  7. package/dist/bin/hsx.js.map +1 -1
  8. package/dist/src/ast.d.ts +135 -10
  9. package/dist/src/ast.d.ts.map +1 -1
  10. package/dist/src/ast.js +24 -0
  11. package/dist/src/ast.js.map +1 -1
  12. package/dist/src/cli.d.ts +4 -1
  13. package/dist/src/cli.d.ts.map +1 -1
  14. package/dist/src/cli.js +172 -16
  15. package/dist/src/cli.js.map +1 -1
  16. package/dist/src/compile.d.ts +42 -7
  17. package/dist/src/compile.d.ts.map +1 -1
  18. package/dist/src/compile.js +82 -16
  19. package/dist/src/compile.js.map +1 -1
  20. package/dist/src/cost.d.ts +78 -0
  21. package/dist/src/cost.d.ts.map +1 -0
  22. package/dist/src/cost.js +369 -0
  23. package/dist/src/cost.js.map +1 -0
  24. package/dist/src/diagnostics.d.ts +11 -0
  25. package/dist/src/diagnostics.d.ts.map +1 -0
  26. package/dist/src/diagnostics.js +420 -0
  27. package/dist/src/diagnostics.js.map +1 -0
  28. package/dist/src/emit.d.ts +34 -0
  29. package/dist/src/emit.d.ts.map +1 -0
  30. package/dist/src/emit.js +137 -0
  31. package/dist/src/emit.js.map +1 -0
  32. package/dist/src/entry-overrides.d.ts +17 -3
  33. package/dist/src/entry-overrides.d.ts.map +1 -1
  34. package/dist/src/entry-overrides.js +180 -35
  35. package/dist/src/entry-overrides.js.map +1 -1
  36. package/dist/src/format.d.ts +10 -0
  37. package/dist/src/format.d.ts.map +1 -0
  38. package/dist/src/format.js +183 -0
  39. package/dist/src/format.js.map +1 -0
  40. package/dist/src/index.d.ts +11 -4
  41. package/dist/src/index.d.ts.map +1 -1
  42. package/dist/src/index.js +10 -3
  43. package/dist/src/index.js.map +1 -1
  44. package/dist/src/ir.d.ts +66 -0
  45. package/dist/src/ir.d.ts.map +1 -0
  46. package/dist/src/ir.js +15 -0
  47. package/dist/src/ir.js.map +1 -0
  48. package/dist/src/lex.d.ts +9 -1
  49. package/dist/src/lex.d.ts.map +1 -1
  50. package/dist/src/lex.js +50 -4
  51. package/dist/src/lex.js.map +1 -1
  52. package/dist/src/limits.d.ts +2 -3
  53. package/dist/src/limits.d.ts.map +1 -1
  54. package/dist/src/limits.js +2 -3
  55. package/dist/src/limits.js.map +1 -1
  56. package/dist/src/lsp/server.d.ts +32 -0
  57. package/dist/src/lsp/server.d.ts.map +1 -0
  58. package/dist/src/lsp/server.js +391 -0
  59. package/dist/src/lsp/server.js.map +1 -0
  60. package/dist/src/modules.d.ts +38 -0
  61. package/dist/src/modules.d.ts.map +1 -0
  62. package/dist/src/modules.js +368 -0
  63. package/dist/src/modules.js.map +1 -0
  64. package/dist/src/parse.d.ts.map +1 -1
  65. package/dist/src/parse.js +590 -17
  66. package/dist/src/parse.js.map +1 -1
  67. package/dist/src/std-bundle.d.ts +2 -0
  68. package/dist/src/std-bundle.d.ts.map +1 -0
  69. package/dist/src/std-bundle.js +88 -0
  70. package/dist/src/std-bundle.js.map +1 -0
  71. package/dist/src/std-library.d.ts +5 -0
  72. package/dist/src/std-library.d.ts.map +1 -0
  73. package/dist/src/std-library.js +9 -0
  74. package/dist/src/std-library.js.map +1 -0
  75. package/dist/src/typecheck.d.ts +8 -0
  76. package/dist/src/typecheck.d.ts.map +1 -0
  77. package/dist/src/typecheck.js +2958 -0
  78. package/dist/src/typecheck.js.map +1 -0
  79. package/dist/src/version.d.ts +4 -15
  80. package/dist/src/version.d.ts.map +1 -1
  81. package/dist/src/version.js +4 -15
  82. package/dist/src/version.js.map +1 -1
  83. package/docs/README.md +26 -0
  84. package/docs/guide/01-first-program.md +24 -0
  85. package/docs/guide/02-money.md +25 -0
  86. package/docs/guide/03-instruments.md +22 -0
  87. package/docs/guide/04-lifecycles.md +25 -0
  88. package/docs/guide/05-fees-and-splits.md +45 -0
  89. package/docs/guide/06-schedules.md +22 -0
  90. package/docs/guide/07-composition.md +46 -0
  91. package/docs/guide/08-writing-a-module.md +26 -0
  92. package/docs/guide/09-cost.md +24 -0
  93. package/docs/guide/10-diagnostics.md +27 -0
  94. package/docs/llms-full.txt +1739 -0
  95. package/docs/llms.txt +45 -0
  96. package/docs/playground.md +54 -0
  97. package/docs/reference/cli.md +35 -0
  98. package/docs/reference/diagnostics.md +619 -0
  99. package/docs/reference/grammar.md +39 -0
  100. package/docs/reference/std/advance.md +35 -0
  101. package/docs/reference/std/cancellable_booking.md +35 -0
  102. package/docs/reference/std/captured_payment.md +43 -0
  103. package/docs/reference/std/conditional_disbursement.md +33 -0
  104. package/docs/reference/std/credit_facility.md +35 -0
  105. package/docs/reference/std/held_payment.md +64 -0
  106. package/docs/reference/std/instant_transfer.md +30 -0
  107. package/docs/reference/std/metered.md +27 -0
  108. package/docs/reference/std/pooled_split.md +26 -0
  109. package/docs/reference/std/premium_forward.md +39 -0
  110. package/docs/reference/std/reconciled_payout.md +33 -0
  111. package/docs/reference/std/recurring_collection.md +25 -0
  112. package/docs/reference/std/rotating_pool.md +42 -0
  113. package/docs/reference/std/scheduled.md +51 -0
  114. package/docs/reference/std/security_deposit.md +46 -0
  115. package/docs/reference/std/settlement_batch.md +42 -0
  116. package/docs/reference/std/swap.md +46 -0
  117. package/docs/reference/std/threshold_pool.md +47 -0
  118. package/docs/reference/std/weighted_distribution.md +39 -0
  119. package/docs/reference/types.md +20 -0
  120. package/docs/reference/udl-output.md +13 -0
  121. package/docs/sessions/2026-09-02-two-instruments.hsx +21 -0
  122. package/docs/sessions/2026-09-02-two-instruments.md +41 -0
  123. package/examples/01-first-program/README.md +45 -0
  124. package/examples/01-first-program/tip-jar.hsx +17 -0
  125. package/examples/02-imports-and-archetypes/README.md +96 -0
  126. package/examples/02-imports-and-archetypes/photo-booth.hsx +37 -0
  127. package/examples/03-diagnostics/README.md +68 -0
  128. package/examples/03-diagnostics/corner-shop-fixed.hsx +22 -0
  129. package/examples/03-diagnostics/corner-shop.hsx +19 -0
  130. package/examples/04-complete-product/README.md +84 -0
  131. package/examples/04-complete-product/study-hall.hsx +67 -0
  132. package/examples/05-watch-club/README.md +9 -0
  133. package/examples/05-watch-club/watch-club.hsx +124 -0
  134. package/examples/README.md +18 -0
  135. package/examples/advance/README.md +3 -0
  136. package/examples/advance/advance.hsx +13 -0
  137. package/examples/advance/advance.udl +339 -0
  138. package/examples/cancellable_booking/README.md +3 -0
  139. package/examples/cancellable_booking/cancellable_booking.hsx +14 -0
  140. package/examples/cancellable_booking/cancellable_booking.udl +391 -0
  141. package/examples/captured_payment/README.md +3 -0
  142. package/examples/captured_payment/captured_payment.hsx +21 -0
  143. package/examples/captured_payment/captured_payment.udl +677 -0
  144. package/examples/conditional_disbursement/README.md +3 -0
  145. package/examples/conditional_disbursement/conditional_disbursement.hsx +17 -0
  146. package/examples/conditional_disbursement/conditional_disbursement.udl +412 -0
  147. package/examples/cost-table.json +386 -0
  148. package/examples/credit_facility/README.md +3 -0
  149. package/examples/credit_facility/credit_facility.hsx +27 -0
  150. package/examples/credit_facility/credit_facility.udl +1373 -0
  151. package/examples/held_payment/README.md +3 -0
  152. package/examples/held_payment/held_payment.hsx +13 -0
  153. package/examples/held_payment/held_payment.udl +507 -0
  154. package/examples/instant_transfer/README.md +3 -0
  155. package/examples/instant_transfer/instant_transfer.hsx +16 -0
  156. package/examples/instant_transfer/instant_transfer.udl +364 -0
  157. package/examples/metered/README.md +3 -0
  158. package/examples/metered/metered.hsx +13 -0
  159. package/examples/metered/metered.udl +247 -0
  160. package/examples/pooled_split/README.md +3 -0
  161. package/examples/pooled_split/pooled_split.hsx +15 -0
  162. package/examples/pooled_split/pooled_split.udl +425 -0
  163. package/examples/premium_forward/README.md +3 -0
  164. package/examples/premium_forward/premium_forward.hsx +19 -0
  165. package/examples/premium_forward/premium_forward.udl +594 -0
  166. package/examples/reconciled_payout/README.md +3 -0
  167. package/examples/reconciled_payout/reconciled_payout.hsx +13 -0
  168. package/examples/reconciled_payout/reconciled_payout.udl +303 -0
  169. package/examples/recurring_collection/README.md +3 -0
  170. package/examples/recurring_collection/recurring_collection.hsx +21 -0
  171. package/examples/recurring_collection/recurring_collection.udl +1130 -0
  172. package/examples/rotating_pool/README.md +3 -0
  173. package/examples/rotating_pool/rotating_pool.hsx +18 -0
  174. package/examples/rotating_pool/rotating_pool.udl +4352 -0
  175. package/examples/scheduled/README.md +3 -0
  176. package/examples/scheduled/scheduled.hsx +12 -0
  177. package/examples/scheduled/scheduled.udl +335 -0
  178. package/examples/security_deposit/README.md +3 -0
  179. package/examples/security_deposit/security_deposit.hsx +21 -0
  180. package/examples/security_deposit/security_deposit.udl +293 -0
  181. package/examples/settlement_batch/README.md +3 -0
  182. package/examples/settlement_batch/settlement_batch.hsx +19 -0
  183. package/examples/settlement_batch/settlement_batch.udl +753 -0
  184. package/examples/swap/README.md +3 -0
  185. package/examples/swap/swap.hsx +19 -0
  186. package/examples/swap/swap.udl +876 -0
  187. package/examples/threshold_pool/README.md +3 -0
  188. package/examples/threshold_pool/threshold_pool.hsx +16 -0
  189. package/examples/threshold_pool/threshold_pool.udl +667 -0
  190. package/examples/weighted_distribution/README.md +3 -0
  191. package/examples/weighted_distribution/weighted_distribution.hsx +20 -0
  192. package/examples/weighted_distribution/weighted_distribution.udl +337 -0
  193. package/package.json +27 -6
  194. package/skills/hsx/SKILL.md +483 -0
  195. package/src/ast.ts +193 -9
  196. package/src/cli.ts +191 -20
  197. package/src/compile.ts +160 -23
  198. package/src/cost.ts +579 -0
  199. package/src/diagnostics.ts +442 -0
  200. package/src/emit.ts +201 -0
  201. package/src/entry-overrides.ts +263 -48
  202. package/src/format.ts +261 -0
  203. package/src/index.ts +34 -4
  204. package/src/ir.ts +99 -0
  205. package/src/lex.ts +59 -4
  206. package/src/limits.ts +2 -3
  207. package/src/lsp/server.ts +460 -0
  208. package/src/modules.ts +446 -0
  209. package/src/parse.ts +655 -21
  210. package/src/std-bundle.ts +91 -0
  211. package/src/std-library.ts +12 -0
  212. package/src/typecheck.ts +3876 -0
  213. package/src/version.ts +4 -16
  214. package/std/SEMANTICS.md +130 -0
  215. package/std/settlements/advance.hsx +177 -0
  216. package/std/settlements/cancellable_booking.hsx +87 -0
  217. package/std/settlements/captured_payment.hsx +144 -0
  218. package/std/settlements/conditional_disbursement.hsx +185 -0
  219. package/std/settlements/credit_facility.hsx +167 -0
  220. package/std/settlements/held_payment.hsx +578 -0
  221. package/std/settlements/index.hsx +3 -0
  222. package/std/settlements/instant_transfer.hsx +190 -0
  223. package/std/settlements/metered.hsx +67 -0
  224. package/std/settlements/pooled_split.hsx +68 -0
  225. package/std/settlements/premium_forward.hsx +257 -0
  226. package/std/settlements/reconciled_payout.hsx +77 -0
  227. package/std/settlements/recurring_collection.hsx +27 -0
  228. package/std/settlements/rotating_pool.hsx +252 -0
  229. package/std/settlements/scheduled.hsx +641 -0
  230. package/std/settlements/security_deposit.hsx +166 -0
  231. package/std/settlements/settlement_batch.hsx +122 -0
  232. package/std/settlements/swap.hsx +1114 -0
  233. package/std/settlements/threshold_pool.hsx +221 -0
  234. package/std/settlements/weighted_distribution.hsx +141 -0
  235. package/dist/src/archetypes.d.ts +0 -17
  236. package/dist/src/archetypes.d.ts.map +0 -1
  237. package/dist/src/archetypes.js +0 -327
  238. package/dist/src/archetypes.js.map +0 -1
  239. package/dist/src/check.d.ts +0 -12
  240. package/dist/src/check.d.ts.map +0 -1
  241. package/dist/src/check.js +0 -1910
  242. package/dist/src/check.js.map +0 -1
  243. package/dist/src/lower.d.ts +0 -146
  244. package/dist/src/lower.d.ts.map +0 -1
  245. package/dist/src/lower.js +0 -4570
  246. package/dist/src/lower.js.map +0 -1
  247. package/dist/src/model.d.ts +0 -467
  248. package/dist/src/model.d.ts.map +0 -1
  249. package/dist/src/model.js +0 -15
  250. package/dist/src/model.js.map +0 -1
  251. package/spec/hsx-ir.schema.json +0 -920
  252. package/src/archetypes.ts +0 -340
  253. package/src/check.ts +0 -2938
  254. package/src/lower.ts +0 -5416
  255. package/src/model.ts +0 -519
package/dist/src/parse.js CHANGED
@@ -23,6 +23,20 @@ export function parseProgram(source) {
23
23
  program: { decls: [], span: { end: 0, start: 0 } },
24
24
  };
25
25
  }
26
+ const first = source.search(/\S/);
27
+ if (first >= 0 && source[first] === "{") {
28
+ return {
29
+ diagnostics: [
30
+ {
31
+ code: "HSX1014",
32
+ fix: "replace the JSON object with HSX declarations",
33
+ message: "JSON is not HSX",
34
+ span: { end: first + 1, start: first },
35
+ },
36
+ ],
37
+ program: { decls: [], span: { end: source.length, start: 0 } },
38
+ };
39
+ }
26
40
  const lexed = lex(source);
27
41
  const parser = new Parser(lexed.tokens, [...lexed.diagnostics]);
28
42
  const program = parser.parseProgram(source.length);
@@ -40,6 +54,10 @@ class Parser {
40
54
  parseProgram(sourceLength) {
41
55
  const decls = [];
42
56
  while (!this.at("eof")) {
57
+ if (this.atPunct(";")) {
58
+ this.advance();
59
+ continue;
60
+ }
43
61
  const decl = this.parseDecl();
44
62
  if (decl) {
45
63
  decls.push(decl);
@@ -50,33 +68,187 @@ class Parser {
50
68
  return { decls, span: { end: sourceLength, start: 0 } };
51
69
  }
52
70
  // --- Declarations --------------------------------------------------------
53
- parseDecl() {
71
+ parseDecl(exported = false) {
54
72
  const token = this.peek();
55
73
  if (token.kind !== "keyword" || token.text === "from") {
56
74
  this.error(token.span, token.kind === "eof"
57
75
  ? "the program ends unexpectedly"
58
- : `expected a declaration (program, import, party, asset, settlement, or port), found "${token.text}"`);
76
+ : `expected a declaration (module, program, use, expose, import, party, asset, subject, instrument, settlement, type, const, or port), found "${token.text}"`);
59
77
  return undefined;
60
78
  }
61
79
  switch (token.text) {
80
+ case "export":
81
+ this.advance();
82
+ return this.parseDecl(true);
83
+ case "module":
84
+ return this.parseModuleDecl(exported);
62
85
  case "program":
63
86
  return this.parseProgramDecl();
87
+ case "use":
88
+ return this.parseUseDecl();
89
+ case "expose":
90
+ return this.parseExposeDecl();
64
91
  case "import":
65
92
  return this.parseImportDecl();
66
93
  case "party":
67
94
  return this.parsePartyDecl();
68
95
  case "asset":
69
96
  return this.parseAssetDecl();
97
+ case "subject":
98
+ return this.parseSubjectDecl(exported);
99
+ case "instrument":
100
+ return this.parseInstrumentDecl(exported);
70
101
  case "settlement":
71
102
  return this.parseSettlementDecl();
72
103
  case "port":
73
104
  return this.parsePortDecl();
105
+ case "type":
106
+ return this.parseTypeDecl(exported);
107
+ case "const":
108
+ return this.parseConstDecl(exported);
74
109
  default:
75
110
  this.error(token.span, `"${token.text}" cannot start a declaration`);
76
111
  this.advance();
77
112
  return undefined;
78
113
  }
79
114
  }
115
+ parseModuleDecl(exported) {
116
+ const start = this.advance();
117
+ if (exported) {
118
+ this.error(start.span, "a module declaration cannot be exported");
119
+ }
120
+ const name = this.parsePath("the module needs a dotted name");
121
+ return name
122
+ ? { kind: "module", name, span: this.spanFrom(start.span) }
123
+ : undefined;
124
+ }
125
+ parseInstrumentDecl(exported) {
126
+ const start = this.advance();
127
+ const name = this.expectIdent("the instrument needs a snake_case name");
128
+ if (!name)
129
+ return undefined;
130
+ const typeParameters = this.parseTypeParameters();
131
+ if (!typeParameters)
132
+ return undefined;
133
+ const hasParameterList = this.atPunct("(");
134
+ const parameters = hasParameterList ? this.parseParameters() : [];
135
+ if (!parameters)
136
+ return undefined;
137
+ if (this.atPunct("=")) {
138
+ this.advance();
139
+ const application = this.parseApplication();
140
+ if (!application)
141
+ return undefined;
142
+ const metadata = this.atPunct("{") ? this.parseBlock() : undefined;
143
+ return {
144
+ application,
145
+ exported,
146
+ kind: "instrument_apply",
147
+ ...(metadata ? { metadata } : {}),
148
+ name,
149
+ span: this.spanFrom(start.span),
150
+ };
151
+ }
152
+ if (!this.atPunct("{")) {
153
+ this.error(this.peek().span, `instrument ${name.name} needs a body or an exported instrument application`);
154
+ return undefined;
155
+ }
156
+ const body = this.parseBlock();
157
+ if (!body)
158
+ return undefined;
159
+ return {
160
+ body,
161
+ exported,
162
+ hasParameterList,
163
+ kind: "instrument",
164
+ name,
165
+ parameters,
166
+ span: this.spanFrom(start.span),
167
+ typeParameters,
168
+ };
169
+ }
170
+ parseTypeDecl(exported) {
171
+ const start = this.advance();
172
+ const name = this.expectIdent("the type needs a name");
173
+ if (!name)
174
+ return undefined;
175
+ if (!this.expectPunct("=", `type ${name.name} needs = before its definition`)) {
176
+ return undefined;
177
+ }
178
+ const value = this.parseExpr();
179
+ return value
180
+ ? { exported, kind: "type", name, span: this.spanFrom(start.span), value }
181
+ : undefined;
182
+ }
183
+ parseConstDecl(exported) {
184
+ const start = this.advance();
185
+ const name = this.expectIdent("the constant needs a name");
186
+ if (!name)
187
+ return undefined;
188
+ let type;
189
+ if (this.atPunct(":")) {
190
+ this.advance();
191
+ type = this.parseExpr();
192
+ if (!type)
193
+ return undefined;
194
+ }
195
+ if (!this.expectPunct("=", `const ${name.name} needs = before its value`)) {
196
+ return undefined;
197
+ }
198
+ const value = this.parseExpr();
199
+ return value
200
+ ? {
201
+ exported,
202
+ kind: "const",
203
+ name,
204
+ span: this.spanFrom(start.span),
205
+ ...(type ? { type } : {}),
206
+ value,
207
+ }
208
+ : undefined;
209
+ }
210
+ parseTypeParameters() {
211
+ if (!this.atPunct("<"))
212
+ return [];
213
+ this.advance();
214
+ const parameters = [];
215
+ while (!this.atPunct(">") && !this.at("eof")) {
216
+ const name = this.expectIdent("expected a type parameter");
217
+ if (!name)
218
+ return undefined;
219
+ parameters.push({ name, span: name.span });
220
+ if (this.atPunct(","))
221
+ this.advance();
222
+ }
223
+ return this.expectPunct(">", "the type parameter list never closes")
224
+ ? parameters
225
+ : undefined;
226
+ }
227
+ parseParameters() {
228
+ this.advance();
229
+ const parameters = [];
230
+ while (!this.atPunct(")") && !this.at("eof")) {
231
+ const name = this.expectIdent("expected a parameter name");
232
+ if (!name)
233
+ return undefined;
234
+ if (!this.expectPunct(":", `parameter ${name.name} needs a type`)) {
235
+ return undefined;
236
+ }
237
+ const type = this.parseExpr();
238
+ if (!type)
239
+ return undefined;
240
+ parameters.push({
241
+ name,
242
+ span: { start: name.span.start, end: type.span.end },
243
+ type,
244
+ });
245
+ if (this.atPunct(","))
246
+ this.advance();
247
+ }
248
+ return this.expectPunct(")", "the parameter list never closes")
249
+ ? parameters
250
+ : undefined;
251
+ }
80
252
  parseProgramDecl() {
81
253
  const start = this.advance();
82
254
  const name = this.expectIdent("the program needs a snake_case name");
@@ -94,9 +266,43 @@ class Parser {
94
266
  ...(title ? { title } : {}),
95
267
  };
96
268
  }
269
+ parseUseDecl() {
270
+ const start = this.advance();
271
+ const instrument = this.expectIdent("use needs a published instrument id, like: use held_settlement");
272
+ return instrument
273
+ ? { instrument, kind: "use", span: this.spanFrom(start.span) }
274
+ : undefined;
275
+ }
276
+ parseExposeDecl() {
277
+ const start = this.advance();
278
+ const instrument = this.expectIdent("expose needs an instrument action, like: expose held_settlement.release as releaseFunds");
279
+ if (!instrument)
280
+ return undefined;
281
+ if (!this.expectPunct(".", "expose separates the instrument and action with .")) {
282
+ return undefined;
283
+ }
284
+ const action = this.expectName("expose needs an action after the instrument id");
285
+ if (!action)
286
+ return undefined;
287
+ if (!this.at("keyword") || this.peek().text !== "as") {
288
+ this.error(this.peek().span, "expose needs as before its public action name");
289
+ return undefined;
290
+ }
291
+ this.advance();
292
+ const publicName = this.expectIdent("expose needs a camelCase public action name");
293
+ return publicName
294
+ ? {
295
+ action,
296
+ instrument,
297
+ kind: "expose",
298
+ publicName,
299
+ span: this.spanFrom(start.span),
300
+ }
301
+ : undefined;
302
+ }
97
303
  parseImportDecl() {
98
304
  const start = this.advance();
99
- if (!this.expectPunct("{", 'import lists its names in braces: import { held_payment } from "settlement"')) {
305
+ if (!this.expectPunct("{", 'import lists its names in braces: import { held_payment } from "std/settlements"')) {
100
306
  return undefined;
101
307
  }
102
308
  const names = [];
@@ -112,12 +318,12 @@ class Parser {
112
318
  return undefined;
113
319
  }
114
320
  if (!this.at("keyword") || this.peek().text !== "from") {
115
- this.error(this.peek().span, 'import needs from: import { held_payment } from "settlement"');
321
+ this.error(this.peek().span, 'import needs from: import { held_payment } from "std/settlements"');
116
322
  return undefined;
117
323
  }
118
324
  this.advance();
119
325
  if (!this.at("string")) {
120
- this.error(this.peek().span, 'the import source must be a quoted module name like "settlement"');
326
+ this.error(this.peek().span, 'the import source must be a quoted module name like "std/settlements"');
121
327
  return undefined;
122
328
  }
123
329
  const sourceToken = this.advance();
@@ -175,6 +381,23 @@ class Parser {
175
381
  span: this.spanFrom(start.span),
176
382
  };
177
383
  }
384
+ parseSubjectDecl(exported) {
385
+ const start = this.advance();
386
+ if (exported) {
387
+ this.error(start.span, "a subject declaration cannot be exported");
388
+ }
389
+ const name = this.expectIdent("the subject needs a snake_case kind name");
390
+ if (!name)
391
+ return undefined;
392
+ if (!this.atPunct("{")) {
393
+ this.error(this.peek().span, `subject ${name.name} needs a body`);
394
+ return undefined;
395
+ }
396
+ const body = this.parseBlock();
397
+ return body
398
+ ? { body, kind: "subject", name, span: this.spanFrom(start.span) }
399
+ : undefined;
400
+ }
178
401
  parseSettlementDecl() {
179
402
  const start = this.advance();
180
403
  const name = this.expectIdent("the settlement needs a name, like: settlement sale = held_payment { ... }");
@@ -194,9 +417,20 @@ class Parser {
194
417
  if (!body)
195
418
  return undefined;
196
419
  return {
197
- archetype,
198
- body,
199
- kind: "settlement",
420
+ application: {
421
+ args: body.entries.map((row) => ({
422
+ kind: "binding",
423
+ name: row.key,
424
+ span: row.value.span,
425
+ type: row.value,
426
+ })),
427
+ callee: { kind: "path", parts: [archetype], span: archetype.span },
428
+ kind: "apply",
429
+ span: { start: archetype.span.start, end: body.span.end },
430
+ typeArgs: [],
431
+ },
432
+ exported: false,
433
+ kind: "instrument_apply",
200
434
  name,
201
435
  span: this.spanFrom(start.span),
202
436
  };
@@ -238,19 +472,23 @@ class Parser {
238
472
  this.depth += 1;
239
473
  return true;
240
474
  }
241
- parseBlock() {
475
+ parseBlock(context = "general") {
242
476
  if (!this.enterNesting(this.peek().span))
243
477
  return undefined;
244
- const block = this.parseBlockUnguarded();
478
+ const block = this.parseBlockUnguarded(context);
245
479
  this.depth -= 1;
246
480
  return block;
247
481
  }
248
- parseBlockUnguarded() {
482
+ parseBlockUnguarded(context) {
249
483
  const open = this.advance();
250
484
  const entries = [];
251
485
  while (!this.atPunct("}") && !this.at("eof")) {
486
+ if (this.atPunct(";") || this.atPunct(",")) {
487
+ this.advance();
488
+ continue;
489
+ }
252
490
  const before = this.index;
253
- const entry = this.parseEntry();
491
+ const entry = this.parseEntry(context);
254
492
  if (!entry) {
255
493
  this.recoverInBlock();
256
494
  // recoverInBlock bails without consuming when it lands on a depth-0
@@ -262,7 +500,7 @@ class Parser {
262
500
  continue;
263
501
  }
264
502
  entries.push(entry);
265
- if (this.atPunct(","))
503
+ if (this.atPunct(",") || this.atPunct(";"))
266
504
  this.advance();
267
505
  }
268
506
  if (!this.expectPunct("}", "this block never closes; add the matching }")) {
@@ -270,15 +508,172 @@ class Parser {
270
508
  }
271
509
  return { entries, kind: "block", span: this.spanFrom(open.span) };
272
510
  }
273
- parseEntry() {
274
- const key = this.expectIdent("expected an entry name here");
511
+ parseEntry(context) {
512
+ const token = this.peek();
513
+ const key = token.kind === "string"
514
+ ? (() => {
515
+ this.advance();
516
+ return {
517
+ kind: "ident",
518
+ name: token.value,
519
+ quoted: !token.value.includes("["),
520
+ span: token.span,
521
+ };
522
+ })()
523
+ : this.isName(token) && (token.text === "action" || token.text === "on")
524
+ ? this.parseIdent()
525
+ : this.parseTemplatedName("expected an entry name here");
275
526
  if (!key)
276
527
  return undefined;
277
528
  const qualifiers = [];
529
+ if (key.name === "for") {
530
+ const binding = this.expectIdent("for needs a compile-time binding name");
531
+ const inWord = binding
532
+ ? this.expectName("for needs in before its finite bound")
533
+ : undefined;
534
+ if (!binding || inWord?.name !== "in") {
535
+ this.error(this.peek().span, "write `for item in <finite bound> { ... }`");
536
+ return undefined;
537
+ }
538
+ const bound = this.parseExpr();
539
+ if (!bound || !this.atPunct("{")) {
540
+ this.error(this.peek().span, "a bounded comprehension needs a body in braces");
541
+ return undefined;
542
+ }
543
+ const value = this.parseBlock(context);
544
+ return value
545
+ ? {
546
+ iteration: { binding, bound },
547
+ key,
548
+ qualifiers,
549
+ span: { start: key.span.start, end: value.span.end },
550
+ value,
551
+ }
552
+ : undefined;
553
+ }
554
+ if (key.name === "action" &&
555
+ (this.isName(this.peek()) || this.atPunct("["))) {
556
+ const action = this.parseTemplatedName("action needs a name");
557
+ if (!action)
558
+ return undefined;
559
+ qualifiers.push(action);
560
+ if (!this.atPunct("{")) {
561
+ this.error(this.peek().span, `action ${qualifiers[0]?.name ?? ""} needs a body`);
562
+ return undefined;
563
+ }
564
+ const value = this.parseBlock();
565
+ return value
566
+ ? {
567
+ key,
568
+ qualifiers,
569
+ span: { start: key.span.start, end: value.span.end },
570
+ value,
571
+ }
572
+ : undefined;
573
+ }
574
+ if (context === "lifecycle" && key.name === "states") {
575
+ const items = [];
576
+ while (!this.atPunct(";") && !this.atPunct("}") && !this.at("eof")) {
577
+ const state = this.parseTemplatedName("expected a lifecycle state");
578
+ if (!state)
579
+ return undefined;
580
+ items.push(state);
581
+ }
582
+ const end = items.at(-1)?.span.end ?? key.span.end;
583
+ return {
584
+ key,
585
+ qualifiers,
586
+ span: { start: key.span.start, end },
587
+ value: { items, kind: "list", span: { start: key.span.end, end } },
588
+ };
589
+ }
590
+ if (context === "lifecycle" && key.name === "initial") {
591
+ const value = this.parseTemplatedName("initial needs one declared lifecycle state");
592
+ return value
593
+ ? {
594
+ key,
595
+ qualifiers,
596
+ span: { start: key.span.start, end: value.span.end },
597
+ value,
598
+ }
599
+ : undefined;
600
+ }
601
+ if (context === "lifecycle" && key.name === "parked") {
602
+ const state = this.parseTemplatedName("parked needs a lifecycle state");
603
+ if (!state)
604
+ return undefined;
605
+ qualifiers.push(state);
606
+ const reasonWord = this.expectName('parked needs reason "..."');
607
+ if (!reasonWord || reasonWord.name !== "reason" || !this.at("string")) {
608
+ this.error(this.peek().span, 'parked needs reason "..."');
609
+ return undefined;
610
+ }
611
+ const token = this.advance();
612
+ const value = {
613
+ kind: "string",
614
+ span: token.span,
615
+ value: token.value,
616
+ };
617
+ return {
618
+ key,
619
+ qualifiers,
620
+ span: { start: key.span.start, end: value.span.end },
621
+ value,
622
+ };
623
+ }
624
+ if (context === "lifecycle" && key.name === "on") {
625
+ const action = this.parseTemplatedName("on needs an action name");
626
+ if (!action)
627
+ return undefined;
628
+ qualifiers.push(action);
629
+ if (!this.expectPunct(":", `on ${action.name} needs : before its source states`)) {
630
+ return undefined;
631
+ }
632
+ while (!this.atPunct("->") && !this.at("eof")) {
633
+ const state = this.parseTemplatedName("expected a source lifecycle state");
634
+ if (!state)
635
+ return undefined;
636
+ qualifiers.push(state);
637
+ if (this.atPunct("|"))
638
+ this.advance();
639
+ }
640
+ if (!this.expectPunct("->", `on ${action.name} needs -> before its target state`)) {
641
+ return undefined;
642
+ }
643
+ const value = this.parseTemplatedName("expected a target lifecycle state");
644
+ return value
645
+ ? {
646
+ key,
647
+ qualifiers,
648
+ span: { start: key.span.start, end: value.span.end },
649
+ value,
650
+ }
651
+ : undefined;
652
+ }
653
+ if (key.name === "notify") {
654
+ const role = this.expectName("notify needs a declared role");
655
+ const via = role
656
+ ? this.expectName("notify needs via before its channel")
657
+ : undefined;
658
+ const channel = via
659
+ ? this.expectName("notify needs a channel")
660
+ : undefined;
661
+ if (!role || via?.name !== "via" || !channel)
662
+ return undefined;
663
+ qualifiers.push(role, channel);
664
+ return {
665
+ key,
666
+ qualifiers,
667
+ span: { start: key.span.start, end: channel.span.end },
668
+ value: { kind: "boolean", span: channel.span, value: true },
669
+ };
670
+ }
278
671
  if (this.atPunct("(")) {
279
672
  this.advance();
280
673
  while (!this.atPunct(")") && !this.at("eof")) {
281
- const qualifier = this.expectIdent(`expected a qualifier inside ${key.name}( ... )`);
674
+ const qualifier = this.at("number")
675
+ ? this.parseIdent()
676
+ : this.expectIdent(`expected a qualifier inside ${key.name}( ... )`);
282
677
  if (!qualifier)
283
678
  return undefined;
284
679
  qualifiers.push(qualifier);
@@ -289,12 +684,26 @@ class Parser {
289
684
  return undefined;
290
685
  }
291
686
  }
687
+ if ((key.name === "requires" || key.name === "computes") &&
688
+ this.isName(this.peek())) {
689
+ while (this.isName(this.peek()))
690
+ qualifiers.push(this.parseIdent());
691
+ }
292
692
  let value;
293
693
  if (this.atPunct("{")) {
294
- value = this.parseBlock();
694
+ value = this.parseBlock(key.name === "lifecycle" || context === "lifecycle"
695
+ ? "lifecycle"
696
+ : "general");
295
697
  }
296
698
  else if (this.atPunct(":")) {
297
699
  this.advance();
700
+ value = this.atPunct("{")
701
+ ? this.parseBlock(key.name === "lifecycle" || context === "lifecycle"
702
+ ? "lifecycle"
703
+ : "general")
704
+ : this.parseExpr();
705
+ }
706
+ else if (this.canStartExpr(this.peek())) {
298
707
  value = this.parseExpr();
299
708
  }
300
709
  else {
@@ -392,8 +801,40 @@ class Parser {
392
801
  ...(within ? { within } : {}),
393
802
  };
394
803
  }
804
+ if (token.kind === "keyword") {
805
+ return this.parseIdent();
806
+ }
807
+ if (token.kind === "ident" &&
808
+ /^[A-Z]{3}$/.test(token.text) &&
809
+ this.peekAt(1).kind === "number") {
810
+ const currency = this.parseIdent();
811
+ const amount = this.advance();
812
+ return {
813
+ currency,
814
+ kind: "money",
815
+ raw: amount.value,
816
+ span: { start: currency.span.start, end: amount.span.end },
817
+ };
818
+ }
395
819
  if (token.kind === "ident") {
396
820
  const ident = this.parseIdent();
821
+ if (ident.name === "true" || ident.name === "false") {
822
+ return {
823
+ kind: "boolean",
824
+ span: ident.span,
825
+ value: ident.name === "true",
826
+ };
827
+ }
828
+ if (ident.name === "decided" && this.atPunct("{")) {
829
+ const body = this.parseBlock();
830
+ if (!body)
831
+ return undefined;
832
+ return {
833
+ body,
834
+ kind: "decided_amount",
835
+ span: { end: body.span.end, start: ident.span.start },
836
+ };
837
+ }
397
838
  if (this.atPunct(".")) {
398
839
  this.advance();
399
840
  const member = this.expectIdent(`expected the exit after ${ident.name}., like ${ident.name}.release`);
@@ -406,6 +847,17 @@ class Parser {
406
847
  span: { end: member.span.end, start: ident.span.start },
407
848
  };
408
849
  }
850
+ if (this.atPunct("<")) {
851
+ const args = this.parseAngleArguments();
852
+ if (!args)
853
+ return undefined;
854
+ return {
855
+ args,
856
+ callee: ident,
857
+ kind: "type_apply",
858
+ span: this.spanFrom(ident.span),
859
+ };
860
+ }
409
861
  if (this.atPunct("(")) {
410
862
  this.advance();
411
863
  const args = [];
@@ -482,6 +934,9 @@ class Parser {
482
934
  peek() {
483
935
  return this.tokens[this.index];
484
936
  }
937
+ peekAt(offset) {
938
+ return this.tokens[Math.min(this.index + offset, this.tokens.length - 1)];
939
+ }
485
940
  previous() {
486
941
  return this.tokens[Math.max(0, this.index - 1)];
487
942
  }
@@ -502,6 +957,124 @@ class Parser {
502
957
  const token = this.advance();
503
958
  return { kind: "ident", name: token.text, span: token.span };
504
959
  }
960
+ isName(token) {
961
+ return token.kind === "ident" || token.kind === "keyword";
962
+ }
963
+ expectName(message) {
964
+ if (this.isName(this.peek()))
965
+ return this.parseIdent();
966
+ this.error(this.peek().span, message);
967
+ return undefined;
968
+ }
969
+ parseTemplatedName(message) {
970
+ if (this.atPunct("[")) {
971
+ const open = this.advance();
972
+ const binding = this.expectIdent("an indexed name needs its loop binding");
973
+ if (!binding || !this.expectPunct("]", "an indexed name needs ]")) {
974
+ return undefined;
975
+ }
976
+ const suffix = this.isName(this.peek()) ? this.parseIdent() : undefined;
977
+ return {
978
+ kind: "ident",
979
+ name: `[${binding.name}]${suffix?.name ?? ""}`,
980
+ span: {
981
+ start: open.span.start,
982
+ end: suffix?.span.end ?? this.previous().span.end,
983
+ },
984
+ };
985
+ }
986
+ const base = this.expectName(message);
987
+ if (!base || !this.atPunct("["))
988
+ return base;
989
+ this.advance();
990
+ const binding = this.expectIdent("an indexed name needs its loop binding");
991
+ if (!binding || !this.expectPunct("]", "an indexed name needs ]")) {
992
+ return undefined;
993
+ }
994
+ const suffix = this.isName(this.peek()) ? this.parseIdent() : undefined;
995
+ return {
996
+ kind: "ident",
997
+ name: `${base.name}[${binding.name}]${suffix?.name ?? ""}`,
998
+ span: {
999
+ start: base.span.start,
1000
+ end: suffix?.span.end ?? this.previous().span.end,
1001
+ },
1002
+ };
1003
+ }
1004
+ canStartExpr(token) {
1005
+ return (token.kind === "ident" ||
1006
+ token.kind === "keyword" ||
1007
+ token.kind === "number" ||
1008
+ token.kind === "percent" ||
1009
+ token.kind === "string" ||
1010
+ (token.kind === "punct" && (token.text === "{" || token.text === "[")));
1011
+ }
1012
+ parsePath(message) {
1013
+ const first = this.expectName(message);
1014
+ if (!first)
1015
+ return undefined;
1016
+ const parts = [first];
1017
+ while (this.atPunct(".")) {
1018
+ this.advance();
1019
+ const part = this.expectName("expected a name after .");
1020
+ if (!part)
1021
+ return undefined;
1022
+ parts.push(part);
1023
+ }
1024
+ return {
1025
+ kind: "path",
1026
+ parts,
1027
+ span: {
1028
+ start: first.span.start,
1029
+ end: parts.at(-1)?.span.end ?? first.span.end,
1030
+ },
1031
+ };
1032
+ }
1033
+ parseAngleArguments() {
1034
+ this.advance();
1035
+ const args = [];
1036
+ while (!this.atPunct(">") && !this.at("eof")) {
1037
+ const arg = this.parseExpr();
1038
+ if (!arg)
1039
+ return undefined;
1040
+ args.push(arg);
1041
+ if (this.atPunct(","))
1042
+ this.advance();
1043
+ }
1044
+ return this.expectPunct(">", "the type argument list never closes")
1045
+ ? args
1046
+ : undefined;
1047
+ }
1048
+ parseApplication() {
1049
+ const callee = this.parsePath("expected an instrument function name");
1050
+ if (!callee)
1051
+ return undefined;
1052
+ const typeArgs = this.atPunct("<") ? this.parseAngleArguments() : [];
1053
+ if (!typeArgs)
1054
+ return undefined;
1055
+ if (!this.expectPunct("(", "an instrument application needs arguments in parentheses")) {
1056
+ return undefined;
1057
+ }
1058
+ const args = [];
1059
+ while (!this.atPunct(")") && !this.at("eof")) {
1060
+ const arg = this.parseExpr();
1061
+ if (!arg)
1062
+ return undefined;
1063
+ args.push(arg);
1064
+ if (this.atPunct(","))
1065
+ this.advance();
1066
+ }
1067
+ if (!this.expectPunct(")", "the instrument argument list never closes")) {
1068
+ return undefined;
1069
+ }
1070
+ return {
1071
+ args,
1072
+ callee,
1073
+ kind: "apply",
1074
+ span: { start: callee.span.start, end: this.previous().span.end },
1075
+ typeArgs,
1076
+ };
1077
+ }
505
1078
  expectIdent(message) {
506
1079
  if (this.at("ident"))
507
1080
  return this.parseIdent();