solve-engine 1.0.0-beta.7 → 1.0.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 (205) hide show
  1. package/dist/{BytecodeBuilder-3egMyuDi.d.cts → BytecodeBuilder-B0xskcv5.d.cts} +2 -1
  2. package/dist/{BytecodeBuilder-3egMyuDi.d.ts → BytecodeBuilder-B0xskcv5.d.ts} +2 -1
  3. package/dist/{Configuration-fVpd-HC-.d.cts → Configuration-B-G5gTRn.d.cts} +107 -29
  4. package/dist/{Configuration-fVpd-HC-.d.ts → Configuration-B-G5gTRn.d.ts} +107 -29
  5. package/dist/{EngineError-DvPSnhZi.d.cts → EngineError-LU7W7AgI.d.cts} +96 -16
  6. package/dist/{EngineError-DvPSnhZi.d.ts → EngineError-LU7W7AgI.d.ts} +96 -16
  7. package/dist/{Lexer-W9MBOP0V.d.ts → Lexer-BN5mt30n.d.ts} +16 -0
  8. package/dist/{Lexer-Cfm79Dak.d.cts → Lexer-D78E2yDW.d.cts} +16 -0
  9. package/dist/{PackageRegistry-CjDt-Jy_.d.ts → PackageRegistry-DFeINv2O.d.ts} +54 -13
  10. package/dist/{PackageRegistry-pHtKythi.d.cts → PackageRegistry-u2zdfFaj.d.cts} +54 -13
  11. package/dist/{Parselet-DEdF9I7n.d.ts → Parselet-BaySkMV3.d.ts} +37 -5
  12. package/dist/{Parselet-Cu0bLSis.d.cts → Parselet-DuI1Pjiq.d.cts} +37 -5
  13. package/dist/{ScopeManager-CKfQtl7l.d.cts → ScopeManager-B6GzdhVG.d.cts} +75 -6
  14. package/dist/{ScopeManager-B5k0nk05.d.ts → ScopeManager-udv4Twwq.d.ts} +75 -6
  15. package/dist/{VMCheckpoints-BmDwIXgK.d.ts → VMCheckpoints-BiaIlOOY.d.ts} +4 -4
  16. package/dist/{VMCheckpoints-CqFuLwrT.d.cts → VMCheckpoints-DwLjivM7.d.cts} +4 -4
  17. package/dist/{Value-KtAQNHur.d.cts → Value-CXJqDH9J.d.cts} +5 -2
  18. package/dist/{Value-KtAQNHur.d.ts → Value-CXJqDH9J.d.ts} +5 -2
  19. package/dist/{chunk-CSG5XVNJ.cjs → chunk-3AFRJYP4.cjs} +556 -414
  20. package/dist/chunk-3AFRJYP4.cjs.map +1 -0
  21. package/dist/chunk-3BOK2LDK.js +17 -0
  22. package/dist/chunk-3BOK2LDK.js.map +1 -0
  23. package/dist/{chunk-6RDSQURE.js → chunk-3D7V24DG.js} +57 -6
  24. package/dist/chunk-3D7V24DG.js.map +1 -0
  25. package/dist/{chunk-MEOHSQEH.js → chunk-3LAEG75D.js} +47 -15
  26. package/dist/chunk-3LAEG75D.js.map +1 -0
  27. package/dist/{chunk-P75775WC.js → chunk-3VNGREMU.js} +118 -41
  28. package/dist/chunk-3VNGREMU.js.map +1 -0
  29. package/dist/{chunk-4AVD7NZW.cjs → chunk-4B2CNWQU.cjs} +13 -7
  30. package/dist/chunk-4B2CNWQU.cjs.map +1 -0
  31. package/dist/{chunk-6GCKCWLB.js → chunk-526PMQOA.js} +126 -69
  32. package/dist/chunk-526PMQOA.js.map +1 -0
  33. package/dist/{chunk-2CS6OMZK.js → chunk-5F4C26RU.js} +8 -5
  34. package/dist/chunk-5F4C26RU.js.map +1 -0
  35. package/dist/{chunk-M4N5GA5Z.cjs → chunk-5KMIY374.cjs} +57 -6
  36. package/dist/chunk-5KMIY374.cjs.map +1 -0
  37. package/dist/{chunk-R24DI24X.cjs → chunk-5LI5EPGJ.cjs} +6 -5
  38. package/dist/chunk-5LI5EPGJ.cjs.map +1 -0
  39. package/dist/{chunk-BAMZT3GL.js → chunk-5WVP4YHP.js} +939 -134
  40. package/dist/chunk-5WVP4YHP.js.map +1 -0
  41. package/dist/{chunk-MZOVP3WI.cjs → chunk-6KFYJ6TD.cjs} +3 -2
  42. package/dist/chunk-6KFYJ6TD.cjs.map +1 -0
  43. package/dist/{chunk-BXKJLM5R.cjs → chunk-6WFMPTGB.cjs} +972 -636
  44. package/dist/chunk-6WFMPTGB.cjs.map +1 -0
  45. package/dist/{chunk-2MV4HBKC.cjs → chunk-A2N2GFCG.cjs} +10 -10
  46. package/dist/{chunk-2MV4HBKC.cjs.map → chunk-A2N2GFCG.cjs.map} +1 -1
  47. package/dist/chunk-ADBA3XBE.cjs +20 -0
  48. package/dist/chunk-ADBA3XBE.cjs.map +1 -0
  49. package/dist/{chunk-MBNQVDVC.cjs → chunk-AMOJYLG2.cjs} +16 -10
  50. package/dist/chunk-AMOJYLG2.cjs.map +1 -0
  51. package/dist/{chunk-CLVQBF5C.cjs → chunk-AQNHHJMJ.cjs} +5 -5
  52. package/dist/{chunk-CLVQBF5C.cjs.map → chunk-AQNHHJMJ.cjs.map} +1 -1
  53. package/dist/{chunk-AHCLWAM5.cjs → chunk-BHTNFEGZ.cjs} +13 -13
  54. package/dist/chunk-BHTNFEGZ.cjs.map +1 -0
  55. package/dist/{chunk-35IWNZPJ.cjs → chunk-E4NUZXVK.cjs} +58 -27
  56. package/dist/chunk-E4NUZXVK.cjs.map +1 -0
  57. package/dist/{chunk-P33SBYGG.js → chunk-GLG5ZMX7.js} +54 -23
  58. package/dist/chunk-GLG5ZMX7.js.map +1 -0
  59. package/dist/{chunk-BVAR5J2F.js → chunk-GQCOSXMG.js} +117 -6
  60. package/dist/chunk-GQCOSXMG.js.map +1 -0
  61. package/dist/{chunk-QDMPOAI7.cjs → chunk-HDP7VK3C.cjs} +1118 -313
  62. package/dist/chunk-HDP7VK3C.cjs.map +1 -0
  63. package/dist/{chunk-GVWHCL2N.js → chunk-HMOISHXR.js} +217 -75
  64. package/dist/chunk-HMOISHXR.js.map +1 -0
  65. package/dist/{chunk-MTX2KVU7.cjs → chunk-HVQFNJKE.cjs} +122 -90
  66. package/dist/chunk-HVQFNJKE.cjs.map +1 -0
  67. package/dist/{chunk-OC6LTXYS.cjs → chunk-JMXUNXQS.cjs} +126 -7
  68. package/dist/chunk-JMXUNXQS.cjs.map +1 -0
  69. package/dist/{chunk-WWOLFXTX.cjs → chunk-KBSXGXPM.cjs} +10 -7
  70. package/dist/chunk-KBSXGXPM.cjs.map +1 -0
  71. package/dist/{chunk-YU2CUNFO.js → chunk-KV7UW6T6.js} +11 -5
  72. package/dist/chunk-KV7UW6T6.js.map +1 -0
  73. package/dist/{chunk-CCJPSHGI.cjs → chunk-N7FNVFXW.cjs} +4 -12
  74. package/dist/chunk-N7FNVFXW.cjs.map +1 -0
  75. package/dist/{chunk-CYFK5SY2.js → chunk-O3ANBHSA.js} +169 -39
  76. package/dist/chunk-O3ANBHSA.js.map +1 -0
  77. package/dist/{chunk-NZFKROS7.cjs → chunk-PA4VC73I.cjs} +130 -72
  78. package/dist/chunk-PA4VC73I.cjs.map +1 -0
  79. package/dist/{chunk-NJTXJ5AG.cjs → chunk-PFUESQTW.cjs} +18 -18
  80. package/dist/{chunk-NJTXJ5AG.cjs.map → chunk-PFUESQTW.cjs.map} +1 -1
  81. package/dist/{chunk-SFQWJMKT.js → chunk-QY25VWBF.js} +51 -12
  82. package/dist/chunk-QY25VWBF.js.map +1 -0
  83. package/dist/{chunk-WC5FFSHB.js → chunk-R3PY4G7J.js} +3 -3
  84. package/dist/{chunk-WC5FFSHB.js.map → chunk-R3PY4G7J.js.map} +1 -1
  85. package/dist/{chunk-M5E34VG5.js → chunk-SBUFNJVE.js} +3 -3
  86. package/dist/{chunk-M5E34VG5.js.map → chunk-SBUFNJVE.js.map} +1 -1
  87. package/dist/{chunk-UKPGSAZW.js → chunk-TMA4RCEN.js} +3 -3
  88. package/dist/{chunk-UKPGSAZW.js.map → chunk-TMA4RCEN.js.map} +1 -1
  89. package/dist/{chunk-V6HA6JJZ.cjs → chunk-U5VAY32K.cjs} +215 -138
  90. package/dist/chunk-U5VAY32K.cjs.map +1 -0
  91. package/dist/{chunk-DUEZFTK2.js → chunk-UKQHXPGW.js} +4 -12
  92. package/dist/chunk-UKQHXPGW.js.map +1 -0
  93. package/dist/{chunk-4CVLFLOB.cjs → chunk-UM6BVY2S.cjs} +201 -69
  94. package/dist/chunk-UM6BVY2S.cjs.map +1 -0
  95. package/dist/{chunk-75JP4BZX.js → chunk-UQ3UIZJC.js} +3 -2
  96. package/dist/chunk-UQ3UIZJC.js.map +1 -0
  97. package/dist/{chunk-PCPX42KL.js → chunk-V4XG5QF6.js} +16 -10
  98. package/dist/chunk-V4XG5QF6.js.map +1 -0
  99. package/dist/{chunk-GPPLSM2Z.js → chunk-VB37OC6I.js} +6 -5
  100. package/dist/chunk-VB37OC6I.js.map +1 -0
  101. package/dist/{chunk-MVBEL57Q.js → chunk-Y7FT4IQT.js} +500 -170
  102. package/dist/chunk-Y7FT4IQT.js.map +1 -0
  103. package/dist/{chunk-OFXOTECC.js → chunk-YPPPYLSR.js} +7 -7
  104. package/dist/chunk-YPPPYLSR.js.map +1 -0
  105. package/dist/{chunk-M4F66R4O.cjs → chunk-YWQ6V4ZN.cjs} +121 -82
  106. package/dist/chunk-YWQ6V4ZN.cjs.map +1 -0
  107. package/dist/constants.cjs +5 -5
  108. package/dist/constants.d.cts +1 -1
  109. package/dist/constants.d.ts +1 -1
  110. package/dist/constants.js +2 -2
  111. package/dist/engine.cjs +31 -31
  112. package/dist/engine.d.cts +11 -11
  113. package/dist/engine.d.ts +11 -11
  114. package/dist/engine.js +21 -21
  115. package/dist/errors.cjs +19 -19
  116. package/dist/errors.d.cts +2 -2
  117. package/dist/errors.d.ts +2 -2
  118. package/dist/errors.js +1 -1
  119. package/dist/format.cjs +29 -22
  120. package/dist/format.cjs.map +1 -1
  121. package/dist/format.d.cts +1 -2
  122. package/dist/format.d.ts +1 -2
  123. package/dist/format.js +23 -16
  124. package/dist/format.js.map +1 -1
  125. package/dist/index.cjs +31 -31
  126. package/dist/index.d.cts +10 -10
  127. package/dist/index.d.ts +10 -10
  128. package/dist/index.js +22 -22
  129. package/dist/language.cjs +8 -8
  130. package/dist/language.cjs.map +1 -1
  131. package/dist/language.d.cts +10 -10
  132. package/dist/language.d.ts +10 -10
  133. package/dist/language.js +6 -6
  134. package/dist/language.js.map +1 -1
  135. package/dist/lexer.cjs +18 -18
  136. package/dist/lexer.d.cts +2 -2
  137. package/dist/lexer.d.ts +2 -2
  138. package/dist/lexer.js +6 -6
  139. package/dist/normalizer.cjs +12 -12
  140. package/dist/normalizer.js +6 -6
  141. package/dist/packages.cjs +39 -39
  142. package/dist/packages.d.cts +9 -9
  143. package/dist/packages.d.ts +9 -9
  144. package/dist/packages.js +16 -16
  145. package/dist/parser.cjs +15 -15
  146. package/dist/parser.d.cts +23 -9
  147. package/dist/parser.d.ts +23 -9
  148. package/dist/parser.js +5 -5
  149. package/dist/{pipeline-CrsRPmsv.d.ts → pipeline-B6k5lCB7.d.ts} +1 -1
  150. package/dist/{pipeline-DPUQt7yL.d.cts → pipeline-BEb3hujr.d.cts} +1 -1
  151. package/dist/resolvers.d.cts +2 -2
  152. package/dist/resolvers.d.ts +2 -2
  153. package/dist/uom.cjs +17 -17
  154. package/dist/uom.d.cts +2 -2
  155. package/dist/uom.d.ts +2 -2
  156. package/dist/uom.js +7 -7
  157. package/dist/utilities.cjs +8 -4
  158. package/dist/utilities.d.cts +19 -3
  159. package/dist/utilities.d.ts +19 -3
  160. package/dist/utilities.js +2 -2
  161. package/dist/vm.cjs +43 -31
  162. package/dist/vm.d.cts +105 -9
  163. package/dist/vm.d.ts +105 -9
  164. package/dist/vm.js +9 -9
  165. package/package.json +1 -1
  166. package/dist/chunk-2CS6OMZK.js.map +0 -1
  167. package/dist/chunk-35IWNZPJ.cjs.map +0 -1
  168. package/dist/chunk-4AVD7NZW.cjs.map +0 -1
  169. package/dist/chunk-4CVLFLOB.cjs.map +0 -1
  170. package/dist/chunk-6GCKCWLB.js.map +0 -1
  171. package/dist/chunk-6RDSQURE.js.map +0 -1
  172. package/dist/chunk-75JP4BZX.js.map +0 -1
  173. package/dist/chunk-AHCLWAM5.cjs.map +0 -1
  174. package/dist/chunk-BAMZT3GL.js.map +0 -1
  175. package/dist/chunk-BVAR5J2F.js.map +0 -1
  176. package/dist/chunk-BXKJLM5R.cjs.map +0 -1
  177. package/dist/chunk-CCJPSHGI.cjs.map +0 -1
  178. package/dist/chunk-CSG5XVNJ.cjs.map +0 -1
  179. package/dist/chunk-CYFK5SY2.js.map +0 -1
  180. package/dist/chunk-DUEZFTK2.js.map +0 -1
  181. package/dist/chunk-GPPLSM2Z.js.map +0 -1
  182. package/dist/chunk-GVWHCL2N.js.map +0 -1
  183. package/dist/chunk-M4F66R4O.cjs.map +0 -1
  184. package/dist/chunk-M4N5GA5Z.cjs.map +0 -1
  185. package/dist/chunk-MBNQVDVC.cjs.map +0 -1
  186. package/dist/chunk-MEOHSQEH.js.map +0 -1
  187. package/dist/chunk-MTX2KVU7.cjs.map +0 -1
  188. package/dist/chunk-MVBEL57Q.js.map +0 -1
  189. package/dist/chunk-MZOVP3WI.cjs.map +0 -1
  190. package/dist/chunk-N5YFR4UP.cjs +0 -10
  191. package/dist/chunk-N5YFR4UP.cjs.map +0 -1
  192. package/dist/chunk-NZFKROS7.cjs.map +0 -1
  193. package/dist/chunk-OC6LTXYS.cjs.map +0 -1
  194. package/dist/chunk-OFXOTECC.js.map +0 -1
  195. package/dist/chunk-P33SBYGG.js.map +0 -1
  196. package/dist/chunk-P75775WC.js.map +0 -1
  197. package/dist/chunk-PCPX42KL.js.map +0 -1
  198. package/dist/chunk-PFSDGGYL.js +0 -8
  199. package/dist/chunk-PFSDGGYL.js.map +0 -1
  200. package/dist/chunk-QDMPOAI7.cjs.map +0 -1
  201. package/dist/chunk-R24DI24X.cjs.map +0 -1
  202. package/dist/chunk-SFQWJMKT.js.map +0 -1
  203. package/dist/chunk-V6HA6JJZ.cjs.map +0 -1
  204. package/dist/chunk-WWOLFXTX.cjs.map +0 -1
  205. package/dist/chunk-YU2CUNFO.js.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import { DiagnosticEventType } from './chunk-G535KJEG.js';
