@harmoniclabs/pebble 0.1.10 → 0.2.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 (97) hide show
  1. package/dist/IR/IRNodes/IRConst.js +14 -3
  2. package/dist/IR/IRNodes/IRNative/index.js +5 -1
  3. package/dist/IR/toUPLC/CompilerOptions.d.ts +22 -7
  4. package/dist/IR/toUPLC/CompilerOptions.js +5 -1
  5. package/dist/IR/tree_utils/bytesToHex.d.ts +8 -0
  6. package/dist/IR/tree_utils/bytesToHex.js +69 -0
  7. package/dist/ast/nodes/expr/functions/FuncExpr.d.ts +16 -2
  8. package/dist/ast/nodes/expr/functions/FuncExpr.js +17 -0
  9. package/dist/ast/nodes/expr/litteral/LitteralExpr.d.ts +2 -1
  10. package/dist/ast/nodes/expr/litteral/LitteralExpr.js +2 -0
  11. package/dist/ast/nodes/expr/litteral/TemplateStrExpr.d.ts +30 -0
  12. package/dist/ast/nodes/expr/litteral/TemplateStrExpr.js +35 -0
  13. package/dist/ast/nodes/statements/ExportStmt.d.ts +3 -3
  14. package/dist/ast/nodes/statements/PebbleStmt.d.ts +4 -3
  15. package/dist/ast/nodes/statements/PebbleStmt.js +6 -2
  16. package/dist/ast/nodes/statements/TestParam.d.ts +18 -0
  17. package/dist/ast/nodes/statements/TestParam.js +18 -0
  18. package/dist/ast/nodes/statements/TestStmt.d.ts +5 -3
  19. package/dist/ast/nodes/statements/TestStmt.js +3 -1
  20. package/dist/ast/nodes/statements/UsingStmt.d.ts +32 -2
  21. package/dist/ast/nodes/statements/UsingStmt.js +39 -3
  22. package/dist/ast/nodes/statements/declarations/NamespaceDecl.d.ts +21 -0
  23. package/dist/ast/nodes/statements/declarations/NamespaceDecl.js +31 -0
  24. package/dist/compiler/AstCompiler/AstCompiler.d.ts +26 -0
  25. package/dist/compiler/AstCompiler/AstCompiler.js +203 -3
  26. package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +13 -2
  27. package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +97 -6
  28. package/dist/compiler/AstCompiler/internal/exprs/_compileFuncExpr.js +12 -5
  29. package/dist/compiler/AstCompiler/internal/exprs/_compileLitteralExpr.js +59 -0
  30. package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.d.ts +2 -3
  31. package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.js +28 -0
  32. package/dist/compiler/AstCompiler/internal/exprs/_compileVarAccessExpr.js +2 -0
  33. package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -1
  34. package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.d.ts +15 -1
  35. package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.js +70 -30
  36. package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.d.ts +11 -0
  37. package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.js +26 -0
  38. package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.d.ts +9 -4
  39. package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.js +51 -10
  40. package/dist/compiler/AstCompiler/internal/types/_compileDataEncodedConcreteType.js +21 -2
  41. package/dist/compiler/AstCompiler/internal/types/_compileSopEncodedConcreteType.js +17 -2
  42. package/dist/compiler/AstCompiler/scope/AstScope.d.ts +70 -1
  43. package/dist/compiler/AstCompiler/scope/AstScope.js +91 -0
  44. package/dist/compiler/AstCompiler/utils/getPropAccessReturnType.js +25 -1
  45. package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.d.ts +36 -0
  46. package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.js +123 -0
  47. package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.d.ts +28 -0
  48. package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.js +95 -0
  49. package/dist/compiler/AstCompiler/utils/resolveNamespacePath.d.ts +37 -0
  50. package/dist/compiler/AstCompiler/utils/resolveNamespacePath.js +93 -0
  51. package/dist/compiler/Compiler.d.ts +9 -1
  52. package/dist/compiler/Compiler.js +204 -9
  53. package/dist/compiler/TirCompiler/expressify/expressifyVars.js +26 -7
  54. package/dist/compiler/test/TestResult.d.ts +38 -0
  55. package/dist/compiler/test/TestResult.js +6 -0
  56. package/dist/compiler/test/fuzz/PRNG.d.ts +26 -0
  57. package/dist/compiler/test/fuzz/PRNG.js +59 -0
  58. package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
  59. package/dist/compiler/tir/expressions/TirExpr.js +2 -0
  60. package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +17 -0
  61. package/dist/compiler/tir/expressions/TirNativeFunc.js +53 -106
  62. package/dist/compiler/tir/expressions/TirShowExpr.d.ts +52 -0
  63. package/dist/compiler/tir/expressions/TirShowExpr.js +199 -0
  64. package/dist/compiler/tir/expressions/TirTraceExpr.js +11 -7
  65. package/dist/compiler/tir/program/TypedProgram.d.ts +101 -0
  66. package/dist/compiler/tir/program/TypedProgram.js +43 -0
  67. package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.d.ts +17 -0
  68. package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.js +70 -0
  69. package/dist/compiler/tir/program/stdScope/populateStdNamespace.d.ts +22 -0
  70. package/dist/compiler/tir/program/stdScope/populateStdNamespace.js +574 -0
  71. package/dist/compiler/tir/program/stdScope/stdScope.d.ts +1 -0
  72. package/dist/compiler/tir/program/stdScope/stdScope.js +1 -1
  73. package/dist/compiler/tir/statements/TirStmt.js +0 -1
  74. package/dist/compiler/tir/statements/TirTestStmt.d.ts +46 -0
  75. package/dist/compiler/tir/statements/TirTestStmt.js +35 -0
  76. package/dist/compiler/tir/types/TirNativeType/TirNativeType.d.ts +50 -1
  77. package/dist/compiler/tir/types/TirNativeType/TirNativeType.js +53 -1
  78. package/dist/compiler/tir/types/TirType.js +3 -1
  79. package/dist/compiler/tir/types/utils/canAssignTo.js +8 -1
  80. package/dist/compiler/tir/types/utils/inferTypeArgs.d.ts +19 -0
  81. package/dist/compiler/tir/types/utils/inferTypeArgs.js +79 -0
  82. package/dist/compiler/tir/types/utils/substituteTypeParams.d.ts +9 -0
  83. package/dist/compiler/tir/types/utils/substituteTypeParams.js +62 -0
  84. package/dist/diagnostics/diagnosticMessages.generated.d.ts +5 -0
  85. package/dist/diagnostics/diagnosticMessages.generated.js +10 -0
  86. package/dist/index.d.ts +2 -0
  87. package/dist/index.js +2 -0
  88. package/dist/parser/Parser.d.ts +73 -3
  89. package/dist/parser/Parser.js +333 -33
  90. package/dist/tokenizer/Token.d.ts +105 -102
  91. package/dist/tokenizer/Token.js +110 -109
  92. package/dist/tokenizer/utils/tokenFromKeyword.js +9 -6
  93. package/dist/utils/semverSatisfies.d.ts +1 -0
  94. package/dist/utils/semverSatisfies.js +161 -0
  95. package/dist/version.generated.d.ts +1 -0
  96. package/dist/version.generated.js +2 -0
  97. package/package.json +3 -2
