@harmoniclabs/pebble 0.1.10 → 0.3.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 (182) hide show
  1. package/dist/IR/CompilationCtx.d.ts +40 -0
  2. package/dist/IR/CompilationCtx.js +54 -0
  3. package/dist/IR/IRHash.d.ts +23 -2
  4. package/dist/IR/IRHash.js +10 -60
  5. package/dist/IR/IRNodes/IRConst.js +35 -4
  6. package/dist/IR/IRNodes/IRHoisted.d.ts +0 -1
  7. package/dist/IR/IRNodes/IRHoisted.js +4 -6
  8. package/dist/IR/IRNodes/IRLetted.d.ts +0 -1
  9. package/dist/IR/IRNodes/IRLetted.js +4 -6
  10. package/dist/IR/IRNodes/IRNative/IRNativeTag.d.ts +22 -2
  11. package/dist/IR/IRNodes/IRNative/IRNativeTag.js +26 -2
  12. package/dist/IR/IRNodes/IRNative/index.d.ts +16 -1
  13. package/dist/IR/IRNodes/IRNative/index.js +31 -2
  14. package/dist/IR/IRNodes/utils/hashVarSym.d.ts +0 -1
  15. package/dist/IR/IRNodes/utils/hashVarSym.js +27 -33
  16. package/dist/IR/toUPLC/CompilerOptions.d.ts +34 -7
  17. package/dist/IR/toUPLC/CompilerOptions.js +19 -10
  18. package/dist/IR/toUPLC/compileIRToUPLC.js +39 -3
  19. package/dist/IR/toUPLC/subRoutines/inlineSingleUseLetBindingsAndReturnRoot.d.ts +23 -0
  20. package/dist/IR/toUPLC/subRoutines/inlineSingleUseLetBindingsAndReturnRoot.js +263 -0
  21. package/dist/IR/toUPLC/subRoutines/introduceCaseForDualHeadTailAndReturnRoot.d.ts +35 -0
  22. package/dist/IR/toUPLC/subRoutines/introduceCaseForDualHeadTailAndReturnRoot.js +169 -0
  23. package/dist/IR/toUPLC/subRoutines/replaceHoistedWithLetted.d.ts +0 -1
  24. package/dist/IR/toUPLC/subRoutines/replaceHoistedWithLetted.js +6 -6
  25. package/dist/IR/toUPLC/subRoutines/replaceNatives/nativeToIR.d.ts +2 -3
  26. package/dist/IR/toUPLC/subRoutines/replaceNatives/nativeToIR.js +106 -65
  27. package/dist/IR/toUPLC/subRoutines/rewriteHeadTailInCaseConsAndReturnRoot.d.ts +30 -0
  28. package/dist/IR/toUPLC/subRoutines/rewriteHeadTailInCaseConsAndReturnRoot.js +95 -0
  29. package/dist/IR/toUPLC/subRoutines/rewriteNativesAppliedToConstantsAndReturnRoot.js +36 -5
  30. package/dist/IR/toUPLC/subRoutines/rewriteToCaseOverConstAndReturnRoot.d.ts +35 -0
  31. package/dist/IR/toUPLC/subRoutines/rewriteToCaseOverConstAndReturnRoot.js +169 -0
  32. package/dist/IR/tree_utils/_ir_caseList.d.ts +15 -0
  33. package/dist/IR/tree_utils/_ir_caseList.js +19 -0
  34. package/dist/IR/tree_utils/bytesToHex.d.ts +8 -0
  35. package/dist/IR/tree_utils/bytesToHex.js +69 -0
  36. package/dist/ast/nodes/expr/functions/FuncExpr.d.ts +16 -2
  37. package/dist/ast/nodes/expr/functions/FuncExpr.js +17 -0
  38. package/dist/ast/nodes/expr/litteral/LitteralExpr.d.ts +2 -1
  39. package/dist/ast/nodes/expr/litteral/LitteralExpr.js +2 -0
  40. package/dist/ast/nodes/expr/litteral/TemplateStrExpr.d.ts +30 -0
  41. package/dist/ast/nodes/expr/litteral/TemplateStrExpr.js +35 -0
  42. package/dist/ast/nodes/statements/ExportStmt.d.ts +3 -3
  43. package/dist/ast/nodes/statements/PebbleStmt.d.ts +4 -3
  44. package/dist/ast/nodes/statements/PebbleStmt.js +6 -2
  45. package/dist/ast/nodes/statements/TestParam.d.ts +18 -0
  46. package/dist/ast/nodes/statements/TestParam.js +18 -0
  47. package/dist/ast/nodes/statements/TestStmt.d.ts +5 -3
  48. package/dist/ast/nodes/statements/TestStmt.js +3 -1
  49. package/dist/ast/nodes/statements/UsingStmt.d.ts +32 -2
  50. package/dist/ast/nodes/statements/UsingStmt.js +39 -3
  51. package/dist/ast/nodes/statements/declarations/NamespaceDecl.d.ts +21 -0
  52. package/dist/ast/nodes/statements/declarations/NamespaceDecl.js +31 -0
  53. package/dist/ast/nodes/statements/declarations/StructDecl.d.ts +16 -2
  54. package/dist/ast/nodes/statements/declarations/StructDecl.js +15 -1
  55. package/dist/compiler/AstCompiler/AstCompiler.d.ts +27 -0
  56. package/dist/compiler/AstCompiler/AstCompiler.js +244 -7
  57. package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +16 -5
  58. package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +97 -6
  59. package/dist/compiler/AstCompiler/internal/exprs/_compileCaseExpr.js +31 -0
  60. package/dist/compiler/AstCompiler/internal/exprs/_compileFuncExpr.js +12 -5
  61. package/dist/compiler/AstCompiler/internal/exprs/_compileIsExpr.js +12 -0
  62. package/dist/compiler/AstCompiler/internal/exprs/_compileLitteralExpr.js +59 -0
  63. package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.d.ts +2 -3
  64. package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.js +64 -0
  65. package/dist/compiler/AstCompiler/internal/exprs/_compileUnaryPrefixExpr.js +13 -1
  66. package/dist/compiler/AstCompiler/internal/exprs/_compileVarAccessExpr.js +2 -0
  67. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileAddExpr.js +18 -5
  68. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileEqualExpr.js +3 -1
  69. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanEqualExpr.js +2 -1
  70. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanExpr.js +2 -1
  71. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanEqualExpr.js +2 -1
  72. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanExpr.js +2 -1
  73. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileMultExpr.js +24 -6
  74. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileNotEqualExpr.js +2 -1
  75. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileSubExpr.js +16 -5
  76. package/dist/compiler/AstCompiler/internal/statements/_compileMatchStmt.js +33 -20
  77. package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -1
  78. package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.d.ts +15 -1
  79. package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.js +70 -30
  80. package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.d.ts +11 -0
  81. package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.js +26 -0
  82. package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.d.ts +9 -4
  83. package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.js +51 -10
  84. package/dist/compiler/AstCompiler/internal/types/_compileDataEncodedConcreteType.js +21 -2
  85. package/dist/compiler/AstCompiler/internal/types/_compileSopEncodedConcreteType.js +17 -2
  86. package/dist/compiler/AstCompiler/scope/AstScope.d.ts +70 -1
  87. package/dist/compiler/AstCompiler/scope/AstScope.js +91 -0
  88. package/dist/compiler/AstCompiler/utils/getPropAccessReturnType.js +36 -1
  89. package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.d.ts +36 -0
  90. package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.js +123 -0
  91. package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.d.ts +28 -0
  92. package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.js +95 -0
  93. package/dist/compiler/AstCompiler/utils/resolveNamespacePath.d.ts +37 -0
  94. package/dist/compiler/AstCompiler/utils/resolveNamespacePath.js +93 -0
  95. package/dist/compiler/Compiler.d.ts +9 -1
  96. package/dist/compiler/Compiler.js +218 -30
  97. package/dist/compiler/TirCompiler/expressify/ExpressifyCtx.js +1 -1
  98. package/dist/compiler/TirCompiler/expressify/expressify.js +30 -2
  99. package/dist/compiler/TirCompiler/expressify/expressifyForStmt.d.ts +2 -1
  100. package/dist/compiler/TirCompiler/expressify/expressifyForStmt.js +45 -7
  101. package/dist/compiler/TirCompiler/expressify/expressifyVars.d.ts +0 -1
  102. package/dist/compiler/TirCompiler/expressify/expressifyVars.js +49 -15
  103. package/dist/compiler/test/TestResult.d.ts +38 -0
  104. package/dist/compiler/test/TestResult.js +6 -0
  105. package/dist/compiler/test/fuzz/PRNG.d.ts +26 -0
  106. package/dist/compiler/test/fuzz/PRNG.js +59 -0
  107. package/dist/compiler/tir/expressions/TirCaseExpr.d.ts +9 -0
  108. package/dist/compiler/tir/expressions/TirCaseExpr.js +144 -122
  109. package/dist/compiler/tir/expressions/TirElemAccessExpr.js +2 -2
  110. package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
  111. package/dist/compiler/tir/expressions/TirExpr.js +2 -0
  112. package/dist/compiler/tir/expressions/TirFromDataExpr.js +102 -67
  113. package/dist/compiler/tir/expressions/TirIsExpr.js +14 -1
  114. package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +18 -2
  115. package/dist/compiler/tir/expressions/TirNativeFunc.js +55 -118
  116. package/dist/compiler/tir/expressions/TirShowExpr.d.ts +52 -0
  117. package/dist/compiler/tir/expressions/TirShowExpr.js +199 -0
  118. package/dist/compiler/tir/expressions/TirToDataExpr.js +3 -0
  119. package/dist/compiler/tir/expressions/TirTraceExpr.js +11 -7
  120. package/dist/compiler/tir/expressions/TirTypeConversionExpr.js +10 -0
  121. package/dist/compiler/tir/expressions/TirVariableAccessExpr.d.ts +2 -3
  122. package/dist/compiler/tir/expressions/TirVariableAccessExpr.js +1 -4
  123. package/dist/compiler/tir/expressions/ToIRTermCtx.d.ts +20 -3
  124. package/dist/compiler/tir/expressions/ToIRTermCtx.js +48 -3
  125. package/dist/compiler/tir/expressions/binary/TirBinaryExpr.d.ts +2 -2
  126. package/dist/compiler/tir/expressions/binary/TirBinaryExpr.js +45 -8
  127. package/dist/compiler/tir/expressions/litteral/TirLitEnumMemberExpr.d.ts +19 -0
  128. package/dist/compiler/tir/expressions/litteral/TirLitEnumMemberExpr.js +24 -0
  129. package/dist/compiler/tir/expressions/litteral/TirLitteralExpr.d.ts +2 -1
  130. package/dist/compiler/tir/expressions/litteral/TirLitteralExpr.js +2 -0
  131. package/dist/compiler/tir/expressions/unary/TirUnaryMinus.js +4 -1
  132. package/dist/compiler/tir/program/TypedProgram.d.ts +101 -0
  133. package/dist/compiler/tir/program/TypedProgram.js +43 -0
  134. package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.d.ts +17 -0
  135. package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.js +70 -0
  136. package/dist/compiler/tir/program/stdScope/populateStdNamespace.d.ts +22 -0
  137. package/dist/compiler/tir/program/stdScope/populateStdNamespace.js +619 -0
  138. package/dist/compiler/tir/program/stdScope/prelude/preludeTypesSrc.js +35 -2
  139. package/dist/compiler/tir/program/stdScope/stdScope.d.ts +8 -0
  140. package/dist/compiler/tir/program/stdScope/stdScope.js +84 -41
  141. package/dist/compiler/tir/statements/TirStmt.js +0 -1
  142. package/dist/compiler/tir/statements/TirTestStmt.d.ts +46 -0
  143. package/dist/compiler/tir/statements/TirTestStmt.js +35 -0
  144. package/dist/compiler/tir/types/TirEnumType.d.ts +21 -0
  145. package/dist/compiler/tir/types/TirEnumType.js +36 -0
  146. package/dist/compiler/tir/types/TirNativeType/TirNativeType.d.ts +53 -2
  147. package/dist/compiler/tir/types/TirNativeType/TirNativeType.js +58 -1
  148. package/dist/compiler/tir/types/TirNativeType/native/array.d.ts +16 -0
  149. package/dist/compiler/tir/types/TirNativeType/native/array.js +38 -0
  150. package/dist/compiler/tir/types/TirNativeType/native/index.d.ts +2 -0
  151. package/dist/compiler/tir/types/TirNativeType/native/index.js +2 -0
  152. package/dist/compiler/tir/types/TirNativeType/native/value.d.ts +18 -0
  153. package/dist/compiler/tir/types/TirNativeType/native/value.js +17 -0
  154. package/dist/compiler/tir/types/TirStructType.js +6 -1
  155. package/dist/compiler/tir/types/TirType.d.ts +3 -2
  156. package/dist/compiler/tir/types/TirType.js +7 -2
  157. package/dist/compiler/tir/types/utils/canAssignTo.js +36 -1
  158. package/dist/compiler/tir/types/utils/canCastTo.js +14 -1
  159. package/dist/compiler/tir/types/utils/getDeconstructableType.d.ts +2 -1
  160. package/dist/compiler/tir/types/utils/getDeconstructableType.js +2 -0
  161. package/dist/compiler/tir/types/utils/inferTypeArgs.d.ts +19 -0
  162. package/dist/compiler/tir/types/utils/inferTypeArgs.js +83 -0
  163. package/dist/compiler/tir/types/utils/normalizeEnumToInt.d.ts +10 -0
  164. package/dist/compiler/tir/types/utils/normalizeEnumToInt.js +17 -0
  165. package/dist/compiler/tir/types/utils/substituteTypeParams.d.ts +9 -0
  166. package/dist/compiler/tir/types/utils/substituteTypeParams.js +67 -0
  167. package/dist/diagnostics/diagnosticMessages.generated.d.ts +10 -0
  168. package/dist/diagnostics/diagnosticMessages.generated.js +20 -0
  169. package/dist/index.d.ts +2 -0
  170. package/dist/index.js +2 -0
  171. package/dist/parser/Parser.d.ts +73 -3
  172. package/dist/parser/Parser.js +362 -46
  173. package/dist/tokenizer/Token.d.ts +106 -102
  174. package/dist/tokenizer/Token.js +111 -109
  175. package/dist/tokenizer/utils/tokenFromKeyword.js +11 -6
  176. package/dist/utils/semverSatisfies.d.ts +1 -0
  177. package/dist/utils/semverSatisfies.js +161 -0
  178. package/dist/version.generated.d.ts +1 -0
  179. package/dist/version.generated.js +2 -0
  180. package/package.json +5 -4
  181. package/dist/IR/tree_utils/_ir_lazyChooseList.d.ts +0 -3
  182. package/dist/IR/tree_utils/_ir_lazyChooseList.js +0 -7