2
- import { defaultEngineContext, builtinFunctions, asConverterRegistry, sharedGlobalVariableStore, SYMBOLIC_NATIVE_BUILTINS, symbolicBuiltin, unifyUom, symbolicNeg, symbolicPow, binaryOp } from './chunk-MVBEL57Q.js';
3
- import { sharedCurrencyExchange } from './chunk-UKPGSAZW.js';
4
- import { getOpCodeName } from './chunk-75JP4BZX.js';
5
- import { collectionToValues, matAt, inBounds, matrixEntryToValue, matIndex, rowMajorToColumnMajor, varNode, matrixCompare, matrixMultiply, CURRENCY_DISPLAY } from './chunk-CYFK5SY2.js';
6
- import { numberValue, isArenaActive, errorValue, matrixValue, rangeValue, datetimeValue, isRateUnit, splitRateUnit, rateValue, uomValue, stringValue, hexValue, percentageValue, persistentValue, symbolicValue, boolValue, bigIntValue, isTimecodeUnit, timecodeFps } from './chunk-BVAR5J2F.js';
7
- import { getMeasure, convertUnit, getBestUnit, getConvertiblePossibilities, isWorkdayUnit } from './chunk-WC5FFSHB.js';
8
- import { UNIT_TABLE } from './chunk-PCPX42KL.js';
9
- import { ErrorFactory, normalizeUnknownError } from './chunk-6RDSQURE.js';
2
+ import { defaultEngineContext, builtinFunctions, asConverterRegistry, sharedGlobalVariableStore, SYMBOLIC_NATIVE_BUILTINS, symbolicBuiltin, compareBigIntOperands, compareUom, incomparableUnitsError, toBigIntOperand, symbolicNeg, power, symbolicPow, binaryOp, bigIntDivisionByZero, unifyUom } from './chunk-Y7FT4IQT.js';
3
+ import { sharedCurrencyExchange } from './chunk-TMA4RCEN.js';
4
+ import { getOpCodeName } from './chunk-UQ3UIZJC.js';
5
+ import { collectionToValues, matAt, inBounds, matrixEntryToValue, matIndex, rowMajorToColumnMajor, varNode, matrixCompare, matrixPower, matrixMultiply, CURRENCY_DISPLAY } from './chunk-O3ANBHSA.js';
6
+ import { numberValue, ValueType, isArenaActive, beginEvaluation, errorValue, chargeAllocation, faultedOperand, checkedArray, matrixValue, rangeValue, datetimeValue, isRateUnit, splitRateUnit, rateValue, uomValue, stringValue, hexValue, percentageValue, persistentValue, symbolicValue, chargeFunctionCall, faultedIn, boolValue, bigIntValue, checkAllocation, isTimecodeUnit, endEvaluation, timecodeFps } from './chunk-GQCOSXMG.js';
7
+ import { getMeasure, convertUnit, getBestUnit, getConvertiblePossibilities, isWorkdayUnit } from './chunk-R3PY4G7J.js';
8
+ import { UNIT_TABLE } from './chunk-V4XG5QF6.js';
9
+ import { ErrorFactory, normalizeUnknownError } from './chunk-3D7V24DG.js';
10
10
 
11
11
  // src/vm/DependencyGraph.ts
12
12
  var DependencyGraph = class {
@@ -330,8 +330,139 @@ var ScopeManager = class {
330
330
  }
331
331
  };
332
332
 
333
+ // src/vm/VMBuiltinArity.ts
334
+ var BUILTIN_ARITY = {
335
+ 0: { name: "sqrt", min: 1, max: 1 },
336
+ 1: { name: "abs", min: 1, max: 1 },
337
+ 2: { name: "sin", min: 1, max: 1 },
338
+ 3: { name: "cos", min: 1, max: 1 },
339
+ 4: { name: "tan", min: 1, max: 1 },
340
+ 5: { name: "log", min: 1, max: 1 },
341
+ 6: { name: "ceil", min: 1, max: 1 },
342
+ 7: { name: "floor", min: 1, max: 1 },
343
+ 8: { name: "round", min: 1, max: 1 },
344
+ // min/max compare as many operands as they are given.
345
+ 9: { name: "min", min: 1, max: Infinity },
346
+ 10: { name: "max", min: 1, max: Infinity },
347
+ 11: { name: "asin", min: 1, max: 1 },
348
+ 12: { name: "acos", min: 1, max: 1 },
349
+ 13: { name: "atan", min: 1, max: 1 },
350
+ 14: { name: "atan2", min: 2, max: 2 },
351
+ 15: { name: "sinh", min: 1, max: 1 },
352
+ 16: { name: "cosh", min: 1, max: 1 },
353
+ 17: { name: "tanh", min: 1, max: 1 },
354
+ 18: { name: "asinh", min: 1, max: 1 },
355
+ 19: { name: "acosh", min: 1, max: 1 },
356
+ 20: { name: "atanh", min: 1, max: 1 },
357
+ 21: { name: "cbrt", min: 1, max: 1 },
358
+ 22: { name: "clz32", min: 1, max: 1 },
359
+ 23: { name: "expm1", min: 1, max: 1 },
360
+ 24: { name: "exp", min: 1, max: 1 },
361
+ 25: { name: "fround", min: 1, max: 1 },
362
+ // hypot is the length of a vector with as many components as it is given.
363
+ 26: { name: "hypot", min: 1, max: Infinity },
364
+ 27: { name: "imul", min: 2, max: 2 },
365
+ 28: { name: "log10", min: 1, max: 1 },
366
+ 29: { name: "log1p", min: 1, max: 1 },
367
+ 30: { name: "log2", min: 1, max: 1 },
368
+ 31: { name: "pow", min: 2, max: 2 },
369
+ 32: { name: "random", min: 0, max: 0 },
370
+ 33: { name: "sign", min: 1, max: 1 },
371
+ 34: { name: "trunc", min: 1, max: 1 },
372
+ 35: { name: "degtorad", min: 1, max: 1 },
373
+ 36: { name: "radtodeg", min: 1, max: 1 },
374
+ 37: { name: "roll", min: 2, max: 2 },
375
+ 38: { name: "gcd", min: 2, max: 2 },
376
+ 39: { name: "lcm", min: 2, max: 2 },
377
+ 40: { name: "permutation", min: 2, max: 2 },
378
+ 41: { name: "combination", min: 2, max: 2 },
379
+ // The aggregates back "average of X, Y, Z" and friends, so they take a
380
+ // list. Their implementations already answer for an empty list rather
381
+ // than indexing into it, and the phrase parselets can emit an empty one,
382
+ // so the minimum stays 0 here instead of being tightened on the way past.
383
+ 42: { name: "average", min: 0, max: Infinity },
384
+ 43: { name: "median", min: 0, max: Infinity },
385
+ 44: { name: "total", min: 0, max: Infinity },
386
+ 45: { name: "count", min: 0, max: Infinity },
387
+ 46: { name: "proportion", min: 3, max: 3 },
388
+ 47: { name: "clamp", min: 3, max: 3 },
389
+ 48: { name: "hex", min: 1, max: 1 },
390
+ 49: { name: "bin", min: 1, max: 1 },
391
+ 50: { name: "int", min: 1, max: 1 },
392
+ 51: { name: "compoundInterest", min: 3, max: 3 },
393
+ 52: { name: "interestEarned", min: 3, max: 3 },
394
+ 53: { name: "compoundInterestRate", min: 3, max: 3 },
395
+ 54: { name: "compoundInterestYears", min: 3, max: 3 },
396
+ 55: { name: "loanRepayment", min: 4, max: 4 },
397
+ 56: { name: "loanInterest", min: 4, max: 4 },
398
+ 57: { name: "monthlyPayment", min: 3, max: 3 },
399
+ 58: { name: "taxAdd", min: 2, max: 2 },
400
+ 59: { name: "taxRemove", min: 2, max: 2 },
401
+ 60: { name: "inflationAdjust", min: 3, max: 3 },
402
+ 61: { name: "root", min: 2, max: 2 },
403
+ 62: { name: "fact", min: 1, max: 1 },
404
+ 63: { name: "transpose", min: 1, max: 1 },
405
+ 64: { name: "det", min: 1, max: 1 },
406
+ 65: { name: "inv", min: 1, max: 1 },
407
+ 66: { name: "dot", min: 2, max: 2 },
408
+ 67: { name: "expand", min: 1, max: 1 },
409
+ 68: { name: "factor", min: 1, max: 1 },
410
+ // solve pushes the two sides of the equation and then the unknown's name.
411
+ 69: { name: "solve", min: 3, max: 3 },
412
+ // der's order of differentiation is optional and defaults to 1.
413
+ 70: { name: "der", min: 2, max: 3 },
414
+ 71: { name: "integral", min: 2, max: 2 },
415
+ 72: { name: "taylor", min: 4, max: 4 },
416
+ // jacobian differentiates however many functions it is handed.
417
+ 73: { name: "jacobian", min: 1, max: Infinity },
418
+ 74: { name: "imaginary", min: 1, max: 1 },
419
+ 75: { name: "conj", min: 1, max: 1 },
420
+ 76: { name: "re", min: 1, max: 1 },
421
+ 77: { name: "im", min: 1, max: 1 },
422
+ 78: { name: "cancel", min: 1, max: 1 },
423
+ 79: { name: "apart", min: 1, max: 1 },
424
+ 80: { name: "compoundInterestEvery", min: 4, max: 4 },
425
+ 81: { name: "compoundInterestEarnedEvery", min: 4, max: 4 },
426
+ 82: { name: "presentValue", min: 3, max: 3 },
427
+ 83: { name: "roi", min: 2, max: 2 },
428
+ 84: { name: "annualisedReturn", min: 3, max: 3 },
429
+ 85: { name: "taxIn", min: 2, max: 2 },
430
+ 86: { name: "taxOn", min: 2, max: 2 },
431
+ 87: { name: "sind", min: 1, max: 1 },
432
+ 88: { name: "cosd", min: 1, max: 1 },
433
+ 89: { name: "tand", min: 1, max: 1 },
434
+ 90: { name: "asind", min: 1, max: 1 },
435
+ 91: { name: "acosd", min: 1, max: 1 },
436
+ 92: { name: "atand", min: 1, max: 1 },
437
+ 93: { name: "daysCount", min: 1, max: 1 },
438
+ 94: { name: "asTwoUnits", min: 3, max: 3 },
439
+ 95: { name: "makeRate", min: 2, max: 2 },
440
+ 96: { name: "atRate", min: 2, max: 2 },
441
+ // Not reachable by name, only through `<value> to <n> dp`; see
442
+ // converters/parselets/RoundingParselets.ts.
443
+ 97: { name: "roundToPlaces", min: 2, max: 2 }
444
+ };
445
+ function plural(count) {
446
+ return count === 1 ? "1 argument" : `${count} arguments`;
447
+ }
448
+ function expectation(arity) {
449
+ if (arity.max === Infinity) return `at least ${plural(arity.min)}`;
450
+ if (arity.min === arity.max) return plural(arity.min);
451
+ return `${arity.min} to ${plural(arity.max)}`;
452
+ }
453
+ function builtinArityError(index, argCount) {
454
+ const arity = BUILTIN_ARITY[index];
455
+ if (arity === void 0) return void 0;
456
+ if (argCount >= arity.min && argCount <= arity.max) return void 0;
457
+ return ErrorFactory.execution(
458
+ "BUILTIN_ARITY_MISMATCH",
459
+ `${arity.name}() takes ${expectation(arity)}, but was given ${argCount === 0 ? "none" : plural(argCount)}`,
460
+ { functionName: arity.name, expectedMin: arity.min, expectedMax: arity.max, actual: argCount }
461
+ );
462
+ }
463
+
333
464
  // src/vm/VM.ts