@@ -21,13 +21,14 @@ import { LitThisExpr } from "../ast/nodes/expr/litteral/LitThisExpr.js";
21
21
  import { LitTrueExpr } from "../ast/nodes/expr/litteral/LitTrueExpr.js";
22
22
  import { ParentesizedExpr } from "../ast/nodes/expr/ParentesizedExpr.js";
23
23
  import { ArrowKind } from "../ast/nodes/expr/functions/ArrowKind.js";
24
- import { FuncExpr } from "../ast/nodes/expr/functions/FuncExpr.js";
24
+ import { FuncExpr, TypeParamDecl } from "../ast/nodes/expr/functions/FuncExpr.js";
25
25
  import { AstBooleanType, AstBytesType, AstListType, AstIntType, AstNativeOptionalType, AstVoidType, AstLinearMapType, AstFuncType } from "../ast/nodes/types/AstNativeTypeExpr.js";
26
26
  import { AstNamedTypeExpr } from "../ast/nodes/types/AstNamedTypeExpr.js";
27
27
  import { LitArrExpr } from "../ast/nodes/expr/litteral/LitArrExpr.js";
28
28
  import { LitNamedObjExpr } from "../ast/nodes/expr/litteral/LitNamedObjExpr.js";
29
29
  import { LitObjExpr } from "../ast/nodes/expr/litteral/LitObjExpr.js";