@@ -0,0 +1,93 @@
1
+ import { DiagnosticCode } from "../../../diagnostics/diagnosticMessages.generated.js";
2
+ /**
3
+ * walks a dotted namespace path against the given scope.
4
+ *
5
+ * - returns the final `NamespaceSymbol` if every segment resolves to a namespace.
6
+ * - if the first segment is not a namespace, returns `undefined`
7
+ * (no diagnostic emitted; the caller can fall back to a different
8
+ * resolution strategy, e.g. struct lookup).
9
+ * - if a later segment is missing or is not a namespace, emits a
10
+ * diagnostic and returns `undefined`.
11
+ */
12
+ export function resolveNamespacePath(ctx, path) {
13
+ const segments = path.segments;
14
+ if (segments.length === 0)
15
+ return undefined;
16
+ const head = segments[0];
17
+ const first = ctx.scope.resolveNamespace(head.text);
18
+ if (!first)
19
+ return undefined;
20
+ const chain = [first];
21
+ let current = first;
22
+ for (let i = 1; i < segments.length; i++) {
23
+ const seg = segments[i];
24
+ const inner = current.publicScope.namespaces.get(seg.text);
25
+ if (!inner) {
26
+ ctx.error(DiagnosticCode.Namespace_0_has_no_exported_member_1, seg.range, current.name, seg.text);
27
+ return undefined;
28
+ }
29
+ chain.push(inner);
30
+ current = inner;
31
+ }
32
+ return { namespace: current, chain };
33
+ }
34
+ /**
35
+ * looks up a member name in a namespace's public scope.
36
+ *
37
+ * the member can be any of:
38
+ * - a value (variable)
39
+ * - a function
40
+ * - a type
41
+ * - an interface
42
+ * - a nested namespace
43
+ *
44
+ * binds the resolved member into `target` (under either `originalName`
45
+ * or `aliasName` if provided). emits a diagnostic and returns `false`
46
+ * if no such member is exported, or if a conflicting binding already
47
+ * exists in `target`.
48
+ */
49
+ export function bindNamespaceMember(ctx, ns, originalName, aliasName, target) {
50
+ const pub = ns.publicScope;
51
+ const name = originalName.text;
52
+ const localName = aliasName?.text ?? name;
53
+ const variable = pub.variables.get(name);
54
+ const fn = pub.functions.get(name);
55
+ const tirFunc = fn ? pub.program.functions.get(fn) : undefined;
56
+ const ty = pub.types.get(name);
57
+ const iface = pub.interfaces.get(name);
58
+ const nested = pub.namespaces.get(name);
59
+ if (!variable && !fn && !ty && !iface && !nested) {
60
+ ctx.error(DiagnosticCode.Namespace_0_has_no_exported_member_1, originalName.range, ns.name, name);
61
+ return false;
62
+ }
63
+ let ok = true;
64
+ if (variable) {
65
+ ok = target.defineValue({ ...variable, name: localName }) && ok;
66
+ }
67
+ if (fn) {
68
+ target.functions.set(localName, fn);
69
+ if (tirFunc) {
70
+ target.defineValue({
71
+ isConstant: true,
72
+ name: localName,
73
+ type: tirFunc.type,
74
+ });
75
+ }
76
+ }
77
+ if (ty) {
78
+ ok = target.defineType(localName, ty) && ok;
79
+ }
80
+ if (iface) {
81
+ if (target.interfaces.has(localName))
82
+ ok = false;
83
+ else
84
+ target.interfaces.set(localName, iface);
85
+ }
86
+ if (nested) {
87
+ ok = target.defineNamespace({
88
+ name: localName,
89
+ publicScope: nested.publicScope
90
+ }) && ok;
91
+ }
92
+ return ok;
93
+ }
@@ -3,11 +3,13 @@ import { DiagnosticMessage } from "../diagnostics/DiagnosticMessage.js";
3
3
  import { CompilerOptions } from "../IR/toUPLC/CompilerOptions.js";