334
- function createVM(registry, maxStackDepth = 200, maxInstructions = 5e4, maxFunctionRecursionDepth = 50, context = defaultEngineContext) {
465
+ function createVM(registry, maxStackDepth = 200, maxInstructions = 5e4, maxFunctionRecursionDepth = 50, maxCollectionSize = 1e5, maxAllocatedElements = 2e6, maxFunctionCalls = 1e4, maxDateOffsetYears = 100, minDateOffsetYears = -100, context = defaultEngineContext) {
335
466
  const stack = [];
336
467
  const variables = /* @__PURE__ */ new Map();
337
468
  let instructionCount = 0;
@@ -350,17 +481,51 @@ function createVM(registry, maxStackDepth = 200, maxInstructions = 5e4, maxFunct
350
481
  getMaxStackDepth() {
351
482
  return maxStackDepth;
352
483
  },
484
+ getMaxCollectionSize() {
485
+ return maxCollectionSize;
486
+ },
487
+ getMaxAllocatedElements() {
488
+ return maxAllocatedElements;
489
+ },
490
+ getMaxFunctionCalls() {
491
+ return maxFunctionCalls;
492
+ },
493
+ getMaxDateOffsetYears() {
494
+ return maxDateOffsetYears;
495
+ },
496
+ getMinDateOffsetYears() {
497
+ return minDateOffsetYears;
498
+ },
353
499
  pop() {
354
500
  if (stack.length === 0) {
355
501
  return numberValue(0);
356
502
  }
357
503
  return stack.pop();
358
504
  },
505
+ /**
506
+ * Popped through `safePop()` for the same reason as `popString()` below.
507
+ * Only the underflow half applies here: `toNumber()` is a real method on
508
+ * every Value, so there was no cast to make a non-number look like one,
509
+ * and the `!` was the whole bug.
510
+ */
359
511
  popNumber() {
360
- return stack.pop().toNumber();
512
+ return safePop(stack).toNumber();
361
513
  },
514
+ /**
515
+ * Popped through the same two guards the dispatch loop below uses, and
516
+ * for the same reason: a package's opcode handler can disagree with its
517
+ * own bytecode exactly as this engine's compiler can disagree with
518
+ * hand-assembled input.
519
+ *
520
+ * This was `stack.pop()!.value as string`, which had neither guard. The
521
+ * `!` is compile-time only, so an empty stack reached `.value` and threw
522
+ * a raw TypeError; so is the cast, so any Value the stack happened to be
523
+ * holding was handed back as a "string" and threw in the handler's own
524
+ * code some lines later. Nothing in this repo calls this method, which is
525
+ * the only reason the fuzz corpus never found either.
526
+ */
362
527
  popString() {
363
- return stack.pop().value;
528
+ return stringOperand(safePop(stack), "VM.popString()");
364
529
  },
365
530
  peek() {
366
531
  return stack[stack.length - 1];
@@ -392,6 +557,9 @@ function createVM(registry, maxStackDepth = 200, maxInstructions = 5e4, maxFunct
392
557
  popCallFrame() {
393
558
  callFrames.pop();
394
559
  },
560
+ getCallFrame() {
561
+ return callFrames.length > 0 ? callFrames[callFrames.length - 1] : void 0;
562
+ },
395
563
  defineUserFunction(name, params, program) {
396
564
  userFunctions.set(name, { name, params, program });
397
565
  },
@@ -460,6 +628,17 @@ function createVM(registry, maxStackDepth = 200, maxInstructions = 5e4, maxFunct
460
628
  }
461
629
  };
462
630
  }
631
+ function matrixProductCells(l, r) {
632
+ if (l.rows === 1 && l.cols === 1) return r.data.length;
633
+ if (r.rows === 1 && r.cols === 1) return l.data.length;
634
+ if (l.cols !== r.rows) return 0;
635
+ return l.rows * r.cols;
636
+ }
637
+ function matrixPowerCells(m, exponent) {
638
+ if (!Number.isInteger(exponent) || exponent < 0) return 0;
639
+ const cells = m.rows * m.cols;
640
+ return cells * (1 + (exponent < 2 ? 1 : 2 * Math.floor(Math.log2(exponent))));
641
+ }
463
642
  function safePop(stack) {
464
643
  if (stack.length === 0) {
465
644
  throw ErrorFactory.internal({
@@ -472,6 +651,124 @@ function safePop(stack) {
472
651
  }
473
652
  return stack.pop();
474
653
  }
654
+ var MALFORMED_BYTECODE_SUGGESTION = "bytecode is caller input on this entry point: rebuild the program with the engine's compiler, or fix the hand-assembled stream";
655
+ function siteName(site) {
656
+ return typeof site === "number" ? getOpCodeName(site) : site;
657
+ }
658
+ function malformedBytecode(code, site, problem, expected, found, context = {}) {
659
+ const name = siteName(site);
660
+ return ErrorFactory.validation({
661
+ code,
662
+ message: `Malformed bytecode: ${name} ${problem}`,
663
+ expected,
664
+ found,
665
+ suggestion: MALFORMED_BYTECODE_SUGGESTION,
666
+ // A named site reports under `site` rather than borrowing `opcodeName`,
667
+ // so a host reading `opcode`/`opcodeName` never gets a method name
668
+ // where it expects an opcode, and never has to tell a missing opcode
669
+ // apart from opcode 0.
670
+ context: typeof site === "number" ? { opcode: site, opcodeName: name, ...context } : { site: name, ...context }
671
+ });
672
+ }
673
+ function operandByte(opcodes, at, op, role) {
674
+ if (at >= opcodes.length) {
675
+ throw malformedBytecode(
676
+ "MALFORMED_BYTECODE_TRUNCATED",
677
+ op,
678
+ `is missing its ${role}: the program ends mid-instruction`,
679
+ `an operand byte at offset ${at}`,
680
+ `a program only ${opcodes.length} byte(s) long`,
681
+ { offset: at, length: opcodes.length, role }
682
+ );
683
+ }
684
+ return opcodes[at];
685
+ }
686
+ function poolIndex(opcodes, at, op, role, poolSize, poolName) {
687
+ const index = operandByte(opcodes, at, op, role);
688
+ if (index >= poolSize) {
689
+ throw malformedBytecode(
690
+ "MALFORMED_BYTECODE_CONSTANT_INDEX",
691
+ op,
692
+ `reads ${poolName} entry ${index}, which does not exist`,
693
+ `an index below ${poolSize}`,
694
+ `${index}`,
695
+ { index, poolSize, pool: poolName }
696
+ );
697
+ }
698
+ return index;
699
+ }
700
+ function poolString(opcodes, at, strings, op, role) {
701
+ return pooledString(strings, operandByte(opcodes, at, op, role), op);
702
+ }
703
+ function pooledString(strings, index, op) {
704
+ const text = strings[index];
705
+ if (typeof text !== "string") {
706
+ throw malformedBytecode(
707
+ "MALFORMED_BYTECODE_CONSTANT_INDEX",
708
+ op,
709
+ `reads string-pool entry ${index}, which is ${index >= strings.length ? "past the end of the pool" : "not a string"}`,
710
+ `an index below ${strings.length}, holding a string`,
711
+ index >= strings.length ? `${index}` : typeof text,
712
+ { index, poolSize: strings.length, pool: "string-pool" }
713
+ );
714
+ }
715
+ return text;
716
+ }
717
+ function stringOperand(value, site, role) {
718
+ if (typeof value.value !== "string") {
719
+ throw malformedBytecode(
720
+ "MALFORMED_BYTECODE_OPERAND_TYPE",
721
+ site,
722
+ `expected a ${role ?? "string"} on the stack`,
723
+ role ? `a string value holding the ${role}` : "a string value",
724
+ `a ${ValueType[value.type] ?? "unknown"} value`,
725
+ role ? { role, valueType: value.type } : { valueType: value.type }
726
+ );
727
+ }
728
+ return value.value;
729
+ }
730
+ function requireKnownBodyKind(kind, ref, op) {
731
+ if (kind !== 0 && kind !== 1 && kind !== 2) {
732
+ throw malformedBytecode(
733
+ "MALFORMED_BYTECODE_BODY_KIND",
734
+ op,
735
+ `has body kind ${kind}, which is not one this VM knows`,
736
+ "0 (inline body), 1 (builtin function) or 2 (user-defined function)",
737
+ `${kind}`,
738
+ { kind, ref }
739
+ );
740
+ }
741
+ }
742
+ function parseBigIntLiteral(text, op) {
743
+ try {
744
+ return BigInt(text);
745
+ } catch {
746
+ throw malformedBytecode(
747
+ "MALFORMED_BYTECODE_BIGINT_LITERAL",
748
+ op,
749
+ `cannot read "${text}" as a whole number`,
750
+ "a string holding an integer, with no decimal point or separators",
751
+ `"${text}"`,
752
+ { literal: text }
753
+ );
754
+ }
755
+ }
756
+ function unrunnableProgram(bytecode) {
757
+ if (bytecode === null || typeof bytecode !== "object") {
758
+ return `expected a bytecode program, got ${bytecode === null ? "null" : typeof bytecode}`;
759
+ }
760
+ const candidate = bytecode;
761
+ if (typeof candidate.opcodes?.length !== "number") {
762
+ return "the program has no `opcodes` array";
763
+ }
764
+ if (typeof candidate.numbers?.length !== "number") {
765
+ return "the program has no `numbers` pool";
766
+ }
767
+ if (typeof candidate.strings?.length !== "number") {
768
+ return "the program has no `strings` pool";
769
+ }
770
+ return null;
771
+ }
475
772
  function invokeFrameBody(params, program, args, vm, pipeline, expression, context, symbolicTolerant) {
476
773
  const frame = /* @__PURE__ */ new Map();
477
774
  for (let i = 0; i < params.length; i++) frame.set(params[i], args[i]);
@@ -493,6 +790,27 @@ function invokeFrameBody(params, program, args, vm, pipeline, expression, contex
493
790
  }
494
791
  return bodyResult.value;
495
792
  }
793
+ function invokeWithBoundUnknown(name, program, vm, pipeline, expression, context, symbolicTolerant) {
794
+ const frame = new Map(vm.getCallFrame());
795
+ frame.set(name, symbolicValue(varNode(name)));
796
+ vm.pushCallFrame(frame);
797
+ let bodyResult;
798
+ try {
799
+ bodyResult = executeBytecode(program, vm, pipeline, expression, context, symbolicTolerant);
800
+ } finally {
801
+ vm.popCallFrame();
802
+ }
803
+ if (bodyResult.type === "pending") {
804
+ throw ErrorFactory.execution(
805
+ "SYMBOLIC_ASYNC_UNSUPPORTED",
806
+ `an algebra verb's expression cannot call an async operation (weather, stocks, currency, ...)`
807
+ );
808
+ }
809
+ if (bodyResult.type === "error") {
810
+ throw bodyResult.error;
811
+ }
812
+ return bodyResult.value;
813
+ }
496
814
  function unifyRatePeriods(l, r) {
497
815
  if (l.type !== 6 /* Uom */ || r.type !== 6 /* Uom */) return null;
498
816
  const leftUnit = l.unit;
@@ -521,6 +839,9 @@ function moneyTimesQuantity(l, r) {
521
839
  }
522
840
  function combinePercentage(l, r, sign) {
523
841
  if (l.type === 5 /* Percentage */) {
842
+ if (r.type === 6 /* Uom */ && r.unit !== void 0) {
843
+ return uomValue(r.toNumber() * (1 + sign * l.toNumber()), r.unit);
844
+ }
524
845
  if (r.type !== 5 /* Percentage */ && r.type !== 0 /* Number */) return null;
525
846
  return percentageValue(l.toNumber() + sign * r.toNumber());
526
847
  }
@@ -543,10 +864,20 @@ function extractDurationMs(value) {
543
864
  }
544
865
  return value.toNumber();
545
866
  }
546
- function addBusinessDays(epochMs, n) {
547
- const date = new Date(epochMs);
867
+ var WORKDAYS_PER_YEAR = 260;
868
+ function addBusinessDays(epochMs, n, vm) {
548
869
  let remaining = Math.trunc(Math.abs(n));
549
870
  const direction = n >= 0 ? 1 : -1;
871
+ const limitYears = direction > 0 ? vm.getMaxDateOffsetYears() : -vm.getMinDateOffsetYears();
872
+ const limitWorkdays = Math.floor(limitYears * WORKDAYS_PER_YEAR);
873
+ if (remaining > limitWorkdays) {
874
+ throw ErrorFactory.execution(
875
+ "DATE_OFFSET_LIMIT_EXCEEDED",
876
+ `A date offset of ${remaining.toLocaleString("en-US")} workdays is about ${Math.round(remaining / WORKDAYS_PER_YEAR).toLocaleString("en-US")} years, past the limit of ${limitYears} years for a workday offset`,
877
+ { requestedWorkdays: remaining, limitYears, limitWorkdays }
878
+ );
879
+ }
880
+ const date = new Date(epochMs);
550
881
  while (remaining > 0) {
551
882
  date.setDate(date.getDate() + direction);
552
883
  const day = date.getDay();
@@ -554,6 +885,67 @@ function addBusinessDays(epochMs, n) {
554
885
  }
555
886
  return date.getTime();
556
887
  }
888
+ var MS_PER_DAY = 864e5;
889
+ var CALENDAR_MONTHS_PER_UNIT = {
890
+ month: 1,
891
+ months: 1,
892
+ mo: 1,
893
+ year: 12,
894
+ years: 12,
895
+ yr: 12,
896
+ y: 12,
897
+ a: 12,
898
+ decade: 120,
899
+ decades: 120,
900
+ dec: 120,
901
+ century: 1200,
902
+ centuries: 1200,
903
+ millennium: 12e3,
904
+ millennia: 12e3
905
+ };
906
+ function daysInMonth(year, monthIndex) {
907
+ return new Date(year, monthIndex + 1, 0).getDate();
908
+ }
909
+ function addCalendarDays(epochMs, days) {
910
+ const whole = Math.trunc(days);
911
+ const date = new Date(epochMs);
912
+ date.setDate(date.getDate() + whole);
913
+ const shifted = date.getTime();
914
+ if (Number.isNaN(shifted)) return epochMs + days * MS_PER_DAY;
915
+ return shifted + (days - whole) * MS_PER_DAY;
916
+ }
917
+ function addCalendarMonths(epochMs, months) {
918
+ const whole = Math.trunc(months);
919
+ const date = new Date(epochMs);
920
+ const dayOfMonth = date.getDate();
921
+ date.setDate(1);
922
+ date.setMonth(date.getMonth() + whole);
923
+ date.setDate(Math.min(dayOfMonth, daysInMonth(date.getFullYear(), date.getMonth())));
924
+ const shifted = date.getTime();
925
+ const monthMs = convertUnit(1, "month", "ms");
926
+ if (Number.isNaN(shifted)) return epochMs + months * monthMs;
927
+ return shifted + (months - whole) * monthMs;
928
+ }
929
+ function shiftDatetime(epochMs, duration, sign, vm) {
930
+ if (duration.type === 6 /* Uom */ && duration.unit !== void 0) {
931
+ const unit = duration.unit;
932
+ const amount = sign * duration.toNumber();
933
+ if (isWorkdayUnit(unit)) return addBusinessDays(epochMs, amount, vm);
934
+ const monthsPerUnit = CALENDAR_MONTHS_PER_UNIT[unit];
935
+ if (monthsPerUnit !== void 0) return addCalendarMonths(epochMs, amount * monthsPerUnit);
936
+ if (getMeasure(unit) === "time") {
937
+ let daysPerUnit = 0;
938
+ try {
939
+ daysPerUnit = convertUnit(1, unit, "day");
940
+ } catch {
941
+ }
942
+ if (Number.isInteger(daysPerUnit) && daysPerUnit >= 1) {
943
+ return addCalendarDays(epochMs, amount * daysPerUnit);
944
+ }
945
+ }
946
+ }
947
+ return epochMs + sign * extractDurationMs(duration);
948
+ }
557
949
  function combineTimecode(tc, r, sign) {
558
950
  const fps = timecodeFps(tc.unit);
559
951
  if (r.type === 6 /* Uom */ && isTimecodeUnit(r.unit)) {
@@ -579,6 +971,54 @@ function isTruthy(value) {
579
971
  if (value.type === 10 /* Boolean */) return value.value;
580
972
  return value.toNumber() !== 0;
581
973
  }
974
+ function incompatibleConversionError(fromUnit, toUnit) {
975
+ return errorValue(
976
+ "INCOMPATIBLE_UNITS",
977
+ `Cannot convert ${fromUnit} to ${toUnit}: they do not measure the same thing`
978
+ );
979
+ }
980
+ var MAX_EXACT_POW_BITS = 65536;
981
+ function exactPowBits(base, exponent) {
982
+ if (!Number.isInteger(exponent) || exponent < 0) return null;
983
+ if (exponent === 0) return 1;
984
+ const magnitude = Math.abs(base.toNumber());
985
+ if (magnitude <= 1) return 1;
986
+ const baseBits = magnitude <= 2 ? 1 : Math.log2(magnitude);
987
+ return baseBits * exponent;
988
+ }
989
+ function bigIntPow(base, exponent) {
990
+ let result = 1n;
991
+ let factor = base;
992
+ let remaining = exponent;
993
+ while (remaining > 0n) {
994
+ if (remaining % 2n === 1n) result *= factor;
995
+ remaining /= 2n;
996
+ if (remaining > 0n) factor *= factor;
997
+ }
998
+ return result;
999
+ }
1000
+ var MAX_EXACT_SHIFT_BITS = MAX_EXACT_POW_BITS;
1001
+ function bigIntBitLength(value) {
1002
+ const magnitude = value < 0n ? -value : value;
1003
+ if (magnitude === 0n) return 0;
1004
+ const asDouble = Number(magnitude);
1005
+ if (Number.isFinite(asDouble)) return Math.floor(Math.log2(asDouble)) + 1;
1006
+ return magnitude.toString(16).length * 4;
1007
+ }
1008
+ function bigIntShift(l, r, direction) {
1009
+ const value = toBigIntOperand(l);
1010
+ const shift = toBigIntOperand(r);
1011
+ if (value === 0n) return bigIntValue(0n);
1012
+ const grownBits = direction * Number(shift);
1013
+ if (grownBits <= 0 || bigIntBitLength(value) + grownBits <= MAX_EXACT_SHIFT_BITS) {
1014
+ return bigIntValue(direction === 1 ? value << shift : value >> shift);
1015
+ }
1016
+ throw ErrorFactory.execution(
1017
+ "BIGINT_SHIFT_LIMIT_EXCEEDED",
1018
+ `Shifting by ${shift} would build an exact integer of about ${(bigIntBitLength(value) + grownBits).toLocaleString("en-US")} bits, past the limit of ${MAX_EXACT_SHIFT_BITS.toLocaleString("en-US")} bits`,
1019
+ { shift: String(shift), limitBits: MAX_EXACT_SHIFT_BITS }
1020
+ );
1021
+ }
582
1022
  function multiplyRateByMatchingUom(rate, multiplier) {
583
1023
  const { numerator, denominator } = splitRateUnit(rate.unit);
584
1024
  const rateMeasure = getMeasure(denominator);
@@ -626,12 +1066,26 @@ function toMultiplierString(value) {
626
1066
  return `${Math.round(multiple * 1e6) / 1e6}x`;
627
1067
  }
628
1068
  function toScientificString(n) {
1069
+ if (!Number.isFinite(n)) return String(n);
629
1070
  if (n === 0) return "0e+0";
630
1071
  const [mantissa, exponent] = n.toExponential().split("e");
631
1072
  const trimmed = mantissa.includes(".") ? mantissa.replace(/0+$/, "").replace(/\.$/, "") : mantissa;
632
1073
  return `${trimmed}e${exponent}`;
633
1074
  }
634
1075
  function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTolerant) {
1076
+ const unrunnable = unrunnableProgram(bytecode);
1077
+ if (unrunnable) {
1078
+ return {
1079
+ type: "error",
1080
+ error: ErrorFactory.validation({
1081
+ code: "MALFORMED_BYTECODE_PROGRAM",
1082
+ message: `Malformed bytecode: ${unrunnable}`,
1083
+ expected: "a BytecodeProgram with opcodes, numbers and strings",
1084
+ found: unrunnable,
1085
+ suggestion: MALFORMED_BYTECODE_SUGGESTION
1086
+ })
1087
+ };
1088
+ }
635
1089
  const { opcodes, numbers, strings, userFunctionBodies, anonymousBodies } = bytecode;
636
1090
  let ip = 0;
637
1091
  let localInstructionCount = 0;
@@ -641,6 +1095,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
641
1095
  const shouldTrace = pipeline?.hasCollectors ?? false;
642
1096
  const hasArena = isArenaActive();
643
1097
  if (opcodes.length === 0) return { type: "value", value: numberValue(0) };
1098
+ beginEvaluation(vm);
644
1099
  try {
645
1100
  while (ip < opcodes.length) {
646
1101
  if (++localInstructionCount > maxInstructions) {
@@ -678,9 +1133,12 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
678
1133
  const result = safePop(stack);
679
1134
  return { type: "value", value: hasArena ? persistentValue(result) : result };
680
1135
  }
681
- case 3 /* DUP */:
682
- stack.push(stack[stack.length - 1]);
1136
+ case 3 /* DUP */: {
1137
+ const top = safePop(stack);
1138
+ stack.push(top);
1139
+ stack.push(top);
683
1140
  break;
1141
+ }
684
1142
  case 2 /* SWAP */: {
685
1143
  const a = safePop(stack), b = safePop(stack);
686
1144
  stack.push(a);
@@ -690,20 +1148,25 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
690
1148
  // ═══════════════════════════════════════════════════════════════
691
1149
  // §2 Push literals (OpCode 10–15)
692
1150
  // ═══════════════════════════════════════════════════════════════
1151
+ // Every operand below goes through a checked read. An index past the
1152
+ // end of a pool used to yield `undefined` and be pushed as a Value,
1153
+ // which then failed somewhere else entirely: PUSH_BIGINT reached
1154
+ // `BigInt(undefined)`, and an undefined string sat on the stack until
1155
+ // a unit or converter name was read off it.
693
1156
  case 10 /* PUSH_NUMBER */:
694
- stack.push(numberValue(numbers[opcodes[ip++]]));
1157
+ stack.push(numberValue(numbers[poolIndex(opcodes, ip++, op, "constant-pool index", numbers.length, "number-pool")]));
695
1158
  break;
696
1159
  case 11 /* PUSH_BIGINT */:
697
- stack.push(bigIntValue(BigInt(strings[opcodes[ip++]])));
1160
+ stack.push(bigIntValue(parseBigIntLiteral(poolString(opcodes, ip++, strings, op, "constant-pool index"), op)));
698
1161
  break;
699
1162
  case 12 /* PUSH_HEX */:
700
- stack.push(hexValue(numbers[opcodes[ip++]]));
1163
+ stack.push(hexValue(numbers[poolIndex(opcodes, ip++, op, "constant-pool index", numbers.length, "number-pool")]));
701
1164
  break;
702
1165
  case 13 /* PUSH_STRING */:
703
- stack.push(stringValue(strings[opcodes[ip++]]));
1166
+ stack.push(stringValue(poolString(opcodes, ip++, strings, op, "constant-pool index")));
704
1167
  break;
705
1168
  case 14 /* PUSH_BOOLEAN */:
706
- stack.push(boolValue(opcodes[ip++] === 1));
1169
+ stack.push(boolValue(operandByte(opcodes, ip++, op, "boolean operand") === 1));
707
1170
  break;
708
1171
  // ═══════════════════════════════════════════════════════════════
709
1172
  // §3 Arithmetic (OpCode 20–27)
@@ -712,6 +1175,11 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
712
1175
  // ═══════════════════════════════════════════════════════════════
713
1176
  case 20 /* ADD */: {
714
1177
  const r = safePop(stack), l = safePop(stack);
1178
+ const addFault = faultedOperand(l, r);
1179
+ if (addFault) {
1180
+ stack.push(addFault);
1181
+ break;
1182
+ }
715
1183
  const pctAdd = combinePercentage(l, r, 1);
716
1184
  const ratePeriodAdd = pctAdd === null ? unifyRatePeriods(l, r) : null;
717
1185
  if (pctAdd !== null) {
@@ -725,13 +1193,13 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
725
1193
  } else if (l.type === 4 /* Datetime */) {
726
1194
  if (r.type === 4 /* Datetime */) {
727
1195
  stack.push(errorValue("INVALID_DATETIME_OP", "Cannot add two datetimes together"));
728
- } else if (r.type === 6 /* Uom */ && isWorkdayUnit(r.unit)) {
729
- stack.push(datetimeValue(addBusinessDays(l.toNumber(), r.toNumber())));
730
1196
  } else {
731
- stack.push(datetimeValue(l.toNumber() + extractDurationMs(r)));
1197
+ stack.push(datetimeValue(shiftDatetime(l.toNumber(), r, 1, vm)));
732
1198
  }
733
1199
  } else if (l.type === 6 /* Uom */ && isTimecodeUnit(l.unit)) {
734
1200
  stack.push(combineTimecode(l, r, 1));
1201
+ } else if (r.type === 4 /* Datetime */) {
1202
+ stack.push(datetimeValue(shiftDatetime(r.toNumber(), l, 1, vm)));
735
1203
  } else {
736
1204
  stack.push(binaryOp(l, r, (a, b) => a + b, (a, b) => a + b, "add"));
737
1205
  }
@@ -739,6 +1207,11 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
739
1207
  }
740
1208
  case 21 /* SUB */: {
741
1209
  const r = safePop(stack), l = safePop(stack);
1210
+ const subFault = faultedOperand(l, r);
1211
+ if (subFault) {
1212
+ stack.push(subFault);
1213
+ break;
1214
+ }
742
1215
  const pctSub = combinePercentage(l, r, -1);
743
1216
  const ratePeriodSub = pctSub === null ? unifyRatePeriods(l, r) : null;
744
1217
  if (pctSub !== null) {
@@ -750,13 +1223,16 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
750
1223
  } else if (l.type === 4 /* Datetime */) {
751
1224
  if (r.type === 4 /* Datetime */) {
752
1225
  stack.push(uomValue(l.toNumber() - r.toNumber(), "ms"));
753
- } else if (r.type === 6 /* Uom */ && isWorkdayUnit(r.unit)) {
754
- stack.push(datetimeValue(addBusinessDays(l.toNumber(), -r.toNumber())));
755
1226
  } else {
756
- stack.push(datetimeValue(l.toNumber() - extractDurationMs(r)));
1227
+ stack.push(datetimeValue(shiftDatetime(l.toNumber(), r, -1, vm)));
757
1228
  }
758
1229
  } else if (l.type === 6 /* Uom */ && isTimecodeUnit(l.unit)) {
759
1230
  stack.push(combineTimecode(l, r, -1));
1231
+ } else if (r.type === 4 /* Datetime */) {
1232
+ stack.push(errorValue(
1233
+ "INVALID_DATETIME_OP",
1234
+ "Cannot subtract a datetime from a value that is not a datetime"
1235
+ ));
760
1236
  } else {
761
1237
  stack.push(binaryOp(l, r, (a, b) => a - b, (a, b) => a - b, "sub"));
762
1238
  }
@@ -767,6 +1243,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
767
1243
  if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
768
1244
  stack.push(numberValue(l.value * r.value));
769
1245
  } else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
1246
+ checkAllocation(matrixProductCells(l.value, r.value), "matrix cells");
770
1247
  stack.push(matrixMultiply(l.value, r.value));
771
1248
  } else if (l.type === 6 /* Uom */ && isRateUnit(l.unit) && r.type === 6 /* Uom */ && r.unit) {
772
1249
  stack.push(multiplyRateByMatchingUom(l, r));
@@ -791,13 +1268,19 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
791
1268
  stack.push(rateValue(lv / rv, l.unit, r.unit));
792
1269
  }
793
1270
  } else {
794
- stack.push(binaryOp(l, r, (a, b) => a / b, (a, b) => a / b, "div"));
1271
+ stack.push(binaryOp(l, r, (a, b) => a / b, (a, b) => {
1272
+ if (b === 0n) throw bigIntDivisionByZero();
1273
+ return a / b;
1274
+ }, "div"));
795
1275
  }
796
1276
  break;
797
1277
  }
798
1278
  case 24 /* MOD */: {
799
1279
  const r = safePop(stack), l = safePop(stack);
800
- stack.push(binaryOp(l, r, (a, b) => a % b, (a, b) => a % b));
1280
+ stack.push(binaryOp(l, r, (a, b) => a % b, (a, b) => {
1281
+ if (b === 0n) throw bigIntDivisionByZero();
1282
+ return a % b;
1283
+ }));
801
1284
  break;
802
1285
  }
803
1286
  case 25 /* EXP */: {
@@ -819,37 +1302,85 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
819
1302
  break;
820
1303
  }
821
1304
  if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
822
- stack.push(numberValue(Math.pow(l.value, r.value)));
1305
+ stack.push(numberValue(power(l.value, r.value)));
1306
+ break;
1307
+ }
1308
+ if (l.type === 7 /* Matrix */ || r.type === 7 /* Matrix */) {
1309
+ if (l.type !== 7 /* Matrix */ || r.type !== 0 /* Number */) {
1310
+ stack.push(errorValue(
1311
+ "MATRIX_POWER_UNSUPPORTED",
1312
+ `^: a matrix may only be the base, raised to a whole number (as in "[1,2;3,4]^2"); "${ValueType[l.type]} ^ ${ValueType[r.type]}" has no matrix reading.`
1313
+ ));
1314
+ break;
1315
+ }
1316
+ checkAllocation(matrixPowerCells(l.value, r.value), "matrix cells");
1317
+ stack.push(matrixPower(l.value, r.value));
823
1318
  break;
824
1319
  }
825
1320
  if (l.type === 9 /* Symbolic */ || r.type === 9 /* Symbolic */) {
826
1321
  stack.push(symbolicPow(l, r));
827
1322
  break;
828
1323
  }
829
- stack.push(numberValue(Math.pow(l.toNumber(), r.toNumber())));
1324
+ if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
1325
+ const powBits = exactPowBits(l, r.toNumber());
1326
+ if (powBits !== null) {
1327
+ if (powBits > MAX_EXACT_POW_BITS) {
1328
+ throw ErrorFactory.execution(
1329
+ "BIGINT_POW_LIMIT_EXCEEDED",
1330
+ `That power would build an exact integer of about ${Math.round(powBits).toLocaleString("en-US")} bits, past the limit of ${MAX_EXACT_POW_BITS.toLocaleString("en-US")} bits`,
1331
+ { limitBits: MAX_EXACT_POW_BITS }
1332
+ );
1333
+ }
1334
+ stack.push(bigIntValue(bigIntPow(toBigIntOperand(l), toBigIntOperand(r))));
1335
+ break;
1336
+ }
1337
+ }
1338
+ stack.push(numberValue(power(l.toNumber(), r.toNumber())));
830
1339
  break;
831
1340
  }
832
1341
  case 26 /* NEG */: {
833
1342
  const v = safePop(stack);
1343
+ const negFault = faultedOperand(v);
1344
+ if (negFault) {
1345
+ stack.push(negFault);
1346
+ break;
1347
+ }
834
1348
  if (v.type === 2 /* BigInt */) stack.push(bigIntValue(-v.value));
835
1349
  else if (v.type === 6 /* Uom */) stack.push(uomValue(-v.toNumber(), v.unit));
1350
+ else if (v.type === 5 /* Percentage */) stack.push(percentageValue(-v.toNumber()));
836
1351
  else if (v.type === 9 /* Symbolic */) stack.push(symbolicNeg(v));
837
1352
  else stack.push(numberValue(-v.toNumber()));
838
1353
  break;
839
1354
  }
840
1355
  case 27 /* POS */: {
841
1356
  const v = safePop(stack);
1357
+ const posFault = faultedOperand(v);
1358
+ if (posFault) {
1359
+ stack.push(posFault);
1360
+ break;
1361
+ }
842
1362
  if (v.type === 6 /* Uom */) stack.push(uomValue(v.toNumber(), v.unit));
1363
+ else if (v.type === 5 /* Percentage */) stack.push(percentageValue(v.toNumber()));
843
1364
  else stack.push(numberValue(v.toNumber()));
844
1365
  break;
845
1366
  }
846
1367
  // ═══════════════════════════════════════════════════════════════
847
1368
  // §4 Bitwise (OpCode 30–36)
1369
+ // None of these routes through binaryOp(), so each one carries
1370
+ // its own faulted-operand check for the reason that function
1371
+ // documents: a bit pattern read off an Error or a Pending is the
1372
+ // bit pattern of zero, and `(5 kg to m) & 1` answered 0 with
1373
+ // nothing to say it had not been asked a real question.
848
1374
  // ═══════════════════════════════════════════════════════════════
849
1375
  case 30 /* LSHIFT */: {
850
1376
  const r = safePop(stack), l = safePop(stack);
1377
+ const shiftFault = faultedOperand(l, r);
1378
+ if (shiftFault) {
1379
+ stack.push(shiftFault);
1380
+ break;
1381
+ }
851
1382
  if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
852
- stack.push(bigIntValue(BigInt(l.toNumber()) << BigInt(r.toNumber())));
1383
+ stack.push(bigIntShift(l, r, 1));
853
1384
  } else {
854
1385
  stack.push(numberValue(l.toNumber() << r.toNumber()));
855
1386
  }
@@ -857,8 +1388,13 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
857
1388
  }
858
1389
  case 31 /* RSHIFT */: {
859
1390
  const r = safePop(stack), l = safePop(stack);
1391
+ const rshiftFault = faultedOperand(l, r);
1392
+ if (rshiftFault) {
1393
+ stack.push(rshiftFault);
1394
+ break;
1395
+ }
860
1396
  if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
861
- stack.push(bigIntValue(BigInt(l.toNumber()) >> BigInt(r.toNumber())));
1397
+ stack.push(bigIntShift(l, r, -1));
862
1398
  } else {
863
1399
  stack.push(numberValue(l.toNumber() >> r.toNumber()));
864
1400
  }
@@ -866,13 +1402,23 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
866
1402
  }
867
1403
  case 32 /* URSHIFT */: {
868
1404
  const r = safePop(stack), l = safePop(stack);
1405
+ const urshiftFault = faultedOperand(l, r);
1406
+ if (urshiftFault) {
1407
+ stack.push(urshiftFault);
1408
+ break;
1409
+ }
869
1410
  stack.push(numberValue(l.toNumber() >>> r.toNumber()));
870
1411
  break;
871
1412
  }
872
1413
  case 33 /* BIT_AND */: {
873
1414
  const r = safePop(stack), l = safePop(stack);
1415
+ const andFault = faultedOperand(l, r);
1416
+ if (andFault) {
1417
+ stack.push(andFault);
1418
+ break;
1419
+ }
874
1420
  if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
875
- stack.push(bigIntValue(BigInt(l.toNumber()) & BigInt(r.toNumber())));
1421
+ stack.push(bigIntValue(toBigIntOperand(l) & toBigIntOperand(r)));
876
1422
  } else {
877
1423
  stack.push(numberValue(l.toNumber() & r.toNumber()));
878
1424
  }
@@ -880,8 +1426,13 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
880
1426
  }
881
1427
  case 34 /* BIT_OR */: {
882
1428
  const r = safePop(stack), l = safePop(stack);
1429
+ const orFault = faultedOperand(l, r);
1430
+ if (orFault) {
1431
+ stack.push(orFault);
1432
+ break;
1433
+ }
883
1434
  if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
884
- stack.push(bigIntValue(BigInt(l.toNumber()) | BigInt(r.toNumber())));
1435
+ stack.push(bigIntValue(toBigIntOperand(l) | toBigIntOperand(r)));
885
1436
  } else {
886
1437
  stack.push(numberValue(l.toNumber() | r.toNumber()));
887
1438
  }
@@ -889,8 +1440,13 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
889
1440
  }
890
1441
  case 35 /* BIT_XOR */: {
891
1442
  const r = safePop(stack), l = safePop(stack);
1443
+ const xorFault = faultedOperand(l, r);
1444
+ if (xorFault) {
1445
+ stack.push(xorFault);
1446
+ break;
1447
+ }
892
1448
  if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
893
- stack.push(bigIntValue(BigInt(l.toNumber()) ^ BigInt(r.toNumber())));
1449
+ stack.push(bigIntValue(toBigIntOperand(l) ^ toBigIntOperand(r)));
894
1450
  } else {
895
1451
  stack.push(numberValue(l.toNumber() ^ r.toNumber()));
896
1452
  }
@@ -898,6 +1454,11 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
898
1454
  }
899
1455
  case 36 /* BIT_NOT */: {
900
1456
  const v = safePop(stack);
1457
+ const notFault = faultedOperand(v);
1458
+ if (notFault) {
1459
+ stack.push(notFault);
1460
+ break;
1461
+ }
901
1462
  if (v.type === 2 /* BigInt */) stack.push(bigIntValue(~v.value));
902
1463
  else stack.push(numberValue(~v.toNumber()));
903
1464
  break;
@@ -905,15 +1466,46 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
905
1466
  // ═══════════════════════════════════════════════════════════════
906
1467
  // §4b Comparison (OpCode 40–45)
907
1468
  // Numeric fast path when both operands are Number;
908
- // EQ/NEQ support UoM unification for same-measure comparison.
1469
+ // all six unify UoM operands before comparing them.
1470
+ //
1471
+ // The four ordering opcodes used to skip that unification and
1472
+ // compare the bare magnitudes, so `1 km > 500 m` answered false
1473
+ // and `30 min < 1 hour` answered false as well. They now go
1474
+ // through compareUom() like EQ/NEQ, which also means they agree
1475
+ // with EQ about where the boundary between "less" and "equal"
1476
+ // falls: `equal` there is a tolerance rather than `===`, so a
1477
+ // pair EQ calls equal is one GT and LT both call false.
1478
+ //
1479
+ // Two units that share no measure cannot be ordered at all, so
1480
+ // the four ordering opcodes answer with an error rather than a
1481
+ // boolean. EQ and NEQ still answer with a boolean, because
1482
+ // "a kilogram is not a metre" is a true statement about a pair
1483
+ // that cannot be converted, whereas "a kilogram is less than a
1484
+ // metre" is not a statement at all.
1485
+ //
1486
+ // A faulted operand is the second case even for EQ and NEQ, and
1487
+ // all six propagate it. A kilogram is a quantity that is not a
1488
+ // metre; an Error is not a quantity, so there is nothing for it
1489
+ // to be equal to. Reading its number gave zero, which made
1490
+ // `(5 kg to m) == 0` answer true.
909
1491
  // ═══════════════════════════════════════════════════════════════
910
1492
  case 40 /* EQ */: {
911
1493
  const r = safePop(stack), l = safePop(stack);
1494
+ const eqFault = faultedOperand(l, r);
1495
+ if (eqFault) {
1496
+ stack.push(eqFault);
1497
+ break;
1498
+ }
912
1499
  if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
913
1500
  stack.push(boolValue(l.value === r.value));
1501
+ } else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
1502
+ const cmp = compareBigIntOperands(l, r);
1503
+ stack.push(boolValue(cmp === null ? l.toNumber() === r.toNumber() : cmp === 0));
1504
+ } else if (l.type === 3 /* String */ && r.type === 3 /* String */) {
1505
+ stack.push(boolValue(l.value === r.value));
914
1506
  } else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
915
- const { lv, rv, sameMeasure } = unifyUom(l, r);
916
- stack.push(boolValue(sameMeasure && lv === rv));
1507
+ const { equal, sameMeasure } = compareUom(l, r);
1508
+ stack.push(boolValue(sameMeasure && equal));
917
1509
  } else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
918
1510
  stack.push(matrixCompare(l.value, r.value, (a, b) => a === b));
919
1511
  } else {
@@ -923,11 +1515,21 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
923
1515
  }
924
1516
  case 41 /* NEQ */: {
925
1517
  const r = safePop(stack), l = safePop(stack);
1518
+ const neqFault = faultedOperand(l, r);
1519
+ if (neqFault) {
1520
+ stack.push(neqFault);
1521
+ break;
1522
+ }
926
1523
  if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
927
1524
  stack.push(boolValue(l.value !== r.value));
1525
+ } else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
1526
+ const cmp = compareBigIntOperands(l, r);
1527
+ stack.push(boolValue(cmp === null ? l.toNumber() !== r.toNumber() : cmp !== 0));
1528
+ } else if (l.type === 3 /* String */ && r.type === 3 /* String */) {
1529
+ stack.push(boolValue(l.value !== r.value));
928
1530
  } else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
929
- const { lv, rv, sameMeasure } = unifyUom(l, r);
930
- stack.push(boolValue(!sameMeasure || lv !== rv));
1531
+ const { equal, sameMeasure } = compareUom(l, r);
1532
+ stack.push(boolValue(!sameMeasure || !equal));
931
1533
  } else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
