@harmoniclabs/pebble 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) 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 +22 -2
  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 +27 -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 +12 -0
  17. package/dist/IR/toUPLC/CompilerOptions.js +14 -9
  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/ast/nodes/statements/declarations/StructDecl.d.ts +16 -2
  35. package/dist/ast/nodes/statements/declarations/StructDecl.js +15 -1
  36. package/dist/compiler/AstCompiler/AstCompiler.d.ts +1 -0
  37. package/dist/compiler/AstCompiler/AstCompiler.js +41 -4
  38. package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +3 -3
  39. package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +57 -10
  40. package/dist/compiler/AstCompiler/internal/exprs/_compileCaseExpr.js +31 -0
  41. package/dist/compiler/AstCompiler/internal/exprs/_compileIsExpr.js +12 -0
  42. package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.js +36 -0
  43. package/dist/compiler/AstCompiler/internal/exprs/_compileUnaryPrefixExpr.js +13 -1
  44. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileAddExpr.js +18 -5
  45. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileEqualExpr.js +3 -1
  46. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanEqualExpr.js +2 -1
  47. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileGreaterThanExpr.js +2 -1
  48. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanEqualExpr.js +2 -1
  49. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileLessThanExpr.js +2 -1
  50. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileMultExpr.js +24 -6
  51. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileNotEqualExpr.js +2 -1
  52. package/dist/compiler/AstCompiler/internal/exprs/binary/_compileSubExpr.js +16 -5
  53. package/dist/compiler/AstCompiler/internal/statements/_compileMatchStmt.js +33 -20
  54. package/dist/compiler/AstCompiler/utils/getPropAccessReturnType.js +11 -0
  55. package/dist/compiler/Compiler.js +20 -27
  56. package/dist/compiler/TirCompiler/expressify/ExpressifyCtx.js +1 -1
  57. package/dist/compiler/TirCompiler/expressify/expressify.js +30 -2
  58. package/dist/compiler/TirCompiler/expressify/expressifyForStmt.d.ts +2 -1
  59. package/dist/compiler/TirCompiler/expressify/expressifyForStmt.js +45 -7
  60. package/dist/compiler/TirCompiler/expressify/expressifyVars.d.ts +0 -1
  61. package/dist/compiler/TirCompiler/expressify/expressifyVars.js +23 -8
  62. package/dist/compiler/tir/expressions/TirCaseExpr.d.ts +9 -0
  63. package/dist/compiler/tir/expressions/TirCaseExpr.js +144 -122
  64. package/dist/compiler/tir/expressions/TirElemAccessExpr.js +2 -2
  65. package/dist/compiler/tir/expressions/TirFromDataExpr.js +102 -67
  66. package/dist/compiler/tir/expressions/TirIsExpr.js +14 -1
  67. package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +1 -2
  68. package/dist/compiler/tir/expressions/TirNativeFunc.js +2 -12
  69. package/dist/compiler/tir/expressions/TirToDataExpr.js +3 -0
  70. package/dist/compiler/tir/expressions/TirTypeConversionExpr.js +10 -0
  71. package/dist/compiler/tir/expressions/TirVariableAccessExpr.d.ts +2 -3
  72. package/dist/compiler/tir/expressions/TirVariableAccessExpr.js +1 -4
  73. package/dist/compiler/tir/expressions/ToIRTermCtx.d.ts +20 -3
  74. package/dist/compiler/tir/expressions/ToIRTermCtx.js +48 -3
  75. package/dist/compiler/tir/expressions/binary/TirBinaryExpr.d.ts +2 -2
  76. package/dist/compiler/tir/expressions/binary/TirBinaryExpr.js +45 -8
  77. package/dist/compiler/tir/expressions/litteral/TirLitEnumMemberExpr.d.ts +19 -0
  78. package/dist/compiler/tir/expressions/litteral/TirLitEnumMemberExpr.js +24 -0
  79. package/dist/compiler/tir/expressions/litteral/TirLitteralExpr.d.ts +2 -1
  80. package/dist/compiler/tir/expressions/litteral/TirLitteralExpr.js +2 -0
  81. package/dist/compiler/tir/expressions/unary/TirUnaryMinus.js +4 -1
  82. package/dist/compiler/tir/program/stdScope/populateStdNamespace.js +49 -4
  83. package/dist/compiler/tir/program/stdScope/prelude/preludeTypesSrc.js +35 -2
  84. package/dist/compiler/tir/program/stdScope/stdScope.d.ts +7 -0
  85. package/dist/compiler/tir/program/stdScope/stdScope.js +83 -40
  86. package/dist/compiler/tir/types/TirEnumType.d.ts +21 -0
  87. package/dist/compiler/tir/types/TirEnumType.js +36 -0
  88. package/dist/compiler/tir/types/TirNativeType/TirNativeType.d.ts +4 -2
  89. package/dist/compiler/tir/types/TirNativeType/TirNativeType.js +5 -0
  90. package/dist/compiler/tir/types/TirNativeType/native/array.d.ts +16 -0
  91. package/dist/compiler/tir/types/TirNativeType/native/array.js +38 -0
  92. package/dist/compiler/tir/types/TirNativeType/native/index.d.ts +2 -0
  93. package/dist/compiler/tir/types/TirNativeType/native/index.js +2 -0
  94. package/dist/compiler/tir/types/TirNativeType/native/value.d.ts +18 -0
  95. package/dist/compiler/tir/types/TirNativeType/native/value.js +17 -0
  96. package/dist/compiler/tir/types/TirStructType.js +6 -1
  97. package/dist/compiler/tir/types/TirType.d.ts +3 -2
  98. package/dist/compiler/tir/types/TirType.js +4 -1
  99. package/dist/compiler/tir/types/utils/canAssignTo.js +28 -0
  100. package/dist/compiler/tir/types/utils/canCastTo.js +14 -1
  101. package/dist/compiler/tir/types/utils/getDeconstructableType.d.ts +2 -1
  102. package/dist/compiler/tir/types/utils/getDeconstructableType.js +2 -0
  103. package/dist/compiler/tir/types/utils/inferTypeArgs.js +4 -0
  104. package/dist/compiler/tir/types/utils/normalizeEnumToInt.d.ts +10 -0
  105. package/dist/compiler/tir/types/utils/normalizeEnumToInt.js +17 -0
  106. package/dist/compiler/tir/types/utils/substituteTypeParams.js +5 -0
  107. package/dist/diagnostics/diagnosticMessages.generated.d.ts +5 -0
  108. package/dist/diagnostics/diagnosticMessages.generated.js +10 -0
  109. package/dist/parser/Parser.js +29 -13
  110. package/dist/tokenizer/Token.d.ts +8 -7
  111. package/dist/tokenizer/Token.js +8 -7
  112. package/dist/tokenizer/utils/tokenFromKeyword.js +2 -0
  113. package/dist/version.generated.d.ts +1 -1
  114. package/dist/version.generated.js +1 -1
  115. package/package.json +3 -3
  116. package/dist/IR/tree_utils/_ir_lazyChooseList.d.ts +0 -3
  117. package/dist/IR/tree_utils/_ir_lazyChooseList.js +0 -7
