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.
- package/dist/{BytecodeBuilder-3egMyuDi.d.cts → BytecodeBuilder-B0xskcv5.d.cts} +2 -1
- package/dist/{BytecodeBuilder-3egMyuDi.d.ts → BytecodeBuilder-B0xskcv5.d.ts} +2 -1
- package/dist/{Configuration-fVpd-HC-.d.cts → Configuration-B-G5gTRn.d.cts} +107 -29
- package/dist/{Configuration-fVpd-HC-.d.ts → Configuration-B-G5gTRn.d.ts} +107 -29
- package/dist/{EngineError-DvPSnhZi.d.cts → EngineError-LU7W7AgI.d.cts} +96 -16
- package/dist/{EngineError-DvPSnhZi.d.ts → EngineError-LU7W7AgI.d.ts} +96 -16
- package/dist/{Lexer-W9MBOP0V.d.ts → Lexer-BN5mt30n.d.ts} +16 -0
- package/dist/{Lexer-Cfm79Dak.d.cts → Lexer-D78E2yDW.d.cts} +16 -0
- package/dist/{PackageRegistry-CjDt-Jy_.d.ts → PackageRegistry-DFeINv2O.d.ts} +54 -13
- package/dist/{PackageRegistry-pHtKythi.d.cts → PackageRegistry-u2zdfFaj.d.cts} +54 -13
- package/dist/{Parselet-DEdF9I7n.d.ts → Parselet-BaySkMV3.d.ts} +37 -5
- package/dist/{Parselet-Cu0bLSis.d.cts → Parselet-DuI1Pjiq.d.cts} +37 -5
- package/dist/{ScopeManager-CKfQtl7l.d.cts → ScopeManager-B6GzdhVG.d.cts} +75 -6
- package/dist/{ScopeManager-B5k0nk05.d.ts → ScopeManager-udv4Twwq.d.ts} +75 -6
- package/dist/{VMCheckpoints-BmDwIXgK.d.ts → VMCheckpoints-BiaIlOOY.d.ts} +4 -4
- package/dist/{VMCheckpoints-CqFuLwrT.d.cts → VMCheckpoints-DwLjivM7.d.cts} +4 -4
- package/dist/{Value-KtAQNHur.d.cts → Value-CXJqDH9J.d.cts} +5 -2
- package/dist/{Value-KtAQNHur.d.ts → Value-CXJqDH9J.d.ts} +5 -2
- package/dist/{chunk-CSG5XVNJ.cjs → chunk-3AFRJYP4.cjs} +556 -414
- package/dist/chunk-3AFRJYP4.cjs.map +1 -0
- package/dist/chunk-3BOK2LDK.js +17 -0
- package/dist/chunk-3BOK2LDK.js.map +1 -0
- package/dist/{chunk-6RDSQURE.js → chunk-3D7V24DG.js} +57 -6
- package/dist/chunk-3D7V24DG.js.map +1 -0
- package/dist/{chunk-MEOHSQEH.js → chunk-3LAEG75D.js} +47 -15
- package/dist/chunk-3LAEG75D.js.map +1 -0
- package/dist/{chunk-P75775WC.js → chunk-3VNGREMU.js} +118 -41
- package/dist/chunk-3VNGREMU.js.map +1 -0
- package/dist/{chunk-4AVD7NZW.cjs → chunk-4B2CNWQU.cjs} +13 -7
- package/dist/chunk-4B2CNWQU.cjs.map +1 -0
- package/dist/{chunk-6GCKCWLB.js → chunk-526PMQOA.js} +126 -69
- package/dist/chunk-526PMQOA.js.map +1 -0
- package/dist/{chunk-2CS6OMZK.js → chunk-5F4C26RU.js} +8 -5
- package/dist/chunk-5F4C26RU.js.map +1 -0
- package/dist/{chunk-M4N5GA5Z.cjs → chunk-5KMIY374.cjs} +57 -6
- package/dist/chunk-5KMIY374.cjs.map +1 -0
- package/dist/{chunk-R24DI24X.cjs → chunk-5LI5EPGJ.cjs} +6 -5
- package/dist/chunk-5LI5EPGJ.cjs.map +1 -0
- package/dist/{chunk-BAMZT3GL.js → chunk-5WVP4YHP.js} +939 -134
- package/dist/chunk-5WVP4YHP.js.map +1 -0
- package/dist/{chunk-MZOVP3WI.cjs → chunk-6KFYJ6TD.cjs} +3 -2
- package/dist/chunk-6KFYJ6TD.cjs.map +1 -0
- package/dist/{chunk-BXKJLM5R.cjs → chunk-6WFMPTGB.cjs} +972 -636
- package/dist/chunk-6WFMPTGB.cjs.map +1 -0
- package/dist/{chunk-2MV4HBKC.cjs → chunk-A2N2GFCG.cjs} +10 -10
- package/dist/{chunk-2MV4HBKC.cjs.map → chunk-A2N2GFCG.cjs.map} +1 -1
- package/dist/chunk-ADBA3XBE.cjs +20 -0
- package/dist/chunk-ADBA3XBE.cjs.map +1 -0
- package/dist/{chunk-MBNQVDVC.cjs → chunk-AMOJYLG2.cjs} +16 -10
- package/dist/chunk-AMOJYLG2.cjs.map +1 -0
- package/dist/{chunk-CLVQBF5C.cjs → chunk-AQNHHJMJ.cjs} +5 -5
- package/dist/{chunk-CLVQBF5C.cjs.map → chunk-AQNHHJMJ.cjs.map} +1 -1
- package/dist/{chunk-AHCLWAM5.cjs → chunk-BHTNFEGZ.cjs} +13 -13
- package/dist/chunk-BHTNFEGZ.cjs.map +1 -0
- package/dist/{chunk-35IWNZPJ.cjs → chunk-E4NUZXVK.cjs} +58 -27
- package/dist/chunk-E4NUZXVK.cjs.map +1 -0
- package/dist/{chunk-P33SBYGG.js → chunk-GLG5ZMX7.js} +54 -23
- package/dist/chunk-GLG5ZMX7.js.map +1 -0
- package/dist/{chunk-BVAR5J2F.js → chunk-GQCOSXMG.js} +117 -6
- package/dist/chunk-GQCOSXMG.js.map +1 -0
- package/dist/{chunk-QDMPOAI7.cjs → chunk-HDP7VK3C.cjs} +1118 -313
- package/dist/chunk-HDP7VK3C.cjs.map +1 -0
- package/dist/{chunk-GVWHCL2N.js → chunk-HMOISHXR.js} +217 -75
- package/dist/chunk-HMOISHXR.js.map +1 -0
- package/dist/{chunk-MTX2KVU7.cjs → chunk-HVQFNJKE.cjs} +122 -90
- package/dist/chunk-HVQFNJKE.cjs.map +1 -0
- package/dist/{chunk-OC6LTXYS.cjs → chunk-JMXUNXQS.cjs} +126 -7
- package/dist/chunk-JMXUNXQS.cjs.map +1 -0
- package/dist/{chunk-WWOLFXTX.cjs → chunk-KBSXGXPM.cjs} +10 -7
- package/dist/chunk-KBSXGXPM.cjs.map +1 -0
- package/dist/{chunk-YU2CUNFO.js → chunk-KV7UW6T6.js} +11 -5
- package/dist/chunk-KV7UW6T6.js.map +1 -0
- package/dist/{chunk-CCJPSHGI.cjs → chunk-N7FNVFXW.cjs} +4 -12
- package/dist/chunk-N7FNVFXW.cjs.map +1 -0
- package/dist/{chunk-CYFK5SY2.js → chunk-O3ANBHSA.js} +169 -39
- package/dist/chunk-O3ANBHSA.js.map +1 -0
- package/dist/{chunk-NZFKROS7.cjs → chunk-PA4VC73I.cjs} +130 -72
- package/dist/chunk-PA4VC73I.cjs.map +1 -0
- package/dist/{chunk-NJTXJ5AG.cjs → chunk-PFUESQTW.cjs} +18 -18
- package/dist/{chunk-NJTXJ5AG.cjs.map → chunk-PFUESQTW.cjs.map} +1 -1
- package/dist/{chunk-SFQWJMKT.js → chunk-QY25VWBF.js} +51 -12
- package/dist/chunk-QY25VWBF.js.map +1 -0
- package/dist/{chunk-WC5FFSHB.js → chunk-R3PY4G7J.js} +3 -3
- package/dist/{chunk-WC5FFSHB.js.map → chunk-R3PY4G7J.js.map} +1 -1
- package/dist/{chunk-M5E34VG5.js → chunk-SBUFNJVE.js} +3 -3
- package/dist/{chunk-M5E34VG5.js.map → chunk-SBUFNJVE.js.map} +1 -1
- package/dist/{chunk-UKPGSAZW.js → chunk-TMA4RCEN.js} +3 -3
- package/dist/{chunk-UKPGSAZW.js.map → chunk-TMA4RCEN.js.map} +1 -1
- package/dist/{chunk-V6HA6JJZ.cjs → chunk-U5VAY32K.cjs} +215 -138
- package/dist/chunk-U5VAY32K.cjs.map +1 -0
- package/dist/{chunk-DUEZFTK2.js → chunk-UKQHXPGW.js} +4 -12
- package/dist/chunk-UKQHXPGW.js.map +1 -0
- package/dist/{chunk-4CVLFLOB.cjs → chunk-UM6BVY2S.cjs} +201 -69
- package/dist/chunk-UM6BVY2S.cjs.map +1 -0
- package/dist/{chunk-75JP4BZX.js → chunk-UQ3UIZJC.js} +3 -2
- package/dist/chunk-UQ3UIZJC.js.map +1 -0
- package/dist/{chunk-PCPX42KL.js → chunk-V4XG5QF6.js} +16 -10
- package/dist/chunk-V4XG5QF6.js.map +1 -0
- package/dist/{chunk-GPPLSM2Z.js → chunk-VB37OC6I.js} +6 -5
- package/dist/chunk-VB37OC6I.js.map +1 -0
- package/dist/{chunk-MVBEL57Q.js → chunk-Y7FT4IQT.js} +500 -170
- package/dist/chunk-Y7FT4IQT.js.map +1 -0
- package/dist/{chunk-OFXOTECC.js → chunk-YPPPYLSR.js} +7 -7
- package/dist/chunk-YPPPYLSR.js.map +1 -0
- package/dist/{chunk-M4F66R4O.cjs → chunk-YWQ6V4ZN.cjs} +121 -82
- package/dist/chunk-YWQ6V4ZN.cjs.map +1 -0
- package/dist/constants.cjs +5 -5
- package/dist/constants.d.cts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +2 -2
- package/dist/engine.cjs +31 -31
- package/dist/engine.d.cts +11 -11
- package/dist/engine.d.ts +11 -11
- package/dist/engine.js +21 -21
- package/dist/errors.cjs +19 -19
- package/dist/errors.d.cts +2 -2
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +1 -1
- package/dist/format.cjs +29 -22
- package/dist/format.cjs.map +1 -1
- package/dist/format.d.cts +1 -2
- package/dist/format.d.ts +1 -2
- package/dist/format.js +23 -16
- package/dist/format.js.map +1 -1
- package/dist/index.cjs +31 -31
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +22 -22
- package/dist/language.cjs +8 -8
- package/dist/language.cjs.map +1 -1
- package/dist/language.d.cts +10 -10
- package/dist/language.d.ts +10 -10
- package/dist/language.js +6 -6
- package/dist/language.js.map +1 -1
- package/dist/lexer.cjs +18 -18
- package/dist/lexer.d.cts +2 -2
- package/dist/lexer.d.ts +2 -2
- package/dist/lexer.js +6 -6
- package/dist/normalizer.cjs +12 -12
- package/dist/normalizer.js +6 -6
- package/dist/packages.cjs +39 -39
- package/dist/packages.d.cts +9 -9
- package/dist/packages.d.ts +9 -9
- package/dist/packages.js +16 -16
- package/dist/parser.cjs +15 -15
- package/dist/parser.d.cts +23 -9
- package/dist/parser.d.ts +23 -9
- package/dist/parser.js +5 -5
- package/dist/{pipeline-CrsRPmsv.d.ts → pipeline-B6k5lCB7.d.ts} +1 -1
- package/dist/{pipeline-DPUQt7yL.d.cts → pipeline-BEb3hujr.d.cts} +1 -1
- package/dist/resolvers.d.cts +2 -2
- package/dist/resolvers.d.ts +2 -2
- package/dist/uom.cjs +17 -17
- package/dist/uom.d.cts +2 -2
- package/dist/uom.d.ts +2 -2
- package/dist/uom.js +7 -7
- package/dist/utilities.cjs +8 -4
- package/dist/utilities.d.cts +19 -3
- package/dist/utilities.d.ts +19 -3
- package/dist/utilities.js +2 -2
- package/dist/vm.cjs +43 -31
- package/dist/vm.d.cts +105 -9
- package/dist/vm.d.ts +105 -9
- package/dist/vm.js +9 -9
- package/package.json +1 -1
- package/dist/chunk-2CS6OMZK.js.map +0 -1
- package/dist/chunk-35IWNZPJ.cjs.map +0 -1
- package/dist/chunk-4AVD7NZW.cjs.map +0 -1
- package/dist/chunk-4CVLFLOB.cjs.map +0 -1
- package/dist/chunk-6GCKCWLB.js.map +0 -1
- package/dist/chunk-6RDSQURE.js.map +0 -1
- package/dist/chunk-75JP4BZX.js.map +0 -1
- package/dist/chunk-AHCLWAM5.cjs.map +0 -1
- package/dist/chunk-BAMZT3GL.js.map +0 -1
- package/dist/chunk-BVAR5J2F.js.map +0 -1
- package/dist/chunk-BXKJLM5R.cjs.map +0 -1
- package/dist/chunk-CCJPSHGI.cjs.map +0 -1
- package/dist/chunk-CSG5XVNJ.cjs.map +0 -1
- package/dist/chunk-CYFK5SY2.js.map +0 -1
- package/dist/chunk-DUEZFTK2.js.map +0 -1
- package/dist/chunk-GPPLSM2Z.js.map +0 -1
- package/dist/chunk-GVWHCL2N.js.map +0 -1
- package/dist/chunk-M4F66R4O.cjs.map +0 -1
- package/dist/chunk-M4N5GA5Z.cjs.map +0 -1
- package/dist/chunk-MBNQVDVC.cjs.map +0 -1
- package/dist/chunk-MEOHSQEH.js.map +0 -1
- package/dist/chunk-MTX2KVU7.cjs.map +0 -1
- package/dist/chunk-MVBEL57Q.js.map +0 -1
- package/dist/chunk-MZOVP3WI.cjs.map +0 -1
- package/dist/chunk-N5YFR4UP.cjs +0 -10
- package/dist/chunk-N5YFR4UP.cjs.map +0 -1
- package/dist/chunk-NZFKROS7.cjs.map +0 -1
- package/dist/chunk-OC6LTXYS.cjs.map +0 -1
- package/dist/chunk-OFXOTECC.js.map +0 -1
- package/dist/chunk-P33SBYGG.js.map +0 -1
- package/dist/chunk-P75775WC.js.map +0 -1
- package/dist/chunk-PCPX42KL.js.map +0 -1
- package/dist/chunk-PFSDGGYL.js +0 -8
- package/dist/chunk-PFSDGGYL.js.map +0 -1
- package/dist/chunk-QDMPOAI7.cjs.map +0 -1
- package/dist/chunk-R24DI24X.cjs.map +0 -1
- package/dist/chunk-SFQWJMKT.js.map +0 -1
- package/dist/chunk-V6HA6JJZ.cjs.map +0 -1
- package/dist/chunk-WWOLFXTX.cjs.map +0 -1
- 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,
|
|
3
|
-
import { sharedCurrencyExchange } from './chunk-
|
|
4
|
-
import { getOpCodeName } from './chunk-
|
|
5
|
-
import { collectionToValues, matAt, inBounds, matrixEntryToValue, matIndex, rowMajorToColumnMajor, varNode, matrixCompare, matrixMultiply, CURRENCY_DISPLAY } from './chunk-
|
|
6
|
-
import { numberValue, isArenaActive, errorValue, matrixValue, rangeValue, datetimeValue, isRateUnit, splitRateUnit, rateValue, uomValue, stringValue, hexValue, percentageValue, persistentValue, symbolicValue, boolValue, bigIntValue, isTimecodeUnit, timecodeFps } from './chunk-
|
|
7
|
-
import { getMeasure, convertUnit, getBestUnit, getConvertiblePossibilities, isWorkdayUnit } from './chunk-
|
|
8
|
-
import { UNIT_TABLE } from './chunk-
|
|
9
|
-
import { ErrorFactory, normalizeUnknownError } from './chunk-
|
|
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
|
|
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.
|
|
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
|
-
|
|
547
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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()
|
|
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() -
|
|
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) =>
|
|
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) =>
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
//
|
|
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 {
|
|
916
|
-
stack.push(boolValue(sameMeasure &&
|
|
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 {
|
|
930
|
-
stack.push(boolValue(!sameMeasure ||
|
|
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
|
|
1007
|
-
const argCount = opcodes
|
|
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
|
-
|
|
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
|
|
1028
|
-
const argCount = opcodes
|
|
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
|
|
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
|
|
1059
|
-
const argCount = opcodes
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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).
|
|
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)
|
|
1206
|
-
const
|
|
1207
|
-
|
|
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)
|
|
1212
|
-
const fromUnit = safePop(stack)
|
|
1213
|
-
const
|
|
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(
|
|
1963
|
+
stack.push(incompatibleConversionError(fromUnit, toUnit));
|
|
1227
1964
|
}
|
|
1228
1965
|
break;
|
|
1229
1966
|
}
|
|
1230
1967
|
case 85 /* UOM_POSSIBILITIES */: {
|
|
1231
|
-
const unit = safePop(stack)
|
|
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)
|
|
1238
|
-
const
|
|
1239
|
-
const
|
|
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)
|
|
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(
|
|
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)
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1375
|
-
const cols = opcodes
|
|
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 =
|
|
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
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
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
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
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
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
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
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
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 =
|
|
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
|
|
1527
|
-
const ref = opcodes
|
|
1528
|
-
const collectionCount = opcodes
|
|
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
|
|
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 =
|
|
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
|
-
|
|
1592
|
-
|
|
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
|
-
|
|
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
|
|
1606
|
-
const ref = opcodes
|
|
1607
|
-
const hasInitial = opcodes
|
|
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
|
|
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
|
-
|
|
1649
|
-
|
|
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
|
-
|
|
1667
|
-
|
|
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-
|
|
1690
|
-
//# sourceMappingURL=chunk-
|
|
2494
|
+
//# sourceMappingURL=chunk-5WVP4YHP.js.map
|
|
2495
|
+
//# sourceMappingURL=chunk-5WVP4YHP.js.map
|