932
1534
  stack.push(matrixCompare(l.value, r.value, (a, b) => a !== b));
933
1535
  } else {
@@ -937,8 +1539,19 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
937
1539
  }
938
1540
  case 42 /* LT */: {
939
1541
  const r = safePop(stack), l = safePop(stack);
1542
+ const ltFault = faultedOperand(l, r);
1543
+ if (ltFault) {
1544
+ stack.push(ltFault);
1545
+ break;
1546
+ }
940
1547
  if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
941
1548
  stack.push(boolValue(l.value < r.value));
1549
+ } else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
1550
+ const cmp = compareBigIntOperands(l, r);
1551
+ stack.push(boolValue(cmp === null ? l.toNumber() < r.toNumber() : cmp < 0));
1552
+ } else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
1553
+ const { lv, rv, equal, sameMeasure } = compareUom(l, r);
1554
+ stack.push(sameMeasure ? boolValue(!equal && lv < rv) : incomparableUnitsError(l, r));
942
1555
  } else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
943
1556
  stack.push(matrixCompare(l.value, r.value, (a, b) => a < b));
944
1557
  } else {
@@ -948,8 +1561,19 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
948
1561
  }
949
1562
  case 43 /* LTE */: {
950
1563
  const r = safePop(stack), l = safePop(stack);
1564
+ const lteFault = faultedOperand(l, r);
1565
+ if (lteFault) {
1566
+ stack.push(lteFault);
1567
+ break;
1568
+ }
951
1569
  if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
952
1570
  stack.push(boolValue(l.value <= r.value));
1571
+ } else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
1572
+ const cmp = compareBigIntOperands(l, r);
1573
+ stack.push(boolValue(cmp === null ? l.toNumber() <= r.toNumber() : cmp <= 0));
1574
+ } else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
1575
+ const { lv, rv, equal, sameMeasure } = compareUom(l, r);
1576
+ stack.push(sameMeasure ? boolValue(equal || lv <= rv) : incomparableUnitsError(l, r));
953
1577
  } else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
