@hyperscale0/hsx 2.3.0 → 2.4.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 +15 -1
- package/dist/src/cli.d.ts +1 -1
- package/dist/src/std-bundle.js +1 -1
- package/dist/src/std-bundle.js.map +1 -1
- package/dist/src/typecheck.d.ts.map +1 -1
- package/dist/src/typecheck.js +118 -87
- package/dist/src/typecheck.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/docs/llms-full.txt +7 -4
- package/docs/llms.txt +2 -2
- package/docs/reference/cli.md +2 -2
- package/docs/reference/diagnostics.md +1 -1
- package/docs/reference/grammar.md +2 -2
- package/docs/reference/std/advance.md +1 -1
- package/docs/reference/std/cancellable_booking.md +5 -2
- package/docs/reference/std/captured_payment.md +1 -1
- package/docs/reference/std/conditional_disbursement.md +1 -1
- package/docs/reference/std/credit_facility.md +1 -1
- package/docs/reference/std/held_payment.md +1 -1
- package/docs/reference/std/instant_transfer.md +1 -1
- package/docs/reference/std/metered.md +1 -1
- package/docs/reference/std/pooled_split.md +1 -1
- package/docs/reference/std/premium_forward.md +1 -1
- package/docs/reference/std/reconciled_payout.md +1 -1
- package/docs/reference/std/rotating_pool.md +1 -1
- package/docs/reference/std/scheduled.md +1 -1
- package/docs/reference/std/security_deposit.md +1 -1
- package/docs/reference/std/settlement_batch.md +1 -1
- package/docs/reference/std/swap.md +1 -1
- package/docs/reference/std/threshold_pool.md +1 -1
- package/docs/reference/std/weighted_distribution.md +1 -1
- package/docs/reference/types.md +1 -1
- package/docs/reference/udl-output.md +1 -1
- package/examples/cancellable_booking/cancellable_booking.udl +1 -0
- package/examples/cost-table.json +36 -4
- package/package.json +3 -3
- package/src/std-bundle.ts +1 -1
- package/src/typecheck.ts +225 -89
- package/src/version.ts +1 -1
- package/std/money_flows/cancellable_booking.hsx +3 -1
package/src/typecheck.ts
CHANGED
|
@@ -284,6 +284,30 @@ export function checkGeneralProgram(
|
|
|
284
284
|
} else templates.set(decl.name.name, decl);
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
+
const prefixByInstrument = new Map<string, string>();
|
|
288
|
+
if (options.publishedCatalog) {
|
|
289
|
+
for (const inst of options.publishedCatalog.instruments) {
|
|
290
|
+
if (inst.idPrefix) {
|
|
291
|
+
prefixByInstrument.set(inst.id, inst.idPrefix);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
for (const [name, decl] of templates) {
|
|
296
|
+
const prefix =
|
|
297
|
+
stringSlot(decl.body, "idPrefix", "id_prefix") ?? prefixFor(name);
|
|
298
|
+
prefixByInstrument.set(name, prefix);
|
|
299
|
+
}
|
|
300
|
+
for (const decl of program.decls) {
|
|
301
|
+
if (decl.kind === "instrument_apply" && decl.metadata) {
|
|
302
|
+
const prefix = stringSlot(decl.metadata, "idPrefix", "id_prefix");
|
|
303
|
+
if (prefix) {
|
|
304
|
+
prefixByInstrument.set(decl.name.name, prefix);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const resolvePrefix = (target: string): string | undefined =>
|
|
309
|
+
prefixByInstrument.get(target);
|
|
310
|
+
|
|
287
311
|
const concrete: ConcreteInstrument[] = [];
|
|
288
312
|
const reachedPorts = new Set<PortDecl>();
|
|
289
313
|
const droppedPorts = new Set<PortDecl>();
|
|
@@ -291,10 +315,10 @@ export function checkGeneralProgram(
|
|
|
291
315
|
declarationScope: readonly ApplicationScopeDecl[],
|
|
292
316
|
) => {
|
|
293
317
|
const scopedAliases = new Map(aliases);
|
|
294
|
-
const scopedConstants = resolveConstants(
|
|
295
|
-
...program.decls,
|
|
296
|
-
|
|
297
|
-
|
|
318
|
+
const scopedConstants = resolveConstants(
|
|
319
|
+
[...program.decls, ...declarationScope],
|
|
320
|
+
resolvePrefix,
|
|
321
|
+
);
|
|
298
322
|
const scopedParties = new Set(parties);
|
|
299
323
|
const scopedPorts = new Map(ports);
|
|
300
324
|
const scopedTemplates = new Map(templates);
|
|
@@ -305,6 +329,10 @@ export function checkGeneralProgram(
|
|
|
305
329
|
if (local.kind === "port") scopedPorts.set(local.name.name, local);
|
|
306
330
|
if (local.kind === "instrument") {
|
|
307
331
|
scopedTemplates.set(local.name.name, local);
|
|
332
|
+
const prefix =
|
|
333
|
+
stringSlot(local.body, "idPrefix", "id_prefix") ??
|
|
334
|
+
prefixFor(local.name.name);
|
|
335
|
+
prefixByInstrument.set(local.name.name, prefix);
|
|
308
336
|
}
|
|
309
337
|
}
|
|
310
338
|
checkPortDeclarations(scopedPorts, scopedParties);
|
|
@@ -350,24 +378,37 @@ export function checkGeneralProgram(
|
|
|
350
378
|
values,
|
|
351
379
|
diagnostics,
|
|
352
380
|
dependencies,
|
|
381
|
+
[],
|
|
382
|
+
resolvePrefix,
|
|
353
383
|
);
|
|
354
384
|
const body = expandComprehensions(
|
|
355
385
|
selected,
|
|
356
386
|
diagnostics,
|
|
357
387
|
values,
|
|
358
388
|
dependencies,
|
|
389
|
+
resolvePrefix,
|
|
359
390
|
);
|
|
391
|
+
const constructed = constructedInstruments(
|
|
392
|
+
decl.name,
|
|
393
|
+
body,
|
|
394
|
+
diagnostics,
|
|
395
|
+
resolvePrefix,
|
|
396
|
+
);
|
|
397
|
+
for (const candidate of constructed) {
|
|
398
|
+
const prefix =
|
|
399
|
+
stringSlot(candidate.body, "idPrefix", "id_prefix") ??
|
|
400
|
+
prefixFor(candidate.name.name);
|
|
401
|
+
prefixByInstrument.set(candidate.name.name, prefix);
|
|
402
|
+
}
|
|
360
403
|
concrete.push(
|
|
361
|
-
...bindActionPorts(
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
parties: scoped.parties,
|
|
370
|
-
})),
|
|
404
|
+
...bindActionPorts(constructed, scoped.ports, reachedPorts).map(
|
|
405
|
+
(candidate) => ({
|
|
406
|
+
...candidate,
|
|
407
|
+
aliases: scoped.aliases,
|
|
408
|
+
callee: candidate.name.name,
|
|
409
|
+
parties: scoped.parties,
|
|
410
|
+
}),
|
|
411
|
+
),
|
|
371
412
|
);
|
|
372
413
|
}
|
|
373
414
|
if (decl.kind !== "instrument_apply") continue;
|
|
@@ -402,32 +443,43 @@ export function checkGeneralProgram(
|
|
|
402
443
|
scoped.aliases,
|
|
403
444
|
scoped.constants,
|
|
404
445
|
boundPorts,
|
|
446
|
+
resolvePrefix,
|
|
405
447
|
);
|
|
406
448
|
if (body) {
|
|
407
449
|
const applicationPorts = new Set<PortDecl>();
|
|
408
450
|
const merged = decl.metadata
|
|
409
451
|
? mergeApplicationMetadata(body, decl.metadata, diagnostics)
|
|
410
452
|
: body;
|
|
453
|
+
const constructed = constructedInstruments(
|
|
454
|
+
decl.name,
|
|
455
|
+
merged,
|
|
456
|
+
diagnostics,
|
|
457
|
+
resolvePrefix,
|
|
458
|
+
);
|
|
459
|
+
for (const candidate of constructed) {
|
|
460
|
+
const prefix =
|
|
461
|
+
stringSlot(candidate.body, "idPrefix", "id_prefix") ??
|
|
462
|
+
prefixFor(candidate.name.name);
|
|
463
|
+
prefixByInstrument.set(candidate.name.name, prefix);
|
|
464
|
+
}
|
|
411
465
|
concrete.push(
|
|
412
|
-
...bindActionPorts(
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
};
|
|
430
|
-
}),
|
|
466
|
+
...bindActionPorts(constructed, boundPorts, applicationPorts).map(
|
|
467
|
+
(candidate) => {
|
|
468
|
+
let candidateCallee = callee;
|
|
469
|
+
if (
|
|
470
|
+
candidate.name.name !== decl.name.name &&
|
|
471
|
+
candidate.name.name.startsWith(`${decl.name.name}_`)
|
|
472
|
+
) {
|
|
473
|
+
candidateCallee = `${callee}${candidate.name.name.slice(decl.name.name.length)}`;
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
...candidate,
|
|
477
|
+
aliases: scoped.aliases,
|
|
478
|
+
callee: candidateCallee,
|
|
479
|
+
parties: scoped.parties,
|
|
480
|
+
};
|
|
481
|
+
},
|
|
482
|
+
),
|
|
431
483
|
);
|
|
432
484
|
for (const port of boundPorts.values()) {
|
|
433
485
|
if (applicationPorts.has(port)) reachedPorts.add(port);
|
|
@@ -457,27 +509,12 @@ export function checkGeneralProgram(
|
|
|
457
509
|
);
|
|
458
510
|
}
|
|
459
511
|
|
|
460
|
-
const prefixByInstrument = new Map<string, string>();
|
|
461
|
-
if (options.publishedCatalog) {
|
|
462
|
-
for (const inst of options.publishedCatalog.instruments) {
|
|
463
|
-
if (inst.idPrefix) {
|
|
464
|
-
prefixByInstrument.set(inst.id, inst.idPrefix);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
for (const [name, decl] of templates) {
|
|
469
|
-
const prefix =
|
|
470
|
-
stringSlot(decl.body, "idPrefix", "id_prefix") ?? prefixFor(name);
|
|
471
|
-
prefixByInstrument.set(name, prefix);
|
|
472
|
-
}
|
|
473
512
|
for (const candidate of allocated) {
|
|
474
513
|
const prefix =
|
|
475
514
|
stringSlot(candidate.body, "idPrefix", "id_prefix") ??
|
|
476
515
|
prefixFor(candidate.name.name);
|
|
477
516
|
prefixByInstrument.set(candidate.name.name, prefix);
|
|
478
517
|
}
|
|
479
|
-
const resolvePrefix = (target: string): string | undefined =>
|
|
480
|
-
prefixByInstrument.get(target);
|
|
481
518
|
|
|
482
519
|
const instruments: TypedInstrument[] = [];
|
|
483
520
|
const ids = new Set<string>();
|
|
@@ -501,7 +538,13 @@ export function checkGeneralProgram(
|
|
|
501
538
|
);
|
|
502
539
|
continue;
|
|
503
540
|
}
|
|
504
|
-
const expandedBody = expandComprehensions(
|
|
541
|
+
const expandedBody = expandComprehensions(
|
|
542
|
+
candidate.body,
|
|
543
|
+
diagnostics,
|
|
544
|
+
new Map(),
|
|
545
|
+
new Map(),
|
|
546
|
+
resolvePrefix,
|
|
547
|
+
);
|
|
505
548
|
const checked = checkInstrument(
|
|
506
549
|
candidate.name,
|
|
507
550
|
expandedBody,
|
|
@@ -1323,12 +1366,19 @@ function constructedInstruments(
|
|
|
1323
1366
|
rootName: IdentExpr,
|
|
1324
1367
|
body: BlockExpr,
|
|
1325
1368
|
diagnostics: GeneralDiagnostic[],
|
|
1369
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
1326
1370
|
): {
|
|
1327
1371
|
readonly body: BlockExpr;
|
|
1328
1372
|
readonly generatedPrefix: boolean;
|
|
1329
1373
|
readonly name: IdentExpr;
|
|
1330
1374
|
}[] {
|
|
1331
|
-
const expanded = expandComprehensions(
|
|
1375
|
+
const expanded = expandComprehensions(
|
|
1376
|
+
body,
|
|
1377
|
+
diagnostics,
|
|
1378
|
+
new Map(),
|
|
1379
|
+
new Map(),
|
|
1380
|
+
resolvePrefix,
|
|
1381
|
+
);
|
|
1332
1382
|
const construction = entry(expanded, "instruments");
|
|
1333
1383
|
const root = {
|
|
1334
1384
|
body: {
|
|
@@ -1498,7 +1548,10 @@ function checkConstants(
|
|
|
1498
1548
|
}
|
|
1499
1549
|
}
|
|
1500
1550
|
|
|
1501
|
-
function resolveConstants(
|
|
1551
|
+
function resolveConstants(
|
|
1552
|
+
decls: readonly Decl[],
|
|
1553
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
1554
|
+
): ReadonlyMap<string, Expr> {
|
|
1502
1555
|
const constants = new Map(
|
|
1503
1556
|
decls
|
|
1504
1557
|
.filter((decl): decl is ConstDecl => decl.kind === "const")
|
|
@@ -1506,7 +1559,7 @@ function resolveConstants(decls: readonly Decl[]): ReadonlyMap<string, Expr> {
|
|
|
1506
1559
|
);
|
|
1507
1560
|
for (let pass = 0; pass < constants.size; pass += 1) {
|
|
1508
1561
|
for (const [name, value] of constants) {
|
|
1509
|
-
constants.set(name, substituteExpr(value, constants));
|
|
1562
|
+
constants.set(name, substituteExpr(value, constants, resolvePrefix));
|
|
1510
1563
|
}
|
|
1511
1564
|
}
|
|
1512
1565
|
return constants;
|
|
@@ -1539,6 +1592,7 @@ function instantiate(
|
|
|
1539
1592
|
aliases: ReadonlyMap<string, Expr>,
|
|
1540
1593
|
constants: ReadonlyMap<string, Expr>,
|
|
1541
1594
|
boundPorts: Map<string, PortDecl>,
|
|
1595
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
1542
1596
|
): BlockExpr | undefined {
|
|
1543
1597
|
const loweredPortShapes = new Map<string, BlockExpr>();
|
|
1544
1598
|
const portDependencies = new Map<string, readonly string[]>();
|
|
@@ -1647,6 +1701,7 @@ function instantiate(
|
|
|
1647
1701
|
const expectedExpr = substituteExpr(
|
|
1648
1702
|
optionalInner(parameter.type) ?? parameter.type,
|
|
1649
1703
|
values,
|
|
1704
|
+
resolvePrefix,
|
|
1650
1705
|
);
|
|
1651
1706
|
const expected = typeOf(expectedExpr, aliases);
|
|
1652
1707
|
const actual = typeOf(value, aliases);
|
|
@@ -1704,6 +1759,7 @@ function instantiate(
|
|
|
1704
1759
|
type: substituteExpr(
|
|
1705
1760
|
optionalInner(parameter.type) ?? parameter.type,
|
|
1706
1761
|
values,
|
|
1762
|
+
resolvePrefix,
|
|
1707
1763
|
),
|
|
1708
1764
|
});
|
|
1709
1765
|
values.set(parameter.name.name, value);
|
|
@@ -1724,6 +1780,7 @@ function instantiate(
|
|
|
1724
1780
|
aliases,
|
|
1725
1781
|
loweredPortShapes,
|
|
1726
1782
|
portDependencies,
|
|
1783
|
+
resolvePrefix,
|
|
1727
1784
|
);
|
|
1728
1785
|
}
|
|
1729
1786
|
}
|
|
@@ -1733,6 +1790,8 @@ function instantiate(
|
|
|
1733
1790
|
values,
|
|
1734
1791
|
diagnostics,
|
|
1735
1792
|
portDependencies,
|
|
1793
|
+
[],
|
|
1794
|
+
resolvePrefix,
|
|
1736
1795
|
);
|
|
1737
1796
|
const dependencyDiagnosticStart = diagnostics.length;
|
|
1738
1797
|
for (const [name, parameter] of missingOptional) {
|
|
@@ -1753,6 +1812,7 @@ function instantiate(
|
|
|
1753
1812
|
diagnostics,
|
|
1754
1813
|
values,
|
|
1755
1814
|
portDependencies,
|
|
1815
|
+
resolvePrefix,
|
|
1756
1816
|
);
|
|
1757
1817
|
const binding: Entry = {
|
|
1758
1818
|
key: { kind: "ident", name: "template_binding", span: application.span },
|
|
@@ -2041,6 +2101,7 @@ function lowerPortShapeField(
|
|
|
2041
2101
|
row: Entry,
|
|
2042
2102
|
aliases: ReadonlyMap<string, Expr>,
|
|
2043
2103
|
diagnostics: GeneralDiagnostic[],
|
|
2104
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
2044
2105
|
): Entry | undefined {
|
|
2045
2106
|
const type = typeOf(row.value, aliases);
|
|
2046
2107
|
if (
|
|
@@ -2079,7 +2140,7 @@ function lowerPortShapeField(
|
|
|
2079
2140
|
extras.max_length = 180;
|
|
2080
2141
|
extras.min_length = 1;
|
|
2081
2142
|
if (type.target)
|
|
2082
|
-
extras.pattern = `^${prefixFor(type.target)}_(sandbox|live)_[a-z0-9]{8,64}$`;
|
|
2143
|
+
extras.pattern = `^${resolvePrefix?.(type.target) ?? prefixFor(type.target)}_(sandbox|live)_[a-z0-9]{8,64}$`;
|
|
2083
2144
|
} else if (type.kind === "account") {
|
|
2084
2145
|
extras.pattern = ACCOUNT_PATTERN;
|
|
2085
2146
|
}
|
|
@@ -2152,6 +2213,7 @@ function lowerPortShape(
|
|
|
2152
2213
|
rawShape: Expr,
|
|
2153
2214
|
aliases: ReadonlyMap<string, Expr>,
|
|
2154
2215
|
diagnostics: GeneralDiagnostic[],
|
|
2216
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
2155
2217
|
): BlockExpr {
|
|
2156
2218
|
if (rawShape.kind !== "block") {
|
|
2157
2219
|
diagnostics.push({
|
|
@@ -2177,7 +2239,13 @@ function lowerPortShape(
|
|
|
2177
2239
|
continue;
|
|
2178
2240
|
}
|
|
2179
2241
|
names.add(row.key.name);
|
|
2180
|
-
const lowered = lowerPortShapeField(
|
|
2242
|
+
const lowered = lowerPortShapeField(
|
|
2243
|
+
port,
|
|
2244
|
+
row,
|
|
2245
|
+
aliases,
|
|
2246
|
+
diagnostics,
|
|
2247
|
+
resolvePrefix,
|
|
2248
|
+
);
|
|
2181
2249
|
if (lowered) entries.push(lowered);
|
|
2182
2250
|
}
|
|
2183
2251
|
return {
|
|
@@ -2284,6 +2352,7 @@ function bindPortCompileValues(
|
|
|
2284
2352
|
aliases: ReadonlyMap<string, Expr> = new Map(),
|
|
2285
2353
|
loweredPortShapes: Map<string, BlockExpr> = new Map(),
|
|
2286
2354
|
dependencies: Map<string, readonly string[]> = new Map(),
|
|
2355
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
2287
2356
|
): void {
|
|
2288
2357
|
const port = ports.get(value.name.name);
|
|
2289
2358
|
if (!port) {
|
|
@@ -2308,7 +2377,13 @@ function bindPortCompileValues(
|
|
|
2308
2377
|
span: port.body.span,
|
|
2309
2378
|
};
|
|
2310
2379
|
const shapeDiagnostics: GeneralDiagnostic[] = [];
|
|
2311
|
-
shape = lowerPortShape(
|
|
2380
|
+
shape = lowerPortShape(
|
|
2381
|
+
port,
|
|
2382
|
+
rawShape,
|
|
2383
|
+
aliases,
|
|
2384
|
+
shapeDiagnostics,
|
|
2385
|
+
resolvePrefix,
|
|
2386
|
+
);
|
|
2312
2387
|
for (const diagnostic of shapeDiagnostics) {
|
|
2313
2388
|
if (
|
|
2314
2389
|
!diagnostics.some(
|
|
@@ -2336,7 +2411,11 @@ function bindPortCompileValues(
|
|
|
2336
2411
|
if (value.deadline) values.set(`${binding}_deadline`, value.deadline);
|
|
2337
2412
|
}
|
|
2338
2413
|
|
|
2339
|
-
function substituteExpr(
|
|
2414
|
+
function substituteExpr(
|
|
2415
|
+
expr: Expr,
|
|
2416
|
+
values: ReadonlyMap<string, Expr>,
|
|
2417
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
2418
|
+
): Expr {
|
|
2340
2419
|
if (expr.kind === "ident") {
|
|
2341
2420
|
return values.get(expr.name) ?? substituteName(expr, values);
|
|
2342
2421
|
}
|
|
@@ -2375,7 +2454,11 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
|
|
|
2375
2454
|
? {
|
|
2376
2455
|
iteration: {
|
|
2377
2456
|
...entry.iteration,
|
|
2378
|
-
bound: substituteExpr(
|
|
2457
|
+
bound: substituteExpr(
|
|
2458
|
+
entry.iteration.bound,
|
|
2459
|
+
values,
|
|
2460
|
+
resolvePrefix,
|
|
2461
|
+
),
|
|
2379
2462
|
},
|
|
2380
2463
|
}
|
|
2381
2464
|
: {}),
|
|
@@ -2394,7 +2477,7 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
|
|
|
2394
2477
|
? nameFromValue(qualifier, value)
|
|
2395
2478
|
: substituteName(qualifier, nestedValues);
|
|
2396
2479
|
}),
|
|
2397
|
-
value: substituteExpr(entry.value, nestedValues),
|
|
2480
|
+
value: substituteExpr(entry.value, nestedValues, resolvePrefix),
|
|
2398
2481
|
};
|
|
2399
2482
|
}),
|
|
2400
2483
|
};
|
|
@@ -2402,13 +2485,15 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
|
|
|
2402
2485
|
if (expr.kind === "list") {
|
|
2403
2486
|
return {
|
|
2404
2487
|
...expr,
|
|
2405
|
-
items: expr.items.map((item) =>
|
|
2488
|
+
items: expr.items.map((item) =>
|
|
2489
|
+
substituteExpr(item, values, resolvePrefix),
|
|
2490
|
+
),
|
|
2406
2491
|
};
|
|
2407
2492
|
}
|
|
2408
2493
|
if (expr.kind === "type_apply") {
|
|
2409
2494
|
return {
|
|
2410
2495
|
...expr,
|
|
2411
|
-
args: expr.args.map((arg) => substituteExpr(arg, values)),
|
|
2496
|
+
args: expr.args.map((arg) => substituteExpr(arg, values, resolvePrefix)),
|
|
2412
2497
|
};
|
|
2413
2498
|
}
|
|
2414
2499
|
if (expr.kind === "call") {
|
|
@@ -2417,7 +2502,9 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
|
|
|
2417
2502
|
);
|
|
2418
2503
|
const substituted = {
|
|
2419
2504
|
...expr,
|
|
2420
|
-
args: expr.args.map((arg) =>
|
|
2505
|
+
args: expr.args.map((arg) =>
|
|
2506
|
+
substituteCompileArgument(arg, values, resolvePrefix),
|
|
2507
|
+
),
|
|
2421
2508
|
};
|
|
2422
2509
|
const structural = new Set([
|
|
2423
2510
|
"at",
|
|
@@ -2426,24 +2513,31 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
|
|
|
2426
2513
|
"keys_except",
|
|
2427
2514
|
"kind",
|
|
2428
2515
|
"len",
|
|
2516
|
+
"prefix",
|
|
2429
2517
|
"values",
|
|
2430
2518
|
]).has(expr.callee.name);
|
|
2431
2519
|
return hasUnbound && !structural
|
|
2432
2520
|
? substituted
|
|
2433
|
-
: (evaluateCompileTimeCall(substituted, !hasUnbound) ??
|
|
2521
|
+
: (evaluateCompileTimeCall(substituted, !hasUnbound, resolvePrefix) ??
|
|
2522
|
+
substituted);
|
|
2434
2523
|
}
|
|
2435
2524
|
if (expr.kind === "apply") {
|
|
2436
2525
|
return {
|
|
2437
2526
|
...expr,
|
|
2438
|
-
args: expr.args.map((arg) => substituteExpr(arg, values)),
|
|
2439
|
-
typeArgs: expr.typeArgs.map((arg) =>
|
|
2527
|
+
args: expr.args.map((arg) => substituteExpr(arg, values, resolvePrefix)),
|
|
2528
|
+
typeArgs: expr.typeArgs.map((arg) =>
|
|
2529
|
+
substituteExpr(arg, values, resolvePrefix),
|
|
2530
|
+
),
|
|
2440
2531
|
};
|
|
2441
2532
|
}
|
|
2442
2533
|
if (expr.kind === "binding") {
|
|
2443
|
-
return { ...expr, type: substituteExpr(expr.type, values) };
|
|
2534
|
+
return { ...expr, type: substituteExpr(expr.type, values, resolvePrefix) };
|
|
2444
2535
|
}
|
|
2445
2536
|
if (expr.kind === "decided_amount") {
|
|
2446
|
-
return {
|
|
2537
|
+
return {
|
|
2538
|
+
...expr,
|
|
2539
|
+
body: substituteExpr(expr.body, values, resolvePrefix) as BlockExpr,
|
|
2540
|
+
};
|
|
2447
2541
|
}
|
|
2448
2542
|
return expr;
|
|
2449
2543
|
}
|
|
@@ -2451,8 +2545,9 @@ function substituteExpr(expr: Expr, values: ReadonlyMap<string, Expr>): Expr {
|
|
|
2451
2545
|
function substituteCompileArgument(
|
|
2452
2546
|
expr: Expr,
|
|
2453
2547
|
values: ReadonlyMap<string, Expr>,
|
|
2548
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
2454
2549
|
): Expr {
|
|
2455
|
-
if (expr.kind !== "ident") return substituteExpr(expr, values);
|
|
2550
|
+
if (expr.kind !== "ident") return substituteExpr(expr, values, resolvePrefix);
|
|
2456
2551
|
const value = values.get(expr.name);
|
|
2457
2552
|
if (!value) return expr;
|
|
2458
2553
|
if (value.kind === "ident" || value.kind === "port_ref") {
|
|
@@ -2551,7 +2646,7 @@ function argumentMatches(
|
|
|
2551
2646
|
);
|
|
2552
2647
|
case "unknown":
|
|
2553
2648
|
if (expected.kind !== "ident") return false;
|
|
2554
|
-
if (expected.name === "unknown") return true;
|
|
2649
|
+
if (expected.name === "unknown" || expected.name === "json") return true;
|
|
2555
2650
|
return (
|
|
2556
2651
|
expected.name === "block" &&
|
|
2557
2652
|
(actual.kind === "block" || actual.kind === "decided_amount")
|
|
@@ -2618,6 +2713,7 @@ function selectCompileTimeRows(
|
|
|
2618
2713
|
diagnostics: GeneralDiagnostic[],
|
|
2619
2714
|
dependencies: ReadonlyMap<string, readonly string[]> = new Map(),
|
|
2620
2715
|
controls: readonly string[] = [],
|
|
2716
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
2621
2717
|
): BlockExpr {
|
|
2622
2718
|
const entries: Entry[] = [];
|
|
2623
2719
|
const locals = new Map(values);
|
|
@@ -2630,13 +2726,16 @@ function selectCompileTimeRows(
|
|
|
2630
2726
|
binding.name,
|
|
2631
2727
|
expressionPorts(row.value, localDependencies),
|
|
2632
2728
|
);
|
|
2633
|
-
locals.set(
|
|
2729
|
+
locals.set(
|
|
2730
|
+
binding.name,
|
|
2731
|
+
substituteExpr(row.value, locals, resolvePrefix),
|
|
2732
|
+
);
|
|
2634
2733
|
}
|
|
2635
2734
|
entries.push(row);
|
|
2636
2735
|
continue;
|
|
2637
2736
|
}
|
|
2638
2737
|
if (row.key.name === "unsupported") {
|
|
2639
|
-
const value = substituteExpr(row.value, locals);
|
|
2738
|
+
const value = substituteExpr(row.value, locals, resolvePrefix);
|
|
2640
2739
|
if (value.kind !== "block") {
|
|
2641
2740
|
diagnostics.push({
|
|
2642
2741
|
code: "HSX1405",
|
|
@@ -2679,6 +2778,7 @@ function selectCompileTimeRows(
|
|
|
2679
2778
|
diagnostics,
|
|
2680
2779
|
localDependencies,
|
|
2681
2780
|
controls,
|
|
2781
|
+
resolvePrefix,
|
|
2682
2782
|
)
|
|
2683
2783
|
: row.value;
|
|
2684
2784
|
const conditionPorts =
|
|
@@ -2755,6 +2855,7 @@ function selectCompileTimeRows(
|
|
|
2755
2855
|
...(subject ? expressionPorts(subject, localDependencies) : []),
|
|
2756
2856
|
]),
|
|
2757
2857
|
],
|
|
2858
|
+
resolvePrefix,
|
|
2758
2859
|
).entries,
|
|
2759
2860
|
);
|
|
2760
2861
|
}
|
|
@@ -2775,6 +2876,7 @@ function compileTimeTruthy(value: Expr | undefined): boolean {
|
|
|
2775
2876
|
function evaluateCompileTimeCall(
|
|
2776
2877
|
expr: Extract<Expr, { readonly kind: "call" }>,
|
|
2777
2878
|
resolveMissing = true,
|
|
2879
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
2778
2880
|
): Expr | undefined {
|
|
2779
2881
|
const [first, second, third, fourth] = expr.args;
|
|
2780
2882
|
const number = (value: Expr | undefined): number | undefined => {
|
|
@@ -2962,7 +3064,9 @@ function evaluateCompileTimeCall(
|
|
|
2962
3064
|
case "words":
|
|
2963
3065
|
return first ? text(nameText(first).replaceAll("_", " ")) : undefined;
|
|
2964
3066
|
case "prefix":
|
|
2965
|
-
return first
|
|
3067
|
+
return first
|
|
3068
|
+
? text(resolvePrefix?.(nameText(first)) ?? prefixFor(nameText(first)))
|
|
3069
|
+
: undefined;
|
|
2966
3070
|
case "owner":
|
|
2967
3071
|
return first?.kind === "settlement_ref"
|
|
2968
3072
|
? text(first.owner.name)
|
|
@@ -3039,6 +3143,7 @@ function expandComprehensions(
|
|
|
3039
3143
|
diagnostics: GeneralDiagnostic[],
|
|
3040
3144
|
values: ReadonlyMap<string, Expr> = new Map(),
|
|
3041
3145
|
dependencies: ReadonlyMap<string, readonly string[]> = new Map(),
|
|
3146
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
3042
3147
|
): BlockExpr {
|
|
3043
3148
|
const budget = { used: 0 };
|
|
3044
3149
|
const expand = (
|
|
@@ -3069,7 +3174,10 @@ function expandComprehensions(
|
|
|
3069
3174
|
binding.name,
|
|
3070
3175
|
expressionPorts(row.value, localDependencies),
|
|
3071
3176
|
);
|
|
3072
|
-
locals.set(
|
|
3177
|
+
locals.set(
|
|
3178
|
+
binding.name,
|
|
3179
|
+
substituteExpr(row.value, locals, resolvePrefix),
|
|
3180
|
+
);
|
|
3073
3181
|
continue;
|
|
3074
3182
|
}
|
|
3075
3183
|
if (
|
|
@@ -3108,7 +3216,7 @@ function expandComprehensions(
|
|
|
3108
3216
|
? nameFromValue(qualifier, value)
|
|
3109
3217
|
: substituteName(qualifier, locals);
|
|
3110
3218
|
}),
|
|
3111
|
-
value: substituteExpr(row.value, locals),
|
|
3219
|
+
value: substituteExpr(row.value, locals, resolvePrefix),
|
|
3112
3220
|
};
|
|
3113
3221
|
entries.push({
|
|
3114
3222
|
...substituted,
|
|
@@ -3120,8 +3228,8 @@ function expandComprehensions(
|
|
|
3120
3228
|
continue;
|
|
3121
3229
|
}
|
|
3122
3230
|
if (row.value.kind !== "block") continue;
|
|
3123
|
-
const bound = substituteExpr(row.iteration.bound, locals);
|
|
3124
|
-
const values = finiteIterationValues(bound);
|
|
3231
|
+
const bound = substituteExpr(row.iteration.bound, locals, resolvePrefix);
|
|
3232
|
+
const values = finiteIterationValues(bound, resolvePrefix);
|
|
3125
3233
|
if (!values) {
|
|
3126
3234
|
diagnostics.push({
|
|
3127
3235
|
code: "HSX1403",
|
|
@@ -3154,6 +3262,7 @@ function expandComprehensions(
|
|
|
3154
3262
|
diagnostics,
|
|
3155
3263
|
localDependencies,
|
|
3156
3264
|
row.conditionPorts,
|
|
3265
|
+
resolvePrefix,
|
|
3157
3266
|
);
|
|
3158
3267
|
entries.push(...expand(body, substitutions, localDependencies).entries);
|
|
3159
3268
|
}
|
|
@@ -3163,10 +3272,15 @@ function expandComprehensions(
|
|
|
3163
3272
|
return expand(block, values);
|
|
3164
3273
|
}
|
|
3165
3274
|
|
|
3166
|
-
function finiteIterationValues(
|
|
3275
|
+
function finiteIterationValues(
|
|
3276
|
+
bound: Expr,
|
|
3277
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
3278
|
+
): readonly Expr[] | undefined {
|
|
3167
3279
|
if (bound.kind === "call") {
|
|
3168
|
-
const evaluated = evaluateCompileTimeCall(bound);
|
|
3169
|
-
return evaluated
|
|
3280
|
+
const evaluated = evaluateCompileTimeCall(bound, true, resolvePrefix);
|
|
3281
|
+
return evaluated
|
|
3282
|
+
? finiteIterationValues(evaluated, resolvePrefix)
|
|
3283
|
+
: undefined;
|
|
3170
3284
|
}
|
|
3171
3285
|
if (bound.kind === "list") return bound.items;
|
|
3172
3286
|
if (bound.kind !== "number" || bound.raw.includes(".")) return undefined;
|
|
@@ -3406,6 +3520,7 @@ function checkInstrument(
|
|
|
3406
3520
|
endpointFields,
|
|
3407
3521
|
holdDestinations,
|
|
3408
3522
|
diagnostics,
|
|
3523
|
+
resolvePrefix,
|
|
3409
3524
|
),
|
|
3410
3525
|
];
|
|
3411
3526
|
})
|
|
@@ -3771,6 +3886,8 @@ function lowerField(
|
|
|
3771
3886
|
},
|
|
3772
3887
|
new Map(),
|
|
3773
3888
|
diagnostics,
|
|
3889
|
+
false,
|
|
3890
|
+
resolvePrefix,
|
|
3774
3891
|
);
|
|
3775
3892
|
}
|
|
3776
3893
|
const type = typeOf(typeExpr, aliases);
|
|
@@ -3938,6 +4055,7 @@ function checkAction(
|
|
|
3938
4055
|
endpointFields: ReadonlyMap<string, string>,
|
|
3939
4056
|
holdDestinations: ReadonlyMap<string, string>,
|
|
3940
4057
|
diagnostics: GeneralDiagnostic[],
|
|
4058
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
3941
4059
|
): TypedAction {
|
|
3942
4060
|
const slots: Record<string, JsonValue> = {};
|
|
3943
4061
|
let omitsPublicAction = false;
|
|
@@ -3999,6 +4117,7 @@ function checkAction(
|
|
|
3999
4117
|
row.value,
|
|
4000
4118
|
definition.target,
|
|
4001
4119
|
diagnostics,
|
|
4120
|
+
resolvePrefix,
|
|
4002
4121
|
);
|
|
4003
4122
|
const moveOffset = Array.isArray(slots.moves) ? slots.moves.length : 0;
|
|
4004
4123
|
const value =
|
|
@@ -4352,7 +4471,9 @@ function bindFieldReferences(
|
|
|
4352
4471
|
(path.includes("requiresExposure") && key === "capField") ||
|
|
4353
4472
|
(path.includes("reconcile") &&
|
|
4354
4473
|
path.includes("exception") &&
|
|
4355
|
-
["amountField", "reasonField", "refField"].includes(key ?? ""))
|
|
4474
|
+
["amountField", "reasonField", "refField"].includes(key ?? "")) ||
|
|
4475
|
+
(path.includes("cascade") &&
|
|
4476
|
+
["inputField", "refField"].includes(key ?? ""))
|
|
4356
4477
|
);
|
|
4357
4478
|
};
|
|
4358
4479
|
const visit = (value: JsonValue, path: readonly string[] = []): void => {
|
|
@@ -4991,11 +5112,12 @@ function blockToObject(
|
|
|
4991
5112
|
substitutions: ReadonlyMap<string, Expr>,
|
|
4992
5113
|
diagnostics: GeneralDiagnostic[],
|
|
4993
5114
|
literalKeys = false,
|
|
5115
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
4994
5116
|
): Record<string, JsonValue> {
|
|
4995
5117
|
if (!block) return {};
|
|
4996
5118
|
const result: Record<string, JsonValue> = {};
|
|
4997
5119
|
for (const row of block.entries) {
|
|
4998
|
-
const value = substituteExpr(row.value, substitutions);
|
|
5120
|
+
const value = substituteExpr(row.value, substitutions, resolvePrefix);
|
|
4999
5121
|
if (twoArgumentMoneyCall(value)) {
|
|
5000
5122
|
diagnostics.push({
|
|
5001
5123
|
code: "HSX1104",
|
|
@@ -5007,7 +5129,7 @@ function blockToObject(
|
|
|
5007
5129
|
continue;
|
|
5008
5130
|
}
|
|
5009
5131
|
result[literalKeys || row.key.quoted ? row.key.name : camel(row.key.name)] =
|
|
5010
|
-
exprToJson(value, diagnostics, literalKeys);
|
|
5132
|
+
exprToJson(value, diagnostics, literalKeys, resolvePrefix);
|
|
5011
5133
|
}
|
|
5012
5134
|
return result;
|
|
5013
5135
|
}
|
|
@@ -5016,6 +5138,7 @@ function exprToJsonForUdlSlot(
|
|
|
5016
5138
|
expr: Expr,
|
|
5017
5139
|
slot: string,
|
|
5018
5140
|
diagnostics: GeneralDiagnostic[],
|
|
5141
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
5019
5142
|
): JsonValue {
|
|
5020
5143
|
if (twoArgumentMoneyCall(expr)) {
|
|
5021
5144
|
diagnostics.push({
|
|
@@ -5030,7 +5153,7 @@ function exprToJsonForUdlSlot(
|
|
|
5030
5153
|
return Object.fromEntries(
|
|
5031
5154
|
expr.entries.map((row) => [
|
|
5032
5155
|
row.key.name,
|
|
5033
|
-
exprToJson(row.value, diagnostics),
|
|
5156
|
+
exprToJson(row.value, diagnostics, false, resolvePrefix),
|
|
5034
5157
|
]),
|
|
5035
5158
|
);
|
|
5036
5159
|
}
|
|
@@ -5041,13 +5164,14 @@ function exprToJsonForUdlSlot(
|
|
|
5041
5164
|
"literalKeys" in clause &&
|
|
5042
5165
|
clause.literalKeys === true,
|
|
5043
5166
|
);
|
|
5044
|
-
return exprToJson(expr, diagnostics, literalKeys);
|
|
5167
|
+
return exprToJson(expr, diagnostics, literalKeys, resolvePrefix);
|
|
5045
5168
|
}
|
|
5046
5169
|
|
|
5047
5170
|
function exprToJson(
|
|
5048
5171
|
expr: Expr,
|
|
5049
5172
|
diagnostics: GeneralDiagnostic[],
|
|
5050
5173
|
literalKeys = false,
|
|
5174
|
+
resolvePrefix?: (target: string) => string | undefined,
|
|
5051
5175
|
): JsonValue {
|
|
5052
5176
|
switch (expr.kind) {
|
|
5053
5177
|
case "boolean":
|
|
@@ -5094,18 +5218,30 @@ function exprToJson(
|
|
|
5094
5218
|
return `${expr.owner.name}.${expr.member.name}`;
|
|
5095
5219
|
case "list":
|
|
5096
5220
|
return expr.items.map((item) =>
|
|
5097
|
-
exprToJson(item, diagnostics, literalKeys),
|
|
5221
|
+
exprToJson(item, diagnostics, literalKeys, resolvePrefix),
|
|
5098
5222
|
);
|
|
5099
5223
|
case "block":
|
|
5100
|
-
return blockToObject(
|
|
5224
|
+
return blockToObject(
|
|
5225
|
+
expr,
|
|
5226
|
+
new Map(),
|
|
5227
|
+
diagnostics,
|
|
5228
|
+
literalKeys,
|
|
5229
|
+
resolvePrefix,
|
|
5230
|
+
);
|
|
5101
5231
|
case "call":
|
|
5102
|
-
return `${expr.callee.name}(${expr.args.map((arg) => String(exprToJson(arg, diagnostics))).join(",")})`;
|
|
5232
|
+
return `${expr.callee.name}(${expr.args.map((arg) => String(exprToJson(arg, diagnostics, false, resolvePrefix))).join(",")})`;
|
|
5103
5233
|
case "type_apply":
|
|
5104
|
-
return `${expr.callee.name}<${expr.args.map((arg) => String(exprToJson(arg, diagnostics))).join(",")}>`;
|
|
5234
|
+
return `${expr.callee.name}<${expr.args.map((arg) => String(exprToJson(arg, diagnostics, false, resolvePrefix))).join(",")}>`;
|
|
5105
5235
|
case "binding":
|
|
5106
|
-
return exprToJson(expr.type, diagnostics);
|
|
5236
|
+
return exprToJson(expr.type, diagnostics, false, resolvePrefix);
|
|
5107
5237
|
case "decided_amount":
|
|
5108
|
-
return blockToObject(
|
|
5238
|
+
return blockToObject(
|
|
5239
|
+
expr.body,
|
|
5240
|
+
new Map(),
|
|
5241
|
+
diagnostics,
|
|
5242
|
+
false,
|
|
5243
|
+
resolvePrefix,
|
|
5244
|
+
);
|
|
5109
5245
|
case "port_ref":
|
|
5110
5246
|
return expr.name.name;
|
|
5111
5247
|
case "apply":
|