@hyperscale0/hsx 1.0.342 → 1.0.350

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 (144) hide show
  1. package/README.md +1 -1
  2. package/bases/cards.hsx +0 -1
  3. package/bases/collections.hsx +0 -2
  4. package/bases/escrow.hsx +0 -1
  5. package/bases/financing.hsx +0 -1
  6. package/bases/insurance.hsx +0 -1
  7. package/bases/lending.hsx +0 -3
  8. package/bases/marketplace.hsx +0 -2
  9. package/bases/money.hsx +0 -1
  10. package/bases/reporting.hsx +0 -2
  11. package/bases/savings.hsx +0 -1
  12. package/bases/wallet.hsx +0 -1
  13. package/dist/bin/hsx.d.ts.map +1 -1
  14. package/dist/bin/hsx.js.map +1 -1
  15. package/dist/src/ast.d.ts +57 -7
  16. package/dist/src/ast.d.ts.map +1 -1
  17. package/dist/src/ast.js.map +1 -1
  18. package/dist/src/attachment-economics.d.ts.map +1 -1
  19. package/dist/src/attachment-economics.js.map +1 -1
  20. package/dist/src/bases-bundle.d.ts.map +1 -1
  21. package/dist/src/bases-bundle.js +11 -11
  22. package/dist/src/bases-bundle.js.map +1 -1
  23. package/dist/src/bases.d.ts.map +1 -1
  24. package/dist/src/bases.js.map +1 -1
  25. package/dist/src/catalog-program.d.ts +1 -1
  26. package/dist/src/catalog-program.d.ts.map +1 -1
  27. package/dist/src/catalog-program.js +0 -13
  28. package/dist/src/catalog-program.js.map +1 -1
  29. package/dist/src/checks.d.ts +60 -0
  30. package/dist/src/checks.d.ts.map +1 -0
  31. package/dist/src/checks.js +240 -0
  32. package/dist/src/checks.js.map +1 -0
  33. package/dist/src/cli.d.ts.map +1 -1
  34. package/dist/src/cli.js +6 -25
  35. package/dist/src/cli.js.map +1 -1
  36. package/dist/src/compile.d.ts +29 -12
  37. package/dist/src/compile.d.ts.map +1 -1
  38. package/dist/src/compile.js +414 -316
  39. package/dist/src/compile.js.map +1 -1
  40. package/dist/src/cost.d.ts.map +1 -1
  41. package/dist/src/cost.js.map +1 -1
  42. package/dist/src/diagnostics.d.ts +14 -1
  43. package/dist/src/diagnostics.d.ts.map +1 -1
  44. package/dist/src/diagnostics.js +31 -0
  45. package/dist/src/diagnostics.js.map +1 -1
  46. package/dist/src/examples-bundle.d.ts.map +1 -1
  47. package/dist/src/examples-bundle.js +15 -15
  48. package/dist/src/examples-bundle.js.map +1 -1
  49. package/dist/src/examples.d.ts.map +1 -1
  50. package/dist/src/examples.js.map +1 -1
  51. package/dist/src/format.d.ts.map +1 -1
  52. package/dist/src/format.js.map +1 -1
  53. package/dist/src/header-source.d.ts +30 -0
  54. package/dist/src/header-source.d.ts.map +1 -1
  55. package/dist/src/header-source.js +120 -9
  56. package/dist/src/header-source.js.map +1 -1
  57. package/dist/src/headers.d.ts +9 -18
  58. package/dist/src/headers.d.ts.map +1 -1
  59. package/dist/src/headers.js +25 -23
  60. package/dist/src/headers.js.map +1 -1
  61. package/dist/src/index.d.ts +4 -3
  62. package/dist/src/index.d.ts.map +1 -1
  63. package/dist/src/index.js +2 -2
  64. package/dist/src/index.js.map +1 -1
  65. package/dist/src/keywords.d.ts +2 -4
  66. package/dist/src/keywords.d.ts.map +1 -1
  67. package/dist/src/keywords.js +6 -5
  68. package/dist/src/keywords.js.map +1 -1
  69. package/dist/src/language.d.ts +3 -4
  70. package/dist/src/language.d.ts.map +1 -1
  71. package/dist/src/language.js +123 -60
  72. package/dist/src/language.js.map +1 -1
  73. package/dist/src/lex.d.ts +1 -1
  74. package/dist/src/lex.d.ts.map +1 -1
  75. package/dist/src/lex.js +3 -5
  76. package/dist/src/lex.js.map +1 -1
  77. package/dist/src/parse.d.ts +3 -1
  78. package/dist/src/parse.d.ts.map +1 -1
  79. package/dist/src/parse.js +234 -22
  80. package/dist/src/parse.js.map +1 -1
  81. package/dist/src/std-bundle.d.ts +2 -1
  82. package/dist/src/std-bundle.d.ts.map +1 -1
  83. package/dist/src/std-bundle.js +16 -15
  84. package/dist/src/std-bundle.js.map +1 -1
  85. package/dist/src/template-guide.d.ts +3 -9
  86. package/dist/src/template-guide.d.ts.map +1 -1
  87. package/dist/src/template-guide.js +16 -23
  88. package/dist/src/template-guide.js.map +1 -1
  89. package/dist/src/toolchain.d.ts +23 -0
  90. package/dist/src/toolchain.d.ts.map +1 -0
  91. package/dist/src/toolchain.js +29 -0
  92. package/dist/src/toolchain.js.map +1 -0
  93. package/dist/src/tunables.d.ts.map +1 -1
  94. package/dist/src/tunables.js.map +1 -1
  95. package/docs/README.md +69 -28
  96. package/examples/camera-rental.hsx +0 -1
  97. package/examples/cards.hsx +0 -1
  98. package/examples/collections.hsx +0 -2
  99. package/examples/escrow.hsx +0 -1
  100. package/examples/financing.hsx +0 -1
  101. package/examples/insurance.hsx +0 -2
  102. package/examples/lending.hsx +0 -3
  103. package/examples/library.hsx +0 -3
  104. package/examples/marketplace.hsx +0 -2
  105. package/examples/money.hsx +0 -1
  106. package/examples/rental-hire.hsx +0 -1
  107. package/examples/reporting.hsx +0 -2
  108. package/examples/savings.hsx +0 -1
  109. package/examples/serviced.hsx +0 -4
  110. package/examples/wallet.hsx +0 -1
  111. package/package.json +8 -8
  112. package/src/ast.ts +56 -3
  113. package/src/bases-bundle.ts +11 -11
  114. package/src/catalog-program.ts +0 -13
  115. package/src/checks.ts +386 -0
  116. package/src/cli.ts +6 -25
  117. package/src/compile.ts +583 -438
  118. package/src/diagnostics.ts +49 -1
  119. package/src/examples-bundle.ts +15 -15
  120. package/src/header-source.ts +187 -11
  121. package/src/headers.ts +31 -32
  122. package/src/index.ts +10 -4
  123. package/src/keywords.ts +6 -5
  124. package/src/language.ts +153 -68
  125. package/src/lex.ts +3 -5
  126. package/src/parse.ts +216 -24
  127. package/src/std-bundle.ts +17 -15
  128. package/src/template-guide.ts +17 -34
  129. package/src/toolchain.ts +51 -0
  130. package/std/booking.hsx +14 -1
  131. package/std/collections.hsx +12 -2
  132. package/std/financing.hsx +10 -16
  133. package/std/money.hsx +2 -3
  134. package/std/reporting.hsx +10 -1
  135. package/dist/src/binding-contract.d.ts +0 -22
  136. package/dist/src/binding-contract.d.ts.map +0 -1
  137. package/dist/src/binding-contract.js +0 -80
  138. package/dist/src/binding-contract.js.map +0 -1
  139. package/dist/src/std-library.d.ts +0 -5
  140. package/dist/src/std-library.d.ts.map +0 -1
  141. package/dist/src/std-library.js +0 -5
  142. package/dist/src/std-library.js.map +0 -1
  143. package/src/binding-contract.ts +0 -111
  144. package/src/std-library.ts +0 -7