954
1578
  stack.push(matrixCompare(l.value, r.value, (a, b) => a <= b));
955
1579
  } else {
@@ -959,8 +1583,19 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
959
1583
  }
960
1584
  case 44 /* GT */: {
961
1585
  const r = safePop(stack), l = safePop(stack);
1586
+ const gtFault = faultedOperand(l, r);
1587
+ if (gtFault) {
1588
+ stack.push(gtFault);
1589
+ break;
1590
+ }
962
1591
  if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
963
1592
  stack.push(boolValue(l.value > r.value));
1593
+ } else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
1594
+ const cmp = compareBigIntOperands(l, r);
1595
+ stack.push(boolValue(cmp === null ? l.toNumber() > r.toNumber() : cmp > 0));
1596
+ } else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
1597
+ const { lv, rv, equal, sameMeasure } = compareUom(l, r);
1598
+ stack.push(sameMeasure ? boolValue(!equal && lv > rv) : incomparableUnitsError(l, r));
964
1599
  } else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
965
1600
  stack.push(matrixCompare(l.value, r.value, (a, b) => a > b));
966
1601
  } else {
@@ -970,8 +1605,19 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
970
1605
  }
971
1606
  case 45 /* GTE */: {
972
1607
  const r = safePop(stack), l = safePop(stack);
1608
+ const gteFault = faultedOperand(l, r);
1609
+ if (gteFault) {
1610
+ stack.push(gteFault);
1611
+ break;
1612
+ }
973
1613
  if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
974
1614
  stack.push(boolValue(l.value >= r.value));
1615
+ } else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
1616
+ const cmp = compareBigIntOperands(l, r);
1617
+ stack.push(boolValue(cmp === null ? l.toNumber() >= r.toNumber() : cmp >= 0));
1618
+ } else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
1619
+ const { lv, rv, equal, sameMeasure } = compareUom(l, r);
1620
+ stack.push(sameMeasure ? boolValue(equal || lv >= rv) : incomparableUnitsError(l, r));
975
1621
  } else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