30
30
  import { LitStrExpr } from "../ast/nodes/expr/litteral/LitStrExpr.js";
31
+ import { TemplateStrExpr } from "../ast/nodes/expr/litteral/TemplateStrExpr.js";
31
32
  import { LitIntExpr } from "../ast/nodes/expr/litteral/LitIntExpr.js";
32
33
  import { LitHexBytesExpr } from "../ast/nodes/expr/litteral/LitHexBytesExpr.js";
33
34
  import { CallExpr } from "../ast/nodes/expr/functions/CallExpr.js";
@@ -44,6 +45,7 @@ import { FailStmt } from "../ast/nodes/statements/FailStmt.js";
44
45
  import { TraceStmt } from "../ast/nodes/statements/TraceStmt.js";
45
46
  import { AssertStmt } from "../ast/nodes/statements/AssertStmt.js";
46
47
  import { TestStmt } from "../ast/nodes/statements/TestStmt.js";
48
+ import { TestParam } from "../ast/nodes/statements/TestParam.js";
47
49
  import { MatchStmt, MatchStmtCase, MatchStmtElseCase } from "../ast/nodes/statements/MatchStmt.js";
48
50
  import { WhileStmt } from "../ast/nodes/statements/WhileStmt.js";
49
51
  import { CaseExpr, CaseExprMatcher, CaseWildcardMatcher } from "../ast/nodes/expr/CaseExpr.js";
@@ -65,7 +67,8 @@ import { makePropAccessExpr } from "../ast/nodes/expr/PropAccessExpr.js";
65
67
  import { makeBinaryExpr } from "../ast/nodes/expr/binary/BinaryExpr.js";
66
68
  import { isAssignmentStmt, makeAssignmentStmt } from "../ast/nodes/statements/AssignmentStmt.js";
67
69
  import { hoistStatementsInplace } from "./hoistStatementsInplace.js";
68
- import { UsingStmt, UsingStmtDeclaredConstructor } from "../ast/nodes/statements/UsingStmt.js";
70
+ import { UsingAliasStmt, UsingPath, UsingStmt, UsingStmtDeclaredConstructor } from "../ast/nodes/statements/UsingStmt.js";
71
+ import { NamespaceDecl, NamespaceMember } from "../ast/nodes/statements/declarations/NamespaceDecl.js";
69
72
  import { IncrStmt } from "../ast/nodes/statements/IncrStmt.js";
70
73
  import { DecrStmt } from "../ast/nodes/statements/DecrStmt.js";
71
74
  import { ExportStmt } from "../ast/nodes/statements/ExportStmt.js";
@@ -151,9 +154,15 @@ export class Parser extends DiagnosticEmitter {
151
154
  break;
152
155
  }
153
156
  case Token.Using: {
157
+ tn.next(); // skip `using`
154
158
  statement = this.parseUsingDecl();
155
159
  break;
156
160
  }
