@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
@@ -1,36 +1,30 @@
1
- // HASH GENERATOR
2
1
  import { fromHex } from "@harmoniclabs/uint8array-utils";
3
- const MAX_SAFE_INTEGER = Number(globalThis.Number?.MAX_SAFE_INTEGER ?? ((2 ** 53) - 1));
4
- const MIN_SAFE_INTEGER = Number(globalThis.Number?.MIN_SAFE_INTEGER ?? -MAX_SAFE_INTEGER);
5
- const _sym_to_hash = new Map();
6
- const _hash_to_sym = new Map();
7
- let _next_hash = MIN_SAFE_INTEGER;
8
- const unusedHashes = [];
9
- function _collectUnusedHashes() {
10
- for (const [h, s] of _hash_to_sym) {
11
- if (!_sym_to_hash.has(s)) {
12
- _hash_to_sym.delete(h);
13
- unusedHashes.push(h);
14
- }
15
- }
16
- }
17
- export function __VERY_UNSAFE_FORGET_VAR_SYM_HASHES_ONLY_USE_AT_END_OF_UPLC_COMPILATION() {
18
- _hash_to_sym.clear();
19
- unusedHashes.length = 0;
20
- _next_hash = MIN_SAFE_INTEGER;
21
- }
2
+ /**
3
+ * Stable per-symbol bytes used as the preimage contribution of an
4
+ * `IRVar` / self-reference when hashing.
5
+ *
6
+ * Each distinct symbol is assigned a unique 8-byte id the first time it
7
+ * is hashed; subsequent calls for the same symbol return the same bytes.
8
+ * This makes hashing a pure function of the (symbol-identity) content,
9
+ * so a term's hash is stable for the life of the process — exactly what
10
+ * content-addressed `IRHash` requires.
11
+ *
12
+ * No global state needs resetting:
13
+ * - the map is a `WeakMap`, so a symbol's entry is collected once the
14
+ * symbol itself is unreachable (no unbounded growth);
15
+ * - the counter is monotonic and never rewinds, so a symbol can never
16
+ * be reassigned a colliding id (the bug that the old reset-based
17
+ * scheme introduced).
18
+ */
19
+ // `any` key: non-registered symbols are valid WeakMap keys at runtime
20
+ // (ES2023 / Node 20+), but older `lib` typings don't model symbol keys.
21
+ const _sym_to_bytes = new WeakMap();
22
+ let _next_id = 0n;
22
23
  export function hashVarSym(s) {
23
- const limitReached = _next_hash >= MAX_SAFE_INTEGER;
24
- if (_next_hash % 0xffff === 0
25
- || limitReached)
26
- _collectUnusedHashes();
27
- if (limitReached
28
- && unusedHashes.length <= 0)
29
- throw new Error("ran out of IR hashes");
30
- const result_hash = unusedHashes.shift() ?? _next_hash++;
31
- _sym_to_hash.set(s, result_hash);
32
- _hash_to_sym.set(result_hash, s);
33
- return fromHex((BigInt(result_hash) + BigInt(MAX_SAFE_INTEGER))
34
- .toString(16)
35
- .padStart(16, "0"));
24
+ let bytes = _sym_to_bytes.get(s);
25
+ if (bytes !== undefined)
26
+ return bytes;
27
+ bytes = fromHex((_next_id++).toString(16).padStart(16, "0"));
28
+ _sym_to_bytes.set(s, bytes);
29
+ return bytes;
36
30
  }
@@ -1,4 +1,6 @@
1
1
  import { UPLCVersion } from "@harmoniclabs/uplc";