976
1622
  stack.push(matrixCompare(l.value, r.value, (a, b) => a >= b));
977
1623
  } else {
@@ -981,14 +1627,30 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
981
1627
  }
982
1628
  // ═══════════════════════════════════════════════════════════════
983
1629
  // §4c Logical / conditional select (OpCode 130–132)
1630
+ //
1631
+ // Truthiness is read through isTruthy(), which reads a number,
1632
+ // which is zero for a fault, so a failed operand used to be
1633
+ // quietly false. Both operands are already evaluated by the time
1634
+ // either opcode runs (this VM has no branch opcodes), so there is
1635
+ // no short-circuit to preserve by ignoring the right-hand one.
984
1636
  // ═══════════════════════════════════════════════════════════════
985
1637
  case 130 /* LOGICAL_AND */: {
986
1638
  const r = safePop(stack), l = safePop(stack);
1639
+ const andLogicFault = faultedOperand(l, r);
1640
+ if (andLogicFault) {
1641
+ stack.push(andLogicFault);
1642
+ break;
1643
+ }
987
1644
  stack.push(boolValue(isTruthy(l) && isTruthy(r)));
988
1645
  break;
989
1646
  }
990
1647
  case 131 /* LOGICAL_OR */: {
991
1648
  const r = safePop(stack), l = safePop(stack);
1649
+ const orLogicFault = faultedOperand(l, r);
1650
+ if (orLogicFault) {
1651
+ stack.push(orLogicFault);
1652
+ break;
1653
+ }
992
1654
  stack.push(boolValue(isTruthy(l) || isTruthy(r)));
993
1655
  break;
994
1656
  }
@@ -996,6 +1658,11 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
996
1658
  const elseVal = safePop(stack);
997
1659
  const thenVal = safePop(stack);
998
1660
  const condition = safePop(stack);
1661
+ const conditionFault = faultedOperand(condition);
1662
+ if (conditionFault) {
1663
+ stack.push(conditionFault);
1664
+ break;
1665
+ }
999
1666
  stack.push(isTruthy(condition) ? thenVal : elseVal);
1000
1667
  break;
1001
1668
  }
@@ -1003,13 +1670,16 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1003
1670
  // §5 Functions (OpCode 50–52)
1004
1671
  // ═══════════════════════════════════════════════════════════════