161
+ case Token.Namespace: {
162
+ tn.next(); // skip `namespace`
163
+ statement = this.parseNamespaceDecl(startPos);
164
+ break;
165
+ }
157
166
  case Token.Enum: {
158
167
  tn.next();
159
168
  statement = this.parseEnum(flags, startPos);
@@ -374,8 +383,19 @@ export class Parser extends DiagnosticEmitter {
374
383
  parseUsingDecl() {
375
384
  const tn = this.tn;
376
385
  const startPos = tn.tokenPos;
377
- if (!tn.skip(Token.OpenBrace))
378
- return this.error(DiagnosticCode._0_expected, tn.range(), "{");
386
+ // alias form: `using <ident> = <NamespacePath>;`
387
+ if (!tn.skip(Token.OpenBrace)) {
388
+ if (!tn.skipIdentifier())
389
+ return this.error(DiagnosticCode._0_expected, tn.range(), "{");
390
+ const alias = new Identifier(tn.readIdentifier(), tn.range());
391
+ if (!tn.skip(Token.Equals))
392
+ return this.error(DiagnosticCode._0_expected, tn.range(), "=");
393
+ const path = this.parseUsingPath();
394
+ if (!path)
395
+ return undefined;
396
+ return new UsingAliasStmt(alias, path, tn.range(startPos, tn.pos));
397
+ }
398
+ // destructure form: `using { a, b: c } = <rhs>;`
379
399
  const members = new Array();
380
400
  while (!tn.skip(Token.CloseBrace)) {
381
401
  const thisStartPos = tn.tokenPos;
@@ -397,10 +417,169 @@ export class Parser extends DiagnosticEmitter {
397
417
  }
398
418
  if (!tn.skip(Token.Equals))
399
419
  return this.error(DiagnosticCode._0_expected, tn.range(), "=");
400
- const structTypeExpr = this.parseTypeExpr();
401
- if (!structTypeExpr)
420
+ const rhs = this.parseUsingRhs();
421
+ if (!rhs)
402
422
  return undefined;
403
- return new UsingStmt(members, structTypeExpr, tn.range(startPos, tn.pos));
423
+ return new UsingStmt(members, rhs, tn.range(startPos, tn.pos));
424
+ }
425
+ /**
426
+ * parse a dotted chain of identifiers: `A`, `A.B`, `A.B.C`, ...
427
+ * used for namespace paths on the RHS of `using` statements.
428
+ */
429
+ parseUsingPath() {
430
+ const tn = this.tn;
431
+ const startPos = tn.tokenPos;
432
+ if (!tn.skipIdentifier())
433
+ return this.error(DiagnosticCode.Identifier_expected, tn.range());
434
+ const segments = [
435
+ new Identifier(tn.readIdentifier(), tn.range())
436
+ ];
437
+ while (tn.skip(Token.Dot)) {
438
+ if (!tn.skipIdentifier())
439
+ return this.error(DiagnosticCode.Identifier_expected, tn.range());
440
+ segments.push(new Identifier(tn.readIdentifier(), tn.range()));
441
+ }
442
+ return new UsingPath(segments, tn.range(startPos, tn.pos));
443
+ }
444
+ /**
445
+ * parse the RHS of `using { ... } = <rhs>;`:
446
+ * - if it begins with a native type keyword (or a generic struct
447
+ * with type args), it's parsed as `AstTypeExpr` (legacy
448
+ * struct-destructure behavior)
449
+ * - if it's a single identifier followed by `<`, it's also parsed as
450
+ * a generic type expression
451
+ * - otherwise, it's parsed as a dotted `UsingPath`
452
+ * (a namespace path; the compiler decides whether it's a struct
453
+ * type or a namespace based on what the head identifier resolves to)
454
+ */
455
+ parseUsingRhs() {
456
+ const tn = this.tn;
457
+ const next = tn.peek();
458
+ // native type keywords always go through parseTypeExpr
459
+ if (next === Token.Void
460
+ || next === Token.Boolean
461
+ || next === Token.Int
462
+ || next === Token.Bytes
463
+ || next === Token.Optional
464
+ || next === Token.List
465
+ || next === Token.LinearMap) {
466
+ const t = this.parseTypeExpr();
467
+ if (!t)
468
+ return undefined;
469
+ return t;
470
+ }
471
+ const path = this.parseUsingPath();
472
+ if (!path)
473
+ return undefined;
474
+ // single-segment path followed by `<...>` → generic struct type expression
475
+ if (path.segments.length === 1
476
+ && tn.peek() === Token.LessThan) {
477
+ tn.next(); // consume `<`
478
+ const tyArgs = [];
479
+ do {
480
+ const ty = this.parseTypeExpr();
481
+ if (!ty)
482
+ return undefined;
483
+ tyArgs.push(ty);
484
+ } while (tn.skip(Token.Comma));
485
+ if (!tn.skip(Token.GreaterThan))
486
+ return this.error(DiagnosticCode._0_expected, tn.range(), ">");
487
+ return new AstNamedTypeExpr(path.segments[0], tyArgs, tn.range(path.range.start, tn.pos));
488
+ }
489
+ return path;
490
+ }
491
+ /**
492
+ * parse a namespace body: `{ (private? <member-decl>)* }`
493
+ *
494
+ * called with `namespace` already consumed; `startPos` is the position
495
+ * of the `namespace` keyword.
496
+ */
497
+ parseNamespaceDecl(startPos) {
498
+ const tn = this.tn;
499
+ if (!tn.skipIdentifier())
500
+ return this.error(DiagnosticCode.Identifier_expected, tn.range());
501
+ const name = new Identifier(tn.readIdentifier(), tn.range());
502
+ if (!tn.skip(Token.OpenBrace))
503
+ return this.error(DiagnosticCode._0_expected, tn.range(), "{");
504
+ const members = [];
505
+ while (!tn.skip(Token.CloseBrace)) {
506
+ tn.skip(Token.Semicolon);
507
+ if (tn.skip(Token.CloseBrace))
508
+ break;
509
+ const memberStartPos = tn.tokenPos;
510
+ const isPrivate = tn.skip(Token.Private);
511
+ const member = this.parseNamespaceMember();
512
+ if (!member)
513
+ return undefined;
514
+ members.push(new NamespaceMember(isPrivate, member, tn.range(memberStartPos, tn.pos)));
515
+ tn.skip(Token.Semicolon);
516
+ }
517
+ return new NamespaceDecl(name, members, tn.range(startPos, tn.pos));
518
+ }
519
+ /**
520
+ * parse a single namespace body member. delegates to the relevant
521
+ * top-level-style parser based on the next token, but rejects
522
+ * declarations that aren't allowed inside a namespace body
523
+ * (no `export`, no `import`, no `using`, no `contract`, no `test`).
524
+ */
525
+ parseNamespaceMember() {
526
+ const tn = this.tn;
527
+ const startPos = tn.tokenPos;
528
+ const flags = CommonFlags.None;
529
+ const first = tn.peek();
530
+ switch (first) {
531
+ case Token.Namespace: {
532
+ tn.next();
533
+ return this.parseNamespaceDecl(startPos);
534
+ }
535
+ case Token.Function: {
536
+ tn.next();
537
+ return this.parseFuncDecl(flags, startPos);
538
+ }
539
+ case Token.Interface: {
540
+ tn.next();
541
+ return this.parseInterface(flags, startPos);
542
+ }
543
+ case Token.Var:
544
+ case Token.Let:
545
+ case Token.Const: {
546
+ tn.next();
547
+ return this.parseVarStmt(first === Token.Const ? CommonFlags.Const : CommonFlags.Let, startPos);
548
+ }
549
+ case Token.Enum: {
550
+ tn.next();
551
+ return this.parseEnum(flags, startPos);
552
+ }
553
+ case Token.Struct: {
554
+ tn.next();
555
+ return this.parseStruct(StructDeclAstFlags.none, flags, startPos);
556
+ }
557
+ case Token.Data: {
558
+ tn.next();
559
+ if (tn.peek() !== Token.Struct)
560
+ return this.error(DiagnosticCode._0_expected, tn.range(), "struct");
561
+ tn.next();
562
+ return this.parseStruct(StructDeclAstFlags.onlyDataEncoding, flags, startPos);
563
+ }
564
+ case Token.Runtime: {
565
+ tn.next();
566
+ if (tn.peek() !== Token.Struct)
567
+ return this.error(DiagnosticCode._0_expected, tn.range(), "struct");
568
+ tn.next();
569
+ return this.parseStruct(StructDeclAstFlags.onlySopEncoding, flags, startPos);
570
+ }
571
+ case Token.Type: {
572
+ tn.next();
573
+ const stmt = this.parseTypeStmt(flags, startPos);
574
+ if (!stmt)
575
+ return undefined;
576
+ if (!(stmt instanceof TypeAliasDecl))
577
+ return this.error(DiagnosticCode._0_expected, stmt.range, "type alias declaration");
578
+ return stmt;
579
+ }
580
+ default:
581
+ return this.error(DiagnosticCode._0_expected, tn.range(), "namespace member declaration");
582
+ }
404
583
  }
405
584
  parseTypeParameters() {
406
585
  const tn = this.tn;
@@ -419,6 +598,41 @@ export class Parser extends DiagnosticEmitter {
419
598
  }
420
599
  return typeParams;
421
600
  }
601
+ /**
602
+ * Parses a function's type-parameter list with optional `implements`
603
+ * interface constraints:
604
+ * `<T, U implements ToData, V implements Foo>`
605
+ *
606
+ * Returns `TypeParamDecl[]` so the compiler can read each param's
607
+ * optional constraint at template-registration time. Struct/interface
608
+ * decls keep using the simpler {@link parseTypeParameters} which
609
+ * returns bare `Identifier[]`.
610
+ */
611
+ parseFuncTypeParameters() {
612
+ const tn = this.tn;
613
+ // at '<': TypeParamDecl (',' TypeParamDecl)* '>'
614
+ const typeParams = new Array();
615
+ while (!tn.skip(Token.GreaterThan)) {
616
+ const startPos = tn.tokenPos;
617
+ if (!tn.skipIdentifier())
618
+ return this.error(DiagnosticCode.Identifier_expected, tn.range());
619
+ const name = new Identifier(tn.readIdentifier(), tn.range());
620
+ let constraint = undefined;
621
+ if (tn.skip(Token.Implements)) {
622
+ constraint = this.parseTypeExpr();
623
+ if (!constraint)
624
+ return undefined;
625
+ }
626
+ typeParams.push(new TypeParamDecl(name, constraint, tn.range(startPos, tn.pos)));
627
+ if (tn.skip(Token.Comma))
628
+ continue;
629
+ if (tn.skip(Token.GreaterThan))
630
+ break;
631
+ else
632
+ return this.error(DiagnosticCode._0_expected, tn.range(), ">");
633
+ }
634
+ return typeParams;
635
+ }
422
636
  parseTypeArguments() {
423
637
  const tn = this.tn;
424
638
  // at '<': TypeParameter (',' TypeParameter)* '>'
@@ -537,7 +751,11 @@ export class Parser extends DiagnosticEmitter {
537
751
  const body = this.parseBlockStmt();
538
752
  if (!body)
539
753
  return undefined;
540
- members.push(new InterfaceMethodImpl(methodName, typeParams ?? [], sig, body, tn.range(startPos, tn.pos)));
754
+ members.push(new InterfaceMethodImpl(methodName,
755
+ // InterfaceMethodImpl still uses bare Identifier[];
756
+ // strip any constraint clauses (method-decl constraints
757
+ // would be a separate feature).
758
+ typeParams?.map(p => p.name) ?? [], sig, body, tn.range(startPos, tn.pos)));
541
759
  }
542
760
  tn.skip(Token.Semicolon); // if any
543
761
  return new TypeImplementsStmt(typeId, interfaceType, members, tn.range());
@@ -747,7 +965,7 @@ export class Parser extends DiagnosticEmitter {
747
965
  let typeParams = undefined;
748
966
  if (tn.skip(Token.LessThan)) {
749
967
  sigStart = tn.tokenPos;
750
- typeParams = this.parseTypeParameters();
968
+ typeParams = this.parseFuncTypeParameters();
751
969
  if (!typeParams || typeParams.length === 0)
752
970
  return undefined;
753
971
  // flags |= CommonFlags.Generic;
@@ -776,15 +994,11 @@ export class Parser extends DiagnosticEmitter {
776
994
  const namedSig = this.parseNamedFuncSig(flags, startPos, defaultReturnType);
777
995
  if (!namedSig)
778
996
  return undefined;
779
- const [name, typeArgs, sig] = namedSig;
780
- const nParams = typeArgs?.length ?? 0;
781
- const typeParams = new Array(nParams);
782
- for (let i = 0; i < nParams; ++i) {
783
- const arg = typeArgs[i];
784
- if (!(arg instanceof AstNamedTypeExpr))
785
- return this.error(DiagnosticCode.Type_parameters_must_be_identifiers, arg.range);
786
- typeParams[i] = arg.name;
787
- }
997
+ const [name, typeParamsRaw, sig] = namedSig;
998
+ // `parseNamedFuncSig` returns `TypeParamDecl[]` (with optional
999
+ // `implements` constraint per param) — `FuncExpr.typeParams` carries
1000
+ // them through to template registration.
1001
+ const typeParams = typeParamsRaw ?? [];
788
1002
  if (!tn.skip(Token.OpenBrace))
789
1003
  return this.error(DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration, tn.range());
790
1004
  const body = this.parseBlockStmt();
@@ -792,7 +1006,7 @@ export class Parser extends DiagnosticEmitter {
792
1006
  return undefined;
793
1007
  const endPos = tn.pos;
794
1008
  tn.skip(Token.Semicolon); // if any
795
- const expr = new FuncExpr(name, flags, typeParams ?? [], sig, body, ArrowKind.None, tn.range(startPos, endPos));
1009
+ const expr = new FuncExpr(name, flags, typeParams, sig, body, ArrowKind.None, tn.range(startPos, endPos));
796
1010
  return new FuncDecl(expr);
797
1011
  }
798
1012
  parseEnum(flags, startPos) {
@@ -1113,11 +1327,15 @@ export class Parser extends DiagnosticEmitter {
1113
1327
  switch (token) {
1114
1328
  case Token.Void: return new AstVoidType(currRange);
1115
1329
  // case Token.True:
1116
- // case Token.False:
1330
+ // case Token.False:
1117
1331
  case Token.Boolean: return new AstBooleanType(currRange);
1118
1332
  case Token.Int: return new AstIntType(currRange);
1119
1333
  // case Token.Number: return new AstIntType( currRange )
1120
1334
  case Token.Bytes: return new AstBytesType(currRange);
1335
+ // `data` is registered in the prelude as a named type; expose it
1336
+ // as a plain `AstNamedTypeExpr` so it can be written in type
1337
+ // position (e.g. `function f(x: data): ...`).
1338
+ case Token.Data: return new AstNamedTypeExpr(new Identifier("data", currRange), [], currRange);
1121
1339
  case Token.Optional: {
1122
1340
  if (!tn.skip(Token.LessThan)) {
1123
1341
  canError && this.error(DiagnosticCode._0_expected, currRange, "Type argument for Optional");
@@ -1308,7 +1526,10 @@ export class Parser extends DiagnosticEmitter {
1308
1526
  if (tn.skipIdentifier(IdentifierHandling.Always)) {
1309
1527
  prop = new Identifier(tn.readIdentifier(), tn.range());
1310
1528
  expr = makePropAccessExpr(token, expr, prop, tn.range(startPos, tn.pos));
1311
- expr = this.tryParseCallExprOrReturnSame(expr);
1529
+ // pass `true` so we attempt `<TypeArgs>` parsing —
1530
+ // generic calls on dotted paths (e.g. `std.x.f<T>(a)`)
1531
+ // need this to consume the type arguments.
1532
+ expr = this.tryParseCallExprOrReturnSame(expr, true);
1312
1533
  break;
1313
1534
  }
1314
1535
  const state = tn.mark();
@@ -1578,9 +1799,7 @@ export class Parser extends DiagnosticEmitter {
1578
1799
  return new LitStrExpr(tn.readString(), tn.range(startPos, tn.pos));
1579
1800
  }
1580
1801
  case Token.StringTemplateLiteralQuote: {
1581
- this.error(DiagnosticCode.Not_implemented_0, tn.range(), "string template literals");
1582
- return undefined;
1583
- // return this.parseTemplateLiteral();
1802
+ return this.parseTemplateLiteral(startPos);
1584
1803
  }
1585
1804
  case Token.IntegerLiteral: {
1586
1805
  const value = tn.readInteger();
@@ -2340,17 +2559,63 @@ export class Parser extends DiagnosticEmitter {
2340
2559
  }
2341
2560
  parseTestStatement() {
2342
2561
  const tn = this.tn;
2343
- // at 'test': string? BlockStmt
2344
- const startPost = tn.pos;
2345
- let testName = undefined;
2346
- if (tn.skip(Token.StringLiteral)) {
2347
- const value = tn.readString();
2348
- testName = new LitStrExpr(value, tn.range(startPost, tn.pos));
2349
- }
2350
- let body = this.parseBlockStmt(true);
2562
+ // at 'test': Identifier '(' TestParam (',' TestParam)* ')' '{' BlockStmt
2563
+ const startPos = tn.pos;
2564
+ if (!tn.skipIdentifier())
2565
+ return this.error(DiagnosticCode.Identifier_expected, tn.range());
2566
+ const testName = new Identifier(tn.readIdentifier(), tn.range());
2567
+ if (!tn.skip(Token.OpenParen))
2568
+ return this.error(DiagnosticCode._0_expected, tn.range(), "(");
2569
+ const params = this.parseTestParameters();
2570
+ if (!params)
2571
+ return undefined;
2572
+ if (!tn.skip(Token.OpenBrace))
2573
+ return this.error(DiagnosticCode.Tests_must_be_specified_in_a_block_statement, tn.range());
2574
+ let body = this.parseBlockStmt();
2351
2575
  if (!body)
2352
2576
  return this.error(DiagnosticCode.Tests_must_be_specified_in_a_block_statement, tn.range());
2353
- return new TestStmt(testName, body, tn.range(startPost, tn.pos));
2577
+ return new TestStmt(testName, params, body, tn.range(startPos, tn.pos));
2578
+ }
2579
+ /**
2580
+ * Parses the parameter list of a `test` declaration:
2581
+ * `( <id>: <type> ('via' <expr>)? (, <id>: <type> ('via' <expr>)? )* )?`
2582
+ *
2583
+ * Assumes the opening `(` has already been consumed by the caller.
2584
+ * Consumes the closing `)` on success.
2585
+ *
2586
+ * `via` is recognised here ONLY — function/method/contract parameter
2587
+ * parsing uses `parseParameters()` which never looks for `Token.Via`.
2588
+ */
2589
+ parseTestParameters() {
2590
+ const tn = this.tn;
2591
+ const params = [];
2592
+ if (tn.skip(Token.CloseParen))
2593
+ return params;
2594
+ while (true) {
2595
+ const paramStart = tn.pos;
2596
+ if (!tn.skipIdentifier())
2597
+ return this.error(DiagnosticCode.Identifier_expected, tn.range());
2598
+ const name = new Identifier(tn.readIdentifier(), tn.range());
2599
+ if (!tn.skip(Token.Colon))
2600
+ return this.error(DiagnosticCode._0_expected, tn.range(), ":");
2601
+ const type = this.parseTypeExpr();
2602
+ if (!type)
2603
+ return undefined;
2604
+ let viaExpr = undefined;
2605
+ if (tn.skip(Token.Via)) {
2606
+ const expr = this.parseExpr();
2607
+ if (!expr)
2608
+ return undefined;
2609
+ viaExpr = expr;
2610
+ }
2611
+ params.push(new TestParam(name, type, viaExpr, tn.range(paramStart, tn.pos)));
2612
+ if (tn.skip(Token.Comma))
2613
+ continue;
2614
+ if (tn.skip(Token.CloseParen))
2615
+ break;
2616
+ return this.error(DiagnosticCode._0_expected, tn.range(), ")");
2617
+ }
2618
+ return params;
2354
2619
  }
2355
2620
  parseMatchStatement() {
2356
2621
  const tn = this.tn;
@@ -2416,6 +2681,41 @@ export class Parser extends DiagnosticEmitter {
2416
2681
  return this.error(DiagnosticCode.Expression_expected, tn.range());
2417
2682
  return new TraceStmt(expr, tn.range());
2418
2683
  }
2684
+ /**
2685
+ * Parse a backtick-delimited template literal:
2686
+ *
2687
+ * `text ${expr1} more text ${expr2} ...`
2688
+ *
2689
+ * Tokenizer state: on entry, the current token is
2690
+ * `Token.StringTemplateLiteralQuote`; the tokenizer is positioned at
2691
+ * the opening backtick. `readString()` (called with no arg) consumes
2692
+ * the backtick + the first fragment up to `${` or to the closing
2693
+ * backtick; if it stopped at `${` it sets `readingTemplateString = true`
2694
+ * and the parser must consume an expression + `}` and then call
2695
+ * `readString(CharCode.Backtick)` to read the next fragment.
2696
+ */
2697
+ parseTemplateLiteral(startPos) {
2698
+ const tn = this.tn;
2699
+ const parts = [];
2700
+ const exprs = [];
2701
+ // First fragment — readString advances past the opening backtick.
2702
+ let fragment = tn.readString();
2703
+ parts.push(fragment);
2704
+ while (tn.readingTemplateString) {
2705
+ // Just consumed `${`. Parse the interpolated expression.
2706
+ const interp = this.parseExpr();
2707
+ if (!interp)
2708
+ return this.error(DiagnosticCode.Expression_expected, tn.range());
2709
+ exprs.push(interp);
2710
+ if (!tn.skip(Token.CloseBrace))
2711
+ return this.error(DiagnosticCode._0_expected, tn.range(), "}");
2712
+ // Read the next fragment up to the next `${` or the closing
2713
+ // backtick. `readingTemplateString` is set inside readString.
2714
+ fragment = tn.readString(96 /* CharCode.Backtick */);
2715
+ parts.push(fragment);
2716
+ }
2717
+ return new TemplateStrExpr(parts, exprs, tn.range(startPos, tn.pos));
2718
+ }
2419
2719
  parseAssertStatement() {
2420
2720
  const tn = this.tn;
2421
2721
  const startPos = tn.tokenPos;