package/src/parse.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import { lex, type Token } from "./lex.ts";
2
2
  import type {
3
3
  BlockExpr,
4
+ CheckDecl,
5
+ CheckStatement,
4
6
  Diagnostic,
5
7
  Entry,
6
8
  Expr,
9
+ Named,
7
10
  Program,
8
11
  Span,
9
12
  } from "./ast.ts";
@@ -23,6 +26,7 @@ export function parseProgram(source: string): {
23
26
  currency: "SAR",
24
27
  header: false,
25
28
  decls: [],
29
+ checks: [],
26
30
  span: { start: 0, end: source.length },
27
31
  };
28
32
  if (new TextEncoder().encode(source).length > 262144)
@@ -63,6 +67,17 @@ export function parseProgram(source: string): {
63
67
  throw error;
64
68
  }
65
69
  }
70
+ /** One value as a header parameter takes it, such as `10%`; undefined if it is not one. */
71
+ export function parseExpression(source: string): Expr | undefined {
72
+ const result = lex(source);
73
+ if (result.diagnostics.length) return undefined;
74
+ try {
75
+ return new Parser(result.tokens).value();
76
+ } catch (error) {
77
+ if (error instanceof ParseFailure) return undefined;
78
+ throw error;
79
+ }
80
+ }
66
81
  class Parser {
67
82
  private index = 0;
68
83
  private depth = 0;
@@ -114,6 +129,12 @@ class Parser {
114
129
  private span(start: number): Span {
115
130
  return { start, end: this.tokens[Math.max(0, this.index - 1)]!.span.end };
116
131
  }
132
+ value(): Expr {
133
+ const value = this.expression();
134
+ if (this.peek().kind !== "eof")
135
+ this.fail("expected one value", "write one value");
136
+ return value;
137
+ }
117
138
  program(program: Program): Program {
118
139
  if (this.eat("header")) program.header = true;
119
140
  else this.expect("program");
@@ -142,14 +163,6 @@ class Parser {
142
163
  program.currency = this.identifier();
143
164
  continue;
144
165
  }
145
- if (this.eat("use")) {
146
- program.decls.push({
147
- kind: "use",
148
- name: this.identifier(),
149
- span: this.span(start),
150
- });
151
- continue;
152
- }
153
166
  if (this.eat("party")) {
154
167
  const name = this.identifier();
155
168
  this.expect(":");
@@ -164,19 +177,20 @@ class Parser {
164
177
  });
165
178
  continue;
166
179
  }
167
- if (this.at("expose") || this.at("hide")) {
168
- const kind = this.take().text as "expose" | "hide";
180
+ if (this.eat("expose")) {
169
181
  const target = this.path();
170
- let name: string | undefined;
171
- if (kind === "expose") {
172
- this.expect("as");
173
- name = this.identifier();
174
- }
175
- program.decls.push(
176
- kind === "expose"
177
- ? { kind, target, name: name!, span: this.span(start) }
178
- : { kind, target, span: this.span(start) },
179
- );
182
+ this.expect("as");
183
+ const name = this.identifier();
184
+ program.decls.push({
185
+ kind: "expose",
186
+ target,
187
+ name,
188
+ span: this.span(start),
189
+ });
190
+ continue;
191
+ }
192
+ if (this.at("check") && this.tokens[this.index + 1]?.kind === "string") {
193
+ program.checks.push(this.check());
180
194
  continue;
181
195
  }
182
196
  if (this.eat("instrument")) {
@@ -222,6 +236,133 @@ class Parser {
222
236
  }
223
237
  return program;
224
238
  }
239
+ private named(): Named {
240
+ const span = this.peek().span;
241
+ return { name: this.identifier(), span };
242
+ }
243
+ private text(): string {
244
+ if (this.peek().kind !== "string")
245
+ this.fail("expected a quoted name", 'write a name in quotes, as "Alice"');
246
+ return JSON.parse(this.take().text) as string;
247
+ }
248
+ /**
249
+ * `check "Name" { ... }`: customers, steps in order, then `expect`. A step
250
+ * is `clock`, `object`, `close` or `[actor] object.action ... -> state`.
251
+ */
252
+ private check(): CheckDecl {
253
+ const start = this.take().span.start;
254
+ const name = this.text();
255
+ this.expect("{");
256
+ const statements: CheckStatement[] = [];
257
+ while (!this.eat("}")) {
258
+ this.separators();
259
+ if (this.eat("}")) break;
260
+ if (this.peek().kind === "eof")
261
+ this.fail("unclosed check, expected }", "add }");
262
+ const at = this.peek().span.start;
263
+ const statement = this.checkStatement();
264
+ if (statement.kind !== "customer" && this.eat("balances"))
265
+ statement.balances = this.block();
266
+ statement.span = this.span(at);
267
+ statements.push(statement);
268
+ this.separators();
269
+ }
270
+ return { name, statements, span: this.span(start) };
271
+ }
272
+ private checkStatement(): CheckStatement {
273
+ const span = this.peek().span;
274
+ if (this.eat("customer")) {
275
+ const id = this.named();
276
+ const title = this.text();
277
+ const organization = this.eat("organization");
278
+ const fund = this.eat("funded") ? this.atom() : undefined;
279
+ const binds: Named[] = [];
280
+ while (this.eat("binds")) binds.push(this.named());
281
+ return {
282
+ kind: "customer",
283
+ id,
284
+ title,
285
+ organization,
286
+ ...(fund ? { fund } : {}),
287
+ binds,
288
+ span,
289
+ };
290
+ }
291
+ if (this.eat("clock")) {
292
+ const at = this.atom();
293
+ if (!this.eat("until")) return { kind: "clock", at, span };
294
+ const object = this.named();
295
+ const attachment = this.eat(".") ? this.named() : undefined;
296
+ const recordPath = this.eat("record") ? this.path() : undefined;
297
+ this.expect("is");
298
+ const state = this.named();
299
+ return {
300
+ kind: "clock",
301
+ at,
302
+ until: {
303
+ object,
304
+ ...(attachment ? { attachment } : {}),
305
+ ...(recordPath ? { recordPath } : {}),
306
+ state,
307
+ },
308
+ span,
309
+ };
310
+ }
311
+ if (this.eat("object")) {
312
+ const id = this.named();
313
+ this.expect(":");
314
+ const objectKind = this.named();
315
+ if (!this.eat("for"))
316
+ this.fail("an object needs its owner", "write for <customer>");
317
+ const owner = this.named();
318
+ const fields = this.at("{") ? this.block() : undefined;
319
+ return {
320
+ kind: "object",
321
+ id,
322
+ objectKind,
323
+ owner,
324
+ ...(fields ? { fields } : {}),
325
+ span,
326
+ };
327
+ }
328
+ if (this.eat("close"))
329
+ return { kind: "close", customer: this.named(), span };
330
+ if (this.eat("expect")) {
331
+ const statement: Extract<CheckStatement, { kind: "expect" }> = {
332
+ kind: "expect",
333
+ span,
334
+ };
335
+ for (;;) {
336
+ if (this.eat("balances")) statement.balances = this.block();
337
+ else if (this.eat("held")) statement.held = this.atom();
338
+ else if (this.eat("pending")) statement.pending = this.block();
339
+ else break;
340
+ }
341
+ return statement;
342
+ }
343
+ const first = this.named();
344
+ const actor = this.at(".") ? undefined : first;
345
+ const object = actor ? this.named() : first;
346
+ this.expect(".");
347
+ const action = this.named();
348
+ const statement: Extract<CheckStatement, { kind: "action" }> = {
349
+ kind: "action",
350
+ ...(actor ? { actor } : {}),
351
+ object,
352
+ action,
353
+ span,
354
+ };
355
+ if (this.at("{")) statement.input = this.block();
356
+ if (this.eat("fields")) statement.fields = this.block();
357
+ if (this.eat("on")) statement.instance = this.named();
358
+ if (this.eat("remember")) statement.remember = this.named();
359
+ this.expect("->");
360
+ if (this.eat("status")) {
361
+ statement.status = this.atom();
362
+ if (this.peek().kind === "string") statement.code = this.text();
363
+ } else statement.state = this.named();
364
+ return statement;
365
+ }
225
366
  private entries(end: string): Entry[] {
226
367
  const entries: Entry[] = [];
227
368
  while (!this.eat(end)) {
@@ -297,6 +438,11 @@ class Parser {
297
438
  this.separators();
298
439
  continue;
299
440
  }
441
+ if (key === "checks" && this.at("{")) {
442
+ entries.push({ key, value: this.checks(), span: this.span(start) });
443
+ this.separators();
444
+ continue;
445
+ }
300
446
  if (key === "action" && !this.at(":")) key += " " + this.identifier();
301
447
  if (key === "economics" && !this.at(":") && !this.at("{"))
302
448
  key += " " + this.path();
@@ -343,8 +489,16 @@ class Parser {
343
489
  span,
344
490
  };
345
491
  }
346
- private operand(): BlockExpr {
492
+ private operand(sums = false): BlockExpr {
347
493
  const value = this.atom();
494
+ // A header check may add tunables, as in `first + second <= limit`.
495
+ if (sums && this.at("+")) {
496
+ const items = [value];
497
+ while (this.eat("+")) items.push(this.atom());
498
+ return this.record({
499
+ sum: { kind: "list", items, span: this.span(value.span.start) },
500
+ });
501
+ }
348
502
  return this.record({
349
503
  [value.kind === "name" &&
350
504
  /^(self|input|party|subject)\./.test(value.value)
@@ -362,7 +516,45 @@ class Parser {
362
516
  );
363
517
  return { kind: "text", value: operator.text, span: operator.span };
364
518
  }
365
- private requirement(): BlockExpr {
519
+ /**
520
+ * `checks { late_from < early_until { message: "...", fix: "..." } }`. A rule
521
+ * is a comparison, `x is set` or `x is <its type>`, and `when s is c` scopes it.
522
+ */
523
+ private checks(): BlockExpr {
524
+ const start = this.peek().span.start;
525
+ this.expect("{");
526
+ const entries: Entry[] = [];
527
+ while (!this.eat("}")) {
528
+ this.separators();
529
+ if (this.eat("}")) break;
530
+ if (this.peek().kind === "eof")
531
+ this.fail("unclosed checks, expected }", "add }");
532
+ const at = this.peek().span.start;
533
+ let rule: BlockExpr;
534
+ if (this.tokens[this.index + 1]?.text === "is") {
535
+ const tunable = this.atom();
536
+ this.expect("is");
537
+ rule = this.record({ kind: this.node("is"), tunable, is: this.atom() });
538
+ } else rule = this.requirement(true);
539
+ const values: Record<string, Expr> = {
540
+ rule: { ...rule, span: this.span(at) },
541
+ };
542
+ if (this.eat("when")) {
543
+ const selector = this.atom();
544
+ this.expect("is");
545
+ values.when = this.record({ selector, is: this.atom() });
546
+ }
547
+ values.explain = this.block();
548
+ entries.push({
549
+ key: "check",
550
+ value: { ...this.record(values), span: this.span(at) },
551
+ span: this.span(at),
552
+ });
553
+ this.separators();
554
+ }
555
+ return { kind: "block", entries, span: this.span(start) };
556
+ }
557
+ private requirement(sums = false): BlockExpr {
366
558
  if (this.eat("unique")) {
367
559
  const namespace = this.atom();
368
560
  this.expect("on");
@@ -416,7 +608,7 @@ class Parser {
416
608
  if (this.eat("instruction")) values.instruction = this.atom();
417
609
  return this.record(values);
418
610
  }
419
- const left = this.operand();
611
+ const left = this.operand(sums);
420
612
  if (this.eat("in")) {
421
613
  const reference = left.entries.find(
422
614
  (entry) => entry.key === "field",
@@ -437,7 +629,7 @@ class Parser {
437
629
  kind: this.node("compare"),
438
630
  left,
439
631
  operator,
440
- right: this.operand(),
632
+ right: this.operand(sums),
441
633
  });
442
634
  }
443
635
  private move(): BlockExpr {