1005
1672
  case 50 /* CALL_PLUGIN */: {
1006
- const fnIdx = opcodes[ip++];
1007
- const argCount = opcodes[ip++];
1673
+ const fnIdx = operandByte(opcodes, ip++, op, "plugin-function index");
1674
+ const argCount = operandByte(opcodes, ip++, op, "argument count");
1008
1675
  const args = [];
1009
1676
  for (let i = 0; i < argCount; i++) args.push(safePop(stack));
1010
1677
  args.reverse();
1011
1678
  const fn = vm.context.pluginFunctions[fnIdx];
1012
- if (!fn) {
1679
+ const pluginArgFault = faultedIn(args);
1680
+ if (pluginArgFault) {
1681
+ stack.push(pluginArgFault);
1682
+ } else if (!fn) {
1013
1683
  stack.push(numberValue(0));
1014
1684
  } else {
1015
1685
  const result = fn(args, context);
@@ -1024,8 +1694,10 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1024
1694
  break;
1025
1695
  }
1026
1696
  case 51 /* CALL_BUILTIN */: {
1027
- const fnIdx = opcodes[ip++];
1028
- const argCount = opcodes[ip++];
1697
+ const fnIdx = operandByte(opcodes, ip++, op, "builtin-function index");
1698
+ const argCount = operandByte(opcodes, ip++, op, "argument count");
1699
+ const arityError = builtinArityError(fnIdx, argCount);
1700
+ if (arityError) throw arityError;
1029
1701
  const args = [];
1030
1702
  let sawSymbolic = false;
1031
1703
  for (let i = 0; i < argCount; i++) {
@@ -1033,6 +1705,11 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1033
1705
  if (arg.type === 9 /* Symbolic */) sawSymbolic = true;
1034
1706
  args.push(arg);
1035
1707
  }
1708
+ const builtinArgFault = faultedIn(args);
1709
+ if (builtinArgFault) {
1710
+ stack.push(builtinArgFault);
1711
+ break;
1712
+ }
1036
1713
  const fn = builtinFunctions[fnIdx];
1037
1714
  if (fn) {
1038
1715
  const ordered = args.reverse();
@@ -1042,7 +1719,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1042
1719
  break;
1043
1720
  }
1044
1721
  case 150 /* DEFINE_USER_FUNCTION */: {
1045
- const bodyIdx = opcodes[ip++];
1722
+ const bodyIdx = operandByte(opcodes, ip++, op, "function-body index");
1046
1723
  const def = userFunctionBodies?.[bodyIdx];
1047
1724
  if (!def) {
1048
1725
  throw ErrorFactory.internal(
@@ -1055,9 +1732,8 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1055
1732
  break;
1056
1733
  }
1057
1734
  case 151 /* CALL_USER_FUNCTION */: {
1058
- const nameIdx = opcodes[ip++];
1059
- const argCount = opcodes[ip++];
1060
- const name = strings[nameIdx];
1735
+ const name = poolString(opcodes, ip++, strings, op, "function-name index");
1736
+ const argCount = operandByte(opcodes, ip++, op, "argument count");
1061
1737
  const args = [];
1062
1738
  for (let i = 0; i < argCount; i++) args.push(safePop(stack));
1063
1739
  args.reverse();
@@ -1074,6 +1750,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1074
1750
  }
1075
1751
  const frame = /* @__PURE__ */ new Map();
1076
1752
  for (let i = 0; i < fn.params.length; i++) frame.set(fn.params[i], args[i]);
1753
+ chargeFunctionCall();
1077
1754
  vm.pushCallFrame(frame);
1078
1755
  let bodyResult;
1079
1756
  try {
@@ -1098,7 +1775,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1098
1775
  // §6 Variables (OpCode 60–63)
1099
1776
  // ═══════════════════════════════════════════════════════════════
1100
1777
  case 60 /* LOAD_VAR */: {
1101
- const varName = strings[opcodes[ip++]];
1778
+ const varName = poolString(opcodes, ip++, strings, op, "variable-name index");
1102
1779
  const val = vm.getVar(varName);
1103
1780
  if (val !== void 0) {
1104
1781
  stack.push(val);
@@ -1115,13 +1792,13 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1115
1792
  }
1116
1793
  case 61 /* STORE_VAR */: {
1117
1794
  const val = safePop(stack);
1118
- const varName = strings[opcodes[ip++]];
1795
+ const varName = poolString(opcodes, ip++, strings, op, "variable-name index");
1119
1796
  vm.setVar(varName, hasArena ? persistentValue(val) : val);
1120
1797
  stack.push(val);
1121
1798
  break;
1122
1799
  }
1123
1800
  case 62 /* LOAD_GLOBAL_VAR */: {
1124
- const varName = strings[opcodes[ip++]];
1801
+ const varName = poolString(opcodes, ip++, strings, op, "variable-name index");
1125
1802
  const globalValue = sharedGlobalVariableStore.get(varName);
1126
1803
  if (globalValue === void 0) {
1127
1804
  throw ErrorFactory.internal({
@@ -1137,41 +1814,75 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1137
1814
  }
1138
1815
  case 63 /* STORE_GLOBAL_VAR */: {
1139
1816
  const val = safePop(stack);
1140
- const varName = strings[opcodes[ip++]];
1817
+ const varName = poolString(opcodes, ip++, strings, op, "variable-name index");
1141
1818
  sharedGlobalVariableStore.set(varName, hasArena ? persistentValue(val) : val);
1142
1819
  stack.push(val);
1143
1820
  break;
1144
1821
  }
1145
1822
  // ═══════════════════════════════════════════════════════════════
1146
1823
  // §7 Type conversions (OpCode 70–74, 140–145)
1824
+ // Every one of these restates a number in another notation, so a
1825
+ // faulted operand has nothing to restate and each propagates it
1826
+ // rather than converting the zero toNumber() reports. Without it
1827
+ // "(5 kg to m) as hex" answered 0x0 and "as binary" answered 0b0.
1147
1828
  // ═══════════════════════════════════════════════════════════════
1148
1829
  case 70 /* TO_NUMBER */: {
1149
1830
  const v = safePop(stack);
1831
+ const toNumberFault = faultedOperand(v);
1832
+ if (toNumberFault) {
1833
+ stack.push(toNumberFault);
1834
+ break;
1835
+ }
1150
1836
  stack.push(numberValue(v.toNumber()));
1151
1837
  break;
1152
1838
  }
1153
1839
  case 71 /* TO_HEX */: {
1154
1840
  const v = safePop(stack);
1155
- stack.push(hexValue(v.toNumber()));
1841
+ const toHexFault = faultedOperand(v);
1842
+ if (toHexFault) {
1843
+ stack.push(toHexFault);
1844
+ break;
1845
+ }
1846
+ stack.push(hexValue(v.type === 2 /* BigInt */ ? v.value : v.toNumber()));
1156
1847
  break;
1157
1848
  }
1158
1849
  case 74 /* TO_PERCENTAGE */: {
1159
1850
  const v = safePop(stack);
1851
+ const toPercentageFault = faultedOperand(v);
1852
+ if (toPercentageFault) {
1853
+ stack.push(toPercentageFault);
1854
+ break;
1855
+ }
1160
1856
  stack.push(percentageValue(v.toNumber()));
1161
1857
  break;
1162
1858
  }
1163
1859
  case 140 /* TO_FRACTION */: {
1164
1860
  const v = safePop(stack);
1861
+ const toFractionFault = faultedOperand(v);
1862
+ if (toFractionFault) {
1863
+ stack.push(toFractionFault);
1864
+ break;
1865
+ }
1165
1866
  stack.push(stringValue(toFractionString(v.toNumber())));
1166
1867
  break;
1167
1868
  }
1168
1869
  case 141 /* TO_MULTIPLIER */: {
1169
1870
  const v = safePop(stack);
1871
+ const toMultiplierFault = faultedOperand(v);
1872
+ if (toMultiplierFault) {
1873
+ stack.push(toMultiplierFault);
1874
+ break;
1875
+ }
1170
1876
  stack.push(stringValue(toMultiplierString(v)));
1171
1877
  break;
1172
1878
  }
1173
1879
  case 142 /* TO_SCI */: {
1174
1880
  const v = safePop(stack);
1881
+ const toSciFault = faultedOperand(v);
1882
+ if (toSciFault) {
1883
+ stack.push(toSciFault);
1884
+ break;
1885
+ }
1175
1886
  stack.push(stringValue(toScientificString(v.toNumber())));
1176
1887
  break;
1177
1888
  }
@@ -1179,17 +1890,32 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1179
1890
  // `(255 as binary) + 1` has to be 256; as a string it was 1.
1180
1891
  case 143 /* TO_BINARY */: {
1181
1892
  const v = safePop(stack);
1182
- stack.push(hexValue(v.toNumber(), "bin"));
1893
+ const toBinaryFault = faultedOperand(v);
1894
+ if (toBinaryFault) {
1895
+ stack.push(toBinaryFault);
1896
+ break;
1897
+ }
1898
+ stack.push(hexValue(v.type === 2 /* BigInt */ ? v.value : v.toNumber(), "bin"));
1183
1899
  break;
1184
1900
  }
1185
1901
  case 144 /* TO_OCTAL */: {
1186
1902
  const v = safePop(stack);
1187
- stack.push(hexValue(v.toNumber(), "oct"));
1903
+ const toOctalFault = faultedOperand(v);
1904
+ if (toOctalFault) {
1905
+ stack.push(toOctalFault);
1906
+ break;
1907
+ }
1908
+ stack.push(hexValue(v.type === 2 /* BigInt */ ? v.value : v.toNumber(), "oct"));
1188
1909
  break;
1189
1910
  }
1190
1911
  case 145 /* CALL_AS_CONVERTER */: {
1191
- const name = safePop(stack).value.toLowerCase();
1912
+ const name = stringOperand(safePop(stack), op, "converter name").toLowerCase();
1192
1913
  const value = safePop(stack);
1914
+ const converterFault = faultedOperand(value);
1915
+ if (converterFault) {
1916
+ stack.push(converterFault);
1917
+ break;
1918
+ }
1193
1919
  const converter = asConverterRegistry.get(name);
1194
1920
  if (!converter) {
1195
1921
  stack.push(errorValue("UNKNOWN_AS_CONVERTER", `Unknown converter "as ${name}"`));
@@ -1202,15 +1928,26 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1202
1928
  // §8 UoM (OpCode 80–84)
1203
1929
  // ═══════════════════════════════════════════════════════════════
1204
1930
  case 80 /* UOM_CONVERT */: {
1205
- const unit = safePop(stack).value;
1206
- const val = safePop(stack).toNumber();
1207
- stack.push(uomValue(val, unit));
1931
+ const unit = stringOperand(safePop(stack), op, "unit name");
1932
+ const operand = safePop(stack);
1933
+ const faulted = faultedOperand(operand);
1934
+ if (faulted) {
1935
+ stack.push(faulted);
1936
+ break;
1937
+ }
1938
+ stack.push(uomValue(operand.toNumber(), unit));
1208
1939
  break;
1209
1940
  }
1210
1941
  case 81 /* UOM_CONVERT_TO */: {
1211
- const toUnit = safePop(stack).value;
1212
- const fromUnit = safePop(stack).value;
1213
- const val = safePop(stack).toNumber();
1942
+ const toUnit = stringOperand(safePop(stack), op, "target unit name");
1943
+ const fromUnit = stringOperand(safePop(stack), op, "source unit name");
1944
+ const operand = safePop(stack);
1945
+ const faulted = faultedOperand(operand);
1946
+ if (faulted) {
1947
+ stack.push(faulted);
1948
+ break;
1949
+ }
1950
+ const val = operand.toNumber();
1214
1951
  const measure = getMeasure(fromUnit);
1215
1952
  const isCurrency = sharedCurrencyExchange.isCurrency(fromUnit) && sharedCurrencyExchange.isCurrency(toUnit);
1216
1953
  if (measure && getMeasure(toUnit) === measure) {
@@ -1223,26 +1960,36 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1223
1960
  stack.push(errorValue("CURRENCY_RATE_UNAVAILABLE", `No exchange rate available for ${fromUnit} to ${toUnit}`));
1224
1961
  }
1225
1962
  } else {
1226
- stack.push(uomValue(val, fromUnit));
1963
+ stack.push(incompatibleConversionError(fromUnit, toUnit));
1227
1964
  }
1228
1965
  break;
1229
1966
  }
1230
1967
  case 85 /* UOM_POSSIBILITIES */: {
1231
- const unit = safePop(stack).value;
1968
+ const unit = stringOperand(safePop(stack), op, "unit name");
1232
1969
  const possibilities = getConvertiblePossibilities(unit);
1233
1970
  stack.push(stringValue(possibilities.length > 0 ? possibilities.join(", ") : `No known units for "${unit}"`));
1234
1971
  break;
1235
1972
  }
1236
1973
  case 83 /* UOM_BEST */: {
1237
- const unit = safePop(stack).value;
1238
- const val = safePop(stack).toNumber();
1239
- const { value, unit: bestUnit } = getBestUnit(val, unit);
1974
+ const unit = stringOperand(safePop(stack), op, "unit name");
1975
+ const operand = safePop(stack);
1976
+ const faulted = faultedOperand(operand);
1977
+ if (faulted) {
1978
+ stack.push(faulted);
1979
+ break;
1980
+ }
1981
+ const { value, unit: bestUnit } = getBestUnit(operand.toNumber(), unit);
1240
1982
  stack.push(uomValue(value, bestUnit));
1241
1983
  break;
1242
1984
  }
1243
1985
  case 84 /* UOM_CONVERT_IN */: {
1244
- const toUnit = safePop(stack).value;
1986
+ const toUnit = stringOperand(safePop(stack), op, "target unit name");
1245
1987
  const left = safePop(stack);
1988
+ const faulted = faultedOperand(left);
1989
+ if (faulted) {
1990
+ stack.push(faulted);
1991
+ break;
1992
+ }
1246
1993
  if (left.type === 6 /* Uom */) {
1247
1994
  const fromUnit = left.unit;
1248
1995
  const val = left.toNumber();
@@ -1258,7 +2005,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1258
2005
  stack.push(errorValue("CURRENCY_RATE_UNAVAILABLE", `No exchange rate available for ${fromUnit} to ${toUnit}`));
1259
2006
  }
1260
2007
  } else {
1261
- stack.push(left);
2008
+ stack.push(incompatibleConversionError(fromUnit, toUnit));
1262
2009
  }
1263
2010
  } else {
1264
2011
  stack.push(uomValue(left.toNumber(), toUnit));
@@ -1267,6 +2014,11 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1267
2014
  }
1268
2015
  case 82 /* UOM_GET_VALUE */: {
1269
2016
  const v = safePop(stack);
2017
+ const getValueFault = faultedOperand(v);
2018
+ if (getValueFault) {
2019
+ stack.push(getValueFault);
2020
+ break;
2021
+ }
1270
2022
  stack.push(numberValue(v.toNumber()));
1271
2023
  break;
1272
2024
  }
@@ -1277,6 +2029,11 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1277
2029
  case 110 /* RATE_DIV */: {
1278
2030
  const denominatorVal = safePop(stack);
1279
2031
  const numeratorVal = safePop(stack);
2032
+ const rateDivFault = faultedOperand(numeratorVal, denominatorVal);
2033
+ if (rateDivFault) {
2034
+ stack.push(rateDivFault);
2035
+ break;
2036
+ }
1280
2037
  if (denominatorVal.type !== 6 /* Uom */ || !denominatorVal.unit) {
1281
2038
  stack.push(errorValue("RATE_MISSING_DENOMINATOR_UNIT", 'Cannot build a rate: the right-hand side of "/" has no unit'));
1282
2039
  break;
@@ -1288,6 +2045,11 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1288
2045
  case 111 /* RATE_MUL */: {
1289
2046
  const multiplier = safePop(stack);
1290
2047
  const rate = safePop(stack);
2048
+ const rateMulFault = faultedOperand(rate, multiplier);
2049
+ if (rateMulFault) {
2050
+ stack.push(rateMulFault);
2051
+ break;
2052
+ }
1291
2053
  if (rate.type !== 6 /* Uom */ || !isRateUnit(rate.unit)) {
1292
2054
  stack.push(errorValue("RATE_MUL_LEFT_NOT_A_RATE", 'Left-hand side of a rate multiplication must be a rate (e.g. "$50/week")'));
1293
2055
  break;
@@ -1300,8 +2062,13 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1300
2062
  break;
1301
2063
  }
1302
2064
  case 112 /* RATE_CONVERT */: {
1303
- const newDenominatorUnit = safePop(stack).value;
2065
+ const newDenominatorUnit = stringOperand(safePop(stack), op, "denominator unit name");
1304
2066
  const rate = safePop(stack);
2067
+ const rateConvertFault = faultedOperand(rate);
2068
+ if (rateConvertFault) {
2069
+ stack.push(rateConvertFault);
2070
+ break;
2071
+ }
1305
2072
  if (rate.type !== 6 /* Uom */ || !isRateUnit(rate.unit)) {
1306
2073
  stack.push(errorValue("RATE_CONVERT_NOT_A_RATE", "Cannot convert a non-rate value's denominator"));
1307
2074
  break;
@@ -1324,7 +2091,13 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1324
2091
  // §8.7 Time, clock-time-of-day (OpCode 120)
1325
2092
  // ═══════════════════════════════════════════════════════════════
1326
2093
  case 120 /* CLOCK_TIME_TODAY */: {
1327
- const totalMinutes = safePop(stack).toNumber();
2094
+ const minutesValue = safePop(stack);
2095
+ const clockFault = faultedOperand(minutesValue);
2096
+ if (clockFault) {
2097
+ stack.push(clockFault);
2098
+ break;
2099
+ }
2100
+ const totalMinutes = minutesValue.toNumber();
1328
2101
  const now = /* @__PURE__ */ new Date();
1329
2102
  const anchored = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
1330
2103
  anchored.setMinutes(totalMinutes);
@@ -1338,28 +2111,43 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1338
2111
  stack.push(datetimeValue(Date.now()));
1339
2112
  break;
1340
2113
  case 95 /* DATE_LITERAL */:
1341
- stack.push(datetimeValue(numbers[opcodes[ip++]]));
2114
+ stack.push(datetimeValue(numbers[poolIndex(opcodes, ip++, op, "constant-pool index", numbers.length, "number-pool")]));
1342
2115
  break;
1343
2116
  case 91 /* DATE_ADD */: {
1344
2117
  const durValue = safePop(stack), dtValue = safePop(stack);
1345
- stack.push(datetimeValue(dtValue.toNumber() + extractDurationMs(durValue)));
2118
+ const dateAddFault = faultedOperand(dtValue, durValue);
2119
+ if (dateAddFault) {
2120
+ stack.push(dateAddFault);
2121
+ break;
2122
+ }
2123
+ stack.push(datetimeValue(shiftDatetime(dtValue.toNumber(), durValue, 1, vm)));
1346
2124
  break;
1347
2125
  }
1348
2126
  case 92 /* DATE_SUB */: {
1349
2127
  const durValue = safePop(stack), dtValue = safePop(stack);
1350
- stack.push(datetimeValue(dtValue.toNumber() - extractDurationMs(durValue)));
2128
+ const dateSubFault = faultedOperand(dtValue, durValue);
2129
+ if (dateSubFault) {
2130
+ stack.push(dateSubFault);
2131
+ break;
2132
+ }
2133
+ stack.push(datetimeValue(shiftDatetime(dtValue.toNumber(), durValue, -1, vm)));
1351
2134
  break;
1352
2135
  }
1353
2136
  case 93 /* DATE_NEXT_WEEKDAY */:
1354
2137
  case 94 /* DATE_LAST_WEEKDAY */: {
1355
- const targetDay = safePop(stack).toNumber();
2138
+ const targetDayValue = safePop(stack);
1356
2139
  const nowValue = safePop(stack);
2140
+ const weekdayFault = faultedOperand(nowValue, targetDayValue);
2141
+ if (weekdayFault) {
2142
+ stack.push(weekdayFault);
2143
+ break;
2144
+ }
2145
+ const targetDay = targetDayValue.toNumber();
1357
2146
  const now = nowValue.toNumber();
1358
2147
  const currentDay = new Date(now).getDay();
1359
2148
  let diffDays = op === 93 /* DATE_NEXT_WEEKDAY */ ? (targetDay - currentDay + 7) % 7 : (currentDay - targetDay + 7) % 7;
1360
2149
  if (diffDays === 0) diffDays = 7;
1361
- const offsetMs = diffDays * 24 * 60 * 60 * 1e3;
1362
- stack.push(datetimeValue(op === 93 /* DATE_NEXT_WEEKDAY */ ? now + offsetMs : now - offsetMs));
2150
+ stack.push(datetimeValue(addCalendarDays(now, op === 93 /* DATE_NEXT_WEEKDAY */ ? diffDays : -diffDays)));
1363
2151
  break;
1364
2152
  }
1365
2153
  // ═══════════════════════════════════════════════════════════════
@@ -1371,25 +2159,28 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1371
2159
  // column-major storage helpers.
1372
2160
  // ═══════════════════════════════════════════════════════════════
1373
2161
  case 152 /* MAT_NEW */: {
1374
- const rows = opcodes[ip++];
1375
- const cols = opcodes[ip++];
2162
+ const rows = operandByte(opcodes, ip++, op, "row count");
2163
+ const cols = operandByte(opcodes, ip++, op, "column count");
1376
2164
  const count = rows * cols;
1377
- const rowMajor = new Array(count);
2165
+ const rowMajor = checkedArray(count, "matrix cells");
2166
+ let cellFault = null;
1378
2167
  for (let i = count - 1; i >= 0; i--) {
1379
2168
  const cellVal = safePop(stack);
2169
+ if (cellFault === null) cellFault = faultedOperand(cellVal);
1380
2170
  rowMajor[i] = cellVal.type === 10 /* Boolean */ ? cellVal.value : cellVal.type === 9 /* Symbolic */ ? cellVal.value : cellVal.toNumber();
1381
2171
  }
2172
+ if (cellFault) {
2173
+ stack.push(cellFault);
2174
+ break;
2175
+ }
1382
2176
  stack.push(matrixValue(rows, cols, rowMajorToColumnMajor(rows, cols, rowMajor)));
1383
2177
  break;
1384
2178
  }
1385
2179
  case 153 /* MAT_INDEX1 */: {
1386
2180
  const indexVal = safePop(stack), matrixVal = safePop(stack);
1387
- if (matrixVal.type === 13 /* Error */) {
1388
- stack.push(matrixVal);
1389
- break;
1390
- }
1391
- if (indexVal.type === 13 /* Error */) {
1392
- stack.push(indexVal);
2181
+ const index1Fault = faultedOperand(matrixVal, indexVal);
2182
+ if (index1Fault) {
2183
+ stack.push(index1Fault);
1393
2184
  break;
1394
2185
  }
1395
2186
  if (matrixVal.type !== 7 /* Matrix */) {
@@ -1411,16 +2202,9 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1411
2202
  }
1412
2203
  case 154 /* MAT_INDEX2 */: {
1413
2204
  const colVal = safePop(stack), rowVal = safePop(stack), matrixVal = safePop(stack);
1414
- if (matrixVal.type === 13 /* Error */) {
1415
- stack.push(matrixVal);
1416
- break;
1417
- }
1418
- if (rowVal.type === 13 /* Error */) {
1419
- stack.push(rowVal);
1420
- break;
1421
- }
1422
- if (colVal.type === 13 /* Error */) {
1423
- stack.push(colVal);
2205
+ const index2Fault = faultedOperand(matrixVal, rowVal, colVal);
2206
+ if (index2Fault) {
2207
+ stack.push(index2Fault);
1424
2208
  break;
1425
2209
  }
1426
2210
  if (matrixVal.type !== 7 /* Matrix */) {
@@ -1443,12 +2227,9 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1443
2227
  }
1444
2228
  case 156 /* RANGE_NEW */: {
1445
2229
  const maxVal = safePop(stack), minVal = safePop(stack);
1446
- if (minVal.type === 13 /* Error */) {
1447
- stack.push(minVal);
1448
- break;
1449
- }
1450
- if (maxVal.type === 13 /* Error */) {
1451
- stack.push(maxVal);
2230
+ const rangeFault = faultedOperand(minVal, maxVal);
2231
+ if (rangeFault) {
2232
+ stack.push(rangeFault);
1452
2233
  break;
1453
2234
  }
1454
2235
  if (minVal.type !== 0 /* Number */ || maxVal.type !== 0 /* Number */) {
@@ -1473,16 +2254,9 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1473
2254
  }
1474
2255
  case 155 /* MAT_SLICE */: {
1475
2256
  const colRangeVal = safePop(stack), rowRangeVal = safePop(stack), matrixVal = safePop(stack);
1476
- if (matrixVal.type === 13 /* Error */) {
1477
- stack.push(matrixVal);
1478
- break;
1479
- }
1480
- if (rowRangeVal.type === 13 /* Error */) {
1481
- stack.push(rowRangeVal);
1482
- break;
1483
- }
1484
- if (colRangeVal.type === 13 /* Error */) {
1485
- stack.push(colRangeVal);
2257
+ const sliceFault = faultedOperand(matrixVal, rowRangeVal, colRangeVal);
2258
+ if (sliceFault) {
2259
+ stack.push(sliceFault);
1486
2260
  break;
1487
2261
  }
1488
2262
  if (matrixVal.type !== 7 /* Matrix */) {
@@ -1505,7 +2279,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1505
2279
  }
1506
2280
  const newRows = rowRange.max - rowRange.min + 1;
1507
2281
  const newCols = colRange.max - colRange.min + 1;
1508
- const data = new Array(newRows * newCols);
2282
+ const data = checkedArray(newRows * newCols, "matrix cells");
1509
2283
  for (let r = 0; r < newRows; r++) {
1510
2284
  for (let c = 0; c < newCols; c++) {
1511
2285
  data[r + c * newRows] = matAt(m, rowRange.min + r, colRange.min + c);
@@ -1523,9 +2297,10 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1523
2297
  // kind-0 bodies are a SEPARATE side-table from userFunctionBodies.
1524
2298
  // ═══════════════════════════════════════════════════════════════
1525
2299
  case 157 /* MAP_INVOKE */: {
1526
- const kind = opcodes[ip++];
1527
- const ref = opcodes[ip++];
1528
- const collectionCount = opcodes[ip++];
2300
+ const kind = operandByte(opcodes, ip++, op, "body kind");
2301
+ const ref = operandByte(opcodes, ip++, op, "body reference");
2302
+ const collectionCount = operandByte(opcodes, ip++, op, "collection count");
2303
+ requireKnownBodyKind(kind, ref, op);
1529
2304
  const rawCollections = new Array(collectionCount);
1530
2305
  for (let i = collectionCount - 1; i >= 0; i--) rawCollections[i] = safePop(stack);
1531
2306
  let paramNames = [];
@@ -1542,7 +2317,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1542
2317
  paramNames = def.params;
1543
2318
  program = def.program;
1544
2319
  } else if (kind === 2) {
1545
- const name = strings[ref];
2320
+ const name = pooledString(strings, ref, op);
1546
2321
  const fn = vm.getUserFunction(name);
1547
2322
  if (!fn) {
1548
2323
  stack.push(errorValue("UNDEFINED_FUNCTION", `map: undefined function "${name}"`));
@@ -1562,11 +2337,12 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1562
2337
  const cellArrays = new Array(collectionCount);
1563
2338
  let mapEarlyError;
1564
2339
  for (let i = 0; i < collectionCount; i++) {
1565
- const resolved = collectionToValues(rawCollections[i]);
2340
+ const resolved = collectionToValues(rawCollections[i], vm.getMaxCollectionSize());
1566
2341
  if (!Array.isArray(resolved)) {
1567
2342
  mapEarlyError = resolved;
1568
2343
  break;
1569
2344
  }
2345
+ chargeAllocation(resolved.length, "collection elements");
1570
2346
  if (collectionLength === -1) {
1571
2347
  collectionLength = resolved.length;
1572
2348
  } else if (resolved.length !== collectionLength) {
@@ -1582,14 +2358,15 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1582
2358
  stack.push(mapEarlyError);
1583
2359
  break;
1584
2360
  }
1585
- const resultData = new Array(collectionLength);
2361
+ const resultData = checkedArray(collectionLength, "matrix cells");
1586
2362
  let mapError;
1587
2363
  for (let i = 0; i < collectionLength; i++) {
1588
2364
  const args = new Array(collectionCount);
1589
2365
  for (let j = 0; j < collectionCount; j++) args[j] = cellArrays[j][i];
1590
2366
  const resultVal = kind === 1 ? builtinFunctions[ref]?.(args) ?? errorValue("UNKNOWN_BUILTIN_FUNCTION", `map: unknown builtin function index ${ref}`) : invokeFrameBody(paramNames, program, args, vm, pipeline, expression, context, !!symbolicTolerant);
1591
- if (resultVal.type === 13 /* Error */) {
1592
- mapError = resultVal;
2367
+ const resultFault = faultedOperand(resultVal);
2368
+ if (resultFault) {
2369
+ mapError = resultFault;
1593
2370
  break;
1594
2371
  }
1595
2372
  resultData[i] = resultVal.type === 10 /* Boolean */ ? resultVal.value : resultVal.type === 9 /* Symbolic */ ? resultVal.value : resultVal.toNumber();
@@ -1598,13 +2375,20 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1598
2375
  stack.push(mapError);
1599
2376
  break;
1600
2377
  }
1601
- stack.push(matrixValue(1, collectionLength, resultData));
2378
+ const firstCollection = rawCollections[0];
2379
+ const sourceShape = firstCollection?.type === 7 /* Matrix */ ? firstCollection.value : void 0;
2380
+ if (sourceShape && sourceShape.rows * sourceShape.cols === collectionLength) {
2381
+ stack.push(matrixValue(sourceShape.rows, sourceShape.cols, resultData));
2382
+ } else {
2383
+ stack.push(matrixValue(1, collectionLength, resultData));
2384
+ }
1602
2385
  break;
1603
2386
  }
1604
2387
  case 158 /* REDUCE_INVOKE */: {
1605
- const kind = opcodes[ip++];
1606
- const ref = opcodes[ip++];
1607
- const hasInitial = opcodes[ip++];
2388
+ const kind = operandByte(opcodes, ip++, op, "body kind");
2389
+ const ref = operandByte(opcodes, ip++, op, "body reference");
2390
+ const hasInitial = operandByte(opcodes, ip++, op, "initial-value flag");
2391
+ requireKnownBodyKind(kind, ref, op);
1608
2392
  const initialVal = hasInitial ? safePop(stack) : void 0;
1609
2393
  const collectionVal = safePop(stack);
1610
2394
  let paramNames = [];
@@ -1621,7 +2405,7 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1621
2405
  paramNames = def.params;
1622
2406
  program = def.program;
1623
2407
  } else if (kind === 2) {
1624
- const name = strings[ref];
2408
+ const name = pooledString(strings, ref, op);
1625
2409
  const fn = vm.getUserFunction(name);
1626
2410
  if (!fn) {
1627
2411
  stack.push(errorValue("UNDEFINED_FUNCTION", `reduce: undefined function "${name}"`));
@@ -1637,16 +2421,18 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1637
2421
  paramNames = fn.params;
1638
2422
  program = fn.program;
1639
2423
  }
1640
- const cells = collectionToValues(collectionVal);
2424
+ const cells = collectionToValues(collectionVal, vm.getMaxCollectionSize());
1641
2425
  if (!Array.isArray(cells)) {
1642
2426
  stack.push(cells);
1643
2427
  break;
1644
2428
  }
2429
+ chargeAllocation(cells.length, "collection elements");
1645
2430
  let acc;
1646
2431
  let startIdx;
1647
2432
  if (initialVal !== void 0) {
1648
- if (initialVal.type === 13 /* Error */) {
1649
- stack.push(initialVal);
2433
+ const initialFault = faultedOperand(initialVal);
2434
+ if (initialFault) {
2435
+ stack.push(initialFault);
1650
2436
  break;
1651
2437
  }
1652
2438
  acc = initialVal;
@@ -1663,8 +2449,9 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1663
2449
  for (let i = startIdx; i < cells.length; i++) {
1664
2450
  const args = [acc, cells[i]];
1665
2451
  const resultVal = kind === 1 ? builtinFunctions[ref]?.(args) ?? errorValue("UNKNOWN_BUILTIN_FUNCTION", `reduce: unknown builtin function index ${ref}`) : invokeFrameBody(paramNames, program, args, vm, pipeline, expression, context, !!symbolicTolerant);
1666
- if (resultVal.type === 13 /* Error */) {
1667
- reduceError = resultVal;
2452
+ const stepFault = faultedOperand(resultVal);
2453
+ if (stepFault) {
2454
+ reduceError = stepFault;
1668
2455
  break;
1669
2456
  }
1670
2457
  acc = resultVal;
@@ -1676,15 +2463,33 @@ function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTo
1676
2463
  stack.push(acc);
1677
2464
  break;
1678
2465
  }
2466
+ // ═══════════════════════════════════════════════════════════════
2467
+ // §12 Algebra verbs' bound unknown (OpCode 159)
2468
+ // ═══════════════════════════════════════════════════════════════
2469
+ case 159 /* BIND_UNKNOWN */: {
2470
+ const ref = operandByte(opcodes, ip++, op, "body reference");
2471
+ const def = anonymousBodies?.[ref];
2472
+ if (!def) {
2473
+ throw ErrorFactory.internal(
2474
+ "INTERNAL_MISSING_ANONYMOUS_BODY",
2475
+ `Internal error: BIND_UNKNOWN referenced missing anonymous body index ${ref}`,
2476
+ { ref }
2477
+ );
2478
+ }
2479
+ stack.push(invokeWithBoundUnknown(def.params[0], def.program, vm, pipeline, expression, context, !!symbolicTolerant));
2480
+ break;
2481
+ }
1679
2482
  }
1680
2483
  }
1681
2484
  const fallback = safePop(stack);
1682
2485
  return { type: "value", value: hasArena ? persistentValue(fallback) : fallback };
1683
2486
  } catch (e) {
1684
2487
  return { type: "error", error: normalizeUnknownError(e) };
2488
+ } finally {
2489
+ endEvaluation();
1685
2490
  }
1686
2491
  }
1687
2492
 
1688
2493
  export { DependencyGraph, ScopeManager, createVM, executeBytecode };
1689
- //# sourceMappingURL=chunk-BAMZT3GL.js.map
1690
- //# sourceMappingURL=chunk-BAMZT3GL.js.map
2494
+ //# sourceMappingURL=chunk-5WVP4YHP.js.map
2495
+ //# sourceMappingURL=chunk-5WVP4YHP.js.map