2
+ import { COMPILER_VERSION } from "../../version.generated.js";
3
+ export { COMPILER_VERSION };
2
4
  export interface CompilerUplcOptimizations {
3
5
  /**
4
6
  *
@@ -21,6 +23,13 @@ export declare const defaultUplcOptimizations: CompilerUplcOptimizations;
21
23
  export declare function isDebugUplcOptimizations(options?: Partial<CompilerUplcOptimizations>): boolean;
22
24
  export declare function completeUplcOptimizations(options: Partial<CompilerUplcOptimizations>, complete?: CompilerUplcOptimizations): CompilerUplcOptimizations;
23
25
  export interface CompilerOptions {
26
+ /**
27
+ * npm-style semver range that the running compiler version must satisfy.
28
+ * Required since `@harmoniclabs/pebble@0.2.0` — the `Compiler` throws
29
+ * when this field is missing or when the running compiler version does
30
+ * not satisfy the range.
31
+ */
32
+ compilerVersion: string;
24
33
  /**
25
34
  * path to the entry file
26
35
  */
@@ -75,11 +84,29 @@ export interface CompilerOptions {
75
84
  *
76
85
  **/
77
86
  addMarker: boolean;
87
+ /**
88
+ * Controls the default Data encoding for single-constructor shortcut
89
+ * struct declarations (`struct Foo { x: int }`).
90
+ *
91
+ * - `"default"`: shortcut single-constructor structs are encoded as
92
+ * `constrData(0, fields)` (backwards-compatible). Use the explicit
93
+ * `untagged` keyword to opt a specific struct in to the minimal form.
94
+ * - `"minimal"`: shortcut single-constructor structs are encoded as
95
+ * `listData(fields)` (smaller / cheaper). The explicit `untagged`
96
+ * keyword still forces minimal regardless.
97
+ */
98
+ encodingStrategy: "default" | "minimal";
78
99
  }
79
- export declare const extremeOptions: CompilerOptions;
80
- export declare const productionOptions: CompilerOptions;
81
- export declare const debugOptions: CompilerOptions;
82
- export declare const defaultOptions: CompilerOptions;
83
- export declare const testOptions: CompilerOptions;
84
- export declare const defulatCompilerOptions: CompilerOptions;
85
- export declare function completeCompilerOptions(options: Partial<CompilerOptions>, complete?: CompilerOptions): CompilerOptions;
100
+ /**
101
+ * Option templates intentionally omit `compilerVersion` — every caller must
102
+ * set it explicitly so a missing `compilerVersion` always surfaces as an
103
+ * error from the `Compiler` constructor.
104
+ */
105
+ export type CompilerDefaults = Omit<CompilerOptions, "compilerVersion">;
106
+ export declare const extremeOptions: CompilerDefaults;
107
+ export declare const productionOptions: CompilerDefaults;
108
+ export declare const debugOptions: CompilerDefaults;
109
+ export declare const defaultOptions: CompilerDefaults;
110
+ export declare const testOptions: CompilerDefaults;
111
+ export declare const defulatCompilerOptions: CompilerDefaults;
112
+ export declare function completeCompilerOptions(options: Partial<CompilerOptions>, complete?: Partial<CompilerOptions>): CompilerOptions;
@@ -1,5 +1,8 @@
1
1
  import { isObject } from "@harmoniclabs/obj-utils";
2
- import { defaultUplcVersion, UPLCVersion } from "@harmoniclabs/uplc";
2
+ import { UPLCVersion } from "@harmoniclabs/uplc";
3
+ import { COMPILER_VERSION } from "../../version.generated.js";
4
+ const defaultTargetUplcVersion = new UPLCVersion(1, 1, 0);
5
+ export { COMPILER_VERSION };
3
6
  export const productionUplcOptimizations = Object.freeze({
4
7
  groupApplications: true,
5
8
  inlineSingleUse: true,
@@ -37,33 +40,36 @@ export const extremeOptions = Object.freeze({
37
40
  root: ".",
38
41
  outDir: "./out",
39
42
  silent: false,
40
- targetUplcVersion: defaultUplcVersion,
43
+ targetUplcVersion: defaultTargetUplcVersion,
41
44
  removeTraces: true,
42
45
  delayHoists: true,
43
46
  uplcOptimizations: productionUplcOptimizations,
44
- addMarker: true
47
+ addMarker: true,
48
+ encodingStrategy: "minimal"
45
49
  });
46
50
  export const productionOptions = Object.freeze({
47
51
  entry: "./src/index.pebble",
48
52
  root: ".",
49
53
  outDir: "./out",
50
54
  silent: false,
51
- targetUplcVersion: defaultUplcVersion,
55
+ targetUplcVersion: defaultTargetUplcVersion,
52
56
  removeTraces: true,
53
57
  delayHoists: true,
54
58
  uplcOptimizations: productionUplcOptimizations,
55
- addMarker: true
59
+ addMarker: true,
60
+ encodingStrategy: "default"
56
61
  });
57
62
  export const debugOptions = Object.freeze({
58
63
  entry: "./src/index.pebble",
59
64
  root: ".",
60
65
  outDir: "./out",
61
66
  silent: false,
62
- targetUplcVersion: defaultUplcVersion,
67
+ targetUplcVersion: defaultTargetUplcVersion,
63
68
  removeTraces: false,
64
69
  delayHoists: false,
65
70
  uplcOptimizations: debugUplcOptimizations,
66
- addMarker: false
71
+ addMarker: false,
72
+ encodingStrategy: "default"
67
73
  });
68
74
  export const defaultOptions = Object.freeze({
69
75
  ...productionOptions,
@@ -75,7 +81,7 @@ export const testOptions = Object.freeze({
75
81
  });
76
82
  export const defulatCompilerOptions = defaultOptions;
77
83
  export function completeCompilerOptions(options, complete = defaultOptions) {
78
- let targetUplcVersion = options.targetUplcVersion instanceof UPLCVersion ? complete.targetUplcVersion : defaultUplcVersion;
84
+ let targetUplcVersion = options.targetUplcVersion instanceof UPLCVersion ? complete.targetUplcVersion : defaultTargetUplcVersion;
79
85
  complete = {
80
86
  ...defaultOptions,
81
87
  ...complete
@@ -99,10 +105,13 @@ export function completeCompilerOptions(options, complete = defaultOptions) {
99
105
  // console.log( "completeUplcOptimizations( uplcOptimizations )",completeUplcOptimizations( uplcOptimizations ))
100
106
  return {
101
107
  ...complete,
102
- targetUplcVersion,
108
+ // forward compilerVersion verbatim — no fallback; Compiler throws if missing/invalid
109
+ compilerVersion: options.compilerVersion ?? complete.compilerVersion,
110
+ targetUplcVersion: targetUplcVersion,
103
111
  removeTraces: options.removeTraces ?? complete.removeTraces,
104
112
  delayHoists: options.delayHoists ?? complete.delayHoists,
105
113
  uplcOptimizations: completeUplcOptimizations(uplcOptimizations),
106
- addMarker: options.addMarker ?? complete.addMarker
114
+ addMarker: options.addMarker ?? complete.addMarker,
115
+ encodingStrategy: options.encodingStrategy ?? complete.encodingStrategy ?? "default"
107
116
  };
108
117
  }
@@ -16,6 +16,10 @@ import { IRCase, IRConstr, IRFunc, IRNative } from "../IRNodes/index.js";
16
16
  import { replaceForcedNativesWithHoisted } from "./subRoutines/replaceForcedNativesWithHoisted.js";
17
17
  import { performUplcOptimizationsAndReturnRoot } from "./subRoutines/performUplcOptimizationsAndReturnRoot/performUplcOptimizationsAndReturnRoot.js";
18
18
  import { rewriteNativesAppliedToConstantsAndReturnRoot } from "./subRoutines/rewriteNativesAppliedToConstantsAndReturnRoot.js";
19
+ import { rewriteToCaseOverConstAndReturnRoot } from "./subRoutines/rewriteToCaseOverConstAndReturnRoot.js";
20
+ import { rewriteHeadTailInCaseConsAndReturnRoot } from "./subRoutines/rewriteHeadTailInCaseConsAndReturnRoot.js";
21
+ import { introduceCaseForDualHeadTailAndReturnRoot } from "./subRoutines/introduceCaseForDualHeadTailAndReturnRoot.js";
22
+ import { inlineSingleUseLetBindingsAndReturnRoot } from "./subRoutines/inlineSingleUseLetBindingsAndReturnRoot.js";
19
23
  import { _debug_assertClosedIR } from "../utils/index.js";
20
24
  import { ToUplcCtx } from "./ctx/ToUplcCtx.js";
21
25
  import { removeUnusedVarsAndReturnRoot } from "./subRoutines/removeUnusuedVarsAndReturnRoot/removeUnusuedVarsAndReturnRoot.js";
@@ -57,6 +61,29 @@ export function compileIRToUPLC(term, paritalOptions = defaultOptions) {
57
61
  term = replaceNativesAndReturnRoot(term);
58
62
  // re-call rewrite to optimize introduced hoisted
59
63
  term = rewriteNativesAppliedToConstantsAndReturnRoot(term);
64
+ // Lower `strictIfThenElse` triple-apps to `IRCase` BEFORE
65
+ // `replaceForcedNativesWithHoisted` would otherwise hoist
66
+ // `(force ifThenElse)` into a shared variable that's no longer
67
+ // pattern-matchable as a native. (`strictChooseList` is already
68
+ // lowered to `IRCase` unconditionally by the earlier
69
+ // `rewriteNativesAppliedToConstantsAndReturnRoot` pass.)
70
+ term = rewriteToCaseOverConstAndReturnRoot(term);
71
+ // Inside `case L of cons h t -> body`, replace any `headList(L)` /
72
+ // `tailList(L)` calls within `body` with `h` / `t`. Drop the now-dead
73
+ // `h`/`t` bindings via a fresh unused-vars sweep.
74
+ term = rewriteHeadTailInCaseConsAndReturnRoot(term);
75
+ term = removeUnusedVarsAndReturnRoot(term);
76
+ // For every IRFunc body where the same list L is accessed via BOTH
77
+ // `headList(L)` and `tailList(L)`, wrap the body in
78
+ // `case L of cons h t -> body' | nil -> error` and substitute the two
79
+ // builtin calls with `h` / `t`. Empirically (bench.headTailVsCase):
80
+ // one case dispatch costs ~128K CPU vs ~160K for two builtin calls,
81
+ // and only ~9 bytes vs ~16. The previous head/tail-in-case-cons pass
82
+ // can then make a second sweep to substitute any further internal
83
+ // references the new case introduced.
84
+ term = introduceCaseForDualHeadTailAndReturnRoot(term);
85
+ term = rewriteHeadTailInCaseConsAndReturnRoot(term);
86
+ term = removeUnusedVarsAndReturnRoot(term);
60
87
  // debugAsserts && _debug_assertions( term );
61
88
  // unwrap top level letted and hoisted;
62
89
  // some natives may be converted to hoisted;
@@ -144,12 +171,21 @@ export function compileIRToUPLC(term, paritalOptions = defaultOptions) {
144
171
  // somethingWasInlined = inlineResult.somethingWasInlined;
145
172
  // }
146
173
  term = removeUnusedVarsAndReturnRoot(term);
174
+ // After `handleLettedAndReturnRoot` lowers `IRLetted` into the
175
+ // `IRApp(IRFunc([p], body), value)` shape, this is the first point
176
+ // where the let-as-application pattern is syntactically visible.
177
+ // Run the inliner here (NOT earlier, where lets are still IRLetted
178
+ // nodes the inliner doesn't recognize). Single-use uses trapped
179
+ // inside nested closures are skipped — see the pass for details.
180
+ term = inlineSingleUseLetBindingsAndReturnRoot(term);
181
+ term = removeUnusedVarsAndReturnRoot(term);
147
182
  term = performUplcOptimizationsAndReturnRoot(term, options);
183
+ // Rewrite strictIfThenElse into IRCase-over-Const, and prune
184
+ // trailing IRError continuations from any IRCase.
185
+ term = rewriteToCaseOverConstAndReturnRoot(term);
148
186
  term = ensureProperlyForcedBuiltinsAndReturnRoot(term);
149
187
  if (options.addMarker &&
150
- options.targetUplcVersion.major >= 1 &&
151
- options.targetUplcVersion.minor >= 1 &&
152
- options.targetUplcVersion.patch >= 0) {
188
+ options.targetUplcVersion.isV3Friendly()) {
153
189
  term = new IRCase(new IRConstr(0, []), [
154
190
  term,
155
191
  // never evaluated
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Inline single-use let-bindings of the shape `((λp. body) value)`:
3
+ *
4
+ * - If `body` references `p` exactly once AND that reference is NOT
5
+ * trapped inside a nested closure (IRFunc/IRRecursive) within
6
+ * `body`, replace the entire app with `body[p := value]`. The
7
+ * nested-closure guard is critical: a use that's syntactically
8
+ * "once" inside a recursive body is actually evaluated once per
9
+ * iteration, and inlining there would move per-iteration work to a
10
+ * value the caller currently evaluates once.
11
+ * - If `body` references `p` zero times, drop the let and replace
12
+ * the app with `body`.
13
+ *
14
+ * IRCase continuations are themselves IRFunc nodes (case-cons branches
15
+ * take h/t lambdas), but the case dispatches each continuation AT MOST
16
+ * ONCE per case eval, so they don't count as "trapping" closures — we
17
+ * treat IRFunc reached *as a direct case continuation* as transparent.
18
+ *
19
+ * If `body` uses `p` two or more times we don't inline either (would
20
+ * duplicate the term in the output).
21
+ */
22
+ import { IRTerm } from "../../IRTerm.js";
23
+ export declare function inlineSingleUseLetBindingsAndReturnRoot(term: IRTerm): IRTerm;
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Inline single-use let-bindings of the shape `((λp. body) value)`:
3
+ *
4
+ * - If `body` references `p` exactly once AND that reference is NOT
5
+ * trapped inside a nested closure (IRFunc/IRRecursive) within
6
+ * `body`, replace the entire app with `body[p := value]`. The
7
+ * nested-closure guard is critical: a use that's syntactically
8
+ * "once" inside a recursive body is actually evaluated once per
9
+ * iteration, and inlining there would move per-iteration work to a
10
+ * value the caller currently evaluates once.
11
+ * - If `body` references `p` zero times, drop the let and replace
12
+ * the app with `body`.
13
+ *
14
+ * IRCase continuations are themselves IRFunc nodes (case-cons branches
15
+ * take h/t lambdas), but the case dispatches each continuation AT MOST
16
+ * ONCE per case eval, so they don't count as "trapping" closures — we
17
+ * treat IRFunc reached *as a direct case continuation* as transparent.
18
+ *
19
+ * If `body` uses `p` two or more times we don't inline either (would
20
+ * duplicate the term in the output).
21
+ */
22
+ import { IRApp } from "../../IRNodes/IRApp.js";
23
+ import { IRCase } from "../../IRNodes/IRCase.js";
24
+ import { IRConst } from "../../IRNodes/IRConst.js";
25
+ import { IRDelayed } from "../../IRNodes/IRDelayed.js";
26
+ import { IRFunc } from "../../IRNodes/IRFunc.js";
27
+ import { IRHoisted } from "../../IRNodes/IRHoisted.js";
28
+ import { IRLetted } from "../../IRNodes/IRLetted.js";
29
+ import { IRRecursive } from "../../IRNodes/IRRecursive.js";
30
+ import { IRVar } from "../../IRNodes/IRVar.js";
31
+ import { _modifyChildFromTo } from "../_internal/_modifyChildFromTo.js";
32
+ export function inlineSingleUseLetBindingsAndReturnRoot(term) {
33
+ // Iterate until a full pass finds nothing to rewrite. Each rewrite
34
+ // restarts the walk from the (possibly new) root because the tree
35
+ // shape near the rewrite changes and may expose further candidates.
36
+ let didChange = true;
37
+ while (didChange) {
38
+ didChange = false;
39
+ const stack = [term];
40
+ outer: while (stack.length > 0) {
41
+ const t = stack.pop();
42
+ if (t instanceof IRApp
43
+ && t.fn instanceof IRFunc
44
+ && t.fn.params.length === 1) {
45
+ const fn = t.fn;
46
+ const p = fn.params[0];
47
+ const value = t.arg;
48
+ // Beta-reduce when the argument is itself just a
49
+ // variable access. Replacing every `IRVar(p)` with
50
+ // `IRVar(x)` costs the same per access (a single env
51
+ // lookup either way) and saves the surrounding
52
+ // `(λp. …) x` lambda + application. Always safe,
53
+ // regardless of how many times `p` is used and
54
+ // regardless of whether the uses sit inside a recursive
55
+ // body — there's no work to multiply.
56
+ if (value instanceof IRVar) {
57
+ const newBody = substituteAllVar(fn.body, p, value);
58
+ term = replaceWithBody(term, t, newBody);
59
+ didChange = true;
60
+ break outer;
61
+ }
62
+ const stats = countVarUses(fn.body, p);
63
+ if (stats.count === 0) {
64
+ // Dead let — replace with body, dropping the value.
65
+ term = replaceWithBody(term, t, fn.body);
66
+ didChange = true;
67
+ break outer;
68
+ }
69
+ if (stats.count === 1 && !stats.trapped) {
70
+ substituteVar(fn.body, p, value);
71
+ term = replaceWithBody(term, t, fn.body);
72
+ didChange = true;
73
+ break outer;
74
+ }
75
+ // Trapped single use: the lone occurrence sits inside a
76
+ // nested closure (recursive body or non-case lambda).
77
+ // Inlining a COMPUTATION there would duplicate the
78
+ // per-iteration work, but a syntactic VALUE (closure /
79
+ // constant / var / delay) only pays its (essentially
80
+ // zero) construction cost — the same as a fresh lookup.
81
+ // Pebble also stores recursive helpers as open-recursion
82
+ // lambdas whose body references their own binding name;
83
+ // inlining such a lambda would orphan that self-ref, so
84
+ // also require that the value has no free `IRVar(p)`.
85
+ if (stats.count === 1
86
+ && stats.trapped
87
+ && isSyntacticValue(value)
88
+ && !containsFreeVar(value, p)) {
89
+ substituteVar(fn.body, p, value);
90
+ term = replaceWithBody(term, t, fn.body);
91
+ didChange = true;
92
+ break outer;
93
+ }
94
+ // count >= 2, or trapped-with-computational-value → leave alone
95
+ }
96
+ stack.push(...t.children());
97
+ }
98
+ }
99
+ return term;
100
+ }
101
+ /** Returns true if `term` contains a free `IRVar(p)` reference. A
102
+ * binding inside `term` that re-uses `p`'s symbol shadows it (these
103
+ * are rare given fresh-symbol minting, but the check is cheap). */
104
+ function containsFreeVar(term, p) {
105
+ const stack = [term];
106
+ while (stack.length > 0) {
107
+ const t = stack.pop();
108
+ if (t instanceof IRVar && t.name === p)
109
+ return true;
110
+ if (t instanceof IRFunc && t.params.includes(p))
111
+ continue;
112
+ if (t instanceof IRRecursive && t.name === p)
113
+ continue;
114
+ stack.push(...t.children());
115
+ }
116
+ return false;
117
+ }
118
+ /**
119
+ * A "syntactic value" is a term whose evaluation does no work beyond
120
+ * binding/closure construction — duplicating it across a recursive
121
+ * boundary doesn't multiply runtime cost. Specifically:
122
+ *
123
+ * - `IRFunc` / `IRRecursive` — closure values; per-construction is
124
+ * ~zero cost, and the body only runs when the closure is applied
125
+ * (which happens at the use-site frequency either way).
126
+ * - `IRConst` — literal value.
127
+ * - `IRVar` — already just an environment lookup.
128
+ * - `IRDelayed` — produces a thunk; the inner term only runs on
129
+ * force, at the use-site frequency.
130
+ *
131
+ * NOTE: `IRNative` is deliberately NOT included. While a bare native
132
+ * reference is itself cheap, in Pebble's pipeline `hoistForcedNatives`
133
+ * specifically wraps each forced builtin (e.g. `headList`,
134
+ * `tailList`, `ifThenElse`) once at the top via `(λvar. …) IRNative`,
135
+ * so that the runtime `force` happens once and is shared. Inlining
136
+ * the `IRNative` back into a recursive body undoes that sharing —
137
+ * the resulting compiled UPLC re-issues the force per iteration.
138
+ * `IRHoisted`/`IRLetted` are transparent wrappers: we unwrap to check
139
+ * the inner term.
140
+ */
141
+ function isSyntacticValue(t) {
142
+ while (t instanceof IRHoisted)
143
+ t = t.hoisted;
144
+ while (t instanceof IRLetted)
145
+ t = t.value;
146
+ return (t instanceof IRFunc
147
+ || t instanceof IRRecursive
148
+ || t instanceof IRConst
149
+ || t instanceof IRVar
150
+ || t instanceof IRDelayed);
151
+ }
152
+ function replaceWithBody(root, app, body) {
153
+ const parent = app.parent;
154
+ if (parent === undefined) {
155
+ body.parent = undefined;
156
+ return body;
157
+ }
158
+ _modifyChildFromTo(parent, app, body);
159
+ return root;
160
+ }
161
+ /** Count IRVar(p) references in `body` accurately. Caps at 2 — we
162
+ * only care about 0/1/many — but ALWAYS walks the whole tree, so we
163
+ * correctly distinguish "1 trapped use" from "1 trapped + 1 more".
164
+ */
165
+ function countVarUses(body, p) {
166
+ let count = 0;
167
+ let firstUseTrapped = false;
168
+ const stack = [{ node: body, trapped: false }];
169
+ while (stack.length > 0) {
170
+ const { node: t, trapped } = stack.pop();
171
+ if (t instanceof IRVar) {
172
+ if (t.name === p) {
173
+ if (count === 0)
174
+ firstUseTrapped = trapped;
175
+ count++;
176
+ if (count >= 2)
177
+ return { count: 2, trapped: false };
178
+ }
179
+ continue;
180
+ }
181
+ // Symbols are minted fresh on each binder, so shadowing is rare,
182
+ // but the guard is cheap and protects against any reuse.
183
+ if (t instanceof IRFunc && t.params.includes(p))
184
+ continue;
185
+ if (t instanceof IRRecursive && t.name === p)
186
+ continue;
187
+ // Determine the trap state for descending into this node:
188
+ // - IRRecursive always traps (loop introduces multi-eval).
189
+ // - IRFunc traps EXCEPT when it's a direct case continuation
190
+ // (`IRCase(_, […, this IRFunc, …])`): the case dispatches
191
+ // it at most once per case eval, so it's transparent.
192
+ let childTrapped = trapped;
193
+ if (t instanceof IRRecursive)
194
+ childTrapped = true;
195
+ else if (t instanceof IRFunc) {
196
+ const parent = t.parent;
197
+ const isCaseContinuation = parent instanceof IRCase
198
+ // direct continuation array membership
199
+ && Array.from(parent.continuations).includes(t);
200
+ if (!isCaseContinuation)
201
+ childTrapped = true;
202
+ }
203
+ for (const c of t.children())
204
+ stack.push({ node: c, trapped: childTrapped });
205
+ }
206
+ return { count: count, trapped: firstUseTrapped };
207
+ }
208
+ /**
209
+ * Replace EVERY `IRVar(p)` inside `body` with a fresh clone of
210
+ * `replacement`. Used for the var-arg beta-reduction: substituting one
211
+ * variable name for another anywhere it appears.
212
+ *
213
+ * Returns the (possibly new) body. If `body` itself is `IRVar(p)`,
214
+ * returns a fresh clone of `replacement` — the caller is responsible
215
+ * for wiring it into the parent.
216
+ */
217
+ function substituteAllVar(body, p, replacement) {
218
+ if (body instanceof IRVar && body.name === p) {
219
+ return replacement.clone();
220
+ }
221
+ const stack = [body];
222
+ while (stack.length > 0) {
223
+ const t = stack.pop();
224
+ // Don't descend into scopes that shadow p (unique-symbol
225
+ // invariant makes this rare, but the guard is cheap).
226
+ if (t instanceof IRFunc && t.params.includes(p))
227
+ continue;
228
+ if (t instanceof IRRecursive && t.name === p)
229
+ continue;
230
+ // Snapshot children before mutating (children() returns a fresh
231
+ // array, so the loop is stable across modifications).
232
+ const children = t.children();
233
+ for (const child of children) {
234
+ if (child instanceof IRVar && child.name === p) {
235
+ // `t` must be a parent term because we got `child` from
236
+ // `t.children()`. Cast to satisfy the type checker.
237
+ _modifyChildFromTo(t, child, replacement.clone());
238
+ }
239
+ else {
240
+ stack.push(child);
241
+ }
242
+ }
243
+ }
244
+ return body;
245
+ }
246
+ /** Replace the (single) IRVar(p) inside `body` with `replacement`. */
247
+ function substituteVar(body, p, replacement) {
248
+ const stack = [body];
249
+ while (stack.length > 0) {
250
+ const t = stack.pop();
251
+ if (t instanceof IRVar && t.name === p) {
252
+ const parent = t.parent;
253
+ if (parent !== undefined)
254
+ _modifyChildFromTo(parent, t, replacement);
255
+ return;
256
+ }
257
+ if (t instanceof IRFunc && t.params.includes(p))
258
+ continue;
259
+ if (t instanceof IRRecursive && t.name === p)
260
+ continue;
261
+ stack.push(...t.children());
262
+ }
263
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * When the same list `L` is used for BOTH `headList(L)` and `tailList(L)`
3
+ * within a single function body, those two builtin calls can be replaced
4
+ * by a single `case L of cons h t -> body[h/headList(L), t/tailList(L)] |
5
+ * nil -> error`. The trade-off (measured in `bench.headTailVsCase`):
6
+ *
7
+ * - one `headList` (or `tailList`) builtin call: 112,100 CPU / 800 mem
8
+ * - one `case L of cons h t -> h | nil -> error`: 128,100 CPU / 900 mem
9
+ *
10
+ * So for a single access the builtin wins; for dual access the case wins
11
+ * by a single dispatch (~96K CPU / 700 mem).
12
+ *
13
+ * This pass walks every `IRFunc` body bottom-up. For each body, it scans
14
+ * the top-level (without descending into nested `IRFunc`/`IRRecursive`,
15
+ * because nested scopes are processed independently). If a free list var
16
+ * `L` has both a `headList(L)` and a `tailList(L)` use in the body, the
17
+ * body is wrapped with `IRCase(IRVar(L), [IRFunc([h, t], body'), IRError])`
18
+ * — where `body'` has those calls replaced with `IRVar(h)` / `IRVar(t)`.
19
+ *
20
+ * Notes:
21
+ * - We only act on `IRFunc` bodies that are NOT immediate continuations
22
+ * of an `IRCase` whose scrutinee is `IRVar(L)` for the same L — that
23
+ * case-cons branch already binds head/tail and the prior
24
+ * `rewriteHeadTailInCaseConsAndReturnRoot` pass has already done the
25
+ * substitution. Wrapping again would be a no-op constructor pair.
26
+ * - The nil branch is `IRError`. Original code that calls `headList` or
27
+ * `tailList` on a nil list errors at evaluation; the new code errors
28
+ * at the case dispatch — same observable behavior whenever either
29
+ * call is actually reached.
30
+ * - The pass iterates: after introducing a case for L, the (now
31
+ * substituted) body might still contain a different L' with dual
32
+ * head/tail uses — handled by re-scanning until no more pairs.
33
+ */
34
+ import { IRTerm } from "../../IRTerm.js";
35
+ export declare function introduceCaseForDualHeadTailAndReturnRoot(term: IRTerm): IRTerm;