@hyperscale0/hsx 1.0.0-alpha.6 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -10
- package/LICENSING.md +3 -3
- package/README.md +37 -149
- package/TRADEMARKS.md +4 -4
- package/dist/src/ast.d.ts +129 -10
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/cli.d.ts +2 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +148 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +41 -4
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +82 -15
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +78 -0
- package/dist/src/cost.d.ts.map +1 -0
- package/dist/src/cost.js +369 -0
- package/dist/src/cost.js.map +1 -0
- package/dist/src/diagnostics.d.ts +11 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +420 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/emit.d.ts +35 -0
- package/dist/src/emit.d.ts.map +1 -0
- package/dist/src/emit.js +359 -0
- package/dist/src/emit.js.map +1 -0
- package/dist/src/entry-overrides.d.ts +17 -3
- package/dist/src/entry-overrides.d.ts.map +1 -1
- package/dist/src/entry-overrides.js +180 -35
- package/dist/src/entry-overrides.js.map +1 -1
- package/dist/src/format.d.ts +10 -0
- package/dist/src/format.d.ts.map +1 -0
- package/dist/src/format.js +183 -0
- package/dist/src/format.js.map +1 -0
- package/dist/src/index.d.ts +10 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/ir.d.ts +66 -0
- package/dist/src/ir.d.ts.map +1 -0
- package/dist/src/ir.js +15 -0
- package/dist/src/ir.js.map +1 -0
- package/dist/src/lex.d.ts +9 -1
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +50 -4
- package/dist/src/lex.js.map +1 -1
- package/dist/src/limits.d.ts +2 -3
- package/dist/src/limits.d.ts.map +1 -1
- package/dist/src/limits.js +2 -3
- package/dist/src/limits.js.map +1 -1
- package/dist/src/modules.d.ts +36 -0
- package/dist/src/modules.d.ts.map +1 -0
- package/dist/src/modules.js +372 -0
- package/dist/src/modules.js.map +1 -0
- package/dist/src/parse.d.ts.map +1 -1
- package/dist/src/parse.js +590 -17
- package/dist/src/parse.js.map +1 -1
- package/dist/src/typecheck.d.ts +8 -0
- package/dist/src/typecheck.d.ts.map +1 -0
- package/dist/src/typecheck.js +2958 -0
- package/dist/src/typecheck.js.map +1 -0
- package/dist/src/version.d.ts +4 -15
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +4 -15
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +23 -0
- package/docs/guide/01-first-program.md +24 -0
- package/docs/guide/02-money.md +25 -0
- package/docs/guide/03-instruments.md +22 -0
- package/docs/guide/04-lifecycles.md +25 -0
- package/docs/guide/05-fees-and-splits.md +45 -0
- package/docs/guide/06-schedules.md +22 -0
- package/docs/guide/07-composition.md +46 -0
- package/docs/guide/08-writing-a-module.md +26 -0
- package/docs/guide/09-cost.md +24 -0
- package/docs/guide/10-diagnostics.md +27 -0
- package/docs/llms-full.txt +1738 -0
- package/docs/llms.txt +45 -0
- package/docs/reference/cli.md +33 -0
- package/docs/reference/diagnostics.md +619 -0
- package/docs/reference/grammar.md +39 -0
- package/docs/reference/std/advance.md +35 -0
- package/docs/reference/std/cancellable_booking.md +35 -0
- package/docs/reference/std/captured_payment.md +43 -0
- package/docs/reference/std/conditional_disbursement.md +33 -0
- package/docs/reference/std/credit_facility.md +35 -0
- package/docs/reference/std/held_payment.md +64 -0
- package/docs/reference/std/instant_transfer.md +30 -0
- package/docs/reference/std/metered.md +27 -0
- package/docs/reference/std/pooled_split.md +26 -0
- package/docs/reference/std/premium_forward.md +39 -0
- package/docs/reference/std/reconciled_payout.md +33 -0
- package/docs/reference/std/recurring_collection.md +25 -0
- package/docs/reference/std/rotating_pool.md +42 -0
- package/docs/reference/std/scheduled.md +51 -0
- package/docs/reference/std/security_deposit.md +46 -0
- package/docs/reference/std/settlement_batch.md +42 -0
- package/docs/reference/std/swap.md +46 -0
- package/docs/reference/std/threshold_pool.md +47 -0
- package/docs/reference/std/weighted_distribution.md +39 -0
- package/docs/reference/types.md +20 -0
- package/docs/reference/udl-output.md +14 -0
- package/docs/sessions/2026-09-02-two-instruments.hsx +21 -0
- package/docs/sessions/2026-09-02-two-instruments.md +41 -0
- package/examples/01-first-program/README.md +45 -0
- package/examples/01-first-program/tip-jar.hsx +17 -0
- package/examples/02-imports-and-archetypes/README.md +99 -0
- package/examples/02-imports-and-archetypes/photo-booth.hsx +37 -0
- package/examples/03-diagnostics/README.md +68 -0
- package/examples/03-diagnostics/corner-shop-fixed.hsx +22 -0
- package/examples/03-diagnostics/corner-shop.hsx +19 -0
- package/examples/04-complete-product/README.md +87 -0
- package/examples/04-complete-product/study-hall.hsx +67 -0
- package/examples/05-watch-club/README.md +9 -0
- package/examples/05-watch-club/watch-club.hsx +124 -0
- package/examples/README.md +18 -0
- package/examples/advance/README.md +3 -0
- package/examples/advance/advance.hsx +13 -0
- package/examples/advance/advance.udl +339 -0
- package/examples/cancellable_booking/README.md +3 -0
- package/examples/cancellable_booking/cancellable_booking.hsx +14 -0
- package/examples/cancellable_booking/cancellable_booking.udl +391 -0
- package/examples/captured_payment/README.md +3 -0
- package/examples/captured_payment/captured_payment.hsx +21 -0
- package/examples/captured_payment/captured_payment.udl +677 -0
- package/examples/conditional_disbursement/README.md +3 -0
- package/examples/conditional_disbursement/conditional_disbursement.hsx +17 -0
- package/examples/conditional_disbursement/conditional_disbursement.udl +412 -0
- package/examples/cost-table.json +386 -0
- package/examples/credit_facility/README.md +3 -0
- package/examples/credit_facility/credit_facility.hsx +27 -0
- package/examples/credit_facility/credit_facility.udl +1373 -0
- package/examples/held_payment/README.md +3 -0
- package/examples/held_payment/held_payment.hsx +13 -0
- package/examples/held_payment/held_payment.udl +507 -0
- package/examples/instant_transfer/README.md +3 -0
- package/examples/instant_transfer/instant_transfer.hsx +16 -0
- package/examples/instant_transfer/instant_transfer.udl +364 -0
- package/examples/metered/README.md +3 -0
- package/examples/metered/metered.hsx +13 -0
- package/examples/metered/metered.udl +247 -0
- package/examples/pooled_split/README.md +3 -0
- package/examples/pooled_split/pooled_split.hsx +15 -0
- package/examples/pooled_split/pooled_split.udl +425 -0
- package/examples/premium_forward/README.md +3 -0
- package/examples/premium_forward/premium_forward.hsx +19 -0
- package/examples/premium_forward/premium_forward.udl +594 -0
- package/examples/reconciled_payout/README.md +3 -0
- package/examples/reconciled_payout/reconciled_payout.hsx +13 -0
- package/examples/reconciled_payout/reconciled_payout.udl +303 -0
- package/examples/recurring_collection/README.md +3 -0
- package/examples/recurring_collection/recurring_collection.hsx +21 -0
- package/examples/recurring_collection/recurring_collection.udl +1130 -0
- package/examples/rotating_pool/README.md +3 -0
- package/examples/rotating_pool/rotating_pool.hsx +18 -0
- package/examples/rotating_pool/rotating_pool.udl +4352 -0
- package/examples/scheduled/README.md +3 -0
- package/examples/scheduled/scheduled.hsx +12 -0
- package/examples/scheduled/scheduled.udl +335 -0
- package/examples/security_deposit/README.md +3 -0
- package/examples/security_deposit/security_deposit.hsx +21 -0
- package/examples/security_deposit/security_deposit.udl +293 -0
- package/examples/settlement_batch/README.md +3 -0
- package/examples/settlement_batch/settlement_batch.hsx +19 -0
- package/examples/settlement_batch/settlement_batch.udl +753 -0
- package/examples/swap/README.md +3 -0
- package/examples/swap/swap.hsx +19 -0
- package/examples/swap/swap.udl +876 -0
- package/examples/threshold_pool/README.md +3 -0
- package/examples/threshold_pool/threshold_pool.hsx +16 -0
- package/examples/threshold_pool/threshold_pool.udl +667 -0
- package/examples/weighted_distribution/README.md +3 -0
- package/examples/weighted_distribution/weighted_distribution.hsx +20 -0
- package/examples/weighted_distribution/weighted_distribution.udl +337 -0
- package/package.json +19 -6
- package/skills/hsx/SKILL.md +483 -0
- package/src/ast.ts +163 -9
- package/src/cli.ts +164 -12
- package/src/compile.ts +160 -20
- package/src/cost.ts +579 -0
- package/src/diagnostics.ts +442 -0
- package/src/emit.ts +447 -0
- package/src/entry-overrides.ts +263 -48
- package/src/format.ts +261 -0
- package/src/index.ts +32 -4
- package/src/ir.ts +99 -0
- package/src/lex.ts +59 -4
- package/src/limits.ts +2 -3
- package/src/modules.ts +444 -0
- package/src/parse.ts +655 -21
- package/src/typecheck.ts +3876 -0
- package/src/version.ts +4 -16
- package/std/SEMANTICS.md +130 -0
- package/std/settlements/advance.hsx +177 -0
- package/std/settlements/cancellable_booking.hsx +87 -0
- package/std/settlements/captured_payment.hsx +144 -0
- package/std/settlements/conditional_disbursement.hsx +185 -0
- package/std/settlements/credit_facility.hsx +167 -0
- package/std/settlements/held_payment.hsx +578 -0
- package/std/settlements/index.hsx +3 -0
- package/std/settlements/instant_transfer.hsx +190 -0
- package/std/settlements/metered.hsx +67 -0
- package/std/settlements/pooled_split.hsx +68 -0
- package/std/settlements/premium_forward.hsx +257 -0
- package/std/settlements/reconciled_payout.hsx +77 -0
- package/std/settlements/recurring_collection.hsx +27 -0
- package/std/settlements/rotating_pool.hsx +252 -0
- package/std/settlements/scheduled.hsx +641 -0
- package/std/settlements/security_deposit.hsx +166 -0
- package/std/settlements/settlement_batch.hsx +122 -0
- package/std/settlements/swap.hsx +1114 -0
- package/std/settlements/threshold_pool.hsx +221 -0
- package/std/settlements/weighted_distribution.hsx +141 -0
- package/dist/src/archetypes.d.ts +0 -17
- package/dist/src/archetypes.d.ts.map +0 -1
- package/dist/src/archetypes.js +0 -327
- package/dist/src/archetypes.js.map +0 -1
- package/dist/src/check.d.ts +0 -12
- package/dist/src/check.d.ts.map +0 -1
- package/dist/src/check.js +0 -1910
- package/dist/src/check.js.map +0 -1
- package/dist/src/lower.d.ts +0 -146
- package/dist/src/lower.d.ts.map +0 -1
- package/dist/src/lower.js +0 -4570
- package/dist/src/lower.js.map +0 -1
- package/dist/src/model.d.ts +0 -467
- package/dist/src/model.d.ts.map +0 -1
- package/dist/src/model.js +0 -15
- package/dist/src/model.js.map +0 -1
- package/spec/hsx-ir.schema.json +0 -920
- package/src/archetypes.ts +0 -340
- package/src/check.ts +0 -2938
- package/src/lower.ts +0 -5416
- package/src/model.ts +0 -519
package/src/ast.ts
CHANGED
|
@@ -78,6 +78,8 @@ export function lineColIn(index: LineIndex, offset: number): LineCol {
|
|
|
78
78
|
export interface IdentExpr {
|
|
79
79
|
readonly kind: "ident";
|
|
80
80
|
readonly name: string;
|
|
81
|
+
/** A literal quoted block key keeps its exact spelling during JSON lowering. */
|
|
82
|
+
readonly quoted?: boolean;
|
|
81
83
|
readonly span: Span;
|
|
82
84
|
}
|
|
83
85
|
|
|
@@ -87,13 +89,51 @@ export interface StringExpr {
|
|
|
87
89
|
readonly value: string;
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
interface NumberExpr {
|
|
92
|
+
export interface NumberExpr {
|
|
91
93
|
readonly kind: "number";
|
|
92
94
|
/** The literal exactly as written, e.g. "99.5". Interpretation is typed later. */
|
|
93
95
|
readonly raw: string;
|
|
94
96
|
readonly span: Span;
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
export interface BooleanExpr {
|
|
100
|
+
readonly kind: "boolean";
|
|
101
|
+
readonly span: Span;
|
|
102
|
+
readonly value: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** A currency-indexed literal. The checker converts it to integer minor units. */
|
|
106
|
+
export interface MoneyExpr {
|
|
107
|
+
readonly currency: IdentExpr;
|
|
108
|
+
readonly kind: "money";
|
|
109
|
+
readonly raw: string;
|
|
110
|
+
readonly span: Span;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A dotted name used for modules, UDL operations, and field paths. */
|
|
114
|
+
export interface PathExpr {
|
|
115
|
+
readonly kind: "path";
|
|
116
|
+
readonly parts: readonly IdentExpr[];
|
|
117
|
+
readonly span: Span;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** A nominal type application such as `money<SAR>` or `ref<invoice>`. */
|
|
121
|
+
export interface TypeApplyExpr {
|
|
122
|
+
readonly args: readonly Expr[];
|
|
123
|
+
readonly callee: IdentExpr;
|
|
124
|
+
readonly kind: "type_apply";
|
|
125
|
+
readonly span: Span;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** A general instrument or constant application, with optional type arguments. */
|
|
129
|
+
export interface ApplyExpr {
|
|
130
|
+
readonly args: readonly Expr[];
|
|
131
|
+
readonly callee: PathExpr;
|
|
132
|
+
readonly kind: "apply";
|
|
133
|
+
readonly span: Span;
|
|
134
|
+
readonly typeArgs: readonly Expr[];
|
|
135
|
+
}
|
|
136
|
+
|
|
97
137
|
export interface PercentExpr {
|
|
98
138
|
/** Exact basis points: 99.5% is 9950. Percents never round. */
|
|
99
139
|
readonly bps: number;
|
|
@@ -110,6 +150,16 @@ export interface CallExpr {
|
|
|
110
150
|
readonly span: Span;
|
|
111
151
|
}
|
|
112
152
|
|
|
153
|
+
/**
|
|
154
|
+
* An exit amount chosen by a decision port and bounded by stored money:
|
|
155
|
+
* `decided { field: damageAmount, bound: depositAmount, remainder: return }`.
|
|
156
|
+
*/
|
|
157
|
+
export interface DecidedAmountExpr {
|
|
158
|
+
readonly body: BlockExpr;
|
|
159
|
+
readonly kind: "decided_amount";
|
|
160
|
+
readonly span: Span;
|
|
161
|
+
}
|
|
162
|
+
|
|
113
163
|
/**
|
|
114
164
|
* A reference to a decision port, optionally bounded (`port dispute within
|
|
115
165
|
* P14D`) or carrying the date that decides when the port has not
|
|
@@ -155,7 +205,7 @@ export interface BlockExpr {
|
|
|
155
205
|
* `amount: price: money(SAR)` binds the field name `price` to type
|
|
156
206
|
* `money(SAR)`: the entry key is `amount`, the value is this binding.
|
|
157
207
|
*/
|
|
158
|
-
interface BindingExpr {
|
|
208
|
+
export interface BindingExpr {
|
|
159
209
|
readonly kind: "binding";
|
|
160
210
|
readonly name: IdentExpr;
|
|
161
211
|
readonly span: Span;
|
|
@@ -163,16 +213,22 @@ interface BindingExpr {
|
|
|
163
213
|
}
|
|
164
214
|
|
|
165
215
|
export type Expr =
|
|
216
|
+
| ApplyExpr
|
|
166
217
|
| BindingExpr
|
|
167
218
|
| BlockExpr
|
|
219
|
+
| BooleanExpr
|
|
168
220
|
| CallExpr
|
|
221
|
+
| DecidedAmountExpr
|
|
169
222
|
| IdentExpr
|
|
170
223
|
| ListExpr
|
|
224
|
+
| MoneyExpr
|
|
171
225
|
| NumberExpr
|
|
226
|
+
| PathExpr
|
|
172
227
|
| PercentExpr
|
|
173
228
|
| PortRefExpr
|
|
174
229
|
| SettlementRefExpr
|
|
175
|
-
| StringExpr
|
|
230
|
+
| StringExpr
|
|
231
|
+
| TypeApplyExpr;
|
|
176
232
|
|
|
177
233
|
// --- Entries ---------------------------------------------------------------
|
|
178
234
|
|
|
@@ -182,6 +238,11 @@ export type Expr =
|
|
|
182
238
|
* and `on_cancel(funded) { ... }` (key + qualifiers + block value).
|
|
183
239
|
*/
|
|
184
240
|
export interface Entry {
|
|
241
|
+
/** `for item in bound { ... }`, expanded before binding or UDL lowering. */
|
|
242
|
+
readonly iteration?: {
|
|
243
|
+
readonly binding: IdentExpr;
|
|
244
|
+
readonly bound: Expr;
|
|
245
|
+
};
|
|
185
246
|
readonly key: IdentExpr;
|
|
186
247
|
readonly qualifiers: readonly IdentExpr[];
|
|
187
248
|
readonly span: Span;
|
|
@@ -198,7 +259,23 @@ export interface ProgramDecl {
|
|
|
198
259
|
readonly title?: StringExpr;
|
|
199
260
|
}
|
|
200
261
|
|
|
201
|
-
/** `
|
|
262
|
+
/** `use held_settlement` selects one published catalog instrument. */
|
|
263
|
+
export interface UseDecl {
|
|
264
|
+
readonly instrument: IdentExpr;
|
|
265
|
+
readonly kind: "use";
|
|
266
|
+
readonly span: Span;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** `expose held_settlement.release as releaseFunds` names a public action. */
|
|
270
|
+
export interface ExposeDecl {
|
|
271
|
+
readonly action: IdentExpr;
|
|
272
|
+
readonly instrument: IdentExpr;
|
|
273
|
+
readonly kind: "expose";
|
|
274
|
+
readonly publicName: IdentExpr;
|
|
275
|
+
readonly span: Span;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** `import { held_payment } from "std/settlements"`. */
|
|
202
279
|
export interface ImportDecl {
|
|
203
280
|
readonly from: StringExpr;
|
|
204
281
|
readonly kind: "import";
|
|
@@ -224,11 +301,10 @@ export interface AssetDecl {
|
|
|
224
301
|
readonly span: Span;
|
|
225
302
|
}
|
|
226
303
|
|
|
227
|
-
/**
|
|
228
|
-
export interface
|
|
229
|
-
readonly archetype: IdentExpr;
|
|
304
|
+
/** A full UDL subject-kind declaration. `asset` remains shorthand for this form. */
|
|
305
|
+
export interface SubjectDecl {
|
|
230
306
|
readonly body: BlockExpr;
|
|
231
|
-
readonly kind: "
|
|
307
|
+
readonly kind: "subject";
|
|
232
308
|
readonly name: IdentExpr;
|
|
233
309
|
readonly span: Span;
|
|
234
310
|
}
|
|
@@ -241,13 +317,89 @@ export interface PortDecl {
|
|
|
241
317
|
readonly span: Span;
|
|
242
318
|
}
|
|
243
319
|
|
|
320
|
+
/** `module std.settlements` gives a file its importable module name. */
|
|
321
|
+
export interface ModuleDecl {
|
|
322
|
+
readonly kind: "module";
|
|
323
|
+
readonly name: PathExpr;
|
|
324
|
+
readonly span: Span;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export interface TypeParameter {
|
|
328
|
+
readonly name: IdentExpr;
|
|
329
|
+
readonly span: Span;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export interface Parameter {
|
|
333
|
+
readonly name: IdentExpr;
|
|
334
|
+
readonly span: Span;
|
|
335
|
+
readonly type: Expr;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** The single general instrument definition form. */
|
|
339
|
+
export interface InstrumentDecl {
|
|
340
|
+
readonly body: BlockExpr;
|
|
341
|
+
/** Module-local declarations carried only while this template is bound. */
|
|
342
|
+
readonly declarationScope?: readonly ApplicationScopeDecl[];
|
|
343
|
+
readonly exported: boolean;
|
|
344
|
+
/** True when the source declared a parameter list, including an empty `()`. */
|
|
345
|
+
readonly hasParameterList: boolean;
|
|
346
|
+
readonly kind: "instrument";
|
|
347
|
+
readonly name: IdentExpr;
|
|
348
|
+
readonly parameters: readonly Parameter[];
|
|
349
|
+
readonly span: Span;
|
|
350
|
+
readonly typeParameters: readonly TypeParameter[];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Instantiation of an exported parameterized instrument. */
|
|
354
|
+
export interface InstrumentApplyDecl {
|
|
355
|
+
readonly application: ApplyExpr;
|
|
356
|
+
/** Module-local declarations carried only while this application is bound. */
|
|
357
|
+
readonly declarationScope?: readonly ApplicationScopeDecl[];
|
|
358
|
+
readonly exported: boolean;
|
|
359
|
+
readonly kind: "instrument_apply";
|
|
360
|
+
readonly metadata?: BlockExpr;
|
|
361
|
+
readonly name: IdentExpr;
|
|
362
|
+
readonly span: Span;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export interface TypeDecl {
|
|
366
|
+
readonly exported: boolean;
|
|
367
|
+
readonly kind: "type";
|
|
368
|
+
readonly name: IdentExpr;
|
|
369
|
+
readonly span: Span;
|
|
370
|
+
readonly value: Expr;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export interface ConstDecl {
|
|
374
|
+
readonly exported: boolean;
|
|
375
|
+
readonly kind: "const";
|
|
376
|
+
readonly name: IdentExpr;
|
|
377
|
+
readonly span: Span;
|
|
378
|
+
readonly type?: Expr;
|
|
379
|
+
readonly value: Expr;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export type ApplicationScopeDecl =
|
|
383
|
+
| ConstDecl
|
|
384
|
+
| InstrumentDecl
|
|
385
|
+
| PartyDecl
|
|
386
|
+
| PortDecl
|
|
387
|
+
| TypeDecl;
|
|
388
|
+
|
|
244
389
|
export type Decl =
|
|
245
390
|
| AssetDecl
|
|
391
|
+
| ConstDecl
|
|
392
|
+
| ExposeDecl
|
|
246
393
|
| ImportDecl
|
|
394
|
+
| InstrumentApplyDecl
|
|
395
|
+
| InstrumentDecl
|
|
396
|
+
| ModuleDecl
|
|
247
397
|
| PartyDecl
|
|
248
398
|
| PortDecl
|
|
249
399
|
| ProgramDecl
|
|
250
|
-
|
|
|
400
|
+
| SubjectDecl
|
|
401
|
+
| TypeDecl
|
|
402
|
+
| UseDecl;
|
|
251
403
|
|
|
252
404
|
// --- Program ---------------------------------------------------------------
|
|
253
405
|
|
|
@@ -263,6 +415,8 @@ export interface Program {
|
|
|
263
415
|
* it never throws, it returns the best-effort tree plus these.
|
|
264
416
|
*/
|
|
265
417
|
export interface Diagnostic {
|
|
418
|
+
readonly code?: string;
|
|
419
|
+
readonly fix?: string;
|
|
266
420
|
readonly message: string;
|
|
267
421
|
readonly span: Span;
|
|
268
422
|
}
|
package/src/cli.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `hsx` command line
|
|
2
|
+
* The `hsx` command line over the one compiler entry point and catalog.
|
|
3
3
|
*
|
|
4
4
|
* `check` prints diagnostics and says nothing else; `build` writes the
|
|
5
5
|
* compiled artifacts as JSON. Neither reads the environment, neither touches
|
|
@@ -10,8 +10,12 @@
|
|
|
10
10
|
* asserting on a subprocess's scrollback.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
13
14
|
import { compile, type CompileResult } from "./compile.ts";
|
|
14
|
-
import {
|
|
15
|
+
import type { UdlCostManifest, UdlCostTable } from "./cost.ts";
|
|
16
|
+
import { hsxDiagnostics } from "./diagnostics.ts";
|
|
17
|
+
import { format } from "./format.ts";
|
|
18
|
+
import { HSX_TARGET_UDL_VERSION, HSX_VERSION } from "./version.ts";
|
|
15
19
|
|
|
16
20
|
/** Filesystem and streams, injected so the CLI stays testable. */
|
|
17
21
|
export interface Io {
|
|
@@ -29,20 +33,27 @@ const OK = 0;
|
|
|
29
33
|
const REFUSED = 1;
|
|
30
34
|
const USAGE = 2;
|
|
31
35
|
|
|
32
|
-
const USAGE_TEXT = `hsx ${HSX_VERSION}, the HSX compiler
|
|
36
|
+
export const USAGE_TEXT = `hsx ${HSX_VERSION}, the HSX compiler
|
|
33
37
|
|
|
34
38
|
Usage:
|
|
35
39
|
hsx check <file.hsx> [--strict]
|
|
36
40
|
hsx build <file.hsx> [--out <file.json>] [--strict]
|
|
41
|
+
hsx cost <file.hsx> [--json] [--out <file.json>] [--strict]
|
|
42
|
+
hsx explain <HSX####>
|
|
43
|
+
hsx format <file.hsx>
|
|
37
44
|
hsx --version
|
|
38
45
|
hsx --help
|
|
39
46
|
|
|
40
47
|
Commands:
|
|
41
48
|
check Compile and report diagnostics. Prints nothing when the program is clean.
|
|
42
|
-
build Compile and write
|
|
49
|
+
build Compile and write canonical UDL and its Business Frame as JSON.
|
|
50
|
+
cost Compile and print the version-pinned cost manifest as a table or JSON.
|
|
51
|
+
explain Print one diagnostic title, fix, and source example.
|
|
52
|
+
format Print the source in the one canonical HSX style.
|
|
43
53
|
|
|
44
54
|
Options:
|
|
45
|
-
--
|
|
55
|
+
--json Print the cost manifest as JSON instead of a table.
|
|
56
|
+
--out <file> Write build or cost JSON to this path instead of stdout.
|
|
46
57
|
--strict Treat warning-severity diagnostics as failures.
|
|
47
58
|
|
|
48
59
|
Exit codes:
|
|
@@ -58,10 +69,16 @@ export async function runCli(argv: readonly string[], io: Io): Promise<number> {
|
|
|
58
69
|
return command === undefined ? USAGE : OK;
|
|
59
70
|
}
|
|
60
71
|
if (command === "--version" || command === "-v") {
|
|
61
|
-
io.out(`${HSX_VERSION} (
|
|
72
|
+
io.out(`${HSX_VERSION} (UDL version ${HSX_TARGET_UDL_VERSION})`);
|
|
62
73
|
return OK;
|
|
63
74
|
}
|
|
64
|
-
if (command
|
|
75
|
+
if (command === "explain") return explainDiagnostic(rest, io);
|
|
76
|
+
if (
|
|
77
|
+
command !== "check" &&
|
|
78
|
+
command !== "build" &&
|
|
79
|
+
command !== "cost" &&
|
|
80
|
+
command !== "format"
|
|
81
|
+
) {
|
|
65
82
|
io.err(`hsx: unknown command "${command}"`);
|
|
66
83
|
io.err(USAGE_TEXT);
|
|
67
84
|
return USAGE;
|
|
@@ -81,7 +98,26 @@ export async function runCli(argv: readonly string[], io: Io): Promise<number> {
|
|
|
81
98
|
return USAGE;
|
|
82
99
|
}
|
|
83
100
|
|
|
84
|
-
|
|
101
|
+
if (command === "format") {
|
|
102
|
+
const result = format(source);
|
|
103
|
+
if (!result.ok) {
|
|
104
|
+
for (const diagnostic of result.diagnostics) {
|
|
105
|
+
io.err(`${parsed.file}:1:1: error [parse] ${diagnostic.message}`);
|
|
106
|
+
}
|
|
107
|
+
return REFUSED;
|
|
108
|
+
}
|
|
109
|
+
io.out(result.formatted.trimEnd());
|
|
110
|
+
return OK;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let costTable: UdlCostTable;
|
|
114
|
+
try {
|
|
115
|
+
costTable = await readDefaultCostTable(io);
|
|
116
|
+
} catch (cause) {
|
|
117
|
+
io.err(`hsx: cannot read the packaged cost table: ${messageOf(cause)}`);
|
|
118
|
+
return USAGE;
|
|
119
|
+
}
|
|
120
|
+
const result = compile(source, { costTable });
|
|
85
121
|
for (const line of diagnosticLines(parsed.file, result)) io.err(line);
|
|
86
122
|
|
|
87
123
|
const refused =
|
|
@@ -93,6 +129,19 @@ export async function runCli(argv: readonly string[], io: Io): Promise<number> {
|
|
|
93
129
|
}
|
|
94
130
|
|
|
95
131
|
if (!result.artifacts) return REFUSED;
|
|
132
|
+
if (command === "cost") {
|
|
133
|
+
const json = `${JSON.stringify(result.artifacts.costManifest, null, 2)}\n`;
|
|
134
|
+
if (parsed.out !== undefined) {
|
|
135
|
+
const writeCode = await writeOutput(parsed.out, json, io);
|
|
136
|
+
return writeCode === OK && refused ? REFUSED : writeCode;
|
|
137
|
+
}
|
|
138
|
+
io.out(
|
|
139
|
+
parsed.json
|
|
140
|
+
? json.trimEnd()
|
|
141
|
+
: renderCostTable(result.artifacts.costManifest),
|
|
142
|
+
);
|
|
143
|
+
return refused ? REFUSED : OK;
|
|
144
|
+
}
|
|
96
145
|
const json = `${JSON.stringify(
|
|
97
146
|
{
|
|
98
147
|
document: result.artifacts.document,
|
|
@@ -116,26 +165,38 @@ export async function runCli(argv: readonly string[], io: Io): Promise<number> {
|
|
|
116
165
|
|
|
117
166
|
interface Options {
|
|
118
167
|
readonly file: string;
|
|
168
|
+
readonly json: boolean;
|
|
119
169
|
readonly out?: string;
|
|
120
170
|
readonly strict: boolean;
|
|
121
171
|
}
|
|
122
172
|
|
|
123
173
|
function parseOptions(
|
|
124
174
|
args: readonly string[],
|
|
125
|
-
command: "build" | "check",
|
|
175
|
+
command: "build" | "check" | "cost" | "format",
|
|
126
176
|
): Options | { readonly error: string } {
|
|
127
177
|
let file: string | undefined;
|
|
178
|
+
let json = false;
|
|
128
179
|
let out: string | undefined;
|
|
129
180
|
let strict = false;
|
|
130
181
|
|
|
131
182
|
for (let index = 0; index < args.length; index += 1) {
|
|
132
183
|
const argument = args[index] as string;
|
|
133
184
|
if (argument === "--strict") {
|
|
185
|
+
if (command === "format") {
|
|
186
|
+
return { error: "hsx format has no style options" };
|
|
187
|
+
}
|
|
134
188
|
strict = true;
|
|
135
189
|
continue;
|
|
136
190
|
}
|
|
191
|
+
if (argument === "--json") {
|
|
192
|
+
if (command !== "cost") return { error: "--json belongs to hsx cost" };
|
|
193
|
+
json = true;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
137
196
|
if (argument === "--out") {
|
|
138
|
-
if (command !== "build"
|
|
197
|
+
if (command !== "build" && command !== "cost") {
|
|
198
|
+
return { error: "--out belongs to hsx build or hsx cost" };
|
|
199
|
+
}
|
|
139
200
|
const value = args[index + 1];
|
|
140
201
|
if (value === undefined || value.startsWith("--")) {
|
|
141
202
|
return { error: "--out needs a file path" };
|
|
@@ -154,7 +215,98 @@ function parseOptions(
|
|
|
154
215
|
}
|
|
155
216
|
|
|
156
217
|
if (file === undefined) return { error: `hsx ${command} needs a file` };
|
|
157
|
-
return { file, ...(out === undefined ? {} : { out }), strict };
|
|
218
|
+
return { file, json, ...(out === undefined ? {} : { out }), strict };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function explainDiagnostic(args: readonly string[], io: Io): number {
|
|
222
|
+
if (args.length !== 1 || args[0]?.startsWith("-")) {
|
|
223
|
+
io.err("hsx: explain needs one diagnostic code");
|
|
224
|
+
return USAGE;
|
|
225
|
+
}
|
|
226
|
+
const diagnostic = hsxDiagnostics.find(({ code }) => code === args[0]);
|
|
227
|
+
if (!diagnostic) {
|
|
228
|
+
io.err(`hsx: unknown diagnostic code ${args[0]}`);
|
|
229
|
+
return USAGE;
|
|
230
|
+
}
|
|
231
|
+
io.out(
|
|
232
|
+
[
|
|
233
|
+
`${diagnostic.code} ${diagnostic.title}`,
|
|
234
|
+
`Stage: ${diagnostic.stage}`,
|
|
235
|
+
`Fix: ${diagnostic.fix}`,
|
|
236
|
+
diagnostic.example === null
|
|
237
|
+
? "Example: unavailable from source alone"
|
|
238
|
+
: `Example:\n${diagnostic.example}`,
|
|
239
|
+
].join("\n"),
|
|
240
|
+
);
|
|
241
|
+
return OK;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function renderCostTable(manifest: UdlCostManifest): string {
|
|
245
|
+
const rows = [
|
|
246
|
+
`costTableVersion\t${manifest.costTableVersion}`,
|
|
247
|
+
"instrument.action\teffect\tunit\tcount\ttotal\tpayer",
|
|
248
|
+
];
|
|
249
|
+
for (const action of manifest.actions) {
|
|
250
|
+
for (const component of action.components) {
|
|
251
|
+
const fixed = BigInt(component.perEventMinor) * BigInt(component.count);
|
|
252
|
+
const unit = [
|
|
253
|
+
`${component.perEventMinor} ${manifest.currency} minor`,
|
|
254
|
+
component.bps > 0 ? `${component.bps} bps` : undefined,
|
|
255
|
+
]
|
|
256
|
+
.filter((part): part is string => part !== undefined)
|
|
257
|
+
.join(" + ");
|
|
258
|
+
const total =
|
|
259
|
+
component.bps === 0
|
|
260
|
+
? `${fixed} ${manifest.currency} minor`
|
|
261
|
+
: [
|
|
262
|
+
fixed === 0n ? undefined : `${fixed} ${manifest.currency} minor`,
|
|
263
|
+
`amount-dependent (${component.bps} bps)`,
|
|
264
|
+
]
|
|
265
|
+
.filter((part): part is string => part !== undefined)
|
|
266
|
+
.join(" + ");
|
|
267
|
+
rows.push(
|
|
268
|
+
[
|
|
269
|
+
`${action.instrument}.${action.action}`,
|
|
270
|
+
component.signature,
|
|
271
|
+
unit,
|
|
272
|
+
component.count,
|
|
273
|
+
total,
|
|
274
|
+
component.payer,
|
|
275
|
+
].join("\t"),
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return rows.join("\n");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function writeOutput(
|
|
283
|
+
path: string,
|
|
284
|
+
contents: string,
|
|
285
|
+
io: Io,
|
|
286
|
+
): Promise<number> {
|
|
287
|
+
try {
|
|
288
|
+
await io.writeFile(path, contents);
|
|
289
|
+
} catch (cause) {
|
|
290
|
+
io.err(`hsx: cannot write ${path}: ${messageOf(cause)}`);
|
|
291
|
+
return USAGE;
|
|
292
|
+
}
|
|
293
|
+
return OK;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function readDefaultCostTable(io: Io): Promise<UdlCostTable> {
|
|
297
|
+
const candidates = [
|
|
298
|
+
fileURLToPath(new URL("../examples/cost-table.json", import.meta.url)),
|
|
299
|
+
fileURLToPath(new URL("../../examples/cost-table.json", import.meta.url)),
|
|
300
|
+
];
|
|
301
|
+
let lastError: unknown;
|
|
302
|
+
for (const path of candidates) {
|
|
303
|
+
try {
|
|
304
|
+
return JSON.parse(await io.readFile(path)) as UdlCostTable;
|
|
305
|
+
} catch (cause) {
|
|
306
|
+
lastError = cause;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
throw lastError;
|
|
158
310
|
}
|
|
159
311
|
|
|
160
312
|
/** `file:line:col: severity [stage] message`, the shape editors already parse. */
|
|
@@ -164,7 +316,7 @@ function diagnosticLines(
|
|
|
164
316
|
): readonly string[] {
|
|
165
317
|
return result.diagnostics.map(
|
|
166
318
|
(diagnostic) =>
|
|
167
|
-
`${file}:${diagnostic.line}:${diagnostic.column}: ${diagnostic.severity} [${diagnostic.stage}] ${diagnostic.message}`,
|
|
319
|
+
`${diagnostic.file ?? file}:${diagnostic.line}:${diagnostic.column}: ${diagnostic.severity} [${diagnostic.stage}] ${diagnostic.message}`,
|
|
168
320
|
);
|
|
169
321
|
}
|
|
170
322
|
|