@@ -5,6 +5,7 @@ import { TirLitFailExpr } from "../../tir/expressions/litteral/TirLitFailExpr.js
5
5
  import { TirLitFalseExpr } from "../../tir/expressions/litteral/TirLitFalseExpr.js";
6
6
  import { TirLitHexBytesExpr } from "../../tir/expressions/litteral/TirLitHexBytesExpr.js";
7
7
  import { TirLitIntExpr } from "../../tir/expressions/litteral/TirLitIntExpr.js";
8
+ import { TirLitEnumMemberExpr } from "../../tir/expressions/litteral/TirLitEnumMemberExpr.js";
8
9
  import { TirLitNamedObjExpr } from "../../tir/expressions/litteral/TirLitNamedObjExpr.js";
9
10
  import { TirLitObjExpr } from "../../tir/expressions/litteral/TirLitObjExpr.js";
10
11
  import { TirLitStrExpr } from "../../tir/expressions/litteral/TirLitStrExpr.js";
@@ -38,8 +39,9 @@ import { TirUnaryMinus } from "../../tir/expressions/unary/TirUnaryMinus.js";
38
39
  import { TirUnaryPlus } from "../../tir/expressions/unary/TirUnaryPlus.js";
39
40
  import { isTirUnaryPrefixExpr } from "../../tir/expressions/unary/TirUnaryPrefixExpr.js";
40
41
  import { TirUnaryTilde } from "../../tir/expressions/unary/TirUnaryTilde.js";
41
- import { bool_t, bytes_t, data_t, int_t, valueAmountOfName } from "../../tir/program/stdScope/stdScope.js";
42
+ import { bool_t, bytes_t, data_t, int_t, valueMapAmountOfName } from "../../tir/program/stdScope/stdScope.js";
42
43
  import { IRNativeTag } from "../../../IR/IRNodes/IRNative/IRNativeTag.js";
44
+ import { currentCompilationCtx } from "../../../IR/CompilationCtx.js";
43
45
  import { TirReturnStmt } from "../../tir/statements/TirReturnStmt.js";