4
4
  import { CompilerIoApi } from "./io/CompilerIoApi.js";
5
5
  import { CheckResult } from "./SourceTypeMap.js";
6
+ import { TestResult } from "./test/TestResult.js";
6
7
  export { CheckResult, SourceTypeMap, TypeEntry, MemberInfo } from "./SourceTypeMap.js";
8
+ export { TestBudget, TestIterationResult, TestResult, TestKind, } from "./test/TestResult.js";
7
9
  export declare class Compiler extends DiagnosticEmitter {
8
10
  readonly io: CompilerIoApi;
9
11
  readonly cfg: CompilerOptions;
10
- constructor(io?: CompilerIoApi, cfg?: CompilerOptions, diagnostics?: DiagnosticMessage[]);
12
+ constructor(io: CompilerIoApi, cfg: CompilerOptions, diagnostics?: DiagnosticMessage[]);
11
13
  check(config?: Partial<CompilerOptions>): Promise<CheckResult>;
12
14
  compile(config?: Partial<CompilerOptions>): Promise<Uint8Array>;
13
15
  export(config: Partial<ExportOptions> & HasFuncitonName): Promise<Uint8Array>;
@@ -16,6 +18,12 @@ export declare class Compiler extends DiagnosticEmitter {
16
18
  budgetSpent: import("@harmoniclabs/plutus-machine").ExBudget;
17
19
  logs: string[];
18
20
  }>;
21
+ test(config?: Partial<CompilerOptions> & {
22
+ nameFilter?: string | RegExp;
23
+ propertyIterations?: number;
24
+ seed?: number;
25
+ }): Promise<TestResult[]>;
26
+ private _runOneTest;
19
27
  runRepl(config?: Partial<CompilerOptions>): Promise<{
20
28
  result: import("@harmoniclabs/plutus-machine").CEKValueObj;
21
29
  budgetSpent: import("@harmoniclabs/plutus-machine").ExBudget;
@@ -1,25 +1,35 @@
1
- import { compileUPLC, Force, parseUPLC, UPLCProgram } from "@harmoniclabs/uplc";
2
- import { Machine } from "@harmoniclabs/plutus-machine";
1
+ import { Application, compileUPLC, Force, parseUPLC, UPLCConst, UPLCProgram } from "@harmoniclabs/uplc";
2
+ import { CEKError, Machine } from "@harmoniclabs/plutus-machine";
3
+ import { DiagnosticCategory } from "../diagnostics/DiagnosticCategory.js";
3
4
  import { DiagnosticEmitter } from "../diagnostics/DiagnosticEmitter.js";
4
- import { defaultOptions } from "../IR/toUPLC/CompilerOptions.js";
5
+ import { COMPILER_VERSION } from "../version.generated.js";
6
+ import { semverSatisfies } from "../utils/semverSatisfies.js";
5
7
  import { AstCompiler } from "./AstCompiler/AstCompiler.js";
6
8
  import { createMemoryCompilerIoApi } from "./io/CompilerIoApi.js";
7
9
  import { compileTypedProgram } from "./TirCompiler/compileTirProgram.js";
8
- import { __VERY_UNSAFE_FORGET_IRHASH_ONLY_USE_AT_END_OF_UPLC_COMPILATION } from "../IR/IRHash.js";
9
- import { __VERY_UNSAFE_FORGET_VAR_SYM_HASHES_ONLY_USE_AT_END_OF_UPLC_COMPILATION } from "../IR/IRNodes/utils/hashVarSym.js";
10
- import { __unsafe_clear_hoisted_hash_to_symbol } from "../IR/IRNodes/IRHoisted.js";
11
- import { __unsafe_clear_letted_hash_to_symbol } from "../IR/IRNodes/IRLetted.js";
12
- import { __unsafe_clear_hoisted_cache } from "../IR/toUPLC/subRoutines/replaceHoistedWithLetted.js";
13
- import { __unsafe_clear_mapToType_cache } from "./TirCompiler/expressify/expressifyVars.js";
10
+ import { CompilationCtx, withCompilationCtx } from "../IR/CompilationCtx.js";
14
11
  import { compileIRToUPLC } from "../IR/toUPLC/compileIRToUPLC.js";
12
+ import { TirFuncExpr } from "./tir/expressions/TirFuncExpr.js";
13
+ import { addBudget, zeroBudget, } from "./test/TestResult.js";
14
+ import { PRNG } from "./test/fuzz/PRNG.js";
15
15
  export { SourceTypeMap } from "./SourceTypeMap.js";
16
16
  export class Compiler extends DiagnosticEmitter {
17
17
  io;
18
18
  cfg;
19
- constructor(io = createMemoryCompilerIoApi({ useConsoleAsOutput: true }), cfg = defaultOptions, diagnostics) {
19
+ constructor(io = createMemoryCompilerIoApi({ useConsoleAsOutput: true }), cfg, diagnostics) {
20
20
  super(diagnostics);
21
21
  this.io = io;
22
22
  this.cfg = cfg;
23
+ const range = cfg?.compilerVersion;
24
+ if (typeof range !== "string" || range.length === 0) {
25
+ throw new Error(`Pebble compiler config is missing "compilerVersion". ` +
26
+ `Starting from @harmoniclabs/pebble@0.2.0 this field is required ` +
27
+ `and must be an npm-style semver range (e.g. "^0.2.0").`);
28
+ }
29
+ if (!semverSatisfies(COMPILER_VERSION, range)) {
30
+ throw new Error(`Pebble compiler version ${COMPILER_VERSION} does not satisfy ` +
31
+ `the configured "compilerVersion" range "${range}".`);
32
+ }
23
33
  if (cfg.silent === true) {
24
34
  this.io.stdout = { write() { } };
25
35
  }
@@ -42,7 +52,6 @@ export class Compiler extends DiagnosticEmitter {
42
52
  const program = await astCompiler.compile();
43
53
  if (this.diagnostics.length > 0) {
44
54
  let msg;
45
- // globalThis.console && console.log( this.diagnostics );
46
55
  const fstErrorMsg = this.diagnostics[0].toString();
47
56
  const nDiags = this.diagnostics.length;
48
57
  for (msg of this.diagnostics) {
@@ -67,7 +76,6 @@ export class Compiler extends DiagnosticEmitter {
67
76
  const program = await astCompiler.export(cfg.functionName, cfg.entry);
68
77
  if (this.diagnostics.length > 0) {
69
78
  let msg;
70
- globalThis.console && console.log(this.diagnostics);
71
79
  const fstErrorMsg = this.diagnostics[0].toString();
72
80
  const nDiags = this.diagnostics.length;
73
81
  while (msg = this.diagnostics.shift()) {
@@ -88,7 +96,6 @@ export class Compiler extends DiagnosticEmitter {
88
96
  const program = await astCompiler.run();
89
97
  if (this.diagnostics.length > 0) {
90
98
  let msg;
91
- globalThis.console && console.log(this.diagnostics);
92
99
  const fstErrorMsg = this.diagnostics[0].toString();
93
100
  const nDiags = this.diagnostics.length;
94
101
  while (msg = this.diagnostics.shift()) {
@@ -102,6 +109,167 @@ export class Compiler extends DiagnosticEmitter {
102
109
  // compiles to `Delay(body)`; force it so the body executes.
103
110
  return Machine.eval(new Force(uplcProgram.body));
104
111
  }
112
+ async test(config) {
113
+ const cfg = {
114
+ ...this.cfg,
115
+ ...config,
116
+ silent: true,
117
+ addMarker: false,
118
+ };
119
+ const nameFilter = config?.nameFilter;
120
+ const matches = (!nameFilter ? () => true :
121
+ typeof nameFilter === "string" ? (n) => n.includes(nameFilter) :
122
+ (n) => nameFilter.test(n));
123
+ const propertyIterations = Math.max(1, config?.propertyIterations ?? 100);
124
+ const seed = config?.seed ?? 0;
125
+ // 1) discovery pass: parse + check, populate program.tests.
126
+ // diagnostics from this pass are surfaced once; subsequent per-test
127
+ // passes use their own diagnostic arrays so we don't double-report.
128
+ const discovery = new AstCompiler(cfg, this.io, this.diagnostics);
129
+ const discoveryResult = await discovery.check();
130
+ const descriptors = discoveryResult.program.tests
131
+ .filter(t => matches(t.name))
132
+ .map(t => {
133
+ const fn = discoveryResult.program.functions.get(t.tirFuncName);
134
+ const paramNames = (fn instanceof TirFuncExpr) ? fn.params.map(p => p.sourceName ?? p.name) : [];
135
+ return {
136
+ name: t.name,
137
+ tirFuncName: t.tirFuncName,
138
+ sourceFile: t.sourceFile,
139
+ range: t.range,
140
+ paramNames,
141
+ fuzzerInfos: t.fuzzerInfos,
142
+ };
143
+ });
144
+ const results = new Array(descriptors.length);
145
+ for (let i = 0; i < descriptors.length; i++) {
146
+ results[i] = await this._runOneTest(cfg, descriptors[i], propertyIterations, seed);
147
+ }
148
+ return results;
149
+ }
150
+ async _runOneTest(cfg, desc, propertyIterations, seed) {
151
+ const isProperty = desc.fuzzerInfos.length > 0;
152
+ // fresh AstCompiler so the expressify pass starts from a clean program
153
+ const localDiagnostics = [];
154
+ const astCompiler = new AstCompiler(cfg, this.io, localDiagnostics);
155
+ await astCompiler.compileFile(cfg.entry, true);
156
+ if (localDiagnostics.some(d => d.category === DiagnosticCategory.Error)) {
157
+ return _failedTestResult(desc, "compile error: " + localDiagnostics.find(d => d.category === DiagnosticCategory.Error).toString(), isProperty ? "property" : "unit");
158
+ }
159
+ const fn = astCompiler.program.functions.get(desc.tirFuncName);
160
+ if (!(fn instanceof TirFuncExpr)) {
161
+ return _failedTestResult(desc, `test function "${desc.name}" not found after re-parse`, isProperty ? "property" : "unit");
162
+ }
163
+ astCompiler.program.contractTirFuncName = desc.tirFuncName;
164
+ let serialized;
165
+ try {
166
+ serialized = this._compileBackend(cfg, astCompiler.program, true);
167
+ }
168
+ catch (err) {
169
+ return _failedTestResult(desc, "backend error: " + (err instanceof Error ? err.message : String(err)), isProperty ? "property" : "unit");
170
+ }
171
+ const uplcProgram = parseUPLC(serialized);
172
+ if (!isProperty) {
173
+ const evalResult = Machine.eval(new Force(uplcProgram.body));
174
+ const isErr = evalResult.result instanceof CEKError;
175
+ const budget = {
176
+ cpu: BigInt(evalResult.budgetSpent.cpu),
177
+ mem: BigInt(evalResult.budgetSpent.mem),
178
+ };
179
+ const iter = {
180
+ passed: !isErr,
181
+ budgetSpent: budget,
182
+ logs: evalResult.logs.slice(),
183
+ error: isErr ? { msg: evalResult.result.msg } : undefined,
184
+ };
185
+ return {
186
+ name: desc.name,
187
+ sourceFile: desc.sourceFile,
188
+ range: desc.range,
189
+ kind: "unit",
190
+ passed: !isErr,
191
+ iterations: [iter],
192
+ totalBudget: addBudget(zeroBudget(), budget),
193
+ };
194
+ }
195
+ // ── Property test ──────────────────────────────────────────────
196
+ // Check that every parameter has an executable fuzzer in v1 (Phase 1).
197
+ const unsupported = desc.fuzzerInfos.find(fi => fi.kind === "unsupported" || fi.kind === "via_not_implemented");
198
+ if (unsupported) {
199
+ const reason = unsupported.kind === "via_not_implemented"
200
+ ? "user-defined fuzzers via the 'via' keyword are not yet executable (Phase 2)"
201
+ : unsupported.reason;
202
+ return {
203
+ name: desc.name,
204
+ sourceFile: desc.sourceFile,
205
+ range: desc.range,
206
+ kind: "property",
207
+ passed: false,
208
+ iterations: [],
209
+ totalBudget: zeroBudget(),
210
+ skippedReason: reason,
211
+ seed,
212
+ };
213
+ }
214
+ // Run N iterations with TS-side sampling.
215
+ const prng = new PRNG(seed);
216
+ const iterations = [];
217
+ let totalBudget = zeroBudget();
218
+ let passedAll = true;
219
+ for (let i = 0; i < propertyIterations; i++) {
220
+ const inputs = [];
221
+ const args = [];
222
+ for (let p = 0; p < desc.fuzzerInfos.length; p++) {
223
+ const fi = desc.fuzzerInfos[p];
224
+ if (fi.kind !== "primitive")
225
+ throw new Error("unreachable: non-primitive after unsupported check");
226
+ const paramName = desc.paramNames[p] ?? `param${p}`;
227
+ if (fi.primitive === "int") {
228
+ const v = prng.nextIntBiased();
229
+ inputs.push({ name: paramName, value: v });
230
+ args.push(UPLCConst.int(v));
231
+ }
232
+ else // bool
233
+ {
234
+ const v = prng.nextBool();
235
+ inputs.push({ name: paramName, value: v });
236
+ args.push(UPLCConst.bool(v));
237
+ }
238
+ }
239
+ let app = uplcProgram.body;
240
+ for (const arg of args)
241
+ app = new Application(app, arg);
242
+ const evalResult = Machine.eval(app);
243
+ const isErr = evalResult.result instanceof CEKError;
244
+ const budget = {
245
+ cpu: BigInt(evalResult.budgetSpent.cpu),
246
+ mem: BigInt(evalResult.budgetSpent.mem),
247
+ };
248
+ const iter = {
249
+ passed: !isErr,
250
+ budgetSpent: budget,
251
+ logs: evalResult.logs.slice(),
252
+ error: isErr ? { msg: evalResult.result.msg } : undefined,
253
+ inputs,
254
+ };
255
+ iterations.push(iter);
256
+ totalBudget = addBudget(totalBudget, budget);
257
+ if (isErr) {
258
+ passedAll = false;
259
+ break; // early exit on first failure (Phase 1; shrinking is Phase 2)
260
+ }
261
+ }
262
+ return {
263
+ name: desc.name,
264
+ sourceFile: desc.sourceFile,
265
+ range: desc.range,
266
+ kind: "property",
267
+ passed: passedAll,
268
+ iterations,
269
+ totalBudget,
270
+ seed,
271
+ };
272
+ }
105
273
  async runRepl(config) {
106
274
  const cfg = {
107
275
  ...this.cfg,
@@ -112,7 +280,6 @@ export class Compiler extends DiagnosticEmitter {
112
280
  const program = await astCompiler.runRepl();
113
281
  if (this.diagnostics.length > 0) {
114
282
  let msg;
115
- globalThis.console && console.log(this.diagnostics);
116
283
  const fstErrorMsg = this.diagnostics[0].toString();
117
284
  const nDiags = this.diagnostics.length;
118
285
  while (msg = this.diagnostics.shift()) {
@@ -126,21 +293,42 @@ export class Compiler extends DiagnosticEmitter {
126
293
  // compiles to `Delay(body)`; force it so the body executes.
127
294
  return Machine.eval(new Force(uplcProgram.body));
128
295
  }
129
- _compileBackend(cfg, program) {
130
- // backend starts here
131
- const ir = compileTypedProgram(cfg, program);
132
- const uplc = compileIRToUPLC(ir, cfg);
133
- const serialized = compileUPLC(new UPLCProgram(cfg.targetUplcVersion, uplc));
134
- const outDir = cfg.outDir;
135
- const outPath = outDir + (outDir.endsWith("/") ? "" : "/") + "out.flat";
136
- this.io.writeFile(outPath, serialized, cfg.root);
137
- this.io.stdout.write(`compiled program written to ${outPath}\n`);
138
- __VERY_UNSAFE_FORGET_IRHASH_ONLY_USE_AT_END_OF_UPLC_COMPILATION();
139
- __VERY_UNSAFE_FORGET_VAR_SYM_HASHES_ONLY_USE_AT_END_OF_UPLC_COMPILATION();
140
- __unsafe_clear_hoisted_hash_to_symbol();
141
- __unsafe_clear_letted_hash_to_symbol();
142
- __unsafe_clear_hoisted_cache();
143
- __unsafe_clear_mapToType_cache();
144
- return serialized;
296
+ _compileBackend(cfg, program, skipFileOutput = false) {
297
+ // Run the whole backend under a fresh per-compilation context. All
298
+ // node-level caches (hoisted/letted naming, hoisted->letted lowering,
299
+ // the mapToType helper cache) live on this context, so they cannot
300
+ // leak into or be perturbed by any other compilation — and they are
301
+ // dropped automatically when this scope exits, even on throw. The
302
+ // content-addressed `IRHash` has no global state at all, so there is
303
+ // nothing else to reset.
304
+ return withCompilationCtx(new CompilationCtx(), () => {
305
+ const ir = compileTypedProgram(cfg, program);
306
+ const uplc = compileIRToUPLC(ir, cfg);
307
+ const serialized = compileUPLC(new UPLCProgram(cfg.targetUplcVersion, uplc));
308
+ if (!skipFileOutput) {
309
+ const outDir = cfg.outDir;
310
+ const outPath = outDir + (outDir.endsWith("/") ? "" : "/") + "out.flat";
311
+ this.io.writeFile(outPath, serialized, cfg.root);
312
+ this.io.stdout.write(`compiled program written to ${outPath}\n`);
313
+ }
314
+ return serialized;
315
+ });
145
316
  }
146
317
  }
318
+ function _failedTestResult(desc, msg, kind = "unit") {
319
+ const budget = zeroBudget();
320
+ return {
321
+ name: desc.name,
322
+ sourceFile: desc.sourceFile,
323
+ range: desc.range,
324
+ kind,
325
+ passed: false,
326
+ iterations: [{
327
+ passed: false,
328
+ budgetSpent: budget,
329
+ logs: [],
330
+ error: { msg },
331
+ }],
332
+ totalBudget: budget,
333
+ };
334
+ }
@@ -303,7 +303,7 @@ export class ExpressifyCtx {
303
303
  if (stmt.rest) {
304
304
  if (isDestructuredField)
305
305
  throw new Error("rest is not implemented in destructured field array-like deconstruct. TODO: map data elems to elem type");
306
- const lettedRest = this.introduceLettedConstant(stmt.rest, new TirCallExpr(TirNativeFunc._dropList(elemsType), [
306
+ const lettedRest = this.introduceLettedConstant(stmt.rest, new TirCallExpr(TirNativeFunc.dropList(elemsType), [
307
307
  new TirLitIntExpr(BigInt(nElems), stmt.range),
308
308
  lettedArrayExpr
309
309
  ], new TirListT(elemsType), stmt.range), stmt.range);
@@ -200,7 +200,7 @@ loopReplacements, assertions = []) {
200
200
  }
201
201
  if (stmt.rest) {
202
202
  const uniqueRestName = getUniqueInternalName(stmt.rest);
203
- const restLetted = ctx.introduceLettedConstant(uniqueRestName, new TirCallExpr(TirNativeFunc._dropList(elemType), [
203
+ const restLetted = ctx.introduceLettedConstant(uniqueRestName, new TirCallExpr(TirNativeFunc.dropList(elemType), [
204
204
  new TirLitIntExpr(BigInt(stmt.elements.length % modTails), stmt.range),
205
205
  lettedArr
206
206
  ], listType, stmt.range), stmt.range);
@@ -395,8 +395,36 @@ loopReplacements, assertions = []) {
395
395
  const returnTypeAndInvalidInit = getBranchStmtReturnType(reassignedAndFlow, ctx, stmt.range);
396
396
  const forStmt = loopToForStmt(stmt);
397
397
  const { bodyStateType, initState } = getBodyStateType(returnTypeAndInvalidInit, forStmt);
398
+ // Optimization: when the loop reassigns exactly one variable
399
+ // and has no user-written `return` inside, skip the SoP wrap
400
+ // around the loop's result. The recursive loop function then
401
+ // returns the bare variable's type, saving one `Reassigns{…}`
402
+ // constructor build per iteration and one IRCase at the exit.
403
+ // Requires the loop to be non-terminating (terminating loops
404
+ // are tail-positioned and their value must match the outer
405
+ // function's expected return type — we don't try to reconcile
406
+ // that here).
407
+ const canBareLower = (reassignedAndFlow.reassigned.length === 1
408
+ && !reassignedAndFlow.returns
409
+ && !definitelyTerminates);
398
410
  const loopExprCtx = ctx.newChild();
399
- const loopExpr = expressifyForStmt(loopExprCtx, forStmt, returnTypeAndInvalidInit.sop, bodyStateType, initState);
411
+ const bareReturnType = canBareLower
412
+ ? bodyStateType.constructors[0].fields[0].type
413
+ : undefined;
414
+ const loopExpr = expressifyForStmt(loopExprCtx, forStmt, returnTypeAndInvalidInit.sop, bodyStateType, initState, bareReturnType);
415
+ if (canBareLower) {
416
+ // Bind the loop's bare result as the variable's new SSA
417
+ // name, then evaluate the rest of the body. No outer
418
+ // case-match needed. Use a fresh internal name so the
419
+ // existing `letted[varName] = initialValue` binding (from
420
+ // the original `let varName = ...` statement) isn't
421
+ // shadowed by a no-op `introduceLettedConstant` call.
422
+ const [varName] = reassignedAndFlow.reassigned;
423
+ const loopResultName = getUniqueInternalName(varName);
424
+ const lettedExpr = ctx.introduceLettedConstant(loopResultName, loopExpr, stmt.range);
425
+ ctx.setNewVariableName(varName, lettedExpr.varName);
426
+ return TirAssertAndContinueExpr.fromStmtsAndContinuation(assertions, expressifyFuncBody(ctx, bodyStmts, loopReplacements));
427
+ }
400
428
  const result = TirAssertAndContinueExpr.fromStmtsAndContinuation(assertions, definitelyTerminates ? loopExpr : wrapNonTerminatingFinalStmtAsCaseExpr(loopExpr, returnTypeAndInvalidInit.sop, ctx, stmt.range, reassignedAndFlow, bodyStmts, loopReplacements));
401
429
  return result;
402
430
  }
@@ -4,6 +4,7 @@ import { TirForOfStmt } from "../../tir/statements/TirForOfStmt.js";
4
4
  import { TirForStmt } from "../../tir/statements/TirForStmt.js";
5
5
  import { TirWhileStmt } from "../../tir/statements/TirWhileStmt.js";
6
6
  import { TirSoPStructType } from "../../tir/types/TirStructType.js";
7
+ import { TirType } from "../../tir/types/TirType.js";
7
8
  import { ExpressifyCtx } from "./ExpressifyCtx.js";
8
9
  export declare function loopToForStmt(stmt: TirWhileStmt | TirForOfStmt | TirForStmt): TirForStmt;
9
- export declare function expressifyForStmt(ctx: ExpressifyCtx, stmt: TirForStmt, returnType: TirSoPStructType, bodyStateType: TirSoPStructType, initState: TirLitNamedObjExpr): TirCallExpr;
10
+ export declare function expressifyForStmt(ctx: ExpressifyCtx, stmt: TirForStmt, returnType: TirSoPStructType, bodyStateType: TirSoPStructType, initState: TirLitNamedObjExpr, bareReturnType?: TirType): TirCallExpr;
@@ -68,7 +68,16 @@ export function loopToForStmt(stmt) {
68
68
  stmt.body, // loopBody
69
69
  stmt.range);
70
70
  }
71
- export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initState) {
71
+ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initState,
72
+ // Optimization: when the loop has exactly one reassigned variable
73
+ // and no user-written `return` inside its body, the SoP wrap
74
+ // (`Reassigns{var}`) on every iteration is unnecessary. The caller
75
+ // can opt into a bare-value lowering by passing the variable's type
76
+ // here; the loop function then returns that type directly, and the
77
+ // call site is expected to bind the result without a case-match.
78
+ bareReturnType) {
79
+ const effectiveReturnType = bareReturnType ?? returnType;
80
+ const isBareMode = bareReturnType !== undefined;
72
81
  let loopBody = stmt.body instanceof TirBlockStmt ? stmt.body : new TirBlockStmt([stmt.body], stmt.range);
73
82
  loopBody = new TirBlockStmt(loopBody.stmts.slice(), loopBody.range);
74
83
  // add final loop updates
@@ -88,9 +97,27 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
88
97
  ], loopBody.range);
89
98
  }
90
99
  const loopFuncName = getUniqueInternalName("loop");
91
- const loopFuncType = new TirFuncT(bodyStateType.constructors[0].fields.map(f => f.type), returnType);
100
+ const loopFuncType = new TirFuncT(bodyStateType.constructors[0].fields.map(f => f.type), effectiveReturnType);
92
101
  const loopReplacements = {
93
102
  compileBreak(ctx, stmt) {
103
+ if (isBareMode) {
104
+ // Bare-value mode: the loop's return type IS the single
105
+ // user variable's type. `break` yields that var's current
106
+ // value directly, no SoP construction.
107
+ const userVarField = bodyStateType.constructors[0].fields[0];
108
+ const resolved = ctx.getVariable(userVarField.name);
109
+ if (isExpressifyFuncParam(resolved)) {
110
+ return new TirVariableAccessExpr({
111
+ variableInfos: {
112
+ name: resolved.name,
113
+ type: resolved.type,
114
+ isConstant: false
115
+ },
116
+ isDefinedOutsideFuncScope: false
117
+ }, stmt.range);
118
+ }
119
+ return resolved;
120
+ }
94
121
  // return first constructor of the return type
95
122
  const ctor = returnType.constructors[0];
96
123
  return new TirLitNamedObjExpr(new Identifier(ctor.name, stmt.range), ctor.fields.map(f => new Identifier(f.name, stmt.range)), bodyStateType.constructors[0].fields
@@ -111,10 +138,21 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
111
138
  }), returnType, stmt.range);
112
139
  },
113
140
  replaceReturnValue(ctx, stmt) {
114
- // return second constructor of the return type
141
+ // Synthetic returns inserted by `expressifyIfBranch` (and
142
+ // analogous callers) carry the inner-if's SoP value as the
143
+ // continuation of the ternary they participate in — they are
144
+ // NOT user-written returns escaping the loop. When the loop
145
+ // has no user-written `return` in its body, `returnType` has
146
+ // only the "break/continue" constructor: in that case the
147
+ // value flowing through is already the right type and we
148
+ // must leave it untouched. (Previously this threw "No return
149
+ // constructor found in return type" when a for-of body
150
+ // contained an `if` that mutated a captured `let` — the
151
+ // synthesized branch-tail returns hit this path even though
152
+ // the user had no `return` inside the loop.)
115
153
  const ctor = returnType.constructors[1];
116
154
  if (!ctor) {
117
- throw new Error("No return constructor found in return type");
155
+ return stmt.value;
118
156
  }
119
157
  return new TirLitNamedObjExpr(new Identifier(ctor.name, stmt.range), [new Identifier(ctor.fields[0].name, stmt.range)], [stmt.value], returnType, stmt.range);
120
158
  },
@@ -149,7 +187,7 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
149
187
  }, stmt.range);
150
188
  }
151
189
  return resolved;
152
- }), returnType, stmt.range);
190
+ }), effectiveReturnType, stmt.range);
153
191
  },
154
192
  };
155
193
  const loopCompilationCtx = ctx.newChild();
@@ -165,7 +203,7 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
165
203
  false, // is constant
166
204
  stmt.range)),
167
205
  // func return type
168
- returnType,
206
+ effectiveReturnType,
169
207
  // func body
170
208
  new TirBlockStmt([
171
209
  new TirReturnStmt(expressifyFuncBody(loopCompilationCtx, loopBody.stmts, loopReplacements, [] // assertions
@@ -176,5 +214,5 @@ export function expressifyForStmt(ctx, stmt, returnType, bodyStateType, initStat
176
214
  );
177
215
  return new TirCallExpr(loopFuncExpr,
178
216
  // loop call init args
179
- initState.values.map(v => expressifyVars(ctx, v.clone())), returnType, stmt.range);
217
+ initState.values.map(v => expressifyVars(ctx, v.clone())), effectiveReturnType, stmt.range);
180
218
  }
@@ -8,4 +8,3 @@ import { ExpressifyCtx } from "./ExpressifyCtx.js";
8
8
  * @returns a the modified expression
9
9
  **/
10
10
  export declare function expressifyVars(ctx: ExpressifyCtx, expr: TirExpr): TirExpr;
11
- export declare function __unsafe_clear_mapToType_cache(): void;