44
46
  import { TirArrayLikeDeconstr } from "../../tir/statements/TirVarDecl/TirArrayLikeDeconstr.js";
45
47
  import { TirNamedDeconstructVarDecl } from "../../tir/statements/TirVarDecl/TirNamedDeconstructVarDecl.js";
@@ -76,6 +78,7 @@ export function expressifyVars(ctx, expr) {
76
78
  || expr instanceof TirLitThisExpr
77
79
  || expr instanceof TirLitStrExpr
78
80
  || expr instanceof TirLitIntExpr
81
+ || expr instanceof TirLitEnumMemberExpr
79
82
  || expr instanceof TirLitHexBytesExpr
80
83
  || expr instanceof TirNativeFunc
81
84
  // hoisted expressions are necessarily closed, so no external variables
@@ -266,6 +269,7 @@ function expressifyPropAccess(ctx, propAccessExpr) {
266
269
  || expr instanceof TirLitArrExpr
267
270
  || expr instanceof TirLitStrExpr
268
271
  || expr instanceof TirLitIntExpr
272
+ || expr instanceof TirLitEnumMemberExpr
269
273
  || expr instanceof TirLitHexBytesExpr
270
274
  || expr instanceof TirNativeFunc
271
275
  || expr instanceof TirPropAccessExpr // `expressifyVars` is called recursively on the object before this check
@@ -372,6 +376,20 @@ function expressifyPropAccess(ctx, propAccessExpr) {
372
376
  throw new Error(`Property '${prop}' does not exist on type '${objType.toString()}'`);
373
377
  const fName = ctor.fields[fIdx].name;
374
378
  const fType = ctor.fields[fIdx].type;
379
+ // Untagged data structs encode as `listData(fields)` rather than
380
+ // `constrData(0, fields)`. Field access lowers directly to
381
+ // `unIData (or _inlineFromData) <$> headList (dropList idx (unListData v))`
382
+ // — no case-rewrite needed, no constructor index to check.
383
+ if (objType instanceof TirDataStructType
384
+ && objType.untagged) {
385
+ const fieldsListT = new TirListT(data_t);
386
+ return new TirFromDataExpr(new TirCallExpr(TirNativeFunc.headList(data_t), [
387
+ new TirCallExpr(TirNativeFunc.dropList(data_t), [
388
+ new TirLitIntExpr(BigInt(fIdx), propAccessExpr.range),
389
+ new TirCallExpr(TirNativeFunc.unListData, [expr], fieldsListT, propAccessExpr.range)
390
+ ], fieldsListT, propAccessExpr.range)
391
+ ], data_t, propAccessExpr.range), fType, propAccessExpr.range);
392
+ }
375
393
  return new TirCaseExpr(expr, [
376
394
  new TirCaseMatcher(new TirNamedDeconstructVarDecl(ctor.name, new Map([
377
395
  [fName, new TirSimpleVarDecl(fName, fType, undefined, true, propAccessExpr.range)]
@@ -412,7 +430,7 @@ function expressifyMethodCall(ctx, methodCall) {
412
430
  // fast-path: `Value.amountOf(policy, name)` compiles directly to
413
431
  // `_amountOfValue(eqByteString(policy))(value)(eqByteString(name))`
414
432
  // skipping the wrapper function indirection.
415
- if (tirMethodName === valueAmountOfName
433
+ if (tirMethodName === valueMapAmountOfName
416
434
  && methodCall.args.length === 2) {
417
435
  const [policyArg, nameArg] = methodCall.args;
418
436
  const bytesToBoolT = new TirFuncT([bytes_t], bool_t);
@@ -582,7 +600,8 @@ function expressifyListMethodCall(ctx, objectExpr, methodCall, methodName, listT
582
600
  throw new Error(`Argument 1 of method 'map' of type 'list' must be a function, got '${arg.type.toString()}'`);
583
601
  const mapReturnT = argFuncType.returnType;
584
602
  const elemTypeTirName = elemsType.toTirTypeKey();
585
- let base_mapToType = _base_mapToType_cache.get(elemTypeTirName)?.clone();
603
+ const mapToTypeCache = currentCompilationCtx().mapToTypeCache;
604
+ let base_mapToType = mapToTypeCache.get(elemTypeTirName)?.clone();
586
605
  if (!base_mapToType) {
587
606
  base_mapToType = new TirHoistedExpr("_map_to_list_of_" + elemTypeTirName, new TirCallExpr(TirNativeFunc._mkMap(elemsType, mapReturnT), [new TirLitArrExpr([], new TirListT(mapReturnT), SourceRange.unknown)], new TirFuncT([
588
607
  // mapping function
@@ -593,7 +612,7 @@ function expressifyListMethodCall(ctx, objectExpr, methodCall, methodName, listT
593
612
  new TirListT(elemsType)
594
613
  ], new TirListT(mapReturnT)), SourceRange.unknown));
595
614
  base_mapToType.varName; // precompute hash and symbol
596
- _base_mapToType_cache.set(elemTypeTirName, base_mapToType.clone());
615
+ mapToTypeCache.set(elemTypeTirName, base_mapToType.clone());
597
616
  }
598
617
  return new TirCallExpr(base_mapToType, [methodCall.args[0], objectExpr], methodCall.type, exprRange);
599
618
  }
@@ -605,7 +624,3 @@ function expressifyListMethodCall(ctx, objectExpr, methodCall, methodName, listT
605
624
  };
606
625
  */
607
626
  }
608
- const _base_mapToType_cache = new Map();
609
- export function __unsafe_clear_mapToType_cache() {
610
- _base_mapToType_cache.clear();
611
- }
@@ -20,6 +20,15 @@ export declare class TirCaseExpr implements ITirExpr {
20
20
  deps(): string[];
21
21
  get isConstant(): boolean;
22
22
  toIR(ctx: ToIRTermCtx): IRTerm;
23
+ /**
24
+ * Lowers a `case` over an untagged data struct (single constructor;
25
+ * runtime form is `listData(fields)`). No constructor dispatch is
26
+ * needed — there's only one possible ctor — so the lowering is just
27
+ * "find the matching arm (or wildcard), extract any bound fields, run
28
+ * the body".
29
+ */
30
+ private _untaggedDataStructToIR;
31
+ private _enumToIR;
23
32
  private _sopStructToIR;
24
33
  private _dataStructToIR;
25
34
  }
@@ -1,20 +1,19 @@
1
1
  import { IRApp, _ir_apps } from "../../../IR/IRNodes/IRApp.js";
2
2
  import { IRCase } from "../../../IR/IRNodes/IRCase.js";
3
3
  import { IRConst } from "../../../IR/IRNodes/IRConst.js";
4
- import { IRDelayed } from "../../../IR/IRNodes/IRDelayed.js";
5
4
  import { IRError } from "../../../IR/IRNodes/IRError.js";
6
5
  import { IRFunc } from "../../../IR/IRNodes/IRFunc.js";
6
+ import { IRLetted } from "../../../IR/IRNodes/IRLetted.js";
7
7
  import { IRNative } from "../../../IR/IRNodes/IRNative/index.js";
8
8
  import { IRVar } from "../../../IR/IRNodes/IRVar.js";
9
- import { _ir_lazyIfThenElse } from "../../../IR/tree_utils/_ir_lazyIfThenElse.js";
10
- import { _ir_let, _ir_let_sym } from "../../../IR/tree_utils/_ir_let.js";
9
+ import { _ir_let_sym } from "../../../IR/tree_utils/_ir_let.js";
11
10
  import { filterSortedStrArrInplace } from "../../../utils/array/filterSortedStrArrInplace.js";
12
11
  import { mergeSortedStrArrInplace } from "../../../utils/array/mergeSortedStrArrInplace.js";
13
12
  import { TirSimpleVarDecl } from "../statements/TirVarDecl/TirSimpleVarDecl.js";
14
13
  import { TirDataOptT } from "../types/TirNativeType/native/Optional/data.js";
15
14
  import { TirSopOptT } from "../types/TirNativeType/native/Optional/sop.js";
16
15
  import { TirDataStructType, TirSoPStructType } from "../types/TirStructType.js";
17
- import { getOptTypeArg } from "../types/utils/getOptTypeArg.js";
16
+ import { TirEnumType } from "../types/TirEnumType.js";
18
17
  import { getUnaliased } from "../types/utils/getUnaliased.js";
19
18
  import { _inlineFromData } from "./TirFromDataExpr.js";
20
19
  export class TirCaseExpr {
@@ -73,13 +72,83 @@ export class TirCaseExpr {
73
72
  if (matchExprType instanceof TirSoPStructType
74
73
  || matchExprType instanceof TirSopOptT)
75
74
  return this._sopStructToIR(matchExprType, ctx);
75
+ if (matchExprType instanceof TirDataStructType
76
+ && matchExprType.untagged)
77
+ return this._untaggedDataStructToIR(matchExprType, ctx);
76
78
  if (matchExprType instanceof TirDataStructType
77
79
  || matchExprType instanceof TirDataOptT)
78
80
  return this._dataStructToIR(matchExprType, ctx);
81
+ if (matchExprType instanceof TirEnumType)
82
+ return this._enumToIR(matchExprType, ctx);
79
83
  console.error(this);
80
84
  throw new Error("`case` expressions are only supported on Sum-of-Products or Data Struct types; got: "
81
85
  + this.matchExpr.type.toString());
82
86
  }
87
+ /**
88
+ * Lowers a `case` over an untagged data struct (single constructor;
89
+ * runtime form is `listData(fields)`). No constructor dispatch is
90
+ * needed — there's only one possible ctor — so the lowering is just
91
+ * "find the matching arm (or wildcard), extract any bound fields, run
92
+ * the body".
93
+ */
94
+ _untaggedDataStructToIR(matchExprType, ctx) {
95
+ if (matchExprType.constructors.length !== 1) {
96
+ throw new Error("untagged data struct must have exactly one constructor");
97
+ }
98
+ const ctor = matchExprType.constructors[0];
99
+ const wildcardBodyIR = this.wildcardCase?.body.toIR(ctx) ?? new IRError();
100
+ const matchedArm = this.cases.find(c => c.pattern.constrName === ctor.name);
101
+ if (!matchedArm) {
102
+ // no matching arm: just run the wildcard body (the scrutinee is
103
+ // evaluated for side-effects via `unListData` only if any field
104
+ // would have been bound; here, nothing).
105
+ return wildcardBodyIR;
106
+ }
107
+ const pattern = matchedArm.pattern;
108
+ const usedFields = ctor.fields
109
+ .map((f, idx) => ({ name: f.name, type: f.type, idx }))
110
+ .filter(f => pattern.fields.has(f.name));
111
+ // 0 bound fields → just run the body; no extraction.
112
+ if (usedFields.length === 0)
113
+ return matchedArm.body.toIR(ctx);
114
+ // fields-list = unListData(scrutinee), bound once and reused via
115
+ // IRLetted so the let-handling pass elides or shares as appropriate.
116
+ const fieldsListLetted = new IRLetted(Symbol("untaggedFields"), _ir_apps(IRNative.unListData, this.matchExpr.toIR(ctx)));
117
+ const branchCtx = ctx.newChild();
118
+ // bind each used field with a let in declaration order.
119
+ // sort by ctor field index so earlier extractions don't need to
120
+ // re-drop past already-bound fields.
121
+ usedFields.sort((a, b) => a.idx - b.idx);
122
+ const bindings = usedFields.map(f => {
123
+ const varDecl = pattern.fields.get(f.name);
124
+ if (!(varDecl instanceof TirSimpleVarDecl))
125
+ throw new Error("case pattern not expressified.");
126
+ const sym = branchCtx.defineVar(varDecl.name);
127
+ return {
128
+ sym,
129
+ extract: _inlineFromData(f.type, _ir_apps(IRNative.headList, _ir_apps(IRNative.dropList, IRConst.int(f.idx), fieldsListLetted.clone())))
130
+ };
131
+ });
132
+ // build nested let-bindings around the body
133
+ let result = matchedArm.body.toIR(branchCtx);
134
+ for (let i = bindings.length - 1; i >= 0; i--) {
135
+ const { sym, extract } = bindings[i];
136
+ result = _ir_let_sym(sym, extract, result);
137
+ }
138
+ return result;
139
+ }
140
+ _enumToIR(enumType, ctx) {
141
+ const wildcardBodyIR = this.wildcardCase?.body.toIR(ctx) ?? new IRError();
142
+ const branches = enumType.members.map(memberName => {
143
+ const branch = this.cases.find(c => c.pattern.constrName === memberName);
144
+ return branch ? branch.body.toIR(ctx) : wildcardBodyIR;
145
+ });
146
+ while (branches.length > 0 && branches[branches.length - 1] instanceof IRError)
147
+ branches.pop();
148
+ return branches.length > 0
149
+ ? new IRCase(this.matchExpr.toIR(ctx), branches)
150
+ : new IRError();
151
+ }
83
152
  _sopStructToIR(matchExprType, ctx) {
84
153
  if (matchExprType instanceof TirSopOptT) {
85
154
  const wildcardBodyIR = this.wildcardCase?.body.toIR(ctx) ?? new IRError();
@@ -172,134 +241,87 @@ export class TirCaseExpr {
172
241
  return branches.length > 0 ? new IRCase(this.matchExpr.toIR(ctx), branches) : new IRError(); // all branches fail, so the whole expression fails
173
242
  }
174
243
  _dataStructToIR(matchExprType, ctx) {
175
- if (matchExprType instanceof TirDataOptT) {
176
- const stmtCtx = ctx.newChild();
177
- stmtCtx.pushUnusedVar(); // debrujin un constr data result
178
- const wildcardBodyIR = this.wildcardCase?.body.toIR(stmtCtx) ?? new IRError();
179
- const someBranch = this.cases.find(c => c.pattern.constrName === "Some");
180
- let someBranchIR = () => new IRFunc([Symbol("unused_some_value")], wildcardBodyIR);
181
- if (someBranch) {
182
- const pattern = someBranch.pattern;
183
- const valueDecl = pattern.fields.get("value");
184
- if (valueDecl
185
- && !(valueDecl instanceof TirSimpleVarDecl))
186
- throw new Error("case pattern not expressified.");
187
- const someBranchCtx = stmtCtx.newChild();
188
- const someBranchVarSym = valueDecl ? someBranchCtx.defineVar(valueDecl.name) : Symbol("some_value_unused");
189
- someBranchIR = (unConstrMatchSym) => new IRApp(new IRFunc([someBranchVarSym], someBranch.body.toIR(someBranchCtx)), _inlineFromData(getOptTypeArg(matchExprType), _ir_apps(IRNative.sndPair, new IRVar(unConstrMatchSym) // unConstrData result
190
- )));
191
- }
192
- const noneBranchIR = this.cases.find(c => c.pattern.constrName === "None")?.body.toIR(stmtCtx) ?? wildcardBodyIR;
193
- return _ir_let(_ir_apps(IRNative.unConstrData, this.matchExpr.toIR(ctx)), unConstrMatchSym => _ir_lazyIfThenElse(
194
- // condition
195
- _ir_apps(IRNative.equalsInteger, IRConst.int(0), _ir_apps(IRNative.fstPair, new IRVar(unConstrMatchSym) // unConstrData result
196
- )),
197
- // then Just{ value }
198
- someBranchIR(unConstrMatchSym),
199
- // else None
200
- noneBranchIR));
201
- }
202
- // TirDataStructType
203
- const stmtCtx = ctx.newChild();
204
- const unConstrStructSym = stmtCtx.pushUnusedVar("unconstrStruct"); // unconstrStruct
205
- const isConstrIdxSym = stmtCtx.pushUnusedVar("isConstrIdx"); // isConstrIdx
206
- const noVarsWildcardBodyIR = (this.wildcardCase?.body.toIR(stmtCtx)
207
- ?? new IRError());
208
- const delayedNoVarWildcardBodyIR = new IRDelayed(noVarsWildcardBodyIR);
209
- let ifThenElseMatchingStatements = noVarsWildcardBodyIR;
244
+ // TirDataOptT extends TirDataStructType (constructors Some{value}, None{}),
245
+ // so the same logic handles it.
246
+ //
247
+ // Lowering structure (UPLC `Case` accepts pair / int scrutinees
248
+ // as untagged constructors):
249
+ //
250
+ // Case (unConstrData scrutinee) [
251
+ // \idxSym fieldsSym ->
252
+ // Case idxSym [body_0, body_1, ..., body_(maxParent)]
253
+ // ]
254
+ //
255
+ // - The outer `Case` over the `pair(int, list<data>)` returned by
256
+ // `unConstrData` extracts both halves via a single 2-arg branch
257
+ // (pair is treated as `constr 0 [fst, snd]`).
258
+ // - The inner `Case` over `idxSym` dispatches by tag in O(1)
259
+ // no `equalsInteger` chain, no `lazyIfThenElse`.
260
+ // - Each inner branch takes 0 args (an int N is treated as
261
+ // `constr N []`). The branch body extracts fields lazily from
262
+ // `fieldsSym` via deferred access on `thenCtx`, wrapped in
263
+ // `IRLetted` so the letted-handling pass dedups, inlines or
264
+ // elides as appropriate.
210
265
  if (this.cases.some(({ pattern }) => matchExprType.constructors.findIndex(ctor => ctor.name === pattern.constrName) < 0))
211
266
  throw new Error("case expression includes unknown constructor.");
212
- const cases = this.cases.sort((a, b) => {
213
- const a_idx = matchExprType.constructors.findIndex(ctor => ctor.name === a.pattern.constrName);
214
- const b_idx = matchExprType.constructors.findIndex(ctor => ctor.name === b.pattern.constrName);
215
- return a_idx - b_idx;
216
- });
217
- for (let i = cases.length - 1; i >= 0; i--) {
218
- const { pattern, body } = cases[i];
267
+ const stmtCtx = ctx.newChild();
268
+ const wildcardBodyIR = this.wildcardCase?.body.toIR(stmtCtx) ?? new IRError();
269
+ // outer destructuring binders symbols allocated up-front so
270
+ // per-arm deferred accesses (closures) reference the same syms
271
+ const idxSym = Symbol("ctorIdx");
272
+ const fieldsListSym = Symbol("fieldsList");
273
+ // The runtime value's ctor tag spans all parent indices known to
274
+ // this type — not just the indices that appear as arm patterns.
275
+ // Size the inner-Case branch array to cover all of them and fill
276
+ // missing slots with the wildcard.
277
+ const armsByParentIdx = new Map();
278
+ // TirDataOptT extends TirDataStructType, so both have parentCtorIdx
279
+ const allParentIdxs = matchExprType.constructors.map((_, i) => matchExprType.parentCtorIdx(i));
280
+ let maxParentIdx = allParentIdxs.reduce((m, x) => x > m ? x : m, -1);
281
+ for (const matchCase of this.cases) {
282
+ const { pattern, body } = matchCase;
219
283
  const ctorIdx = matchExprType.constructors.findIndex(ctor => ctor.name === pattern.constrName);
220
- if (ctorIdx < 0)
221
- throw new Error("case expression includes unknown constructor."); // unreachable
222
284
  const ctor = matchExprType.constructors[ctorIdx];
223
- const usedFieldsCtorNames = (ctor.fields.map(f => f.name)
224
- .filter(fName => pattern.fields.has(fName)));
225
- if (usedFieldsCtorNames.length <= 0) {
226
- ifThenElseMatchingStatements = _ir_lazyIfThenElse(
227
- // condition (compare against the PARENT ctor index — the
228
- // value's runtime tag is unaffected by narrowing)
229
- _ir_apps(new IRVar(isConstrIdxSym), // isConstrIdx
230
- IRConst.int(matchExprType instanceof TirDataStructType
231
- ? matchExprType.parentCtorIdx(ctorIdx)
232
- : ctorIdx)),
233
- // then
234
- body.toIR(stmtCtx),
235
- // else
236
- ifThenElseMatchingStatements);
237
- continue;
238
- }
239
- function indexOfField(fieldName) {
240
- const idx = ctor.fields.findIndex(f => f.name === fieldName);
241
- if (idx < 0)
242
- throw new Error("case pattern not expressified.");
243
- return idx;
244
- }
245
- if (usedFieldsCtorNames.length === 1) {
246
- const thenCtx = stmtCtx.newChild();
247
- const fName = usedFieldsCtorNames[0];
285
+ const parentCtorIdx = matchExprType instanceof TirDataStructType
286
+ ? matchExprType.parentCtorIdx(ctorIdx)
287
+ : ctorIdx;
288
+ // bind each pattern-named field as a deferred access on
289
+ // `fieldsListSym`. Each access produces an `IRLetted`
290
+ // wrapping `_inlineFromData(type, headList(_dropList(idx, fields)))`.
291
+ const armCtx = stmtCtx.newChild();
292
+ const usedFieldsCtorNames = ctor.fields
293
+ .map(f => f.name)
294
+ .filter(fName => pattern.fields.has(fName));
295
+ for (const fName of usedFieldsCtorNames) {
248
296
  const patternVarDecl = pattern.fields.get(fName);
249
297
  if (!(patternVarDecl instanceof TirSimpleVarDecl))
250
298
  throw new Error("case pattern not expressified.");
251
- const introVar = thenCtx.defineVar(patternVarDecl.name);
252
- const thenCase = _ir_let_sym(introVar, _inlineFromData(patternVarDecl.type, _ir_apps(IRNative.headList, _ir_apps(IRNative._dropList, IRConst.int(indexOfField(fName)), // constr field index
253
- _ir_apps(// fileds as data list
254
- IRNative.sndPair, new IRVar(unConstrStructSym) // unconstrStruct
255
- )))), body.toIR(thenCtx));
256
- ifThenElseMatchingStatements = _ir_lazyIfThenElse(
257
- // condition (compare against the PARENT ctor index — the
258
- // value's runtime tag is unaffected by narrowing)
259
- _ir_apps(new IRVar(isConstrIdxSym), // isConstrIdx
260
- IRConst.int(matchExprType instanceof TirDataStructType
261
- ? matchExprType.parentCtorIdx(ctorIdx)
262
- : ctorIdx)),
263
- // then
264
- thenCase,
265
- // else
266
- ifThenElseMatchingStatements);
267
- continue;
268
- } // single field used edge case
269
- // multiple fields used
270
- const thenCtx = stmtCtx.newChild();
271
- const fieldsAsDataList = thenCtx.pushUnusedVar(); // fileds as data list
272
- const sortedUsedFields = [...usedFieldsCtorNames].sort((a, b) => indexOfField(a) - indexOfField(b));
273
- const introducedVars = new Array(sortedUsedFields.length);
274
- for (let fIdx = 0; fIdx < sortedUsedFields.length; fIdx++) {
275
- const fName = sortedUsedFields[fIdx];
276
- const patternVarDecl = pattern.fields.get(fName);
277
- if (!(patternVarDecl instanceof TirSimpleVarDecl))
299
+ const fieldIdx = ctor.fields.findIndex(f => f.name === fName);
300
+ if (fieldIdx < 0)
278
301
  throw new Error("case pattern not expressified.");
279
- introducedVars[fIdx] = thenCtx.defineVar(patternVarDecl.name);
302
+ const fieldType = patternVarDecl.type;
303
+ armCtx.defineDeferredAccess(patternVarDecl.name, () => new IRLetted(Symbol(`${ctor.name}_${fName}`), _inlineFromData(fieldType, _ir_apps(IRNative.headList, _ir_apps(IRNative.dropList, IRConst.int(fieldIdx), new IRVar(fieldsListSym))))));
280
304
  }
281
- const extractedFields = sortedUsedFields.map(fName => _inlineFromData(ctor.fields.find(f => f.name === fName).type, _ir_apps(IRNative.headList, _ir_apps(IRNative._dropList, IRConst.int(indexOfField(fName)), // constr field index
282
- new IRVar(fieldsAsDataList) // fileds as data list
283
- ))));
284
- const thenCase = _ir_let_sym(fieldsAsDataList, _ir_apps(IRNative.sndPair, new IRVar(unConstrStructSym) // unconstrStruct
285
- ), // fileds as data list
286
- _ir_apps(new IRFunc(introducedVars, body.toIR(thenCtx)), ...extractedFields));
287
- ifThenElseMatchingStatements = _ir_lazyIfThenElse(
288
- // condition (compare against the PARENT ctor index — the
289
- // value's runtime tag is unaffected by narrowing)
290
- _ir_apps(new IRVar(isConstrIdxSym), // isConstrIdx
291
- IRConst.int(matchExprType instanceof TirDataStructType
292
- ? matchExprType.parentCtorIdx(ctorIdx)
293
- : ctorIdx)),
294
- // then
295
- thenCase,
296
- // else
297
- ifThenElseMatchingStatements);
305
+ armsByParentIdx.set(parentCtorIdx, body.toIR(armCtx));
306
+ }
307
+ // build inner-Case branches sized to `maxParentIdx + 1`, filling
308
+ // missing slots with the wildcard body
309
+ const innerBranches = new Array(maxParentIdx + 1);
310
+ for (let i = 0; i < innerBranches.length; i++) {
311
+ innerBranches[i] = armsByParentIdx.get(i) ?? wildcardBodyIR;
298
312
  }
299
- return _ir_let_sym(unConstrStructSym, _ir_apps(IRNative.unConstrData, this.matchExpr.toIR(ctx)), // unconstrStruct
300
- _ir_let_sym(isConstrIdxSym, _ir_apps(IRNative.equalsInteger, _ir_apps(IRNative.fstPair, new IRVar(unConstrStructSym) // unConstrData result
301
- )), // isConstrIdx
302
- ifThenElseMatchingStatements));
313
+ // trailing `IRError` branches can be omitted — the CEK machine
314
+ // fails naturally if no branch exists for the runtime tag
315
+ while (innerBranches.length > 0
316
+ && innerBranches[innerBranches.length - 1] instanceof IRError)
317
+ innerBranches.pop();
318
+ const innerCase = innerBranches.length > 0
319
+ ? new IRCase(new IRVar(idxSym), innerBranches)
320
+ : new IRError();
321
+ // outer Case over the pair: single branch destructures (idx, fields)
322
+ return new IRCase(_ir_apps(IRNative.unConstrData, this.matchExpr.toIR(ctx)), [
323
+ new IRFunc([idxSym, fieldsListSym], innerCase)
324
+ ]);
303
325
  }
304
326
  }
305
327
  export class TirCaseMatcher {
@@ -44,8 +44,8 @@ export class TirElemAccessExpr {
44
44
  const result = Machine.evalSimple(compileIRToUPLC(irIndex));
45
45
  if ((result instanceof CEKConst)
46
46
  && (typeof result.value === "number" || typeof result.value === "bigint"))
47
- return _ir_apps(IRNative.headList, _ir_apps(IRNative._dropList, IRConst.int(result.value), irArr));
47
+ return _ir_apps(IRNative.headList, _ir_apps(IRNative.dropList, IRConst.int(result.value), irArr));
48
48
  }
49
- return _ir_apps(IRNative.headList, _ir_apps(IRNative._dropList, irIndex, irArr));
49
+ return _ir_apps(IRNative.headList, _ir_apps(IRNative.dropList, irIndex, irArr));
50
50
  }
51
51
  }