solve-engine 1.2.0 → 2.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/README.md +12 -17
- package/dist/{BytecodeBuilder-Bp9xeTmX.d.cts → BytecodeBuilder-DGGeByAP.d.cts} +18 -0
- package/dist/{BytecodeBuilder-Bp9xeTmX.d.ts → BytecodeBuilder-DGGeByAP.d.ts} +18 -0
- package/dist/{Configuration-C9W8tJv_.d.cts → Configuration-B6W0-T67.d.cts} +17 -4
- package/dist/{Configuration-C9W8tJv_.d.ts → Configuration-B6W0-T67.d.ts} +17 -4
- package/dist/{EngineError-CNA_rHY-.d.cts → EngineError-Ce1lPkjZ.d.cts} +2 -0
- package/dist/{EngineError-CNA_rHY-.d.ts → EngineError-Ce1lPkjZ.d.ts} +2 -0
- package/dist/{PackageCompatibility-B6NX9IaN.d.cts → PackageCompatibility-Bn5jnBtA.d.cts} +2 -2
- package/dist/{PackageCompatibility-Ceg5XdAp.d.ts → PackageCompatibility-CIvq2MP5.d.ts} +2 -2
- package/dist/{PackageRegistry-DrCPQNyy.d.ts → PackageRegistry-DzeiWcv4.d.ts} +93 -172
- package/dist/{PackageRegistry-DnJYVw9G.d.cts → PackageRegistry-dpdurBVO.d.cts} +93 -172
- package/dist/{Parselet-Bhf7d0_W.d.ts → Parselet-B-zoPrTa.d.ts} +8 -5
- package/dist/{Parselet-Cv7S7KRt.d.cts → Parselet-C4Wu055_.d.cts} +8 -5
- package/dist/{ScopeManager-Bzq87GfA.d.ts → ScopeManager-C2LDOrDx.d.ts} +7 -16
- package/dist/{ScopeManager-DRDBVfan.d.cts → ScopeManager-D7uMXbhS.d.cts} +7 -16
- package/dist/{VMCheckpoints-C6c17Hbo.d.ts → VMCheckpoints-CGyFhivM.d.ts} +3 -3
- package/dist/{VMCheckpoints-dZxSMGHG.d.cts → VMCheckpoints-I9jg3jWR.d.cts} +3 -3
- package/dist/{Value-DZNQ_PU7.d.cts → Value-CCi_Eecs.d.cts} +40 -0
- package/dist/{Value-DZNQ_PU7.d.ts → Value-CCi_Eecs.d.ts} +40 -0
- package/dist/{WorkerError-Djd7GGGa.d.cts → WorkerError-B4p6PNfx.d.cts} +1 -1
- package/dist/{WorkerError-B7t7R2-m.d.ts → WorkerError-Dhj9BQ_A.d.ts} +1 -1
- package/dist/chunk-2JMFUQZH.js +2 -0
- package/dist/chunk-2JMFUQZH.js.map +1 -0
- package/dist/chunk-2RWAXT6O.js +0 -1
- package/dist/chunk-4BZIYU7S.cjs +3 -0
- package/dist/chunk-4BZIYU7S.cjs.map +1 -0
- package/dist/chunk-4J7XZSZJ.cjs +5 -0
- package/dist/chunk-4J7XZSZJ.cjs.map +1 -0
- package/dist/chunk-4QVBSG6B.cjs +2 -0
- package/dist/chunk-4QVBSG6B.cjs.map +1 -0
- package/dist/chunk-5IJD5L3O.cjs +2 -0
- package/dist/chunk-5IJD5L3O.cjs.map +1 -0
- package/dist/chunk-62LMTO4F.js +2 -0
- package/dist/chunk-62LMTO4F.js.map +1 -0
- package/dist/chunk-6CY44ZPG.js +2 -0
- package/dist/chunk-6CY44ZPG.js.map +1 -0
- package/dist/chunk-6G3PMEPA.js +2 -0
- package/dist/chunk-6G3PMEPA.js.map +1 -0
- package/dist/chunk-6INX773F.cjs +2 -0
- package/dist/chunk-6INX773F.cjs.map +1 -0
- package/dist/chunk-6KFVG6UY.cjs +2 -0
- package/dist/{chunk-YJHY7CMN.cjs.map → chunk-6KFVG6UY.cjs.map} +1 -1
- package/dist/chunk-76F3LCIL.js +4 -0
- package/dist/chunk-76F3LCIL.js.map +1 -0
- package/dist/chunk-7GK3XEZF.js +2 -0
- package/dist/chunk-7GK3XEZF.js.map +1 -0
- package/dist/chunk-APN442VD.cjs +2 -0
- package/dist/chunk-APN442VD.cjs.map +1 -0
- package/dist/chunk-AXMQG5TS.cjs +2 -0
- package/dist/chunk-AXMQG5TS.cjs.map +1 -0
- package/dist/chunk-CIQOK7Q4.cjs +2 -0
- package/dist/chunk-CIQOK7Q4.cjs.map +1 -0
- package/dist/chunk-E6Z4NQAS.js +2 -0
- package/dist/chunk-E6Z4NQAS.js.map +1 -0
- package/dist/chunk-EFJCC6FJ.cjs +2 -0
- package/dist/chunk-EFJCC6FJ.cjs.map +1 -0
- package/dist/chunk-ESUN2THE.cjs +2 -0
- package/dist/chunk-ESUN2THE.cjs.map +1 -0
- package/dist/chunk-EVAOPEO4.js +2 -0
- package/dist/chunk-EVAOPEO4.js.map +1 -0
- package/dist/chunk-EYRBENHC.js +3 -0
- package/dist/chunk-EYRBENHC.js.map +1 -0
- package/dist/chunk-FDE5HWDI.cjs +2 -0
- package/dist/chunk-FDE5HWDI.cjs.map +1 -0
- package/dist/chunk-FWW3M36J.cjs +2 -0
- package/dist/chunk-FWW3M36J.cjs.map +1 -0
- package/dist/chunk-GOIQYUWV.js +2 -0
- package/dist/chunk-GOIQYUWV.js.map +1 -0
- package/dist/chunk-HFKDWQUL.js +2 -0
- package/dist/chunk-HFKDWQUL.js.map +1 -0
- package/dist/chunk-HLBYHXQD.cjs +3 -0
- package/dist/chunk-HLBYHXQD.cjs.map +1 -0
- package/dist/chunk-IANAMBJQ.cjs +2 -0
- package/dist/chunk-IANAMBJQ.cjs.map +1 -0
- package/dist/chunk-IJMNVBIS.js +2 -0
- package/dist/chunk-IJMNVBIS.js.map +1 -0
- package/dist/chunk-J4LZD4ZY.js +3 -0
- package/dist/chunk-J4LZD4ZY.js.map +1 -0
- package/dist/chunk-JAK3RAFA.cjs +2 -0
- package/dist/chunk-JAK3RAFA.cjs.map +1 -0
- package/dist/chunk-JFAJN3C2.cjs +2 -0
- package/dist/chunk-JFAJN3C2.cjs.map +1 -0
- package/dist/chunk-JPFCXLJV.js +2 -0
- package/dist/chunk-JPFCXLJV.js.map +1 -0
- package/dist/chunk-JSPKKNPQ.js +3 -0
- package/dist/chunk-JSPKKNPQ.js.map +1 -0
- package/dist/chunk-L3FPQSGN.js +2 -0
- package/dist/chunk-L3FPQSGN.js.map +1 -0
- package/dist/chunk-LR4E4A4W.cjs +2 -0
- package/dist/chunk-LR4E4A4W.cjs.map +1 -0
- package/dist/chunk-MIQBJGGR.js +2 -0
- package/dist/chunk-MIQBJGGR.js.map +1 -0
- package/dist/chunk-MPB7KUN5.js +3 -0
- package/dist/chunk-MPB7KUN5.js.map +1 -0
- package/dist/chunk-MSYX32PF.cjs +2 -0
- package/dist/chunk-MSYX32PF.cjs.map +1 -0
- package/dist/chunk-MVZUTETT.js +2 -0
- package/dist/chunk-MVZUTETT.js.map +1 -0
- package/dist/chunk-N7N7MPMK.cjs +2 -0
- package/dist/chunk-N7N7MPMK.cjs.map +1 -0
- package/dist/chunk-NGBBNL4K.cjs +1 -3
- package/dist/chunk-P6WPEUAJ.cjs +2 -0
- package/dist/chunk-P6WPEUAJ.cjs.map +1 -0
- package/dist/chunk-PS272DZJ.cjs +2 -0
- package/dist/chunk-PS272DZJ.cjs.map +1 -0
- package/dist/chunk-QXQE7X4S.js +2 -0
- package/dist/chunk-QXQE7X4S.js.map +1 -0
- package/dist/chunk-R2ENWQ76.js +2 -0
- package/dist/chunk-R2ENWQ76.js.map +1 -0
- package/dist/chunk-R2SZFJTT.cjs +2 -0
- package/dist/chunk-R2SZFJTT.cjs.map +1 -0
- package/dist/chunk-RAF57WYO.js +2 -0
- package/dist/chunk-RAF57WYO.js.map +1 -0
- package/dist/chunk-RSF25YSJ.js +2 -0
- package/dist/chunk-RSF25YSJ.js.map +1 -0
- package/dist/chunk-SOMGSIO4.cjs +3 -0
- package/dist/chunk-SOMGSIO4.cjs.map +1 -0
- package/dist/chunk-SUXRZWH2.cjs +3 -0
- package/dist/chunk-SUXRZWH2.cjs.map +1 -0
- package/dist/chunk-SYYE3QAX.cjs +2 -0
- package/dist/chunk-SYYE3QAX.cjs.map +1 -0
- package/dist/chunk-SZGK6RIP.cjs +2 -0
- package/dist/chunk-SZGK6RIP.cjs.map +1 -0
- package/dist/chunk-TCZSPH4A.js +2 -0
- package/dist/chunk-TCZSPH4A.js.map +1 -0
- package/dist/chunk-TLG7VZRX.cjs +2 -0
- package/dist/chunk-TLG7VZRX.cjs.map +1 -0
- package/dist/chunk-TRBXW6HW.js +2 -0
- package/dist/{chunk-AQNHHJMJ.cjs.map → chunk-TRBXW6HW.js.map} +1 -1
- package/dist/chunk-TTFO4YDP.cjs +2 -0
- package/dist/chunk-TTFO4YDP.cjs.map +1 -0
- package/dist/chunk-TWC7K3ZX.js +2 -0
- package/dist/chunk-TWC7K3ZX.js.map +1 -0
- package/dist/chunk-UDQ57ZRX.cjs +2 -0
- package/dist/chunk-UDQ57ZRX.cjs.map +1 -0
- package/dist/chunk-UI6IFGBT.js +2 -0
- package/dist/chunk-UI6IFGBT.js.map +1 -0
- package/dist/chunk-UURM7P3P.cjs +4 -0
- package/dist/chunk-UURM7P3P.cjs.map +1 -0
- package/dist/chunk-W2JAR233.js +2 -0
- package/dist/chunk-W2JAR233.js.map +1 -0
- package/dist/chunk-WGZ3FNHI.js +2 -0
- package/dist/chunk-WGZ3FNHI.js.map +1 -0
- package/dist/chunk-WN5CMP4T.cjs +3 -0
- package/dist/chunk-WN5CMP4T.cjs.map +1 -0
- package/dist/chunk-XEIDV5JP.js +2 -0
- package/dist/chunk-XEIDV5JP.js.map +1 -0
- package/dist/chunk-XNIWQHXY.js +2 -0
- package/dist/chunk-XNIWQHXY.js.map +1 -0
- package/dist/chunk-XR7HEC7B.js +5 -0
- package/dist/chunk-XR7HEC7B.js.map +1 -0
- package/dist/chunk-Y7O4D3BJ.cjs +2 -0
- package/dist/chunk-Y7O4D3BJ.cjs.map +1 -0
- package/dist/chunk-YAYBVVG2.js +3 -0
- package/dist/chunk-YAYBVVG2.js.map +1 -0
- package/dist/chunk-YJ2OPULK.js +2 -0
- package/dist/chunk-YJ2OPULK.js.map +1 -0
- package/dist/chunk-YVKHEAFT.js +2 -0
- package/dist/chunk-YVKHEAFT.js.map +1 -0
- package/dist/chunk-ZCNBT44X.cjs +2 -0
- package/dist/chunk-ZCNBT44X.cjs.map +1 -0
- package/dist/chunk-ZLRJ7TDP.cjs +2 -0
- package/dist/chunk-ZLRJ7TDP.cjs.map +1 -0
- package/dist/constants.cjs +1 -21
- package/dist/constants.d.cts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -4
- package/dist/engine.cjs +1 -94
- package/dist/engine.d.cts +10 -11
- package/dist/engine.d.ts +10 -11
- package/dist/engine.js +1 -37
- package/dist/errors.cjs +1 -105
- package/dist/errors.d.cts +3 -3
- package/dist/errors.d.ts +3 -3
- package/dist/errors.js +1 -4
- package/dist/format.cjs +1 -26
- package/dist/format.d.cts +2 -2
- package/dist/format.d.ts +2 -2
- package/dist/format.js +1 -9
- package/dist/index.cjs +1 -314
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -20
- package/dist/index.d.ts +52 -20
- package/dist/index.js +1 -278
- package/dist/index.js.map +1 -1
- package/dist/language.cjs +1 -369
- package/dist/language.cjs.map +1 -1
- package/dist/language.d.cts +9 -10
- package/dist/language.d.ts +9 -10
- package/dist/language.js +1 -348
- package/dist/language.js.map +1 -1
- package/dist/lexer.cjs +1 -63
- package/dist/lexer.js +1 -10
- package/dist/normalizer.cjs +1 -39
- package/dist/normalizer.js +1 -10
- package/dist/packages.cjs +1 -132
- package/dist/packages.d.cts +8 -9
- package/dist/packages.d.ts +8 -9
- package/dist/packages.js +1 -23
- package/dist/parser.cjs +1 -54
- package/dist/parser.d.cts +8 -8
- package/dist/parser.d.ts +8 -8
- package/dist/parser.js +1 -9
- package/dist/{pipeline-D-PS9gVw.d.ts → pipeline-72QmQlGx.d.ts} +1 -1
- package/dist/{pipeline-DtSBJ8TS.d.cts → pipeline-DCQicwWX.d.cts} +1 -1
- package/dist/resolvers.cjs +1 -12
- package/dist/resolvers.d.cts +2 -2
- package/dist/resolvers.d.ts +2 -2
- package/dist/resolvers.js +1 -3
- package/dist/services.cjs +1 -20
- package/dist/services.js +1 -3
- package/dist/testing.cjs +3 -477
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +9 -10
- package/dist/testing.d.ts +9 -10
- package/dist/testing.js +3 -469
- package/dist/testing.js.map +1 -1
- package/dist/uom.cjs +1 -55
- package/dist/uom.d.cts +2 -2
- package/dist/uom.d.ts +2 -2
- package/dist/uom.js +1 -10
- package/dist/utilities.cjs +1 -36
- package/dist/utilities.js +1 -7
- package/dist/vm.cjs +1 -295
- package/dist/vm.cjs.map +1 -1
- package/dist/vm.d.cts +22 -10
- package/dist/vm.d.ts +22 -10
- package/dist/vm.js +1 -193
- package/dist/vm.js.map +1 -1
- package/dist/worker.cjs +2 -492
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.cts +16 -17
- package/dist/worker.d.ts +16 -17
- package/dist/worker.js +2 -483
- package/dist/worker.js.map +1 -1
- package/package.json +1 -13
- package/dist/chunk-3BOK2LDK.js +0 -17
- package/dist/chunk-3BOK2LDK.js.map +0 -1
- package/dist/chunk-3VGDNEE3.js +0 -84
- package/dist/chunk-3VGDNEE3.js.map +0 -1
- package/dist/chunk-4K5W7EH2.cjs +0 -112
- package/dist/chunk-4K5W7EH2.cjs.map +0 -1
- package/dist/chunk-5EFKSKMA.js +0 -413
- package/dist/chunk-5EFKSKMA.js.map +0 -1
- package/dist/chunk-5HRB36DK.js +0 -405
- package/dist/chunk-5HRB36DK.js.map +0 -1
- package/dist/chunk-5ON7PUAZ.js +0 -12
- package/dist/chunk-5ON7PUAZ.js.map +0 -1
- package/dist/chunk-7AXJNQMF.cjs +0 -297
- package/dist/chunk-7AXJNQMF.cjs.map +0 -1
- package/dist/chunk-7JM3F4KL.js +0 -1795
- package/dist/chunk-7JM3F4KL.js.map +0 -1
- package/dist/chunk-7USV76HV.cjs +0 -341
- package/dist/chunk-7USV76HV.cjs.map +0 -1
- package/dist/chunk-A2DZ5BEJ.cjs +0 -1930
- package/dist/chunk-A2DZ5BEJ.cjs.map +0 -1
- package/dist/chunk-ADBA3XBE.cjs +0 -20
- package/dist/chunk-ADBA3XBE.cjs.map +0 -1
- package/dist/chunk-AKNGX4TZ.cjs +0 -236
- package/dist/chunk-AKNGX4TZ.cjs.map +0 -1
- package/dist/chunk-ALYRJ72W.cjs +0 -434
- package/dist/chunk-ALYRJ72W.cjs.map +0 -1
- package/dist/chunk-AMOJYLG2.cjs +0 -1692
- package/dist/chunk-AMOJYLG2.cjs.map +0 -1
- package/dist/chunk-AOH6UR2L.js +0 -825
- package/dist/chunk-AOH6UR2L.js.map +0 -1
- package/dist/chunk-AQNHHJMJ.cjs +0 -261
- package/dist/chunk-B7LXJ6LC.cjs +0 -36
- package/dist/chunk-B7LXJ6LC.cjs.map +0 -1
- package/dist/chunk-BESTLPRK.cjs +0 -6919
- package/dist/chunk-BESTLPRK.cjs.map +0 -1
- package/dist/chunk-BLI4NIQY.js +0 -98
- package/dist/chunk-BLI4NIQY.js.map +0 -1
- package/dist/chunk-BSPXMGIS.js +0 -455
- package/dist/chunk-BSPXMGIS.js.map +0 -1
- package/dist/chunk-C2OMO3WN.cjs +0 -2806
- package/dist/chunk-C2OMO3WN.cjs.map +0 -1
- package/dist/chunk-CSDQMDU2.js +0 -204
- package/dist/chunk-CSDQMDU2.js.map +0 -1
- package/dist/chunk-EJ3ILXX6.cjs +0 -223
- package/dist/chunk-EJ3ILXX6.cjs.map +0 -1
- package/dist/chunk-ENRIK36Q.cjs +0 -94
- package/dist/chunk-ENRIK36Q.cjs.map +0 -1
- package/dist/chunk-F4UB4XIL.js +0 -109
- package/dist/chunk-F4UB4XIL.js.map +0 -1
- package/dist/chunk-FD5ZZHEU.js +0 -92
- package/dist/chunk-FD5ZZHEU.js.map +0 -1
- package/dist/chunk-FE4RC37D.js +0 -2801
- package/dist/chunk-FE4RC37D.js.map +0 -1
- package/dist/chunk-FEAIOR7N.cjs +0 -91
- package/dist/chunk-FEAIOR7N.cjs.map +0 -1
- package/dist/chunk-FR7RNOPT.cjs +0 -3303
- package/dist/chunk-FR7RNOPT.cjs.map +0 -1
- package/dist/chunk-G2YPPLGX.cjs +0 -484
- package/dist/chunk-G2YPPLGX.cjs.map +0 -1
- package/dist/chunk-GCM5JHMX.js +0 -88
- package/dist/chunk-GCM5JHMX.js.map +0 -1
- package/dist/chunk-GDEU72RG.cjs +0 -829
- package/dist/chunk-GDEU72RG.cjs.map +0 -1
- package/dist/chunk-HKTQCQ4Z.cjs +0 -8383
- package/dist/chunk-HKTQCQ4Z.cjs.map +0 -1
- package/dist/chunk-HWHU642X.cjs +0 -316
- package/dist/chunk-HWHU642X.cjs.map +0 -1
- package/dist/chunk-ICEKS6YC.cjs +0 -507
- package/dist/chunk-ICEKS6YC.cjs.map +0 -1
- package/dist/chunk-IJGOFP2J.js +0 -6904
- package/dist/chunk-IJGOFP2J.js.map +0 -1
- package/dist/chunk-IQJGDZOP.js +0 -333
- package/dist/chunk-IQJGDZOP.js.map +0 -1
- package/dist/chunk-IT74O4BE.cjs +0 -456
- package/dist/chunk-IT74O4BE.cjs.map +0 -1
- package/dist/chunk-KXDQKVNX.cjs +0 -459
- package/dist/chunk-KXDQKVNX.cjs.map +0 -1
- package/dist/chunk-L2TE7PMO.cjs +0 -14
- package/dist/chunk-L2TE7PMO.cjs.map +0 -1
- package/dist/chunk-L5YHV6BQ.js +0 -499
- package/dist/chunk-L5YHV6BQ.js.map +0 -1
- package/dist/chunk-LO7HU5WB.cjs +0 -209
- package/dist/chunk-LO7HU5WB.cjs.map +0 -1
- package/dist/chunk-LSALB3IN.js +0 -292
- package/dist/chunk-LSALB3IN.js.map +0 -1
- package/dist/chunk-MI7OTUHL.cjs +0 -339
- package/dist/chunk-MI7OTUHL.cjs.map +0 -1
- package/dist/chunk-MIGXCBXT.cjs +0 -381
- package/dist/chunk-MIGXCBXT.cjs.map +0 -1
- package/dist/chunk-MVTOCRV2.js +0 -213
- package/dist/chunk-MVTOCRV2.js.map +0 -1
- package/dist/chunk-N7FNVFXW.cjs +0 -32
- package/dist/chunk-N7FNVFXW.cjs.map +0 -1
- package/dist/chunk-N7OA4WPJ.js +0 -362
- package/dist/chunk-N7OA4WPJ.js.map +0 -1
- package/dist/chunk-NAWZATKT.js +0 -478
- package/dist/chunk-NAWZATKT.js.map +0 -1
- package/dist/chunk-NLNDMWZC.js +0 -31
- package/dist/chunk-NLNDMWZC.js.map +0 -1
- package/dist/chunk-ORM7CHYZ.js +0 -1531
- package/dist/chunk-ORM7CHYZ.js.map +0 -1
- package/dist/chunk-OUHT5Z36.cjs +0 -393
- package/dist/chunk-OUHT5Z36.cjs.map +0 -1
- package/dist/chunk-PCZVMMWU.cjs +0 -74
- package/dist/chunk-PCZVMMWU.cjs.map +0 -1
- package/dist/chunk-RNKDIXDV.js +0 -336
- package/dist/chunk-RNKDIXDV.js.map +0 -1
- package/dist/chunk-SBUFNJVE.js +0 -258
- package/dist/chunk-SBUFNJVE.js.map +0 -1
- package/dist/chunk-TC6BTUSU.cjs +0 -1799
- package/dist/chunk-TC6BTUSU.cjs.map +0 -1
- package/dist/chunk-TPOK3UNI.js +0 -8355
- package/dist/chunk-TPOK3UNI.js.map +0 -1
- package/dist/chunk-UF6JG5SG.js +0 -312
- package/dist/chunk-UF6JG5SG.js.map +0 -1
- package/dist/chunk-UKQHXPGW.js +0 -30
- package/dist/chunk-UKQHXPGW.js.map +0 -1
- package/dist/chunk-V4XG5QF6.js +0 -1685
- package/dist/chunk-V4XG5QF6.js.map +0 -1
- package/dist/chunk-VEKIWQ3F.cjs +0 -39
- package/dist/chunk-VEKIWQ3F.cjs.map +0 -1
- package/dist/chunk-WMQRJTAO.js +0 -3266
- package/dist/chunk-WMQRJTAO.js.map +0 -1
- package/dist/chunk-X6K3HUOY.cjs +0 -86
- package/dist/chunk-X6K3HUOY.cjs.map +0 -1
- package/dist/chunk-XBTEO4OB.js +0 -391
- package/dist/chunk-XBTEO4OB.js.map +0 -1
- package/dist/chunk-XIYYHA65.cjs +0 -101
- package/dist/chunk-XIYYHA65.cjs.map +0 -1
- package/dist/chunk-YJHY7CMN.cjs +0 -1534
- package/dist/chunk-YLJ4XMA6.js +0 -33
- package/dist/chunk-YLJ4XMA6.js.map +0 -1
- package/dist/chunk-YNIWOYW6.js +0 -232
- package/dist/chunk-YNIWOYW6.js.map +0 -1
- package/dist/chunk-Z2XP767T.cjs +0 -35
- package/dist/chunk-Z2XP767T.cjs.map +0 -1
- package/dist/chunk-Z4X2RWOF.js +0 -37
- package/dist/chunk-Z4X2RWOF.js.map +0 -1
- package/dist/chunk-ZN6YYPIW.js +0 -67
- package/dist/chunk-ZN6YYPIW.js.map +0 -1
- package/dist/chunk-ZNFFTYWH.js +0 -1854
- package/dist/chunk-ZNFFTYWH.js.map +0 -1
- package/dist/variables.cjs +0 -17
- package/dist/variables.cjs.map +0 -1
- package/dist/variables.d.cts +0 -87
- package/dist/variables.d.ts +0 -87
- package/dist/variables.js +0 -4
- package/dist/variables.js.map +0 -1
package/dist/chunk-FE4RC37D.js
DELETED
|
@@ -1,2801 +0,0 @@
|
|
|
1
|
-
import { DiagnosticEventType } from './chunk-UF6JG5SG.js';
|
|
2
|
-
import { defaultEngineContext, builtinFunctions, moneyExactMagnitude, asConverterRegistry, sharedGlobalVariableStore, SYMBOLIC_NATIVE_BUILTINS, symbolicBuiltin, compareRationalOperands, compareBigIntOperands, compareUom, incomparableUnitsError, toBigIntOperand, symbolicNeg, power, symbolicPow, binaryOp, bigIntDivisionByZero, uncertainOp, unifyUom, exactRationalOp, describeConversionMismatch, scaleMoneyExact, scaleMoneyByPercent } from './chunk-WMQRJTAO.js';
|
|
3
|
-
import { sharedCurrencyExchange } from './chunk-BSPXMGIS.js';
|
|
4
|
-
import { getOpCodeName } from './chunk-BLI4NIQY.js';
|
|
5
|
-
import { collectionToValues, matAt, inBounds, matrixEntryToValue, matIndex, rowMajorToColumnMajor, varNode, matrixCompare, decimalNegate, rationalNeg, matrixPower, matrixMultiply, decimalFromLiteral, decimalToNumber, CURRENCY_DISPLAY } from './chunk-ZNFFTYWH.js';
|
|
6
|
-
import { numberValue, ValueType, isArenaActive, beginEvaluation, errorValue, chargeAllocation, faultedOperand, checkedArray, matrixValue, rangeValue, datetimeValue, isRateUnit, splitRateUnit, rateValue, uomValue, stringValue, uomValueExact, hexValue, percentageValue, colourValue, persistentValue, symbolicValue, chargeFunctionCall, faultedIn, boolValue, bigIntValue, numberValueUncertain, numberValueRational, numberValueExact, checkAllocation, isTimecodeUnit, endEvaluation, timecodeFps } from './chunk-5EFKSKMA.js';
|
|
7
|
-
import { getMeasure, convertUnit, convertRate, getBestUnit, getConvertiblePossibilities, isWorkdayUnit } from './chunk-MVTOCRV2.js';
|
|
8
|
-
import { UNIT_TABLE } from './chunk-V4XG5QF6.js';
|
|
9
|
-
import { ErrorFactory, CoreErrorCodes, normalizeUnknownError } from './chunk-N7OA4WPJ.js';
|
|
10
|
-
|
|
11
|
-
// src/vm/DependencyGraph.ts
|
|
12
|
-
var DependencyGraph = class {
|
|
13
|
-
constructor() {
|
|
14
|
-
this.consumers = /* @__PURE__ */ new Map();
|
|
15
|
-
this.dependencies = /* @__PURE__ */ new Map();
|
|
16
|
-
this.writes = /* @__PURE__ */ new Map();
|
|
17
|
-
this.lineReads = /* @__PURE__ */ new Map();
|
|
18
|
-
// Data source dependencies: line number -> Set of query keys
|
|
19
|
-
this.dataSourceDependencies = /* @__PURE__ */ new Map();
|
|
20
|
-
// Reverse map: query key -> Set of line numbers
|
|
21
|
-
this.dataSourceConsumers = /* @__PURE__ */ new Map();
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Register a line's variable reads and writes in the dependency graph.
|
|
25
|
-
*
|
|
26
|
-
* If re-registering the same line (e.g., after editing), old consumer
|
|
27
|
-
* references are cleaned up first. Write-variables are removed from
|
|
28
|
-
* the consumer set so that redefinition breaks the old dependency chain.
|
|
29
|
-
*
|
|
30
|
-
* @param lineNumber - 1-based line number in the document
|
|
31
|
-
* @param reads - Variable names this line reads
|
|
32
|
-
* @param writes - Variable names this line writes (assigns to)
|
|
33
|
-
*/
|
|
34
|
-
registerLine(lineNumber, reads, writes) {
|
|
35
|
-
const oldReads = this.lineReads.get(lineNumber);
|
|
36
|
-
if (oldReads) {
|
|
37
|
-
for (const oldRead of oldReads) {
|
|
38
|
-
const consumers = this.consumers.get(oldRead);
|
|
39
|
-
if (consumers) consumers.delete(lineNumber);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
this.lineReads.set(lineNumber, new Set(reads));
|
|
43
|
-
for (const dep of reads) {
|
|
44
|
-
if (!this.consumers.has(dep)) this.consumers.set(dep, /* @__PURE__ */ new Set());
|
|
45
|
-
this.consumers.get(dep).add(lineNumber);
|
|
46
|
-
}
|
|
47
|
-
if (writes.length > 0) {
|
|
48
|
-
this.dependencies.set(lineNumber, new Set(reads));
|
|
49
|
-
this.writes.set(lineNumber, new Set(writes));
|
|
50
|
-
for (const write of writes) {
|
|
51
|
-
const prevConsumer = this.consumers.get(write);
|
|
52
|
-
if (prevConsumer) prevConsumer.delete(lineNumber);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Register a line's dependency on an external data source (e.g., currency rate, OSRS GE price).
|
|
58
|
-
*
|
|
59
|
-
* When the data source updates, {@link getAffectedLinesByDataSource} returns all lines
|
|
60
|
-
* that depend on this data, enabling targeted re-evaluation.
|
|
61
|
-
*
|
|
62
|
-
* @param lineNumber - 1-based line number in the document
|
|
63
|
-
* @param dataSourceId - Unique identifier for the data source (e.g., "currency", "osrs-ge")
|
|
64
|
-
* @param queryKey - Query key array identifying the specific data (e.g., ["USD", "EUR"])
|
|
65
|
-
*/
|
|
66
|
-
registerLineDataSourceDependency(lineNumber, dataSourceId, queryKey) {
|
|
67
|
-
const queryKeyStr = JSON.stringify(queryKey);
|
|
68
|
-
const key = `${dataSourceId}:${queryKeyStr}`;
|
|
69
|
-
if (!this.dataSourceDependencies.has(lineNumber)) {
|
|
70
|
-
this.dataSourceDependencies.set(lineNumber, /* @__PURE__ */ new Set());
|
|
71
|
-
}
|
|
72
|
-
this.dataSourceDependencies.get(lineNumber).add(key);
|
|
73
|
-
if (!this.dataSourceConsumers.has(key)) {
|
|
74
|
-
this.dataSourceConsumers.set(key, /* @__PURE__ */ new Set());
|
|
75
|
-
}
|
|
76
|
-
this.dataSourceConsumers.get(key).add(lineNumber);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Find all lines affected by a changed variable via BFS through the consumer graph.
|
|
80
|
-
*
|
|
81
|
-
* When a variable is modified (e.g., `:x = 5` changes to `:x = 10`), this returns
|
|
82
|
-
* all lines that transitively depend on it, lines that read `x`, lines that read
|
|
83
|
-
* variables written by those lines, and so on.
|
|
84
|
-
*
|
|
85
|
-
* @param changedVariable - The variable name that changed
|
|
86
|
-
* @returns Set of line numbers that need re-evaluation
|
|
87
|
-
*/
|
|
88
|
-
getAffectedLines(changedVariable) {
|
|
89
|
-
const visited = /* @__PURE__ */ new Set();
|
|
90
|
-
const queue = [changedVariable];
|
|
91
|
-
while (queue.length > 0) {
|
|
92
|
-
const varName = queue.pop();
|
|
93
|
-
const consumers = this.consumers.get(varName);
|
|
94
|
-
if (!consumers) continue;
|
|
95
|
-
for (const line of consumers) {
|
|
96
|
-
if (visited.has(line)) continue;
|
|
97
|
-
visited.add(line);
|
|
98
|
-
const lineWrites = this.writes.get(line);
|
|
99
|
-
if (lineWrites) {
|
|
100
|
-
for (const writtenVar of lineWrites) {
|
|
101
|
-
queue.push(writtenVar);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return visited;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Phase 1.4 DAG-walk optimization: return affected lines in dependency-safe
|
|
110
|
-
* topological order. Uses Kahn's algorithm (BFS-based) to ensure every line
|
|
111
|
-
* is evaluated AFTER all lines it depends on have been processed.
|
|
112
|
-
*
|
|
113
|
-
* This is more correct than ascending line-number sort, which fails when
|
|
114
|
-
* variable definitions and their consumers are not in document order.
|
|
115
|
-
*
|
|
116
|
-
* @returns Line numbers in topological order (producers before consumers).
|
|
117
|
-
*/
|
|
118
|
-
getAffectedLinesInOrder(startVariable) {
|
|
119
|
-
const affected = this.getAffectedLines(startVariable);
|
|
120
|
-
if (affected.size === 0) return [];
|
|
121
|
-
const inDegree = /* @__PURE__ */ new Map();
|
|
122
|
-
const adjacency = /* @__PURE__ */ new Map();
|
|
123
|
-
for (const line of affected) {
|
|
124
|
-
if (!inDegree.has(line)) inDegree.set(line, 0);
|
|
125
|
-
if (!adjacency.has(line)) adjacency.set(line, []);
|
|
126
|
-
}
|
|
127
|
-
const producerOf = /* @__PURE__ */ new Map();
|
|
128
|
-
for (const line of affected) {
|
|
129
|
-
const lineWrites = this.writes.get(line);
|
|
130
|
-
if (lineWrites) {
|
|
131
|
-
for (const w of lineWrites) producerOf.set(w, line);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
for (const line of affected) {
|
|
135
|
-
const reads = this.lineReads.get(line);
|
|
136
|
-
if (!reads) continue;
|
|
137
|
-
for (const readVar of reads) {
|
|
138
|
-
const producer = producerOf.get(readVar);
|
|
139
|
-
if (producer !== void 0 && producer !== line) {
|
|
140
|
-
adjacency.get(producer).push(line);
|
|
141
|
-
inDegree.set(line, (inDegree.get(line) ?? 0) + 1);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const queue = [];
|
|
146
|
-
for (const [line, degree] of inDegree) {
|
|
147
|
-
if (degree === 0) queue.push(line);
|
|
148
|
-
}
|
|
149
|
-
if (queue.length === 0 && affected.size > 0) {
|
|
150
|
-
const sorted = Array.from(affected).sort((a, b) => a - b);
|
|
151
|
-
queue.push(sorted[0]);
|
|
152
|
-
}
|
|
153
|
-
const ordered = [];
|
|
154
|
-
while (queue.length > 0) {
|
|
155
|
-
const current = queue.shift();
|
|
156
|
-
ordered.push(current);
|
|
157
|
-
for (const downstream of adjacency.get(current) ?? []) {
|
|
158
|
-
const newDegree = (inDegree.get(downstream) ?? 1) - 1;
|
|
159
|
-
inDegree.set(downstream, newDegree);
|
|
160
|
-
if (newDegree === 0) queue.push(downstream);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
if (ordered.length < affected.size) {
|
|
164
|
-
const orderedSet = new Set(ordered);
|
|
165
|
-
const remaining = Array.from(affected).filter((l) => !orderedSet.has(l)).sort((a, b) => a - b);
|
|
166
|
-
ordered.push(...remaining);
|
|
167
|
-
}
|
|
168
|
-
return ordered;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Find all lines affected by a data source update.
|
|
172
|
-
*
|
|
173
|
-
* When an async data source resolves (e.g., currency rate fetch completes),
|
|
174
|
-
* this returns all lines that depend on that specific data query.
|
|
175
|
-
*
|
|
176
|
-
* @param dataSourceId - The data source identifier
|
|
177
|
-
* @param queryKey - The query key that was updated
|
|
178
|
-
* @returns Set of line numbers that need re-evaluation
|
|
179
|
-
*/
|
|
180
|
-
getAffectedLinesByDataSource(dataSourceId, queryKey) {
|
|
181
|
-
const queryKeyStr = JSON.stringify(queryKey);
|
|
182
|
-
const key = `${dataSourceId}:${queryKeyStr}`;
|
|
183
|
-
return this.dataSourceConsumers.get(key) ?? /* @__PURE__ */ new Set();
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Remove a line from the dependency graph (e.g., when a line is deleted from the document).
|
|
187
|
-
*
|
|
188
|
-
* Cleans up all consumer references, write registrations, and data source dependencies
|
|
189
|
-
* for the removed line. O(k) where k is the number of variables the line reads.
|
|
190
|
-
*
|
|
191
|
-
* @param lineNumber - The line number being removed
|
|
192
|
-
*/
|
|
193
|
-
removeLine(lineNumber) {
|
|
194
|
-
const reads = this.lineReads.get(lineNumber);
|
|
195
|
-
if (reads) {
|
|
196
|
-
for (const readVar of reads) {
|
|
197
|
-
const consumers = this.consumers.get(readVar);
|
|
198
|
-
if (consumers) consumers.delete(lineNumber);
|
|
199
|
-
}
|
|
200
|
-
this.lineReads.delete(lineNumber);
|
|
201
|
-
}
|
|
202
|
-
this.dependencies.delete(lineNumber);
|
|
203
|
-
this.writes.delete(lineNumber);
|
|
204
|
-
const dataSourceKeys = this.dataSourceDependencies.get(lineNumber);
|
|
205
|
-
if (dataSourceKeys) {
|
|
206
|
-
for (const key of dataSourceKeys) {
|
|
207
|
-
this.dataSourceConsumers.get(key)?.delete(lineNumber);
|
|
208
|
-
}
|
|
209
|
-
this.dataSourceDependencies.delete(lineNumber);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Get all line numbers that consume (read) a given variable.
|
|
214
|
-
*
|
|
215
|
-
* @param variable - The variable name
|
|
216
|
-
* @returns Set of line numbers that read this variable, or empty set if none
|
|
217
|
-
*/
|
|
218
|
-
getConsumers(variable) {
|
|
219
|
-
return this.consumers.get(variable) ?? /* @__PURE__ */ new Set();
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Get all variables that a line depends on (reads).
|
|
223
|
-
*
|
|
224
|
-
* @param lineNumber - The line number to query
|
|
225
|
-
* @returns Set of variable names this line reads, or empty set if none
|
|
226
|
-
*/
|
|
227
|
-
getDependencies(lineNumber) {
|
|
228
|
-
return this.dependencies.get(lineNumber) ?? /* @__PURE__ */ new Set();
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Get all variables that a line writes (assigns to).
|
|
232
|
-
*
|
|
233
|
-
* @param lineNumber - The line number to query
|
|
234
|
-
* @returns Set of variable names this line writes, or empty set if none
|
|
235
|
-
*/
|
|
236
|
-
getWrites(lineNumber) {
|
|
237
|
-
return this.writes.get(lineNumber) ?? /* @__PURE__ */ new Set();
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Get a serializable snapshot of the entire dependency graph for diagnostics.
|
|
241
|
-
*
|
|
242
|
-
* Returns plain objects (not Maps/Sets) so consumers don't need to reach
|
|
243
|
-
* into private fields. Used by playground diagnostic tabs for DAG visualization.
|
|
244
|
-
*/
|
|
245
|
-
getSnapshot() {
|
|
246
|
-
const consumers = {};
|
|
247
|
-
for (const [variable, lines] of this.consumers) {
|
|
248
|
-
consumers[variable] = Array.from(lines);
|
|
249
|
-
}
|
|
250
|
-
const writes = {};
|
|
251
|
-
for (const [line, vars] of this.writes) {
|
|
252
|
-
writes[line] = Array.from(vars);
|
|
253
|
-
}
|
|
254
|
-
const reads = {};
|
|
255
|
-
for (const [line, vars] of this.lineReads) {
|
|
256
|
-
reads[line] = Array.from(vars);
|
|
257
|
-
}
|
|
258
|
-
const dataSourceDeps = {};
|
|
259
|
-
for (const [line, keys] of this.dataSourceDependencies) {
|
|
260
|
-
dataSourceDeps[line] = Array.from(keys);
|
|
261
|
-
}
|
|
262
|
-
const dataSourceConsumers = {};
|
|
263
|
-
for (const [key, lines] of this.dataSourceConsumers) {
|
|
264
|
-
dataSourceConsumers[key] = Array.from(lines);
|
|
265
|
-
}
|
|
266
|
-
return { consumers, writes, reads, dataSourceDeps, dataSourceConsumers };
|
|
267
|
-
}
|
|
268
|
-
/** Clear all dependency graph state. Called on document switch or engine reset. */
|
|
269
|
-
clear() {
|
|
270
|
-
this.consumers.clear();
|
|
271
|
-
this.dependencies.clear();
|
|
272
|
-
this.writes.clear();
|
|
273
|
-
this.lineReads.clear();
|
|
274
|
-
this.dataSourceDependencies.clear();
|
|
275
|
-
this.dataSourceConsumers.clear();
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
// src/vm/ScopeManager.ts
|
|
280
|
-
var ScopeManager = class {
|
|
281
|
-
constructor() {
|
|
282
|
-
this.definitions = /* @__PURE__ */ new Map();
|
|
283
|
-
}
|
|
284
|
-
/**
|
|
285
|
-
* Record a variable definition at `lineNumber`. A variable may have
|
|
286
|
-
* multiple definitions across different lines (e.g. redefined further
|
|
287
|
-
* down a document), each call adds one, keeping the internal stack
|
|
288
|
-
* sorted by line so {@link read} can binary-scan for "most recent
|
|
289
|
-
* definition at or before" a given line.
|
|
290
|
-
*/
|
|
291
|
-
write(variable, lineNumber, expr) {
|
|
292
|
-
if (!this.definitions.has(variable)) {
|
|
293
|
-
this.definitions.set(variable, []);
|
|
294
|
-
}
|
|
295
|
-
const stack = this.definitions.get(variable);
|
|
296
|
-
stack.push({ line: lineNumber, expr });
|
|
297
|
-
stack.sort((a, b) => a.line - b.line);
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Look up a variable's value as seen from `readLine`: returns the result
|
|
301
|
-
* of the closest definition at or before `readLine`, not simply the most
|
|
302
|
-
* recently-written one, so a read on line 5 of a variable redefined on
|
|
303
|
-
* lines 2 and 10 sees line 2's value, not line 10's. Returns `undefined`
|
|
304
|
-
* if the variable has no definition at or before `readLine`.
|
|
305
|
-
*/
|
|
306
|
-
read(variable, readLine) {
|
|
307
|
-
const stack = this.definitions.get(variable);
|
|
308
|
-
if (!stack) return void 0;
|
|
309
|
-
for (let i = stack.length - 1; i >= 0; i--) {
|
|
310
|
-
if (stack[i].line <= readLine) return stack[i].expr.lastResult;
|
|
311
|
-
}
|
|
312
|
-
return void 0;
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Drop every definition of `variable` that comes strictly after
|
|
316
|
-
* `definitionLine`. Call this when a line is re-evaluated with a new
|
|
317
|
-
* definition, so stale later-line definitions from a previous edit don't
|
|
318
|
-
* linger and get returned by {@link read}.
|
|
319
|
-
*/
|
|
320
|
-
invalidateDownstream(variable, definitionLine) {
|
|
321
|
-
const stack = this.definitions.get(variable);
|
|
322
|
-
if (!stack) return;
|
|
323
|
-
const idx = stack.findIndex((s) => s.line === definitionLine);
|
|
324
|
-
if (idx === -1) return;
|
|
325
|
-
stack.splice(idx + 1);
|
|
326
|
-
}
|
|
327
|
-
/** Remove every tracked variable definition, used when an engine/document is reset. */
|
|
328
|
-
clear() {
|
|
329
|
-
this.definitions.clear();
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
// src/vm/BusinessDays.ts
|
|
334
|
-
var MS_PER_DAY = 864e5;
|
|
335
|
-
function isWeekend(epochMs) {
|
|
336
|
-
const day = new Date(epochMs).getDay();
|
|
337
|
-
return day === 0 || day === 6;
|
|
338
|
-
}
|
|
339
|
-
function isBusinessDay(epochMs, isHoliday) {
|
|
340
|
-
if (isWeekend(epochMs)) return false;
|
|
341
|
-
return isHoliday ? !isHoliday(epochMs) : true;
|
|
342
|
-
}
|
|
343
|
-
function startOfLocalDay(epochMs) {
|
|
344
|
-
const d = new Date(epochMs);
|
|
345
|
-
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
346
|
-
}
|
|
347
|
-
function addBusinessDays(startMs, n, isHoliday, maxCalendarSteps) {
|
|
348
|
-
let remaining = Math.trunc(Math.abs(n));
|
|
349
|
-
const direction = n >= 0 ? 1 : -1;
|
|
350
|
-
const date = new Date(startMs);
|
|
351
|
-
let steps = 0;
|
|
352
|
-
while (remaining > 0) {
|
|
353
|
-
date.setDate(date.getDate() + direction);
|
|
354
|
-
steps++;
|
|
355
|
-
if (steps > maxCalendarSteps) return null;
|
|
356
|
-
if (isBusinessDay(date.getTime(), isHoliday)) remaining--;
|
|
357
|
-
}
|
|
358
|
-
return date.getTime();
|
|
359
|
-
}
|
|
360
|
-
function countBusinessDaysBetween(aMs, bMs, isHoliday, maxCalendarSteps) {
|
|
361
|
-
const startDay = startOfLocalDay(Math.min(aMs, bMs));
|
|
362
|
-
const endDay = startOfLocalDay(Math.max(aMs, bMs));
|
|
363
|
-
const spanDays = Math.round((endDay - startDay) / MS_PER_DAY);
|
|
364
|
-
if (spanDays > maxCalendarSteps) return null;
|
|
365
|
-
let count = 0;
|
|
366
|
-
const date = new Date(startDay);
|
|
367
|
-
for (let i = 0; i <= spanDays; i++) {
|
|
368
|
-
if (isBusinessDay(date.getTime(), isHoliday)) count++;
|
|
369
|
-
date.setDate(date.getDate() + 1);
|
|
370
|
-
}
|
|
371
|
-
return count;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// src/vm/VMBuiltinArity.ts
|
|
375
|
-
var BUILTIN_ARITY = {
|
|
376
|
-
0: { name: "sqrt", min: 1, max: 1 },
|
|
377
|
-
1: { name: "abs", min: 1, max: 1 },
|
|
378
|
-
2: { name: "sin", min: 1, max: 1 },
|
|
379
|
-
3: { name: "cos", min: 1, max: 1 },
|
|
380
|
-
4: { name: "tan", min: 1, max: 1 },
|
|
381
|
-
5: { name: "log", min: 1, max: 1 },
|
|
382
|
-
6: { name: "ceil", min: 1, max: 1 },
|
|
383
|
-
7: { name: "floor", min: 1, max: 1 },
|
|
384
|
-
8: { name: "round", min: 1, max: 2 },
|
|
385
|
-
// min/max compare as many operands as they are given.
|
|
386
|
-
9: { name: "min", min: 1, max: Infinity },
|
|
387
|
-
10: { name: "max", min: 1, max: Infinity },
|
|
388
|
-
11: { name: "asin", min: 1, max: 1 },
|
|
389
|
-
12: { name: "acos", min: 1, max: 1 },
|
|
390
|
-
13: { name: "atan", min: 1, max: 1 },
|
|
391
|
-
14: { name: "atan2", min: 2, max: 2 },
|
|
392
|
-
15: { name: "sinh", min: 1, max: 1 },
|
|
393
|
-
16: { name: "cosh", min: 1, max: 1 },
|
|
394
|
-
17: { name: "tanh", min: 1, max: 1 },
|
|
395
|
-
18: { name: "asinh", min: 1, max: 1 },
|
|
396
|
-
19: { name: "acosh", min: 1, max: 1 },
|
|
397
|
-
20: { name: "atanh", min: 1, max: 1 },
|
|
398
|
-
21: { name: "cbrt", min: 1, max: 1 },
|
|
399
|
-
22: { name: "clz32", min: 1, max: 1 },
|
|
400
|
-
23: { name: "expm1", min: 1, max: 1 },
|
|
401
|
-
24: { name: "exp", min: 1, max: 1 },
|
|
402
|
-
25: { name: "fround", min: 1, max: 1 },
|
|
403
|
-
// hypot is the length of a vector with as many components as it is given.
|
|
404
|
-
26: { name: "hypot", min: 1, max: Infinity },
|
|
405
|
-
27: { name: "imul", min: 2, max: 2 },
|
|
406
|
-
28: { name: "log10", min: 1, max: 1 },
|
|
407
|
-
29: { name: "log1p", min: 1, max: 1 },
|
|
408
|
-
30: { name: "log2", min: 1, max: 1 },
|
|
409
|
-
31: { name: "pow", min: 2, max: 2 },
|
|
410
|
-
32: { name: "random", min: 0, max: 0 },
|
|
411
|
-
33: { name: "sign", min: 1, max: 1 },
|
|
412
|
-
34: { name: "trunc", min: 1, max: 1 },
|
|
413
|
-
35: { name: "degtorad", min: 1, max: 1 },
|
|
414
|
-
36: { name: "radtodeg", min: 1, max: 1 },
|
|
415
|
-
37: { name: "roll", min: 2, max: 2 },
|
|
416
|
-
38: { name: "gcd", min: 2, max: 2 },
|
|
417
|
-
39: { name: "lcm", min: 2, max: 2 },
|
|
418
|
-
40: { name: "permutation", min: 2, max: 2 },
|
|
419
|
-
41: { name: "combination", min: 2, max: 2 },
|
|
420
|
-
// The aggregates back "average of X, Y, Z" and friends, so they take a
|
|
421
|
-
// list. Their implementations already answer for an empty list rather
|
|
422
|
-
// than indexing into it, and the phrase parselets can emit an empty one,
|
|
423
|
-
// so the minimum stays 0 here instead of being tightened on the way past.
|
|
424
|
-
42: { name: "average", min: 0, max: Infinity },
|
|
425
|
-
43: { name: "median", min: 0, max: Infinity },
|
|
426
|
-
44: { name: "total", min: 0, max: Infinity },
|
|
427
|
-
45: { name: "count", min: 0, max: Infinity },
|
|
428
|
-
46: { name: "proportion", min: 3, max: 3 },
|
|
429
|
-
47: { name: "clamp", min: 3, max: 3 },
|
|
430
|
-
48: { name: "hex", min: 1, max: 1 },
|
|
431
|
-
49: { name: "bin", min: 1, max: 1 },
|
|
432
|
-
50: { name: "int", min: 1, max: 1 },
|
|
433
|
-
51: { name: "compoundInterest", min: 3, max: 3 },
|
|
434
|
-
52: { name: "interestEarned", min: 3, max: 3 },
|
|
435
|
-
53: { name: "compoundInterestRate", min: 3, max: 3 },
|
|
436
|
-
54: { name: "compoundInterestYears", min: 3, max: 3 },
|
|
437
|
-
55: { name: "loanRepayment", min: 4, max: 4 },
|
|
438
|
-
56: { name: "loanInterest", min: 4, max: 4 },
|
|
439
|
-
57: { name: "monthlyPayment", min: 3, max: 3 },
|
|
440
|
-
58: { name: "taxAdd", min: 2, max: 2 },
|
|
441
|
-
59: { name: "taxRemove", min: 2, max: 2 },
|
|
442
|
-
60: { name: "inflationAdjust", min: 3, max: 3 },
|
|
443
|
-
61: { name: "root", min: 2, max: 2 },
|
|
444
|
-
62: { name: "fact", min: 1, max: 1 },
|
|
445
|
-
63: { name: "transpose", min: 1, max: 1 },
|
|
446
|
-
64: { name: "det", min: 1, max: 1 },
|
|
447
|
-
65: { name: "inv", min: 1, max: 1 },
|
|
448
|
-
66: { name: "dot", min: 2, max: 2 },
|
|
449
|
-
67: { name: "expand", min: 1, max: 1 },
|
|
450
|
-
68: { name: "factor", min: 1, max: 1 },
|
|
451
|
-
// solve pushes the two sides of the equation and then the unknown's name.
|
|
452
|
-
69: { name: "solve", min: 3, max: 3 },
|
|
453
|
-
// der's order of differentiation is optional and defaults to 1.
|
|
454
|
-
70: { name: "der", min: 2, max: 3 },
|
|
455
|
-
71: { name: "integral", min: 2, max: 2 },
|
|
456
|
-
72: { name: "taylor", min: 4, max: 4 },
|
|
457
|
-
// jacobian differentiates however many functions it is handed.
|
|
458
|
-
73: { name: "jacobian", min: 1, max: Infinity },
|
|
459
|
-
74: { name: "imaginary", min: 1, max: 1 },
|
|
460
|
-
75: { name: "conj", min: 1, max: 1 },
|
|
461
|
-
76: { name: "re", min: 1, max: 1 },
|
|
462
|
-
77: { name: "im", min: 1, max: 1 },
|
|
463
|
-
78: { name: "cancel", min: 1, max: 1 },
|
|
464
|
-
79: { name: "apart", min: 1, max: 1 },
|
|
465
|
-
80: { name: "compoundInterestEvery", min: 4, max: 4 },
|
|
466
|
-
81: { name: "compoundInterestEarnedEvery", min: 4, max: 4 },
|
|
467
|
-
82: { name: "presentValue", min: 3, max: 3 },
|
|
468
|
-
83: { name: "roi", min: 2, max: 2 },
|
|
469
|
-
84: { name: "annualisedReturn", min: 3, max: 3 },
|
|
470
|
-
85: { name: "taxIn", min: 2, max: 2 },
|
|
471
|
-
86: { name: "taxOn", min: 2, max: 2 },
|
|
472
|
-
87: { name: "sind", min: 1, max: 1 },
|
|
473
|
-
88: { name: "cosd", min: 1, max: 1 },
|
|
474
|
-
89: { name: "tand", min: 1, max: 1 },
|
|
475
|
-
90: { name: "asind", min: 1, max: 1 },
|
|
476
|
-
91: { name: "acosd", min: 1, max: 1 },
|
|
477
|
-
92: { name: "atand", min: 1, max: 1 },
|
|
478
|
-
93: { name: "daysCount", min: 1, max: 1 },
|
|
479
|
-
94: { name: "asTwoUnits", min: 3, max: 3 },
|
|
480
|
-
95: { name: "makeRate", min: 2, max: 2 },
|
|
481
|
-
96: { name: "atRate", min: 2, max: 2 },
|
|
482
|
-
// Not reachable by name, only through `<value> to <n> dp`; see
|
|
483
|
-
// converters/parselets/RoundingParselets.ts.
|
|
484
|
-
97: { name: "roundToPlaces", min: 2, max: 2 },
|
|
485
|
-
98: { name: "splitEach", min: 2, max: 2 },
|
|
486
|
-
99: { name: "savingsGoalPayment", min: 3, max: 3 },
|
|
487
|
-
100: { name: "savingsGoalPeriods", min: 5, max: 5 }
|
|
488
|
-
};
|
|
489
|
-
function plural(count) {
|
|
490
|
-
return count === 1 ? "1 argument" : `${count} arguments`;
|
|
491
|
-
}
|
|
492
|
-
function expectation(arity) {
|
|
493
|
-
if (arity.max === Infinity) return `at least ${plural(arity.min)}`;
|
|
494
|
-
if (arity.min === arity.max) return plural(arity.min);
|
|
495
|
-
return `${arity.min} to ${plural(arity.max)}`;
|
|
496
|
-
}
|
|
497
|
-
function builtinArityError(index, argCount) {
|
|
498
|
-
const arity = BUILTIN_ARITY[index];
|
|
499
|
-
if (arity === void 0) return void 0;
|
|
500
|
-
if (argCount >= arity.min && argCount <= arity.max) return void 0;
|
|
501
|
-
return ErrorFactory.execution(
|
|
502
|
-
"BUILTIN_ARITY_MISMATCH",
|
|
503
|
-
`${arity.name}() takes ${expectation(arity)}, but was given ${argCount === 0 ? "none" : plural(argCount)}`,
|
|
504
|
-
{ functionName: arity.name, expectedMin: arity.min, expectedMax: arity.max, actual: argCount }
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
// src/vm/VM.ts
|
|
509
|
-
function createVM(registry, maxStackDepth = 200, maxInstructions = 5e4, maxFunctionRecursionDepth = 50, maxCollectionSize = 1e5, maxAllocatedElements = 2e6, maxFunctionCalls = 1e4, maxDateOffsetYears = 100, minDateOffsetYears = -100, context = defaultEngineContext, holidayPredicate) {
|
|
510
|
-
const stack = [];
|
|
511
|
-
const variables = /* @__PURE__ */ new Map();
|
|
512
|
-
let instructionCount = 0;
|
|
513
|
-
let activeSignal;
|
|
514
|
-
let abortCurrent;
|
|
515
|
-
const callFrames = [];
|
|
516
|
-
const userFunctions = /* @__PURE__ */ new Map();
|
|
517
|
-
const equations = /* @__PURE__ */ new Map();
|
|
518
|
-
const scalarEquations = /* @__PURE__ */ new Map();
|
|
519
|
-
return {
|
|
520
|
-
push(v) {
|
|
521
|
-
if (stack.length < maxStackDepth) {
|
|
522
|
-
stack.push(v);
|
|
523
|
-
}
|
|
524
|
-
},
|
|
525
|
-
getMaxStackDepth() {
|
|
526
|
-
return maxStackDepth;
|
|
527
|
-
},
|
|
528
|
-
getMaxCollectionSize() {
|
|
529
|
-
return maxCollectionSize;
|
|
530
|
-
},
|
|
531
|
-
getMaxAllocatedElements() {
|
|
532
|
-
return maxAllocatedElements;
|
|
533
|
-
},
|
|
534
|
-
getMaxFunctionCalls() {
|
|
535
|
-
return maxFunctionCalls;
|
|
536
|
-
},
|
|
537
|
-
getMaxDateOffsetYears() {
|
|
538
|
-
return maxDateOffsetYears;
|
|
539
|
-
},
|
|
540
|
-
getMinDateOffsetYears() {
|
|
541
|
-
return minDateOffsetYears;
|
|
542
|
-
},
|
|
543
|
-
// False whenever no calendar was configured, which is what makes
|
|
544
|
-
// unconfigured working-day arithmetic weekends-only. See
|
|
545
|
-
// vm/BusinessDays.ts, the only reader.
|
|
546
|
-
isHoliday(epochMs) {
|
|
547
|
-
return holidayPredicate ? holidayPredicate(epochMs) : false;
|
|
548
|
-
},
|
|
549
|
-
pop() {
|
|
550
|
-
if (stack.length === 0) {
|
|
551
|
-
return numberValue(0);
|
|
552
|
-
}
|
|
553
|
-
return stack.pop();
|
|
554
|
-
},
|
|
555
|
-
/**
|
|
556
|
-
* Popped through `safePop()` for the same reason as `popString()` below.
|
|
557
|
-
* Only the underflow half applies here: `toNumber()` is a real method on
|
|
558
|
-
* every Value, so there was no cast to make a non-number look like one,
|
|
559
|
-
* and the `!` was the whole bug.
|
|
560
|
-
*/
|
|
561
|
-
popNumber() {
|
|
562
|
-
return safePop(stack).toNumber();
|
|
563
|
-
},
|
|
564
|
-
/**
|
|
565
|
-
* Popped through the same two guards the dispatch loop below uses, and
|
|
566
|
-
* for the same reason: a package's opcode handler can disagree with its
|
|
567
|
-
* own bytecode exactly as this engine's compiler can disagree with
|
|
568
|
-
* hand-assembled input.
|
|
569
|
-
*
|
|
570
|
-
* This was `stack.pop()!.value as string`, which had neither guard. The
|
|
571
|
-
* `!` is compile-time only, so an empty stack reached `.value` and threw
|
|
572
|
-
* a raw TypeError; so is the cast, so any Value the stack happened to be
|
|
573
|
-
* holding was handed back as a "string" and threw in the handler's own
|
|
574
|
-
* code some lines later. Nothing in this repo calls this method, which is
|
|
575
|
-
* the only reason the fuzz corpus never found either.
|
|
576
|
-
*/
|
|
577
|
-
popString() {
|
|
578
|
-
return stringOperand(safePop(stack), "VM.popString()");
|
|
579
|
-
},
|
|
580
|
-
peek() {
|
|
581
|
-
return stack[stack.length - 1];
|
|
582
|
-
},
|
|
583
|
-
getStack() {
|
|
584
|
-
return stack;
|
|
585
|
-
},
|
|
586
|
-
registry,
|
|
587
|
-
getVar(key) {
|
|
588
|
-
if (callFrames.length > 0) {
|
|
589
|
-
const frameValue = callFrames[callFrames.length - 1].get(key);
|
|
590
|
-
if (frameValue !== void 0) return frameValue;
|
|
591
|
-
}
|
|
592
|
-
return variables.get(key);
|
|
593
|
-
},
|
|
594
|
-
setVar(key, val) {
|
|
595
|
-
variables.set(key, val);
|
|
596
|
-
},
|
|
597
|
-
deleteVar(key) {
|
|
598
|
-
variables.delete(key);
|
|
599
|
-
},
|
|
600
|
-
pushCallFrame(frame) {
|
|
601
|
-
if (callFrames.length >= maxFunctionRecursionDepth) {
|
|
602
|
-
throw ErrorFactory.execution(
|
|
603
|
-
"FUNCTION_RECURSION_LIMIT_EXCEEDED",
|
|
604
|
-
`Function call nesting exceeded maximum depth of ${maxFunctionRecursionDepth} (possible infinite recursion, e.g. f(x) = f(x))`,
|
|
605
|
-
{ maxFunctionRecursionDepth }
|
|
606
|
-
);
|
|
607
|
-
}
|
|
608
|
-
callFrames.push(frame);
|
|
609
|
-
},
|
|
610
|
-
popCallFrame() {
|
|
611
|
-
callFrames.pop();
|
|
612
|
-
},
|
|
613
|
-
getCallFrame() {
|
|
614
|
-
return callFrames.length > 0 ? callFrames[callFrames.length - 1] : void 0;
|
|
615
|
-
},
|
|
616
|
-
defineUserFunction(name, params, program) {
|
|
617
|
-
userFunctions.set(name, { name, params, program });
|
|
618
|
-
},
|
|
619
|
-
getUserFunction(name) {
|
|
620
|
-
return userFunctions.get(name);
|
|
621
|
-
},
|
|
622
|
-
hasUserFunction(name) {
|
|
623
|
-
return userFunctions.has(name);
|
|
624
|
-
},
|
|
625
|
-
getVariableEntries() {
|
|
626
|
-
return Array.from(variables.entries());
|
|
627
|
-
},
|
|
628
|
-
getUserFunctionDefs() {
|
|
629
|
-
return Array.from(userFunctions.values());
|
|
630
|
-
},
|
|
631
|
-
defineEquation(variable, factorNames, rhsProgram) {
|
|
632
|
-
equations.set(variable, { variable, factorNames, rhsProgram });
|
|
633
|
-
},
|
|
634
|
-
getEquation(variable) {
|
|
635
|
-
return equations.get(variable);
|
|
636
|
-
},
|
|
637
|
-
hasEquation(variable) {
|
|
638
|
-
return equations.has(variable);
|
|
639
|
-
},
|
|
640
|
-
defineScalarEquation(variable, lhsProgram, rhsProgram) {
|
|
641
|
-
scalarEquations.set(variable, { variable, lhsProgram, rhsProgram });
|
|
642
|
-
},
|
|
643
|
-
getScalarEquation(variable) {
|
|
644
|
-
return scalarEquations.get(variable);
|
|
645
|
-
},
|
|
646
|
-
hasScalarEquation(variable) {
|
|
647
|
-
return scalarEquations.has(variable);
|
|
648
|
-
},
|
|
649
|
-
reset() {
|
|
650
|
-
stack.length = 0;
|
|
651
|
-
variables.clear();
|
|
652
|
-
callFrames.length = 0;
|
|
653
|
-
userFunctions.clear();
|
|
654
|
-
equations.clear();
|
|
655
|
-
scalarEquations.clear();
|
|
656
|
-
instructionCount = 0;
|
|
657
|
-
if (abortCurrent) {
|
|
658
|
-
abortCurrent();
|
|
659
|
-
abortCurrent = void 0;
|
|
660
|
-
}
|
|
661
|
-
activeSignal = void 0;
|
|
662
|
-
},
|
|
663
|
-
get activeSignal() {
|
|
664
|
-
return activeSignal;
|
|
665
|
-
},
|
|
666
|
-
set activeSignal(s) {
|
|
667
|
-
activeSignal = s;
|
|
668
|
-
},
|
|
669
|
-
get abortCurrent() {
|
|
670
|
-
return abortCurrent;
|
|
671
|
-
},
|
|
672
|
-
set abortCurrent(f) {
|
|
673
|
-
abortCurrent = f;
|
|
674
|
-
},
|
|
675
|
-
context,
|
|
676
|
-
getMaxInstructions() {
|
|
677
|
-
return maxInstructions;
|
|
678
|
-
},
|
|
679
|
-
getInstructionCount() {
|
|
680
|
-
return instructionCount;
|
|
681
|
-
},
|
|
682
|
-
incrementInstructions(n) {
|
|
683
|
-
instructionCount += n;
|
|
684
|
-
if (instructionCount > maxInstructions) {
|
|
685
|
-
throw ErrorFactory.execution("INSTRUCTION_LIMIT_EXCEEDED", `Execution exceeded maximum of ${maxInstructions} instructions`);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
function matrixProductCells(l, r) {
|
|
691
|
-
if (l.rows === 1 && l.cols === 1) return r.data.length;
|
|
692
|
-
if (r.rows === 1 && r.cols === 1) return l.data.length;
|
|
693
|
-
if (l.cols !== r.rows) return 0;
|
|
694
|
-
return l.rows * r.cols;
|
|
695
|
-
}
|
|
696
|
-
function matrixPowerCells(m, exponent) {
|
|
697
|
-
if (!Number.isInteger(exponent) || exponent < 0) return 0;
|
|
698
|
-
const cells = m.rows * m.cols;
|
|
699
|
-
return cells * (1 + (exponent < 2 ? 1 : 2 * Math.floor(Math.log2(exponent))));
|
|
700
|
-
}
|
|
701
|
-
function safePop(stack) {
|
|
702
|
-
if (stack.length === 0) {
|
|
703
|
-
throw ErrorFactory.internal({
|
|
704
|
-
code: "STACK_UNDERFLOW",
|
|
705
|
-
message: "Stack underflow: an opcode expected a value on the stack but it was empty",
|
|
706
|
-
expected: "at least one value on the VM stack",
|
|
707
|
-
found: "an empty stack",
|
|
708
|
-
suggestion: "this indicates corrupted bytecode or a bug in a package's parselet (mismatched push/pop counts), not a user-input error"
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
return stack.pop();
|
|
712
|
-
}
|
|
713
|
-
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";
|
|
714
|
-
function siteName(site) {
|
|
715
|
-
return typeof site === "number" ? getOpCodeName(site) : site;
|
|
716
|
-
}
|
|
717
|
-
function malformedBytecode(code, site, problem, expected, found, context = {}) {
|
|
718
|
-
const name = siteName(site);
|
|
719
|
-
return ErrorFactory.validation({
|
|
720
|
-
code,
|
|
721
|
-
message: `Malformed bytecode: ${name} ${problem}`,
|
|
722
|
-
expected,
|
|
723
|
-
found,
|
|
724
|
-
suggestion: MALFORMED_BYTECODE_SUGGESTION,
|
|
725
|
-
// A named site reports under `site` rather than borrowing `opcodeName`,
|
|
726
|
-
// so a host reading `opcode`/`opcodeName` never gets a method name
|
|
727
|
-
// where it expects an opcode, and never has to tell a missing opcode
|
|
728
|
-
// apart from opcode 0.
|
|
729
|
-
context: typeof site === "number" ? { opcode: site, opcodeName: name, ...context } : { site: name, ...context }
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
function operandByte(opcodes, at, op, role) {
|
|
733
|
-
if (at >= opcodes.length) {
|
|
734
|
-
throw malformedBytecode(
|
|
735
|
-
"MALFORMED_BYTECODE_TRUNCATED",
|
|
736
|
-
op,
|
|
737
|
-
`is missing its ${role}: the program ends mid-instruction`,
|
|
738
|
-
`an operand byte at offset ${at}`,
|
|
739
|
-
`a program only ${opcodes.length} byte(s) long`,
|
|
740
|
-
{ offset: at, length: opcodes.length, role }
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
return opcodes[at];
|
|
744
|
-
}
|
|
745
|
-
function poolIndex(opcodes, at, op, role, poolSize, poolName) {
|
|
746
|
-
const index = operandByte(opcodes, at, op, role);
|
|
747
|
-
if (index >= poolSize) {
|
|
748
|
-
throw malformedBytecode(
|
|
749
|
-
"MALFORMED_BYTECODE_CONSTANT_INDEX",
|
|
750
|
-
op,
|
|
751
|
-
`reads ${poolName} entry ${index}, which does not exist`,
|
|
752
|
-
`an index below ${poolSize}`,
|
|
753
|
-
`${index}`,
|
|
754
|
-
{ index, poolSize, pool: poolName }
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
return index;
|
|
758
|
-
}
|
|
759
|
-
function poolString(opcodes, at, strings, op, role) {
|
|
760
|
-
return pooledString(strings, operandByte(opcodes, at, op, role), op);
|
|
761
|
-
}
|
|
762
|
-
function pooledString(strings, index, op) {
|
|
763
|
-
const text = strings[index];
|
|
764
|
-
if (typeof text !== "string") {
|
|
765
|
-
throw malformedBytecode(
|
|
766
|
-
"MALFORMED_BYTECODE_CONSTANT_INDEX",
|
|
767
|
-
op,
|
|
768
|
-
`reads string-pool entry ${index}, which is ${index >= strings.length ? "past the end of the pool" : "not a string"}`,
|
|
769
|
-
`an index below ${strings.length}, holding a string`,
|
|
770
|
-
index >= strings.length ? `${index}` : typeof text,
|
|
771
|
-
{ index, poolSize: strings.length, pool: "string-pool" }
|
|
772
|
-
);
|
|
773
|
-
}
|
|
774
|
-
return text;
|
|
775
|
-
}
|
|
776
|
-
function stringOperand(value, site, role) {
|
|
777
|
-
if (typeof value.value !== "string") {
|
|
778
|
-
throw malformedBytecode(
|
|
779
|
-
"MALFORMED_BYTECODE_OPERAND_TYPE",
|
|
780
|
-
site,
|
|
781
|
-
`expected a ${role ?? "string"} on the stack`,
|
|
782
|
-
role ? `a string value holding the ${role}` : "a string value",
|
|
783
|
-
`a ${ValueType[value.type] ?? "unknown"} value`,
|
|
784
|
-
role ? { role, valueType: value.type } : { valueType: value.type }
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
return value.value;
|
|
788
|
-
}
|
|
789
|
-
function requireKnownBodyKind(kind, ref, op) {
|
|
790
|
-
if (kind !== 0 && kind !== 1 && kind !== 2) {
|
|
791
|
-
throw malformedBytecode(
|
|
792
|
-
"MALFORMED_BYTECODE_BODY_KIND",
|
|
793
|
-
op,
|
|
794
|
-
`has body kind ${kind}, which is not one this VM knows`,
|
|
795
|
-
"0 (inline body), 1 (builtin function) or 2 (user-defined function)",
|
|
796
|
-
`${kind}`,
|
|
797
|
-
{ kind, ref }
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
function parseBigIntLiteral(text, op) {
|
|
802
|
-
try {
|
|
803
|
-
return BigInt(text);
|
|
804
|
-
} catch {
|
|
805
|
-
throw malformedBytecode(
|
|
806
|
-
"MALFORMED_BYTECODE_BIGINT_LITERAL",
|
|
807
|
-
op,
|
|
808
|
-
`cannot read "${text}" as a whole number`,
|
|
809
|
-
"a string holding an integer, with no decimal point or separators",
|
|
810
|
-
`"${text}"`,
|
|
811
|
-
{ literal: text }
|
|
812
|
-
);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
function unrunnableProgram(bytecode) {
|
|
816
|
-
if (bytecode === null || typeof bytecode !== "object") {
|
|
817
|
-
return `expected a bytecode program, got ${bytecode === null ? "null" : typeof bytecode}`;
|
|
818
|
-
}
|
|
819
|
-
const candidate = bytecode;
|
|
820
|
-
if (typeof candidate.opcodes?.length !== "number") {
|
|
821
|
-
return "the program has no `opcodes` array";
|
|
822
|
-
}
|
|
823
|
-
if (typeof candidate.numbers?.length !== "number") {
|
|
824
|
-
return "the program has no `numbers` pool";
|
|
825
|
-
}
|
|
826
|
-
if (typeof candidate.strings?.length !== "number") {
|
|
827
|
-
return "the program has no `strings` pool";
|
|
828
|
-
}
|
|
829
|
-
return null;
|
|
830
|
-
}
|
|
831
|
-
function invokeFrameBody(params, program, args, vm, pipeline, expression, context, symbolicTolerant) {
|
|
832
|
-
const frame = /* @__PURE__ */ new Map();
|
|
833
|
-
for (let i = 0; i < params.length; i++) frame.set(params[i], args[i]);
|
|
834
|
-
vm.pushCallFrame(frame);
|
|
835
|
-
let bodyResult;
|
|
836
|
-
try {
|
|
837
|
-
bodyResult = executeBytecode(program, vm, pipeline, expression, context, symbolicTolerant);
|
|
838
|
-
} finally {
|
|
839
|
-
vm.popCallFrame();
|
|
840
|
-
}
|
|
841
|
-
if (bodyResult.type === "pending") {
|
|
842
|
-
throw ErrorFactory.execution(
|
|
843
|
-
"MAP_REDUCE_ASYNC_UNSUPPORTED",
|
|
844
|
-
`map/reduce transform bodies calling an async operation (weather, stocks, currency, ...) aren't supported`
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
if (bodyResult.type === "error") {
|
|
848
|
-
throw bodyResult.error;
|
|
849
|
-
}
|
|
850
|
-
return bodyResult.value;
|
|
851
|
-
}
|
|
852
|
-
function invokeWithBoundUnknown(name, program, vm, pipeline, expression, context, symbolicTolerant) {
|
|
853
|
-
const frame = new Map(vm.getCallFrame());
|
|
854
|
-
frame.set(name, symbolicValue(varNode(name)));
|
|
855
|
-
vm.pushCallFrame(frame);
|
|
856
|
-
let bodyResult;
|
|
857
|
-
try {
|
|
858
|
-
bodyResult = executeBytecode(program, vm, pipeline, expression, context, symbolicTolerant);
|
|
859
|
-
} finally {
|
|
860
|
-
vm.popCallFrame();
|
|
861
|
-
}
|
|
862
|
-
if (bodyResult.type === "pending") {
|
|
863
|
-
throw ErrorFactory.execution(
|
|
864
|
-
"SYMBOLIC_ASYNC_UNSUPPORTED",
|
|
865
|
-
`an algebra verb's expression cannot call an async operation (weather, stocks, currency, ...)`
|
|
866
|
-
);
|
|
867
|
-
}
|
|
868
|
-
if (bodyResult.type === "error") {
|
|
869
|
-
throw bodyResult.error;
|
|
870
|
-
}
|
|
871
|
-
return bodyResult.value;
|
|
872
|
-
}
|
|
873
|
-
function unifyRatePeriods(l, r) {
|
|
874
|
-
if (l.type !== 6 /* Uom */ || r.type !== 6 /* Uom */) return null;
|
|
875
|
-
const leftUnit = l.unit;
|
|
876
|
-
const rightUnit = r.unit;
|
|
877
|
-
if (leftUnit === void 0 || rightUnit === void 0) return null;
|
|
878
|
-
const leftSlash = leftUnit.indexOf("/");
|
|
879
|
-
const rightSlash = rightUnit.indexOf("/");
|
|
880
|
-
if (leftSlash < 0 || rightSlash < 0) return null;
|
|
881
|
-
if (leftUnit.slice(0, leftSlash) !== rightUnit.slice(0, rightSlash)) return null;
|
|
882
|
-
const leftPeriod = UNIT_TABLE[leftUnit.slice(leftSlash + 1).toLowerCase()];
|
|
883
|
-
const rightPeriod = UNIT_TABLE[rightUnit.slice(rightSlash + 1).toLowerCase()];
|
|
884
|
-
if (leftPeriod === void 0 || rightPeriod === void 0) return null;
|
|
885
|
-
if (leftPeriod[0] !== rightPeriod[0]) return null;
|
|
886
|
-
return l.toNumber() * (rightPeriod[1] / leftPeriod[1]);
|
|
887
|
-
}
|
|
888
|
-
function moneyTimesQuantity(l, r) {
|
|
889
|
-
if (l.type !== 6 /* Uom */ || r.type !== 6 /* Uom */) return null;
|
|
890
|
-
const leftUnit = l.unit;
|
|
891
|
-
const rightUnit = r.unit;
|
|
892
|
-
if (leftUnit === void 0 || rightUnit === void 0) return null;
|
|
893
|
-
if (leftUnit.includes("/") || rightUnit.includes("/")) return null;
|
|
894
|
-
const leftIsMoney = CURRENCY_DISPLAY[leftUnit.toUpperCase()] !== void 0;
|
|
895
|
-
const rightIsMoney = CURRENCY_DISPLAY[rightUnit.toUpperCase()] !== void 0;
|
|
896
|
-
if (leftIsMoney === rightIsMoney) return null;
|
|
897
|
-
return uomValue(l.toNumber() * r.toNumber(), leftIsMoney ? leftUnit : rightUnit);
|
|
898
|
-
}
|
|
899
|
-
function combinePercentage(l, r, sign) {
|
|
900
|
-
if (l.type === 5 /* Percentage */) {
|
|
901
|
-
if (r.type === 6 /* Uom */ && r.unit !== void 0) {
|
|
902
|
-
return scaleMoneyByPercent(r, r.unit, l.toNumber(), sign);
|
|
903
|
-
}
|
|
904
|
-
if (r.type !== 5 /* Percentage */ && r.type !== 0 /* Number */) return null;
|
|
905
|
-
return percentageValue(l.toNumber() + sign * r.toNumber());
|
|
906
|
-
}
|
|
907
|
-
if (r.type !== 5 /* Percentage */) return null;
|
|
908
|
-
const factor = 1 + sign * r.toNumber();
|
|
909
|
-
if (l.type === 0 /* Number */) {
|
|
910
|
-
const scaled = l.toNumber() * factor;
|
|
911
|
-
if (l.uncertainty !== void 0) return numberValueUncertain(scaled, Math.abs(l.uncertainty * factor));
|
|
912
|
-
return numberValue(scaled);
|
|
913
|
-
}
|
|
914
|
-
if (l.type === 6 /* Uom */ && l.unit !== void 0) return scaleMoneyByPercent(l, l.unit, r.toNumber(), sign);
|
|
915
|
-
return null;
|
|
916
|
-
}
|
|
917
|
-
function multiplyPercentWithUncertainty(l, r) {
|
|
918
|
-
const pct = l.type === 5 /* Percentage */ ? l : r.type === 5 /* Percentage */ ? r : null;
|
|
919
|
-
if (pct === null) return null;
|
|
920
|
-
const other = pct === l ? r : l;
|
|
921
|
-
if (other.type !== 0 /* Number */ || other.uncertainty === void 0) return null;
|
|
922
|
-
const factor = pct.toNumber();
|
|
923
|
-
return numberValueUncertain(other.toNumber() * factor, Math.abs(other.uncertainty * factor));
|
|
924
|
-
}
|
|
925
|
-
function multiplyMoneyByScalarExact(l, r) {
|
|
926
|
-
const money = l.type === 6 /* Uom */ ? l : r;
|
|
927
|
-
const scalar = l.type === 6 /* Uom */ ? r : l;
|
|
928
|
-
if (money.type !== 6 /* Uom */ || money.unit === void 0 || !sharedCurrencyExchange.isCurrency(money.unit)) return null;
|
|
929
|
-
if (scalar.type !== 0 /* Number */ && scalar.type !== 5 /* Percentage */) return null;
|
|
930
|
-
if (scalar.rational !== void 0) return null;
|
|
931
|
-
return scaleMoneyExact(money, scalar.toNumber(), money.unit);
|
|
932
|
-
}
|
|
933
|
-
function dividePercentWithUncertainty(l, r) {
|
|
934
|
-
if (r.type === 5 /* Percentage */ && l.type === 0 /* Number */ && l.uncertainty !== void 0) {
|
|
935
|
-
const factor = r.toNumber();
|
|
936
|
-
if (factor === 0) return null;
|
|
937
|
-
return numberValueUncertain(l.toNumber() / factor, Math.abs(l.uncertainty / factor));
|
|
938
|
-
}
|
|
939
|
-
if (l.type === 5 /* Percentage */ && r.type === 0 /* Number */ && r.uncertainty !== void 0) {
|
|
940
|
-
const b = r.toNumber();
|
|
941
|
-
if (b === 0) return null;
|
|
942
|
-
return numberValueUncertain(l.toNumber() / b, Math.abs(l.toNumber() * r.uncertainty / (b * b)));
|
|
943
|
-
}
|
|
944
|
-
return null;
|
|
945
|
-
}
|
|
946
|
-
function extractDurationMs(value) {
|
|
947
|
-
if (value.type === 6 /* Uom */) {
|
|
948
|
-
const unit = value.unit;
|
|
949
|
-
if (unit && getMeasure(unit) === "time") {
|
|
950
|
-
try {
|
|
951
|
-
return convertUnit(value.toNumber(), unit, "ms");
|
|
952
|
-
} catch {
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
return 0;
|
|
956
|
-
}
|
|
957
|
-
return value.toNumber();
|
|
958
|
-
}
|
|
959
|
-
var WORKDAYS_PER_YEAR = 260;
|
|
960
|
-
function addBusinessDays2(epochMs, n, vm) {
|
|
961
|
-
const remaining = Math.trunc(Math.abs(n));
|
|
962
|
-
const direction = n >= 0 ? 1 : -1;
|
|
963
|
-
const limitYears = direction > 0 ? vm.getMaxDateOffsetYears() : -vm.getMinDateOffsetYears();
|
|
964
|
-
const limitWorkdays = Math.floor(limitYears * WORKDAYS_PER_YEAR);
|
|
965
|
-
if (remaining > limitWorkdays) {
|
|
966
|
-
throw ErrorFactory.execution(
|
|
967
|
-
"DATE_OFFSET_LIMIT_EXCEEDED",
|
|
968
|
-
`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`,
|
|
969
|
-
{ requestedWorkdays: remaining, limitYears, limitWorkdays }
|
|
970
|
-
);
|
|
971
|
-
}
|
|
972
|
-
const maxCalendarSteps = limitWorkdays * 7 + 7;
|
|
973
|
-
const landed = addBusinessDays(epochMs, n, (ms) => vm.isHoliday(ms), maxCalendarSteps);
|
|
974
|
-
if (landed === null) {
|
|
975
|
-
throw ErrorFactory.execution(
|
|
976
|
-
"DATE_OFFSET_LIMIT_EXCEEDED",
|
|
977
|
-
`A working-day offset of ${remaining.toLocaleString("en-US")} could not be reached within the ${limitYears}-year limit; the configured holiday calendar leaves too few working days`,
|
|
978
|
-
{ requestedWorkdays: remaining, limitYears, maxCalendarSteps }
|
|
979
|
-
);
|
|
980
|
-
}
|
|
981
|
-
return landed;
|
|
982
|
-
}
|
|
983
|
-
var MS_PER_DAY2 = 864e5;
|
|
984
|
-
var CALENDAR_MONTHS_PER_UNIT = {
|
|
985
|
-
month: 1,
|
|
986
|
-
months: 1,
|
|
987
|
-
mo: 1,
|
|
988
|
-
year: 12,
|
|
989
|
-
years: 12,
|
|
990
|
-
yr: 12,
|
|
991
|
-
y: 12,
|
|
992
|
-
a: 12,
|
|
993
|
-
decade: 120,
|
|
994
|
-
decades: 120,
|
|
995
|
-
dec: 120,
|
|
996
|
-
century: 1200,
|
|
997
|
-
centuries: 1200,
|
|
998
|
-
millennium: 12e3,
|
|
999
|
-
millennia: 12e3
|
|
1000
|
-
};
|
|
1001
|
-
function daysInMonth(year, monthIndex) {
|
|
1002
|
-
return new Date(year, monthIndex + 1, 0).getDate();
|
|
1003
|
-
}
|
|
1004
|
-
function addCalendarDays(epochMs, days) {
|
|
1005
|
-
const whole = Math.trunc(days);
|
|
1006
|
-
const date = new Date(epochMs);
|
|
1007
|
-
date.setDate(date.getDate() + whole);
|
|
1008
|
-
const shifted = date.getTime();
|
|
1009
|
-
if (Number.isNaN(shifted)) return epochMs + days * MS_PER_DAY2;
|
|
1010
|
-
return shifted + (days - whole) * MS_PER_DAY2;
|
|
1011
|
-
}
|
|
1012
|
-
function addCalendarMonths(epochMs, months) {
|
|
1013
|
-
const whole = Math.trunc(months);
|
|
1014
|
-
const date = new Date(epochMs);
|
|
1015
|
-
const dayOfMonth = date.getDate();
|
|
1016
|
-
date.setDate(1);
|
|
1017
|
-
date.setMonth(date.getMonth() + whole);
|
|
1018
|
-
date.setDate(Math.min(dayOfMonth, daysInMonth(date.getFullYear(), date.getMonth())));
|
|
1019
|
-
const shifted = date.getTime();
|
|
1020
|
-
const monthMs = convertUnit(1, "month", "ms");
|
|
1021
|
-
if (Number.isNaN(shifted)) return epochMs + months * monthMs;
|
|
1022
|
-
return shifted + (months - whole) * monthMs;
|
|
1023
|
-
}
|
|
1024
|
-
function shiftDatetime(epochMs, duration, sign, vm) {
|
|
1025
|
-
if (duration.type === 6 /* Uom */ && duration.unit !== void 0) {
|
|
1026
|
-
const unit = duration.unit;
|
|
1027
|
-
const amount = sign * duration.toNumber();
|
|
1028
|
-
if (isWorkdayUnit(unit)) return addBusinessDays2(epochMs, amount, vm);
|
|
1029
|
-
const monthsPerUnit = CALENDAR_MONTHS_PER_UNIT[unit];
|
|
1030
|
-
if (monthsPerUnit !== void 0) return addCalendarMonths(epochMs, amount * monthsPerUnit);
|
|
1031
|
-
if (getMeasure(unit) === "time") {
|
|
1032
|
-
let daysPerUnit = 0;
|
|
1033
|
-
try {
|
|
1034
|
-
daysPerUnit = convertUnit(1, unit, "day");
|
|
1035
|
-
} catch {
|
|
1036
|
-
}
|
|
1037
|
-
if (Number.isInteger(daysPerUnit) && daysPerUnit >= 1) {
|
|
1038
|
-
return addCalendarDays(epochMs, amount * daysPerUnit);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
return epochMs + sign * extractDurationMs(duration);
|
|
1043
|
-
}
|
|
1044
|
-
function combineTimecode(tc, r, sign) {
|
|
1045
|
-
const fps = timecodeFps(tc.unit);
|
|
1046
|
-
if (r.type === 6 /* Uom */ && isTimecodeUnit(r.unit)) {
|
|
1047
|
-
const rFps = timecodeFps(r.unit);
|
|
1048
|
-
if (rFps !== fps) {
|
|
1049
|
-
return errorValue(
|
|
1050
|
-
"TIMECODE_FPS_MISMATCH",
|
|
1051
|
-
`Cannot combine timecodes at different frame rates (${fps} fps vs ${rFps} fps)`
|
|
1052
|
-
);
|
|
1053
|
-
}
|
|
1054
|
-
return sign === 1 ? uomValue(tc.toNumber() + r.toNumber(), tc.unit) : uomValue(tc.toNumber() - r.toNumber(), "frames");
|
|
1055
|
-
}
|
|
1056
|
-
if (r.type === 6 /* Uom */ && r.unit === "frames") {
|
|
1057
|
-
return uomValue(tc.toNumber() + sign * r.toNumber(), tc.unit);
|
|
1058
|
-
}
|
|
1059
|
-
if (r.type === 6 /* Uom */ && r.unit && getMeasure(r.unit) === "time") {
|
|
1060
|
-
const seconds = convertUnit(r.toNumber(), r.unit, "s");
|
|
1061
|
-
return uomValue(tc.toNumber() + sign * seconds * fps, tc.unit);
|
|
1062
|
-
}
|
|
1063
|
-
return uomValue(tc.toNumber() + sign * r.toNumber(), tc.unit);
|
|
1064
|
-
}
|
|
1065
|
-
function isTruthy(value) {
|
|
1066
|
-
if (value.type === 10 /* Boolean */) return value.value;
|
|
1067
|
-
return value.toNumber() !== 0;
|
|
1068
|
-
}
|
|
1069
|
-
function incompatibleConversionError(fromUnit, toUnit) {
|
|
1070
|
-
const named = describeConversionMismatch(fromUnit, toUnit);
|
|
1071
|
-
return errorValue(
|
|
1072
|
-
"INCOMPATIBLE_UNITS",
|
|
1073
|
-
named ?? `Cannot convert ${fromUnit} to ${toUnit}: they do not measure the same thing`
|
|
1074
|
-
);
|
|
1075
|
-
}
|
|
1076
|
-
var MAX_EXACT_POW_BITS = 65536;
|
|
1077
|
-
function exactPowBits(base, exponent) {
|
|
1078
|
-
if (!Number.isInteger(exponent) || exponent < 0) return null;
|
|
1079
|
-
if (exponent === 0) return 1;
|
|
1080
|
-
const magnitude = Math.abs(base.toNumber());
|
|
1081
|
-
if (magnitude <= 1) return 1;
|
|
1082
|
-
const baseBits = magnitude <= 2 ? 1 : Math.log2(magnitude);
|
|
1083
|
-
return baseBits * exponent;
|
|
1084
|
-
}
|
|
1085
|
-
function bigIntPow(base, exponent) {
|
|
1086
|
-
let result = 1n;
|
|
1087
|
-
let factor = base;
|
|
1088
|
-
let remaining = exponent;
|
|
1089
|
-
while (remaining > 0n) {
|
|
1090
|
-
if (remaining % 2n === 1n) result *= factor;
|
|
1091
|
-
remaining /= 2n;
|
|
1092
|
-
if (remaining > 0n) factor *= factor;
|
|
1093
|
-
}
|
|
1094
|
-
return result;
|
|
1095
|
-
}
|
|
1096
|
-
var MAX_EXACT_SHIFT_BITS = MAX_EXACT_POW_BITS;
|
|
1097
|
-
function bigIntBitLength(value) {
|
|
1098
|
-
const magnitude = value < 0n ? -value : value;
|
|
1099
|
-
if (magnitude === 0n) return 0;
|
|
1100
|
-
const asDouble = Number(magnitude);
|
|
1101
|
-
if (Number.isFinite(asDouble)) return Math.floor(Math.log2(asDouble)) + 1;
|
|
1102
|
-
return magnitude.toString(16).length * 4;
|
|
1103
|
-
}
|
|
1104
|
-
function bigIntShift(l, r, direction) {
|
|
1105
|
-
const value = toBigIntOperand(l);
|
|
1106
|
-
const shift = toBigIntOperand(r);
|
|
1107
|
-
if (value === 0n) return bigIntValue(0n);
|
|
1108
|
-
const grownBits = direction * Number(shift);
|
|
1109
|
-
if (grownBits <= 0 || bigIntBitLength(value) + grownBits <= MAX_EXACT_SHIFT_BITS) {
|
|
1110
|
-
return bigIntValue(direction === 1 ? value << shift : value >> shift);
|
|
1111
|
-
}
|
|
1112
|
-
throw ErrorFactory.execution(
|
|
1113
|
-
"BIGINT_SHIFT_LIMIT_EXCEEDED",
|
|
1114
|
-
`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`,
|
|
1115
|
-
{ shift: String(shift), limitBits: MAX_EXACT_SHIFT_BITS }
|
|
1116
|
-
);
|
|
1117
|
-
}
|
|
1118
|
-
function multiplyRateByMatchingUom(rate, multiplier) {
|
|
1119
|
-
const { numerator, denominator } = splitRateUnit(rate.unit);
|
|
1120
|
-
const rateMeasure = getMeasure(denominator);
|
|
1121
|
-
const multiplierMeasure = getMeasure(multiplier.unit);
|
|
1122
|
-
if (!rateMeasure || rateMeasure !== multiplierMeasure) {
|
|
1123
|
-
return errorValue(
|
|
1124
|
-
"RATE_MUL_MEASURE_MISMATCH",
|
|
1125
|
-
`Cannot multiply a "${denominator}"-denominated rate by "${multiplier.unit}" \u2014 different measures`
|
|
1126
|
-
);
|
|
1127
|
-
}
|
|
1128
|
-
const multiplierInDenominatorUnit = convertUnit(multiplier.toNumber(), multiplier.unit, denominator);
|
|
1129
|
-
const total = rate.toNumber() * multiplierInDenominatorUnit;
|
|
1130
|
-
return numerator ? uomValue(total, numerator) : numberValue(total);
|
|
1131
|
-
}
|
|
1132
|
-
function toFractionString(n) {
|
|
1133
|
-
if (Number.isNaN(n)) return "NaN";
|
|
1134
|
-
if (!isFinite(n)) return n > 0 ? "Infinity" : "-Infinity";
|
|
1135
|
-
const negative = n < 0;
|
|
1136
|
-
const abs = Math.abs(n);
|
|
1137
|
-
const whole = Math.floor(abs);
|
|
1138
|
-
const frac = abs - whole;
|
|
1139
|
-
if (frac < 1e-9) return `${negative ? "-" : ""}${whole}`;
|
|
1140
|
-
let h0 = 0, h1 = 1, k0 = 1, k1 = 0;
|
|
1141
|
-
let b = frac;
|
|
1142
|
-
const maxDenominator = 1e6;
|
|
1143
|
-
for (let i = 0; i < 30; i++) {
|
|
1144
|
-
const a = Math.floor(b + 1e-9);
|
|
1145
|
-
const h2 = a * h1 + h0, k2 = a * k1 + k0;
|
|
1146
|
-
if (k2 > maxDenominator) break;
|
|
1147
|
-
h0 = h1;
|
|
1148
|
-
h1 = h2;
|
|
1149
|
-
k0 = k1;
|
|
1150
|
-
k1 = k2;
|
|
1151
|
-
if (Math.abs(frac - h1 / k1) < 1e-9) break;
|
|
1152
|
-
const rem = b - a;
|
|
1153
|
-
if (rem < 1e-9) break;
|
|
1154
|
-
b = 1 / rem;
|
|
1155
|
-
}
|
|
1156
|
-
const numerator = whole * k1 + h1;
|
|
1157
|
-
return `${negative ? "-" : ""}${numerator}/${k1}`;
|
|
1158
|
-
}
|
|
1159
|
-
function rationalToFractionString(r) {
|
|
1160
|
-
return r.d === 1n ? String(r.n) : `${r.n}/${r.d}`;
|
|
1161
|
-
}
|
|
1162
|
-
function toMultiplierString(value) {
|
|
1163
|
-
const n = value.toNumber();
|
|
1164
|
-
const multiple = value.type === 5 /* Percentage */ ? 1 + n : n;
|
|
1165
|
-
return `${Math.round(multiple * 1e6) / 1e6}x`;
|
|
1166
|
-
}
|
|
1167
|
-
function toScientificString(n) {
|
|
1168
|
-
if (!Number.isFinite(n)) return String(n);
|
|
1169
|
-
if (n === 0) return "0e+0";
|
|
1170
|
-
const [mantissa, exponent] = n.toExponential().split("e");
|
|
1171
|
-
const trimmed = mantissa.includes(".") ? mantissa.replace(/0+$/, "").replace(/\.$/, "") : mantissa;
|
|
1172
|
-
return `${trimmed}e${exponent}`;
|
|
1173
|
-
}
|
|
1174
|
-
function executeBytecode(bytecode, vm, pipeline, expression, context, symbolicTolerant) {
|
|
1175
|
-
const unrunnable = unrunnableProgram(bytecode);
|
|
1176
|
-
if (unrunnable) {
|
|
1177
|
-
return {
|
|
1178
|
-
type: "error",
|
|
1179
|
-
error: ErrorFactory.validation({
|
|
1180
|
-
code: "MALFORMED_BYTECODE_PROGRAM",
|
|
1181
|
-
message: `Malformed bytecode: ${unrunnable}`,
|
|
1182
|
-
expected: "a BytecodeProgram with opcodes, numbers and strings",
|
|
1183
|
-
found: unrunnable,
|
|
1184
|
-
suggestion: MALFORMED_BYTECODE_SUGGESTION
|
|
1185
|
-
})
|
|
1186
|
-
};
|
|
1187
|
-
}
|
|
1188
|
-
const { opcodes, numbers, strings, userFunctionBodies, anonymousBodies } = bytecode;
|
|
1189
|
-
let ip = 0;
|
|
1190
|
-
let localInstructionCount = 0;
|
|
1191
|
-
const maxInstructions = vm.getMaxInstructions();
|
|
1192
|
-
const maxStackDepth = vm.getMaxStackDepth();
|
|
1193
|
-
const stack = vm.getStack();
|
|
1194
|
-
const shouldTrace = pipeline?.hasCollectors ?? false;
|
|
1195
|
-
const hasArena = isArenaActive();
|
|
1196
|
-
if (opcodes.length === 0) return { type: "value", value: numberValue(0) };
|
|
1197
|
-
beginEvaluation(vm);
|
|
1198
|
-
try {
|
|
1199
|
-
while (ip < opcodes.length) {
|
|
1200
|
-
if (++localInstructionCount > maxInstructions) {
|
|
1201
|
-
throw ErrorFactory.execution("INSTRUCTION_LIMIT_EXCEEDED", `Execution exceeded maximum of ${maxInstructions} instructions`);
|
|
1202
|
-
}
|
|
1203
|
-
if (stack.length > maxStackDepth) {
|
|
1204
|
-
throw ErrorFactory.execution("STACK_LIMIT_EXCEEDED", `Execution exceeded maximum stack depth of ${maxStackDepth}`);
|
|
1205
|
-
}
|
|
1206
|
-
const op = opcodes[ip++];
|
|
1207
|
-
if (shouldTrace) {
|
|
1208
|
-
const stackSnapshot = stack.map((v) => ({
|
|
1209
|
-
type: v.type,
|
|
1210
|
-
value: v.value,
|
|
1211
|
-
unit: v.unit
|
|
1212
|
-
}));
|
|
1213
|
-
pipeline.fireVmStep({
|
|
1214
|
-
type: DiagnosticEventType.VmStep,
|
|
1215
|
-
elapsedNs: 0,
|
|
1216
|
-
expression: expression ?? "",
|
|
1217
|
-
opcode: op,
|
|
1218
|
-
opcodeName: getOpCodeName(op),
|
|
1219
|
-
ip: ip - 1,
|
|
1220
|
-
stackDepth: stack.length,
|
|
1221
|
-
instructionNumber: localInstructionCount,
|
|
1222
|
-
stack: stackSnapshot
|
|
1223
|
-
});
|
|
1224
|
-
}
|
|
1225
|
-
switch (op) {
|
|
1226
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1227
|
-
// §1 Stack operations (OpCode 0–3)
|
|
1228
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1229
|
-
case 0 /* NOP */:
|
|
1230
|
-
break;
|
|
1231
|
-
case 1 /* HALT */: {
|
|
1232
|
-
const result = safePop(stack);
|
|
1233
|
-
return { type: "value", value: hasArena ? persistentValue(result) : result };
|
|
1234
|
-
}
|
|
1235
|
-
case 3 /* DUP */: {
|
|
1236
|
-
const top = safePop(stack);
|
|
1237
|
-
stack.push(top);
|
|
1238
|
-
stack.push(top);
|
|
1239
|
-
break;
|
|
1240
|
-
}
|
|
1241
|
-
case 2 /* SWAP */: {
|
|
1242
|
-
const a = safePop(stack), b = safePop(stack);
|
|
1243
|
-
stack.push(a);
|
|
1244
|
-
stack.push(b);
|
|
1245
|
-
break;
|
|
1246
|
-
}
|
|
1247
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1248
|
-
// §2 Push literals (OpCode 10–15)
|
|
1249
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1250
|
-
// Every operand below goes through a checked read. An index past the
|
|
1251
|
-
// end of a pool used to yield `undefined` and be pushed as a Value,
|
|
1252
|
-
// which then failed somewhere else entirely: PUSH_BIGINT reached
|
|
1253
|
-
// `BigInt(undefined)`, and an undefined string sat on the stack until
|
|
1254
|
-
// a unit or converter name was read off it.
|
|
1255
|
-
case 10 /* PUSH_NUMBER */:
|
|
1256
|
-
stack.push(numberValue(numbers[poolIndex(opcodes, ip++, op, "constant-pool index", numbers.length, "number-pool")]));
|
|
1257
|
-
break;
|
|
1258
|
-
case 11 /* PUSH_BIGINT */:
|
|
1259
|
-
stack.push(bigIntValue(parseBigIntLiteral(poolString(opcodes, ip++, strings, op, "constant-pool index"), op)));
|
|
1260
|
-
break;
|
|
1261
|
-
case 16 /* PUSH_DECIMAL */: {
|
|
1262
|
-
const dec = decimalFromLiteral(poolString(opcodes, ip++, strings, op, "constant-pool index"));
|
|
1263
|
-
stack.push(numberValueExact(decimalToNumber(dec), dec));
|
|
1264
|
-
break;
|
|
1265
|
-
}
|
|
1266
|
-
case 12 /* PUSH_HEX */:
|
|
1267
|
-
stack.push(hexValue(numbers[poolIndex(opcodes, ip++, op, "constant-pool index", numbers.length, "number-pool")]));
|
|
1268
|
-
break;
|
|
1269
|
-
case 13 /* PUSH_STRING */:
|
|
1270
|
-
stack.push(stringValue(poolString(opcodes, ip++, strings, op, "constant-pool index")));
|
|
1271
|
-
break;
|
|
1272
|
-
case 14 /* PUSH_BOOLEAN */:
|
|
1273
|
-
stack.push(boolValue(operandByte(opcodes, ip++, op, "boolean operand") === 1));
|
|
1274
|
-
break;
|
|
1275
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1276
|
-
// §3 Arithmetic (OpCode 20–27)
|
|
1277
|
-
// Binary ops (ADD/SUB/MUL/DIV/MOD/EXP) have inlined numeric
|
|
1278
|
-
// fast paths; unary ops (NEG/POS) handle BigInt and UoM.
|
|
1279
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1280
|
-
case 20 /* ADD */: {
|
|
1281
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1282
|
-
const addFault = faultedOperand(l, r);
|
|
1283
|
-
if (addFault) {
|
|
1284
|
-
stack.push(addFault);
|
|
1285
|
-
break;
|
|
1286
|
-
}
|
|
1287
|
-
if (l.uncertainty !== void 0 || r.uncertainty !== void 0) {
|
|
1288
|
-
const uncAdd = uncertainOp(l, r, "add");
|
|
1289
|
-
if (uncAdd) {
|
|
1290
|
-
stack.push(uncAdd);
|
|
1291
|
-
break;
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1295
|
-
const ratAdd = exactRationalOp(l, r, "add");
|
|
1296
|
-
if (ratAdd) {
|
|
1297
|
-
stack.push(ratAdd);
|
|
1298
|
-
break;
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
const pctAdd = combinePercentage(l, r, 1);
|
|
1302
|
-
const ratePeriodAdd = pctAdd === null ? unifyRatePeriods(l, r) : null;
|
|
1303
|
-
if (pctAdd !== null) {
|
|
1304
|
-
stack.push(pctAdd);
|
|
1305
|
-
} else if (ratePeriodAdd !== null) {
|
|
1306
|
-
stack.push(uomValue(ratePeriodAdd + r.toNumber(), r.unit));
|
|
1307
|
-
} else if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1308
|
-
stack.push(numberValue(l.value + r.value));
|
|
1309
|
-
} else if (l.type === 10 /* Boolean */ && r.type === 10 /* Boolean */) {
|
|
1310
|
-
stack.push(boolValue(l.value && r.value));
|
|
1311
|
-
} else if (l.type === 4 /* Datetime */) {
|
|
1312
|
-
if (r.type === 4 /* Datetime */) {
|
|
1313
|
-
stack.push(errorValue("INVALID_DATETIME_OP", "Cannot add two datetimes together"));
|
|
1314
|
-
} else {
|
|
1315
|
-
stack.push(datetimeValue(shiftDatetime(l.toNumber(), r, 1, vm)));
|
|
1316
|
-
}
|
|
1317
|
-
} else if (l.type === 6 /* Uom */ && isTimecodeUnit(l.unit)) {
|
|
1318
|
-
stack.push(combineTimecode(l, r, 1));
|
|
1319
|
-
} else if (r.type === 4 /* Datetime */) {
|
|
1320
|
-
stack.push(datetimeValue(shiftDatetime(r.toNumber(), l, 1, vm)));
|
|
1321
|
-
} else {
|
|
1322
|
-
stack.push(binaryOp(l, r, (a, b) => a + b, (a, b) => a + b, "add"));
|
|
1323
|
-
}
|
|
1324
|
-
break;
|
|
1325
|
-
}
|
|
1326
|
-
case 21 /* SUB */: {
|
|
1327
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1328
|
-
const subFault = faultedOperand(l, r);
|
|
1329
|
-
if (subFault) {
|
|
1330
|
-
stack.push(subFault);
|
|
1331
|
-
break;
|
|
1332
|
-
}
|
|
1333
|
-
if (l.uncertainty !== void 0 || r.uncertainty !== void 0) {
|
|
1334
|
-
const uncSub = uncertainOp(l, r, "sub");
|
|
1335
|
-
if (uncSub) {
|
|
1336
|
-
stack.push(uncSub);
|
|
1337
|
-
break;
|
|
1338
|
-
}
|
|
1339
|
-
}
|
|
1340
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1341
|
-
const ratSub = exactRationalOp(l, r, "sub");
|
|
1342
|
-
if (ratSub) {
|
|
1343
|
-
stack.push(ratSub);
|
|
1344
|
-
break;
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
const pctSub = combinePercentage(l, r, -1);
|
|
1348
|
-
const ratePeriodSub = pctSub === null ? unifyRatePeriods(l, r) : null;
|
|
1349
|
-
if (pctSub !== null) {
|
|
1350
|
-
stack.push(pctSub);
|
|
1351
|
-
} else if (ratePeriodSub !== null) {
|
|
1352
|
-
stack.push(uomValue(ratePeriodSub - r.toNumber(), r.unit));
|
|
1353
|
-
} else if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1354
|
-
stack.push(numberValue(l.value - r.value));
|
|
1355
|
-
} else if (l.type === 4 /* Datetime */) {
|
|
1356
|
-
if (r.type === 4 /* Datetime */) {
|
|
1357
|
-
stack.push(uomValue(l.toNumber() - r.toNumber(), "ms"));
|
|
1358
|
-
} else {
|
|
1359
|
-
stack.push(datetimeValue(shiftDatetime(l.toNumber(), r, -1, vm)));
|
|
1360
|
-
}
|
|
1361
|
-
} else if (l.type === 6 /* Uom */ && isTimecodeUnit(l.unit)) {
|
|
1362
|
-
stack.push(combineTimecode(l, r, -1));
|
|
1363
|
-
} else if (r.type === 4 /* Datetime */) {
|
|
1364
|
-
stack.push(errorValue(
|
|
1365
|
-
"INVALID_DATETIME_OP",
|
|
1366
|
-
"Cannot subtract a datetime from a value that is not a datetime"
|
|
1367
|
-
));
|
|
1368
|
-
} else {
|
|
1369
|
-
stack.push(binaryOp(l, r, (a, b) => a - b, (a, b) => a - b, "sub"));
|
|
1370
|
-
}
|
|
1371
|
-
break;
|
|
1372
|
-
}
|
|
1373
|
-
case 22 /* MUL */: {
|
|
1374
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1375
|
-
const pctUnc = multiplyPercentWithUncertainty(l, r);
|
|
1376
|
-
if (pctUnc) {
|
|
1377
|
-
stack.push(pctUnc);
|
|
1378
|
-
break;
|
|
1379
|
-
}
|
|
1380
|
-
if (l.uncertainty !== void 0 || r.uncertainty !== void 0) {
|
|
1381
|
-
const uncMul = uncertainOp(l, r, "mul");
|
|
1382
|
-
if (uncMul) {
|
|
1383
|
-
stack.push(uncMul);
|
|
1384
|
-
break;
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1388
|
-
const ratMul = exactRationalOp(l, r, "mul");
|
|
1389
|
-
if (ratMul) {
|
|
1390
|
-
stack.push(ratMul);
|
|
1391
|
-
break;
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
const moneyScalar = multiplyMoneyByScalarExact(l, r);
|
|
1395
|
-
if (moneyScalar) {
|
|
1396
|
-
stack.push(moneyScalar);
|
|
1397
|
-
break;
|
|
1398
|
-
}
|
|
1399
|
-
if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1400
|
-
stack.push(numberValue(l.value * r.value));
|
|
1401
|
-
} else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
|
|
1402
|
-
checkAllocation(matrixProductCells(l.value, r.value), "matrix cells");
|
|
1403
|
-
stack.push(matrixMultiply(l.value, r.value));
|
|
1404
|
-
} else if (l.type === 6 /* Uom */ && isRateUnit(l.unit) && r.type === 6 /* Uom */ && r.unit) {
|
|
1405
|
-
stack.push(multiplyRateByMatchingUom(l, r));
|
|
1406
|
-
} else if (r.type === 6 /* Uom */ && isRateUnit(r.unit) && l.type === 6 /* Uom */ && l.unit) {
|
|
1407
|
-
stack.push(multiplyRateByMatchingUom(r, l));
|
|
1408
|
-
} else if (moneyTimesQuantity(l, r) !== null) {
|
|
1409
|
-
stack.push(moneyTimesQuantity(l, r));
|
|
1410
|
-
} else {
|
|
1411
|
-
stack.push(binaryOp(l, r, (a, b) => a * b, (a, b) => a * b, "mul"));
|
|
1412
|
-
}
|
|
1413
|
-
break;
|
|
1414
|
-
}
|
|
1415
|
-
case 23 /* DIV */: {
|
|
1416
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1417
|
-
const pctDivUnc = dividePercentWithUncertainty(l, r);
|
|
1418
|
-
if (pctDivUnc) {
|
|
1419
|
-
stack.push(pctDivUnc);
|
|
1420
|
-
break;
|
|
1421
|
-
}
|
|
1422
|
-
if (l.uncertainty !== void 0 || r.uncertainty !== void 0) {
|
|
1423
|
-
const uncDiv = uncertainOp(l, r, "div");
|
|
1424
|
-
if (uncDiv) {
|
|
1425
|
-
stack.push(uncDiv);
|
|
1426
|
-
break;
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
|
|
1430
|
-
const { lv, rv, sameMeasure } = unifyUom(l, r);
|
|
1431
|
-
if (sameMeasure) {
|
|
1432
|
-
stack.push(numberValue(lv / rv));
|
|
1433
|
-
} else if (sharedCurrencyExchange.isCurrency(l.unit) && sharedCurrencyExchange.isCurrency(r.unit)) {
|
|
1434
|
-
stack.push(errorValue("INCOMPATIBLE_UNITS", `Cannot combine incompatible units: ${l.unit} and ${r.unit}`));
|
|
1435
|
-
} else {
|
|
1436
|
-
stack.push(rateValue(lv / rv, l.unit, r.unit));
|
|
1437
|
-
}
|
|
1438
|
-
} else {
|
|
1439
|
-
const ratDiv = exactRationalOp(l, r, "div");
|
|
1440
|
-
if (ratDiv) {
|
|
1441
|
-
stack.push(ratDiv);
|
|
1442
|
-
break;
|
|
1443
|
-
}
|
|
1444
|
-
stack.push(binaryOp(l, r, (a, b) => a / b, (a, b) => {
|
|
1445
|
-
if (b === 0n) throw bigIntDivisionByZero();
|
|
1446
|
-
return a / b;
|
|
1447
|
-
}, "div"));
|
|
1448
|
-
}
|
|
1449
|
-
break;
|
|
1450
|
-
}
|
|
1451
|
-
case 24 /* MOD */: {
|
|
1452
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1453
|
-
stack.push(binaryOp(l, r, (a, b) => a % b, (a, b) => {
|
|
1454
|
-
if (b === 0n) throw bigIntDivisionByZero();
|
|
1455
|
-
return a % b;
|
|
1456
|
-
}));
|
|
1457
|
-
break;
|
|
1458
|
-
}
|
|
1459
|
-
case 25 /* EXP */: {
|
|
1460
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1461
|
-
if (l.type === 13 /* Error */) {
|
|
1462
|
-
stack.push(l);
|
|
1463
|
-
break;
|
|
1464
|
-
}
|
|
1465
|
-
if (r.type === 13 /* Error */) {
|
|
1466
|
-
stack.push(r);
|
|
1467
|
-
break;
|
|
1468
|
-
}
|
|
1469
|
-
if (l.type === 12 /* Pending */) {
|
|
1470
|
-
stack.push(l);
|
|
1471
|
-
break;
|
|
1472
|
-
}
|
|
1473
|
-
if (r.type === 12 /* Pending */) {
|
|
1474
|
-
stack.push(r);
|
|
1475
|
-
break;
|
|
1476
|
-
}
|
|
1477
|
-
if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1478
|
-
stack.push(numberValue(power(l.value, r.value)));
|
|
1479
|
-
break;
|
|
1480
|
-
}
|
|
1481
|
-
if (l.type === 7 /* Matrix */ || r.type === 7 /* Matrix */) {
|
|
1482
|
-
if (l.type !== 7 /* Matrix */ || r.type !== 0 /* Number */) {
|
|
1483
|
-
stack.push(errorValue(
|
|
1484
|
-
"MATRIX_POWER_UNSUPPORTED",
|
|
1485
|
-
`^: 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.`
|
|
1486
|
-
));
|
|
1487
|
-
break;
|
|
1488
|
-
}
|
|
1489
|
-
checkAllocation(matrixPowerCells(l.value, r.value), "matrix cells");
|
|
1490
|
-
stack.push(matrixPower(l.value, r.value));
|
|
1491
|
-
break;
|
|
1492
|
-
}
|
|
1493
|
-
if (l.type === 9 /* Symbolic */ || r.type === 9 /* Symbolic */) {
|
|
1494
|
-
stack.push(symbolicPow(l, r));
|
|
1495
|
-
break;
|
|
1496
|
-
}
|
|
1497
|
-
if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1498
|
-
const powBits = exactPowBits(l, r.toNumber());
|
|
1499
|
-
if (powBits !== null) {
|
|
1500
|
-
if (powBits > MAX_EXACT_POW_BITS) {
|
|
1501
|
-
throw ErrorFactory.execution(
|
|
1502
|
-
"BIGINT_POW_LIMIT_EXCEEDED",
|
|
1503
|
-
`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`,
|
|
1504
|
-
{ limitBits: MAX_EXACT_POW_BITS }
|
|
1505
|
-
);
|
|
1506
|
-
}
|
|
1507
|
-
stack.push(bigIntValue(bigIntPow(toBigIntOperand(l), toBigIntOperand(r))));
|
|
1508
|
-
break;
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
stack.push(numberValue(power(l.toNumber(), r.toNumber())));
|
|
1512
|
-
break;
|
|
1513
|
-
}
|
|
1514
|
-
case 26 /* NEG */: {
|
|
1515
|
-
const v = safePop(stack);
|
|
1516
|
-
const negFault = faultedOperand(v);
|
|
1517
|
-
if (negFault) {
|
|
1518
|
-
stack.push(negFault);
|
|
1519
|
-
break;
|
|
1520
|
-
}
|
|
1521
|
-
if (v.type === 2 /* BigInt */) stack.push(bigIntValue(-v.value));
|
|
1522
|
-
else if (v.type === 6 /* Uom */ && v.exact !== void 0) stack.push(uomValueExact(-v.toNumber(), v.unit, decimalNegate(v.exact)));
|
|
1523
|
-
else if (v.type === 6 /* Uom */) stack.push(uomValue(-v.toNumber(), v.unit));
|
|
1524
|
-
else if (v.type === 5 /* Percentage */) stack.push(percentageValue(-v.toNumber()));
|
|
1525
|
-
else if (v.type === 9 /* Symbolic */) stack.push(symbolicNeg(v));
|
|
1526
|
-
else if (v.type === 0 /* Number */ && v.rational !== void 0) stack.push(numberValueRational(-v.toNumber(), rationalNeg(v.rational)));
|
|
1527
|
-
else if (v.type === 0 /* Number */ && v.exact !== void 0) stack.push(numberValueExact(-v.toNumber(), decimalNegate(v.exact)));
|
|
1528
|
-
else stack.push(numberValue(-v.toNumber()));
|
|
1529
|
-
break;
|
|
1530
|
-
}
|
|
1531
|
-
case 27 /* POS */: {
|
|
1532
|
-
const v = safePop(stack);
|
|
1533
|
-
const posFault = faultedOperand(v);
|
|
1534
|
-
if (posFault) {
|
|
1535
|
-
stack.push(posFault);
|
|
1536
|
-
break;
|
|
1537
|
-
}
|
|
1538
|
-
if (v.type === 6 /* Uom */ && v.exact !== void 0) stack.push(uomValueExact(v.toNumber(), v.unit, v.exact));
|
|
1539
|
-
else if (v.type === 6 /* Uom */) stack.push(uomValue(v.toNumber(), v.unit));
|
|
1540
|
-
else if (v.type === 5 /* Percentage */) stack.push(percentageValue(v.toNumber()));
|
|
1541
|
-
else if (v.type === 0 /* Number */ && v.rational !== void 0) stack.push(numberValueRational(v.toNumber(), v.rational));
|
|
1542
|
-
else if (v.type === 0 /* Number */ && v.exact !== void 0) stack.push(numberValueExact(v.toNumber(), v.exact));
|
|
1543
|
-
else stack.push(numberValue(v.toNumber()));
|
|
1544
|
-
break;
|
|
1545
|
-
}
|
|
1546
|
-
case 28 /* MAKE_UNCERTAIN */: {
|
|
1547
|
-
const spread = safePop(stack), center = safePop(stack);
|
|
1548
|
-
const uncFault = faultedOperand(center, spread);
|
|
1549
|
-
if (uncFault) {
|
|
1550
|
-
stack.push(uncFault);
|
|
1551
|
-
break;
|
|
1552
|
-
}
|
|
1553
|
-
stack.push(numberValueUncertain(center.toNumber(), Math.abs(spread.toNumber())));
|
|
1554
|
-
break;
|
|
1555
|
-
}
|
|
1556
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1557
|
-
// §4 Bitwise (OpCode 30–36)
|
|
1558
|
-
// None of these routes through binaryOp(), so each one carries
|
|
1559
|
-
// its own faulted-operand check for the reason that function
|
|
1560
|
-
// documents: a bit pattern read off an Error or a Pending is the
|
|
1561
|
-
// bit pattern of zero, and `(5 kg to m) & 1` answered 0 with
|
|
1562
|
-
// nothing to say it had not been asked a real question.
|
|
1563
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1564
|
-
case 30 /* LSHIFT */: {
|
|
1565
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1566
|
-
const shiftFault = faultedOperand(l, r);
|
|
1567
|
-
if (shiftFault) {
|
|
1568
|
-
stack.push(shiftFault);
|
|
1569
|
-
break;
|
|
1570
|
-
}
|
|
1571
|
-
if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1572
|
-
stack.push(bigIntShift(l, r, 1));
|
|
1573
|
-
} else {
|
|
1574
|
-
stack.push(numberValue(l.toNumber() << r.toNumber()));
|
|
1575
|
-
}
|
|
1576
|
-
break;
|
|
1577
|
-
}
|
|
1578
|
-
case 31 /* RSHIFT */: {
|
|
1579
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1580
|
-
const rshiftFault = faultedOperand(l, r);
|
|
1581
|
-
if (rshiftFault) {
|
|
1582
|
-
stack.push(rshiftFault);
|
|
1583
|
-
break;
|
|
1584
|
-
}
|
|
1585
|
-
if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1586
|
-
stack.push(bigIntShift(l, r, -1));
|
|
1587
|
-
} else {
|
|
1588
|
-
stack.push(numberValue(l.toNumber() >> r.toNumber()));
|
|
1589
|
-
}
|
|
1590
|
-
break;
|
|
1591
|
-
}
|
|
1592
|
-
case 32 /* URSHIFT */: {
|
|
1593
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1594
|
-
const urshiftFault = faultedOperand(l, r);
|
|
1595
|
-
if (urshiftFault) {
|
|
1596
|
-
stack.push(urshiftFault);
|
|
1597
|
-
break;
|
|
1598
|
-
}
|
|
1599
|
-
stack.push(numberValue(l.toNumber() >>> r.toNumber()));
|
|
1600
|
-
break;
|
|
1601
|
-
}
|
|
1602
|
-
case 33 /* BIT_AND */: {
|
|
1603
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1604
|
-
const andFault = faultedOperand(l, r);
|
|
1605
|
-
if (andFault) {
|
|
1606
|
-
stack.push(andFault);
|
|
1607
|
-
break;
|
|
1608
|
-
}
|
|
1609
|
-
if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1610
|
-
stack.push(bigIntValue(toBigIntOperand(l) & toBigIntOperand(r)));
|
|
1611
|
-
} else {
|
|
1612
|
-
stack.push(numberValue(l.toNumber() & r.toNumber()));
|
|
1613
|
-
}
|
|
1614
|
-
break;
|
|
1615
|
-
}
|
|
1616
|
-
case 34 /* BIT_OR */: {
|
|
1617
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1618
|
-
const orFault = faultedOperand(l, r);
|
|
1619
|
-
if (orFault) {
|
|
1620
|
-
stack.push(orFault);
|
|
1621
|
-
break;
|
|
1622
|
-
}
|
|
1623
|
-
if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1624
|
-
stack.push(bigIntValue(toBigIntOperand(l) | toBigIntOperand(r)));
|
|
1625
|
-
} else {
|
|
1626
|
-
stack.push(numberValue(l.toNumber() | r.toNumber()));
|
|
1627
|
-
}
|
|
1628
|
-
break;
|
|
1629
|
-
}
|
|
1630
|
-
case 35 /* BIT_XOR */: {
|
|
1631
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1632
|
-
const xorFault = faultedOperand(l, r);
|
|
1633
|
-
if (xorFault) {
|
|
1634
|
-
stack.push(xorFault);
|
|
1635
|
-
break;
|
|
1636
|
-
}
|
|
1637
|
-
if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1638
|
-
stack.push(bigIntValue(toBigIntOperand(l) ^ toBigIntOperand(r)));
|
|
1639
|
-
} else {
|
|
1640
|
-
stack.push(numberValue(l.toNumber() ^ r.toNumber()));
|
|
1641
|
-
}
|
|
1642
|
-
break;
|
|
1643
|
-
}
|
|
1644
|
-
case 36 /* BIT_NOT */: {
|
|
1645
|
-
const v = safePop(stack);
|
|
1646
|
-
const notFault = faultedOperand(v);
|
|
1647
|
-
if (notFault) {
|
|
1648
|
-
stack.push(notFault);
|
|
1649
|
-
break;
|
|
1650
|
-
}
|
|
1651
|
-
if (v.type === 2 /* BigInt */) stack.push(bigIntValue(~v.value));
|
|
1652
|
-
else stack.push(numberValue(~v.toNumber()));
|
|
1653
|
-
break;
|
|
1654
|
-
}
|
|
1655
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1656
|
-
// §4b Comparison (OpCode 40–45)
|
|
1657
|
-
// Numeric fast path when both operands are Number;
|
|
1658
|
-
// all six unify UoM operands before comparing them.
|
|
1659
|
-
//
|
|
1660
|
-
// The four ordering opcodes used to skip that unification and
|
|
1661
|
-
// compare the bare magnitudes, so `1 km > 500 m` answered false
|
|
1662
|
-
// and `30 min < 1 hour` answered false as well. They now go
|
|
1663
|
-
// through compareUom() like EQ/NEQ, which also means they agree
|
|
1664
|
-
// with EQ about where the boundary between "less" and "equal"
|
|
1665
|
-
// falls: `equal` there is a tolerance rather than `===`, so a
|
|
1666
|
-
// pair EQ calls equal is one GT and LT both call false.
|
|
1667
|
-
//
|
|
1668
|
-
// Two units that share no measure cannot be ordered at all, so
|
|
1669
|
-
// the four ordering opcodes answer with an error rather than a
|
|
1670
|
-
// boolean. EQ and NEQ still answer with a boolean, because
|
|
1671
|
-
// "a kilogram is not a metre" is a true statement about a pair
|
|
1672
|
-
// that cannot be converted, whereas "a kilogram is less than a
|
|
1673
|
-
// metre" is not a statement at all.
|
|
1674
|
-
//
|
|
1675
|
-
// A faulted operand is the second case even for EQ and NEQ, and
|
|
1676
|
-
// all six propagate it. A kilogram is a quantity that is not a
|
|
1677
|
-
// metre; an Error is not a quantity, so there is nothing for it
|
|
1678
|
-
// to be equal to. Reading its number gave zero, which made
|
|
1679
|
-
// `(5 kg to m) == 0` answer true.
|
|
1680
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1681
|
-
case 40 /* EQ */: {
|
|
1682
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1683
|
-
const eqFault = faultedOperand(l, r);
|
|
1684
|
-
if (eqFault) {
|
|
1685
|
-
stack.push(eqFault);
|
|
1686
|
-
break;
|
|
1687
|
-
}
|
|
1688
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1689
|
-
const cmp = compareRationalOperands(l, r);
|
|
1690
|
-
if (cmp !== null) {
|
|
1691
|
-
stack.push(boolValue(cmp === 0));
|
|
1692
|
-
break;
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1696
|
-
stack.push(boolValue(l.value === r.value));
|
|
1697
|
-
} else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1698
|
-
const cmp = compareBigIntOperands(l, r);
|
|
1699
|
-
stack.push(boolValue(cmp === null ? l.toNumber() === r.toNumber() : cmp === 0));
|
|
1700
|
-
} else if (l.type === 3 /* String */ && r.type === 3 /* String */) {
|
|
1701
|
-
stack.push(boolValue(l.value === r.value));
|
|
1702
|
-
} else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
|
|
1703
|
-
const { equal, sameMeasure } = compareUom(l, r);
|
|
1704
|
-
stack.push(boolValue(sameMeasure && equal));
|
|
1705
|
-
} else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
|
|
1706
|
-
stack.push(matrixCompare(l.value, r.value, (a, b) => a === b));
|
|
1707
|
-
} else if (l.type === 14 /* Colour */ || r.type === 14 /* Colour */) {
|
|
1708
|
-
if (l.type === 14 /* Colour */ && r.type === 14 /* Colour */) {
|
|
1709
|
-
const a = l.value, b = r.value;
|
|
1710
|
-
stack.push(boolValue(a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a));
|
|
1711
|
-
} else {
|
|
1712
|
-
stack.push(boolValue(false));
|
|
1713
|
-
}
|
|
1714
|
-
} else {
|
|
1715
|
-
stack.push(boolValue(l.toNumber() === r.toNumber()));
|
|
1716
|
-
}
|
|
1717
|
-
break;
|
|
1718
|
-
}
|
|
1719
|
-
case 41 /* NEQ */: {
|
|
1720
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1721
|
-
const neqFault = faultedOperand(l, r);
|
|
1722
|
-
if (neqFault) {
|
|
1723
|
-
stack.push(neqFault);
|
|
1724
|
-
break;
|
|
1725
|
-
}
|
|
1726
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1727
|
-
const cmp = compareRationalOperands(l, r);
|
|
1728
|
-
if (cmp !== null) {
|
|
1729
|
-
stack.push(boolValue(cmp !== 0));
|
|
1730
|
-
break;
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1734
|
-
stack.push(boolValue(l.value !== r.value));
|
|
1735
|
-
} else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1736
|
-
const cmp = compareBigIntOperands(l, r);
|
|
1737
|
-
stack.push(boolValue(cmp === null ? l.toNumber() !== r.toNumber() : cmp !== 0));
|
|
1738
|
-
} else if (l.type === 3 /* String */ && r.type === 3 /* String */) {
|
|
1739
|
-
stack.push(boolValue(l.value !== r.value));
|
|
1740
|
-
} else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
|
|
1741
|
-
const { equal, sameMeasure } = compareUom(l, r);
|
|
1742
|
-
stack.push(boolValue(!sameMeasure || !equal));
|
|
1743
|
-
} else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
|
|
1744
|
-
stack.push(matrixCompare(l.value, r.value, (a, b) => a !== b));
|
|
1745
|
-
} else if (l.type === 14 /* Colour */ || r.type === 14 /* Colour */) {
|
|
1746
|
-
if (l.type === 14 /* Colour */ && r.type === 14 /* Colour */) {
|
|
1747
|
-
const a = l.value, b = r.value;
|
|
1748
|
-
stack.push(boolValue(!(a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a)));
|
|
1749
|
-
} else {
|
|
1750
|
-
stack.push(boolValue(true));
|
|
1751
|
-
}
|
|
1752
|
-
} else {
|
|
1753
|
-
stack.push(boolValue(l.toNumber() !== r.toNumber()));
|
|
1754
|
-
}
|
|
1755
|
-
break;
|
|
1756
|
-
}
|
|
1757
|
-
case 42 /* LT */: {
|
|
1758
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1759
|
-
const ltFault = faultedOperand(l, r);
|
|
1760
|
-
if (ltFault) {
|
|
1761
|
-
stack.push(ltFault);
|
|
1762
|
-
break;
|
|
1763
|
-
}
|
|
1764
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1765
|
-
const cmp = compareRationalOperands(l, r);
|
|
1766
|
-
if (cmp !== null) {
|
|
1767
|
-
stack.push(boolValue(cmp < 0));
|
|
1768
|
-
break;
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1772
|
-
stack.push(boolValue(l.value < r.value));
|
|
1773
|
-
} else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1774
|
-
const cmp = compareBigIntOperands(l, r);
|
|
1775
|
-
stack.push(boolValue(cmp === null ? l.toNumber() < r.toNumber() : cmp < 0));
|
|
1776
|
-
} else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
|
|
1777
|
-
const { lv, rv, equal, sameMeasure } = compareUom(l, r);
|
|
1778
|
-
stack.push(sameMeasure ? boolValue(!equal && lv < rv) : incomparableUnitsError(l, r));
|
|
1779
|
-
} else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
|
|
1780
|
-
stack.push(matrixCompare(l.value, r.value, (a, b) => a < b));
|
|
1781
|
-
} else {
|
|
1782
|
-
stack.push(boolValue(l.toNumber() < r.toNumber()));
|
|
1783
|
-
}
|
|
1784
|
-
break;
|
|
1785
|
-
}
|
|
1786
|
-
case 43 /* LTE */: {
|
|
1787
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1788
|
-
const lteFault = faultedOperand(l, r);
|
|
1789
|
-
if (lteFault) {
|
|
1790
|
-
stack.push(lteFault);
|
|
1791
|
-
break;
|
|
1792
|
-
}
|
|
1793
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1794
|
-
const cmp = compareRationalOperands(l, r);
|
|
1795
|
-
if (cmp !== null) {
|
|
1796
|
-
stack.push(boolValue(cmp <= 0));
|
|
1797
|
-
break;
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1801
|
-
stack.push(boolValue(l.value <= r.value));
|
|
1802
|
-
} else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1803
|
-
const cmp = compareBigIntOperands(l, r);
|
|
1804
|
-
stack.push(boolValue(cmp === null ? l.toNumber() <= r.toNumber() : cmp <= 0));
|
|
1805
|
-
} else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
|
|
1806
|
-
const { lv, rv, equal, sameMeasure } = compareUom(l, r);
|
|
1807
|
-
stack.push(sameMeasure ? boolValue(equal || lv <= rv) : incomparableUnitsError(l, r));
|
|
1808
|
-
} else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
|
|
1809
|
-
stack.push(matrixCompare(l.value, r.value, (a, b) => a <= b));
|
|
1810
|
-
} else {
|
|
1811
|
-
stack.push(boolValue(l.toNumber() <= r.toNumber()));
|
|
1812
|
-
}
|
|
1813
|
-
break;
|
|
1814
|
-
}
|
|
1815
|
-
case 44 /* GT */: {
|
|
1816
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1817
|
-
const gtFault = faultedOperand(l, r);
|
|
1818
|
-
if (gtFault) {
|
|
1819
|
-
stack.push(gtFault);
|
|
1820
|
-
break;
|
|
1821
|
-
}
|
|
1822
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1823
|
-
const cmp = compareRationalOperands(l, r);
|
|
1824
|
-
if (cmp !== null) {
|
|
1825
|
-
stack.push(boolValue(cmp > 0));
|
|
1826
|
-
break;
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1830
|
-
stack.push(boolValue(l.value > r.value));
|
|
1831
|
-
} else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1832
|
-
const cmp = compareBigIntOperands(l, r);
|
|
1833
|
-
stack.push(boolValue(cmp === null ? l.toNumber() > r.toNumber() : cmp > 0));
|
|
1834
|
-
} else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
|
|
1835
|
-
const { lv, rv, equal, sameMeasure } = compareUom(l, r);
|
|
1836
|
-
stack.push(sameMeasure ? boolValue(!equal && lv > rv) : incomparableUnitsError(l, r));
|
|
1837
|
-
} else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
|
|
1838
|
-
stack.push(matrixCompare(l.value, r.value, (a, b) => a > b));
|
|
1839
|
-
} else {
|
|
1840
|
-
stack.push(boolValue(l.toNumber() > r.toNumber()));
|
|
1841
|
-
}
|
|
1842
|
-
break;
|
|
1843
|
-
}
|
|
1844
|
-
case 45 /* GTE */: {
|
|
1845
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1846
|
-
const gteFault = faultedOperand(l, r);
|
|
1847
|
-
if (gteFault) {
|
|
1848
|
-
stack.push(gteFault);
|
|
1849
|
-
break;
|
|
1850
|
-
}
|
|
1851
|
-
if (l.rational !== void 0 || r.rational !== void 0) {
|
|
1852
|
-
const cmp = compareRationalOperands(l, r);
|
|
1853
|
-
if (cmp !== null) {
|
|
1854
|
-
stack.push(boolValue(cmp >= 0));
|
|
1855
|
-
break;
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
if (l.type === 0 /* Number */ && r.type === 0 /* Number */) {
|
|
1859
|
-
stack.push(boolValue(l.value >= r.value));
|
|
1860
|
-
} else if (l.type === 2 /* BigInt */ || r.type === 2 /* BigInt */) {
|
|
1861
|
-
const cmp = compareBigIntOperands(l, r);
|
|
1862
|
-
stack.push(boolValue(cmp === null ? l.toNumber() >= r.toNumber() : cmp >= 0));
|
|
1863
|
-
} else if (l.type === 6 /* Uom */ && r.type === 6 /* Uom */) {
|
|
1864
|
-
const { lv, rv, equal, sameMeasure } = compareUom(l, r);
|
|
1865
|
-
stack.push(sameMeasure ? boolValue(equal || lv >= rv) : incomparableUnitsError(l, r));
|
|
1866
|
-
} else if (l.type === 7 /* Matrix */ && r.type === 7 /* Matrix */) {
|
|
1867
|
-
stack.push(matrixCompare(l.value, r.value, (a, b) => a >= b));
|
|
1868
|
-
} else {
|
|
1869
|
-
stack.push(boolValue(l.toNumber() >= r.toNumber()));
|
|
1870
|
-
}
|
|
1871
|
-
break;
|
|
1872
|
-
}
|
|
1873
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1874
|
-
// §4c Logical / conditional select (OpCode 130–132)
|
|
1875
|
-
//
|
|
1876
|
-
// Truthiness is read through isTruthy(), which reads a number,
|
|
1877
|
-
// which is zero for a fault, so a failed operand used to be
|
|
1878
|
-
// quietly false. Both operands are already evaluated by the time
|
|
1879
|
-
// either opcode runs (this VM has no branch opcodes), so there is
|
|
1880
|
-
// no short-circuit to preserve by ignoring the right-hand one.
|
|
1881
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1882
|
-
case 130 /* LOGICAL_AND */: {
|
|
1883
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1884
|
-
const andLogicFault = faultedOperand(l, r);
|
|
1885
|
-
if (andLogicFault) {
|
|
1886
|
-
stack.push(andLogicFault);
|
|
1887
|
-
break;
|
|
1888
|
-
}
|
|
1889
|
-
stack.push(boolValue(isTruthy(l) && isTruthy(r)));
|
|
1890
|
-
break;
|
|
1891
|
-
}
|
|
1892
|
-
case 131 /* LOGICAL_OR */: {
|
|
1893
|
-
const r = safePop(stack), l = safePop(stack);
|
|
1894
|
-
const orLogicFault = faultedOperand(l, r);
|
|
1895
|
-
if (orLogicFault) {
|
|
1896
|
-
stack.push(orLogicFault);
|
|
1897
|
-
break;
|
|
1898
|
-
}
|
|
1899
|
-
stack.push(boolValue(isTruthy(l) || isTruthy(r)));
|
|
1900
|
-
break;
|
|
1901
|
-
}
|
|
1902
|
-
case 132 /* SELECT */: {
|
|
1903
|
-
const elseVal = safePop(stack);
|
|
1904
|
-
const thenVal = safePop(stack);
|
|
1905
|
-
const condition = safePop(stack);
|
|
1906
|
-
const conditionFault = faultedOperand(condition);
|
|
1907
|
-
if (conditionFault) {
|
|
1908
|
-
stack.push(conditionFault);
|
|
1909
|
-
break;
|
|
1910
|
-
}
|
|
1911
|
-
stack.push(isTruthy(condition) ? thenVal : elseVal);
|
|
1912
|
-
break;
|
|
1913
|
-
}
|
|
1914
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1915
|
-
// §5 Functions (OpCode 50–52)
|
|
1916
|
-
// ═══════════════════════════════════════════════════════════════
|
|
1917
|
-
case 50 /* CALL_PLUGIN */: {
|
|
1918
|
-
const fnIdx = operandByte(opcodes, ip++, op, "plugin-function index");
|
|
1919
|
-
const argCount = operandByte(opcodes, ip++, op, "argument count");
|
|
1920
|
-
const args = [];
|
|
1921
|
-
for (let i = 0; i < argCount; i++) args.push(safePop(stack));
|
|
1922
|
-
args.reverse();
|
|
1923
|
-
const fn = vm.context.pluginFunctions[fnIdx];
|
|
1924
|
-
const pluginArgFault = faultedIn(args);
|
|
1925
|
-
if (pluginArgFault) {
|
|
1926
|
-
stack.push(pluginArgFault);
|
|
1927
|
-
} else if (!fn) {
|
|
1928
|
-
stack.push(numberValue(0));
|
|
1929
|
-
} else {
|
|
1930
|
-
const result = fn(args, context);
|
|
1931
|
-
if (result instanceof Promise) {
|
|
1932
|
-
const cacheKey = `plugin:${fnIdx}:${args.map((a) => String(a.value ?? "")).join("|")}`;
|
|
1933
|
-
const signal = vm.activeSignal;
|
|
1934
|
-
const packageId = vm.context.pluginFunctionOwners[fnIdx] ?? "";
|
|
1935
|
-
return { type: "pending", queryKey: cacheKey, resolver: result, packageId, signal };
|
|
1936
|
-
}
|
|
1937
|
-
stack.push(result);
|
|
1938
|
-
}
|
|
1939
|
-
break;
|
|
1940
|
-
}
|
|
1941
|
-
case 51 /* CALL_BUILTIN */: {
|
|
1942
|
-
const fnIdx = operandByte(opcodes, ip++, op, "builtin-function index");
|
|
1943
|
-
const argCount = operandByte(opcodes, ip++, op, "argument count");
|
|
1944
|
-
const arityError = builtinArityError(fnIdx, argCount);
|
|
1945
|
-
if (arityError) throw arityError;
|
|
1946
|
-
const args = [];
|
|
1947
|
-
let sawSymbolic = false;
|
|
1948
|
-
for (let i = 0; i < argCount; i++) {
|
|
1949
|
-
const arg = safePop(stack);
|
|
1950
|
-
if (arg.type === 9 /* Symbolic */) sawSymbolic = true;
|
|
1951
|
-
args.push(arg);
|
|
1952
|
-
}
|
|
1953
|
-
const builtinArgFault = faultedIn(args);
|
|
1954
|
-
if (builtinArgFault) {
|
|
1955
|
-
stack.push(builtinArgFault);
|
|
1956
|
-
break;
|
|
1957
|
-
}
|
|
1958
|
-
const fn = builtinFunctions[fnIdx];
|
|
1959
|
-
if (fn) {
|
|
1960
|
-
const ordered = args.reverse();
|
|
1961
|
-
const routeSymbolically = sawSymbolic && !SYMBOLIC_NATIVE_BUILTINS.has(fnIdx);
|
|
1962
|
-
stack.push(routeSymbolically ? symbolicBuiltin(fnIdx, ordered) : fn(ordered));
|
|
1963
|
-
}
|
|
1964
|
-
break;
|
|
1965
|
-
}
|
|
1966
|
-
case 150 /* DEFINE_USER_FUNCTION */: {
|
|
1967
|
-
const bodyIdx = operandByte(opcodes, ip++, op, "function-body index");
|
|
1968
|
-
const def = userFunctionBodies?.[bodyIdx];
|
|
1969
|
-
if (!def) {
|
|
1970
|
-
throw ErrorFactory.internal(
|
|
1971
|
-
"INTERNAL_MISSING_FUNCTION_BODY",
|
|
1972
|
-
`Internal error: DEFINE_USER_FUNCTION referenced missing body index ${bodyIdx}`,
|
|
1973
|
-
{ bodyIdx }
|
|
1974
|
-
);
|
|
1975
|
-
}
|
|
1976
|
-
vm.defineUserFunction(def.name, def.params, def.program);
|
|
1977
|
-
break;
|
|
1978
|
-
}
|
|
1979
|
-
case 151 /* CALL_USER_FUNCTION */: {
|
|
1980
|
-
const name = poolString(opcodes, ip++, strings, op, "function-name index");
|
|
1981
|
-
const argCount = operandByte(opcodes, ip++, op, "argument count");
|
|
1982
|
-
const args = [];
|
|
1983
|
-
for (let i = 0; i < argCount; i++) args.push(safePop(stack));
|
|
1984
|
-
args.reverse();
|
|
1985
|
-
const fn = vm.getUserFunction(name);
|
|
1986
|
-
if (!fn) {
|
|
1987
|
-
throw ErrorFactory.execution("UNDEFINED_FUNCTION", `Undefined function: ${name}`, { name });
|
|
1988
|
-
}
|
|
1989
|
-
if (argCount !== fn.params.length) {
|
|
1990
|
-
throw ErrorFactory.execution(
|
|
1991
|
-
"FUNCTION_ARITY_MISMATCH",
|
|
1992
|
-
`${name} expects ${fn.params.length} argument(s) but got ${argCount}`,
|
|
1993
|
-
{ name, expected: fn.params.length, actual: argCount }
|
|
1994
|
-
);
|
|
1995
|
-
}
|
|
1996
|
-
const frame = /* @__PURE__ */ new Map();
|
|
1997
|
-
for (let i = 0; i < fn.params.length; i++) frame.set(fn.params[i], args[i]);
|
|
1998
|
-
chargeFunctionCall();
|
|
1999
|
-
vm.pushCallFrame(frame);
|
|
2000
|
-
let bodyResult;
|
|
2001
|
-
try {
|
|
2002
|
-
bodyResult = executeBytecode(fn.program, vm, pipeline, expression, context);
|
|
2003
|
-
} finally {
|
|
2004
|
-
vm.popCallFrame();
|
|
2005
|
-
}
|
|
2006
|
-
if (bodyResult.type === "pending") {
|
|
2007
|
-
throw ErrorFactory.execution(
|
|
2008
|
-
"USER_FUNCTION_ASYNC_UNSUPPORTED",
|
|
2009
|
-
`${name}: user-defined functions with async bodies (weather, stocks, currency, ...) aren't supported`,
|
|
2010
|
-
{ name }
|
|
2011
|
-
);
|
|
2012
|
-
}
|
|
2013
|
-
if (bodyResult.type === "error") {
|
|
2014
|
-
throw bodyResult.error;
|
|
2015
|
-
}
|
|
2016
|
-
stack.push(bodyResult.value);
|
|
2017
|
-
break;
|
|
2018
|
-
}
|
|
2019
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2020
|
-
// §6 Variables (OpCode 60–63)
|
|
2021
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2022
|
-
case 60 /* LOAD_VAR */: {
|
|
2023
|
-
const varName = poolString(opcodes, ip++, strings, op, "variable-name index");
|
|
2024
|
-
const val = vm.getVar(varName);
|
|
2025
|
-
if (val !== void 0) {
|
|
2026
|
-
stack.push(val);
|
|
2027
|
-
} else if (symbolicTolerant) {
|
|
2028
|
-
stack.push(symbolicValue(varNode(varName)));
|
|
2029
|
-
} else {
|
|
2030
|
-
throw ErrorFactory.execution(
|
|
2031
|
-
"UNDEFINED_VARIABLE",
|
|
2032
|
-
`Undefined variable: ${varName}`,
|
|
2033
|
-
{ varName }
|
|
2034
|
-
);
|
|
2035
|
-
}
|
|
2036
|
-
break;
|
|
2037
|
-
}
|
|
2038
|
-
case 61 /* STORE_VAR */: {
|
|
2039
|
-
const val = safePop(stack);
|
|
2040
|
-
const varName = poolString(opcodes, ip++, strings, op, "variable-name index");
|
|
2041
|
-
vm.setVar(varName, hasArena ? persistentValue(val) : val);
|
|
2042
|
-
stack.push(val);
|
|
2043
|
-
break;
|
|
2044
|
-
}
|
|
2045
|
-
case 62 /* LOAD_GLOBAL_VAR */: {
|
|
2046
|
-
const varName = poolString(opcodes, ip++, strings, op, "variable-name index");
|
|
2047
|
-
const globalValue = sharedGlobalVariableStore.get(varName);
|
|
2048
|
-
if (globalValue === void 0) {
|
|
2049
|
-
throw ErrorFactory.internal({
|
|
2050
|
-
code: "GLOBAL_VARIABLE_NOT_RESOLVED",
|
|
2051
|
-
message: `Global variable "${varName}" was read before it resolved`,
|
|
2052
|
-
expected: `global variable "${varName}" to already be resolved (async preflight should guarantee this)`,
|
|
2053
|
-
found: "no value in the global variable store",
|
|
2054
|
-
context: { varName }
|
|
2055
|
-
});
|
|
2056
|
-
}
|
|
2057
|
-
stack.push(globalValue);
|
|
2058
|
-
break;
|
|
2059
|
-
}
|
|
2060
|
-
case 63 /* STORE_GLOBAL_VAR */: {
|
|
2061
|
-
const val = safePop(stack);
|
|
2062
|
-
const varName = poolString(opcodes, ip++, strings, op, "variable-name index");
|
|
2063
|
-
sharedGlobalVariableStore.set(varName, hasArena ? persistentValue(val) : val);
|
|
2064
|
-
stack.push(val);
|
|
2065
|
-
break;
|
|
2066
|
-
}
|
|
2067
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2068
|
-
// §7 Type conversions (OpCode 70–74, 140–145)
|
|
2069
|
-
// Every one of these restates a number in another notation, so a
|
|
2070
|
-
// faulted operand has nothing to restate and each propagates it
|
|
2071
|
-
// rather than converting the zero toNumber() reports. Without it
|
|
2072
|
-
// "(5 kg to m) as hex" answered 0x0 and "as binary" answered 0b0.
|
|
2073
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2074
|
-
case 70 /* TO_NUMBER */: {
|
|
2075
|
-
const v = safePop(stack);
|
|
2076
|
-
const toNumberFault = faultedOperand(v);
|
|
2077
|
-
if (toNumberFault) {
|
|
2078
|
-
stack.push(toNumberFault);
|
|
2079
|
-
break;
|
|
2080
|
-
}
|
|
2081
|
-
stack.push(numberValue(v.toNumber()));
|
|
2082
|
-
break;
|
|
2083
|
-
}
|
|
2084
|
-
case 71 /* TO_HEX */: {
|
|
2085
|
-
const v = safePop(stack);
|
|
2086
|
-
const toHexFault = faultedOperand(v);
|
|
2087
|
-
if (toHexFault) {
|
|
2088
|
-
stack.push(toHexFault);
|
|
2089
|
-
break;
|
|
2090
|
-
}
|
|
2091
|
-
if (v.type === 14 /* Colour */) {
|
|
2092
|
-
const c = v.value;
|
|
2093
|
-
stack.push(colourValue({ r: c.r, g: c.g, b: c.b, a: c.a, format: "hex" }));
|
|
2094
|
-
break;
|
|
2095
|
-
}
|
|
2096
|
-
stack.push(hexValue(v.type === 2 /* BigInt */ ? v.value : v.toNumber()));
|
|
2097
|
-
break;
|
|
2098
|
-
}
|
|
2099
|
-
case 74 /* TO_PERCENTAGE */: {
|
|
2100
|
-
const v = safePop(stack);
|
|
2101
|
-
const toPercentageFault = faultedOperand(v);
|
|
2102
|
-
if (toPercentageFault) {
|
|
2103
|
-
stack.push(toPercentageFault);
|
|
2104
|
-
break;
|
|
2105
|
-
}
|
|
2106
|
-
stack.push(percentageValue(v.toNumber()));
|
|
2107
|
-
break;
|
|
2108
|
-
}
|
|
2109
|
-
case 140 /* TO_FRACTION */: {
|
|
2110
|
-
const v = safePop(stack);
|
|
2111
|
-
const toFractionFault = faultedOperand(v);
|
|
2112
|
-
if (toFractionFault) {
|
|
2113
|
-
stack.push(toFractionFault);
|
|
2114
|
-
break;
|
|
2115
|
-
}
|
|
2116
|
-
stack.push(stringValue(v.rational !== void 0 ? rationalToFractionString(v.rational) : toFractionString(v.toNumber())));
|
|
2117
|
-
break;
|
|
2118
|
-
}
|
|
2119
|
-
case 141 /* TO_MULTIPLIER */: {
|
|
2120
|
-
const v = safePop(stack);
|
|
2121
|
-
const toMultiplierFault = faultedOperand(v);
|
|
2122
|
-
if (toMultiplierFault) {
|
|
2123
|
-
stack.push(toMultiplierFault);
|
|
2124
|
-
break;
|
|
2125
|
-
}
|
|
2126
|
-
stack.push(stringValue(toMultiplierString(v)));
|
|
2127
|
-
break;
|
|
2128
|
-
}
|
|
2129
|
-
case 142 /* TO_SCI */: {
|
|
2130
|
-
const v = safePop(stack);
|
|
2131
|
-
const toSciFault = faultedOperand(v);
|
|
2132
|
-
if (toSciFault) {
|
|
2133
|
-
stack.push(toSciFault);
|
|
2134
|
-
break;
|
|
2135
|
-
}
|
|
2136
|
-
stack.push(stringValue(toScientificString(v.toNumber())));
|
|
2137
|
-
break;
|
|
2138
|
-
}
|
|
2139
|
-
// Numeric, like TO_HEX above. A base is a way of writing a number, so
|
|
2140
|
-
// `(255 as binary) + 1` has to be 256; as a string it was 1.
|
|
2141
|
-
case 143 /* TO_BINARY */: {
|
|
2142
|
-
const v = safePop(stack);
|
|
2143
|
-
const toBinaryFault = faultedOperand(v);
|
|
2144
|
-
if (toBinaryFault) {
|
|
2145
|
-
stack.push(toBinaryFault);
|
|
2146
|
-
break;
|
|
2147
|
-
}
|
|
2148
|
-
stack.push(hexValue(v.type === 2 /* BigInt */ ? v.value : v.toNumber(), "bin"));
|
|
2149
|
-
break;
|
|
2150
|
-
}
|
|
2151
|
-
case 144 /* TO_OCTAL */: {
|
|
2152
|
-
const v = safePop(stack);
|
|
2153
|
-
const toOctalFault = faultedOperand(v);
|
|
2154
|
-
if (toOctalFault) {
|
|
2155
|
-
stack.push(toOctalFault);
|
|
2156
|
-
break;
|
|
2157
|
-
}
|
|
2158
|
-
stack.push(hexValue(v.type === 2 /* BigInt */ ? v.value : v.toNumber(), "oct"));
|
|
2159
|
-
break;
|
|
2160
|
-
}
|
|
2161
|
-
case 145 /* CALL_AS_CONVERTER */: {
|
|
2162
|
-
const name = stringOperand(safePop(stack), op, "converter name").toLowerCase();
|
|
2163
|
-
const value = safePop(stack);
|
|
2164
|
-
const converterFault = faultedOperand(value);
|
|
2165
|
-
if (converterFault) {
|
|
2166
|
-
stack.push(converterFault);
|
|
2167
|
-
break;
|
|
2168
|
-
}
|
|
2169
|
-
const converter = asConverterRegistry.get(name);
|
|
2170
|
-
if (!converter) {
|
|
2171
|
-
stack.push(errorValue("UNKNOWN_AS_CONVERTER", `Unknown converter "as ${name}"`));
|
|
2172
|
-
} else {
|
|
2173
|
-
stack.push(converter(value));
|
|
2174
|
-
}
|
|
2175
|
-
break;
|
|
2176
|
-
}
|
|
2177
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2178
|
-
// §8 UoM (OpCode 80–84)
|
|
2179
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2180
|
-
case 80 /* UOM_CONVERT */: {
|
|
2181
|
-
const unit = stringOperand(safePop(stack), op, "unit name");
|
|
2182
|
-
const operand = safePop(stack);
|
|
2183
|
-
const faulted = faultedOperand(operand);
|
|
2184
|
-
if (faulted) {
|
|
2185
|
-
stack.push(faulted);
|
|
2186
|
-
break;
|
|
2187
|
-
}
|
|
2188
|
-
const money = moneyExactMagnitude(operand, unit);
|
|
2189
|
-
if (money) stack.push(uomValueExact(operand.toNumber(), unit, money));
|
|
2190
|
-
else stack.push(uomValue(operand.toNumber(), unit));
|
|
2191
|
-
break;
|
|
2192
|
-
}
|
|
2193
|
-
case 81 /* UOM_CONVERT_TO */: {
|
|
2194
|
-
const toUnit = stringOperand(safePop(stack), op, "target unit name");
|
|
2195
|
-
const fromUnit = stringOperand(safePop(stack), op, "source unit name");
|
|
2196
|
-
const operand = safePop(stack);
|
|
2197
|
-
const faulted = faultedOperand(operand);
|
|
2198
|
-
if (faulted) {
|
|
2199
|
-
stack.push(faulted);
|
|
2200
|
-
break;
|
|
2201
|
-
}
|
|
2202
|
-
const val = operand.toNumber();
|
|
2203
|
-
const measure = getMeasure(fromUnit);
|
|
2204
|
-
const isCurrency = sharedCurrencyExchange.isCurrency(fromUnit) && sharedCurrencyExchange.isCurrency(toUnit);
|
|
2205
|
-
if (measure && getMeasure(toUnit) === measure) {
|
|
2206
|
-
stack.push(uomValue(convertUnit(val, fromUnit, toUnit), toUnit));
|
|
2207
|
-
} else if (isCurrency) {
|
|
2208
|
-
const converted = sharedCurrencyExchange.convertSync(val, fromUnit, toUnit);
|
|
2209
|
-
if (converted !== null) {
|
|
2210
|
-
stack.push(uomValue(converted, toUnit));
|
|
2211
|
-
} else {
|
|
2212
|
-
stack.push(errorValue("CURRENCY_RATE_UNAVAILABLE", `No exchange rate available for ${fromUnit} to ${toUnit}`));
|
|
2213
|
-
}
|
|
2214
|
-
} else {
|
|
2215
|
-
const rate = convertRate(val, fromUnit, toUnit);
|
|
2216
|
-
if (rate !== null) {
|
|
2217
|
-
stack.push(uomValue(rate, toUnit));
|
|
2218
|
-
} else {
|
|
2219
|
-
stack.push(incompatibleConversionError(fromUnit, toUnit));
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
break;
|
|
2223
|
-
}
|
|
2224
|
-
case 85 /* UOM_POSSIBILITIES */: {
|
|
2225
|
-
const unit = stringOperand(safePop(stack), op, "unit name");
|
|
2226
|
-
const possibilities = getConvertiblePossibilities(unit);
|
|
2227
|
-
stack.push(stringValue(possibilities.length > 0 ? possibilities.join(", ") : `No known units for "${unit}"`));
|
|
2228
|
-
break;
|
|
2229
|
-
}
|
|
2230
|
-
case 83 /* UOM_BEST */: {
|
|
2231
|
-
const unit = stringOperand(safePop(stack), op, "unit name");
|
|
2232
|
-
const operand = safePop(stack);
|
|
2233
|
-
const faulted = faultedOperand(operand);
|
|
2234
|
-
if (faulted) {
|
|
2235
|
-
stack.push(faulted);
|
|
2236
|
-
break;
|
|
2237
|
-
}
|
|
2238
|
-
const { value, unit: bestUnit } = getBestUnit(operand.toNumber(), unit);
|
|
2239
|
-
stack.push(uomValue(value, bestUnit));
|
|
2240
|
-
break;
|
|
2241
|
-
}
|
|
2242
|
-
case 84 /* UOM_CONVERT_IN */: {
|
|
2243
|
-
const toUnit = stringOperand(safePop(stack), op, "target unit name");
|
|
2244
|
-
const left = safePop(stack);
|
|
2245
|
-
const faulted = faultedOperand(left);
|
|
2246
|
-
if (faulted) {
|
|
2247
|
-
stack.push(faulted);
|
|
2248
|
-
break;
|
|
2249
|
-
}
|
|
2250
|
-
if (left.type === 6 /* Uom */) {
|
|
2251
|
-
const fromUnit = left.unit;
|
|
2252
|
-
const val = left.toNumber();
|
|
2253
|
-
const measure = getMeasure(fromUnit);
|
|
2254
|
-
const isCurrency = sharedCurrencyExchange.isCurrency(fromUnit) && sharedCurrencyExchange.isCurrency(toUnit);
|
|
2255
|
-
if (measure && getMeasure(toUnit) === measure) {
|
|
2256
|
-
stack.push(uomValue(convertUnit(val, fromUnit, toUnit), toUnit));
|
|
2257
|
-
} else if (isCurrency) {
|
|
2258
|
-
const converted = sharedCurrencyExchange.convertSync(val, fromUnit, toUnit);
|
|
2259
|
-
if (converted !== null) {
|
|
2260
|
-
stack.push(uomValue(converted, toUnit));
|
|
2261
|
-
} else {
|
|
2262
|
-
stack.push(errorValue("CURRENCY_RATE_UNAVAILABLE", `No exchange rate available for ${fromUnit} to ${toUnit}`));
|
|
2263
|
-
}
|
|
2264
|
-
} else {
|
|
2265
|
-
const rate = convertRate(val, fromUnit, toUnit);
|
|
2266
|
-
if (rate !== null) {
|
|
2267
|
-
stack.push(uomValue(rate, toUnit));
|
|
2268
|
-
} else {
|
|
2269
|
-
stack.push(incompatibleConversionError(fromUnit, toUnit));
|
|
2270
|
-
}
|
|
2271
|
-
}
|
|
2272
|
-
} else {
|
|
2273
|
-
stack.push(uomValue(left.toNumber(), toUnit));
|
|
2274
|
-
}
|
|
2275
|
-
break;
|
|
2276
|
-
}
|
|
2277
|
-
case 82 /* UOM_GET_VALUE */: {
|
|
2278
|
-
const v = safePop(stack);
|
|
2279
|
-
const getValueFault = faultedOperand(v);
|
|
2280
|
-
if (getValueFault) {
|
|
2281
|
-
stack.push(getValueFault);
|
|
2282
|
-
break;
|
|
2283
|
-
}
|
|
2284
|
-
stack.push(numberValue(v.toNumber()));
|
|
2285
|
-
break;
|
|
2286
|
-
}
|
|
2287
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2288
|
-
// §8.5 Rate, "quantity per unit of something" (OpCode 110–112)
|
|
2289
|
-
// See vm/Value.ts's rateValue()/isRateUnit()/splitRateUnit().
|
|
2290
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2291
|
-
case 110 /* RATE_DIV */: {
|
|
2292
|
-
const denominatorVal = safePop(stack);
|
|
2293
|
-
const numeratorVal = safePop(stack);
|
|
2294
|
-
const rateDivFault = faultedOperand(numeratorVal, denominatorVal);
|
|
2295
|
-
if (rateDivFault) {
|
|
2296
|
-
stack.push(rateDivFault);
|
|
2297
|
-
break;
|
|
2298
|
-
}
|
|
2299
|
-
if (denominatorVal.type !== 6 /* Uom */ || !denominatorVal.unit) {
|
|
2300
|
-
stack.push(errorValue("RATE_MISSING_DENOMINATOR_UNIT", 'Cannot build a rate: the right-hand side of "/" has no unit'));
|
|
2301
|
-
break;
|
|
2302
|
-
}
|
|
2303
|
-
const numeratorUnit = numeratorVal.type === 6 /* Uom */ ? numeratorVal.unit ?? "" : "";
|
|
2304
|
-
stack.push(rateValue(numeratorVal.toNumber() / denominatorVal.toNumber(), numeratorUnit, denominatorVal.unit));
|
|
2305
|
-
break;
|
|
2306
|
-
}
|
|
2307
|
-
case 111 /* RATE_MUL */: {
|
|
2308
|
-
const multiplier = safePop(stack);
|
|
2309
|
-
const rate = safePop(stack);
|
|
2310
|
-
const rateMulFault = faultedOperand(rate, multiplier);
|
|
2311
|
-
if (rateMulFault) {
|
|
2312
|
-
stack.push(rateMulFault);
|
|
2313
|
-
break;
|
|
2314
|
-
}
|
|
2315
|
-
if (rate.type !== 6 /* Uom */ || !isRateUnit(rate.unit)) {
|
|
2316
|
-
stack.push(errorValue("RATE_MUL_LEFT_NOT_A_RATE", 'Left-hand side of a rate multiplication must be a rate (e.g. "$50/week")'));
|
|
2317
|
-
break;
|
|
2318
|
-
}
|
|
2319
|
-
if (multiplier.type !== 6 /* Uom */ || !multiplier.unit) {
|
|
2320
|
-
stack.push(errorValue("RATE_MUL_RIGHT_MISSING_UNIT", "Right-hand side of a rate multiplication must have a unit matching the rate's denominator"));
|
|
2321
|
-
break;
|
|
2322
|
-
}
|
|
2323
|
-
stack.push(multiplyRateByMatchingUom(rate, multiplier));
|
|
2324
|
-
break;
|
|
2325
|
-
}
|
|
2326
|
-
case 112 /* RATE_CONVERT */: {
|
|
2327
|
-
const newDenominatorUnit = stringOperand(safePop(stack), op, "denominator unit name");
|
|
2328
|
-
const rate = safePop(stack);
|
|
2329
|
-
const rateConvertFault = faultedOperand(rate);
|
|
2330
|
-
if (rateConvertFault) {
|
|
2331
|
-
stack.push(rateConvertFault);
|
|
2332
|
-
break;
|
|
2333
|
-
}
|
|
2334
|
-
if (rate.type !== 6 /* Uom */ || !isRateUnit(rate.unit)) {
|
|
2335
|
-
stack.push(errorValue("RATE_CONVERT_NOT_A_RATE", "Cannot convert a non-rate value's denominator"));
|
|
2336
|
-
break;
|
|
2337
|
-
}
|
|
2338
|
-
const { numerator, denominator } = splitRateUnit(rate.unit);
|
|
2339
|
-
const rateMeasure = getMeasure(denominator);
|
|
2340
|
-
const targetMeasure = getMeasure(newDenominatorUnit);
|
|
2341
|
-
if (!rateMeasure || rateMeasure !== targetMeasure) {
|
|
2342
|
-
stack.push(errorValue(
|
|
2343
|
-
"RATE_CONVERT_MEASURE_MISMATCH",
|
|
2344
|
-
`Cannot convert a "${denominator}"-denominated rate to "${newDenominatorUnit}" \u2014 different measures`
|
|
2345
|
-
));
|
|
2346
|
-
break;
|
|
2347
|
-
}
|
|
2348
|
-
const factor = convertUnit(1, newDenominatorUnit, denominator);
|
|
2349
|
-
stack.push(rateValue(rate.toNumber() * factor, numerator, newDenominatorUnit));
|
|
2350
|
-
break;
|
|
2351
|
-
}
|
|
2352
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2353
|
-
// §8.7 Time, clock-time-of-day (OpCode 120)
|
|
2354
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2355
|
-
case 120 /* CLOCK_TIME_TODAY */: {
|
|
2356
|
-
const minutesValue = safePop(stack);
|
|
2357
|
-
const clockFault = faultedOperand(minutesValue);
|
|
2358
|
-
if (clockFault) {
|
|
2359
|
-
stack.push(clockFault);
|
|
2360
|
-
break;
|
|
2361
|
-
}
|
|
2362
|
-
const totalMinutes = minutesValue.toNumber();
|
|
2363
|
-
const now = /* @__PURE__ */ new Date();
|
|
2364
|
-
const anchored = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
|
2365
|
-
anchored.setMinutes(totalMinutes);
|
|
2366
|
-
stack.push(datetimeValue(anchored.getTime()));
|
|
2367
|
-
break;
|
|
2368
|
-
}
|
|
2369
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2370
|
-
// §9 Datetime (OpCode 90–93)
|
|
2371
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2372
|
-
case 90 /* DATE_NOW */:
|
|
2373
|
-
stack.push(datetimeValue(Date.now()));
|
|
2374
|
-
break;
|
|
2375
|
-
case 95 /* DATE_LITERAL */:
|
|
2376
|
-
stack.push(datetimeValue(numbers[poolIndex(opcodes, ip++, op, "constant-pool index", numbers.length, "number-pool")]));
|
|
2377
|
-
break;
|
|
2378
|
-
case 91 /* DATE_ADD */: {
|
|
2379
|
-
const durValue = safePop(stack), dtValue = safePop(stack);
|
|
2380
|
-
const dateAddFault = faultedOperand(dtValue, durValue);
|
|
2381
|
-
if (dateAddFault) {
|
|
2382
|
-
stack.push(dateAddFault);
|
|
2383
|
-
break;
|
|
2384
|
-
}
|
|
2385
|
-
stack.push(datetimeValue(shiftDatetime(dtValue.toNumber(), durValue, 1, vm)));
|
|
2386
|
-
break;
|
|
2387
|
-
}
|
|
2388
|
-
case 92 /* DATE_SUB */: {
|
|
2389
|
-
const durValue = safePop(stack), dtValue = safePop(stack);
|
|
2390
|
-
const dateSubFault = faultedOperand(dtValue, durValue);
|
|
2391
|
-
if (dateSubFault) {
|
|
2392
|
-
stack.push(dateSubFault);
|
|
2393
|
-
break;
|
|
2394
|
-
}
|
|
2395
|
-
stack.push(datetimeValue(shiftDatetime(dtValue.toNumber(), durValue, -1, vm)));
|
|
2396
|
-
break;
|
|
2397
|
-
}
|
|
2398
|
-
case 96 /* DATE_WORKDAY_OFFSET */: {
|
|
2399
|
-
const workdayDirection = operandByte(opcodes, ip++, op, "workday offset direction") === 1 ? -1 : 1;
|
|
2400
|
-
const anchorValue = safePop(stack), countValue = safePop(stack);
|
|
2401
|
-
const offsetFault = faultedOperand(countValue, anchorValue);
|
|
2402
|
-
if (offsetFault) {
|
|
2403
|
-
stack.push(offsetFault);
|
|
2404
|
-
break;
|
|
2405
|
-
}
|
|
2406
|
-
if (anchorValue.type !== 4 /* Datetime */) {
|
|
2407
|
-
stack.push(errorValue(
|
|
2408
|
-
CoreErrorCodes.WORKDAY_OFFSET_EXPECTED_DATE,
|
|
2409
|
-
`"working days after/before/from" expects a date to count from, got ${ValueType[anchorValue.type] ?? "an unsupported value"}`
|
|
2410
|
-
));
|
|
2411
|
-
break;
|
|
2412
|
-
}
|
|
2413
|
-
stack.push(datetimeValue(addBusinessDays2(anchorValue.toNumber(), workdayDirection * countValue.toNumber(), vm)));
|
|
2414
|
-
break;
|
|
2415
|
-
}
|
|
2416
|
-
case 97 /* DATE_WORKDAYS_BETWEEN */: {
|
|
2417
|
-
const endValue = safePop(stack), startValue = safePop(stack);
|
|
2418
|
-
const betweenFault = faultedOperand(startValue, endValue);
|
|
2419
|
-
if (betweenFault) {
|
|
2420
|
-
stack.push(betweenFault);
|
|
2421
|
-
break;
|
|
2422
|
-
}
|
|
2423
|
-
if (startValue.type !== 4 /* Datetime */ || endValue.type !== 4 /* Datetime */) {
|
|
2424
|
-
stack.push(errorValue(
|
|
2425
|
-
CoreErrorCodes.WORKDAYS_BETWEEN_EXPECTED_DATES,
|
|
2426
|
-
`"working days between" expects two dates, got ${ValueType[startValue.type] ?? "an unsupported value"} and ${ValueType[endValue.type] ?? "an unsupported value"}`
|
|
2427
|
-
));
|
|
2428
|
-
break;
|
|
2429
|
-
}
|
|
2430
|
-
const spanLimitDays = Math.ceil((vm.getMaxDateOffsetYears() - vm.getMinDateOffsetYears()) * 366);
|
|
2431
|
-
const workdayCount = countBusinessDaysBetween(startValue.toNumber(), endValue.toNumber(), (ms) => vm.isHoliday(ms), spanLimitDays);
|
|
2432
|
-
if (workdayCount === null) {
|
|
2433
|
-
stack.push(errorValue(
|
|
2434
|
-
CoreErrorCodes.WORKDAYS_BETWEEN_RANGE_TOO_LARGE,
|
|
2435
|
-
`The two dates are more than ${vm.getMaxDateOffsetYears() - vm.getMinDateOffsetYears()} years apart, past the working-day count's limit`
|
|
2436
|
-
));
|
|
2437
|
-
break;
|
|
2438
|
-
}
|
|
2439
|
-
stack.push(numberValue(workdayCount));
|
|
2440
|
-
break;
|
|
2441
|
-
}
|
|
2442
|
-
case 93 /* DATE_NEXT_WEEKDAY */:
|
|
2443
|
-
case 94 /* DATE_LAST_WEEKDAY */: {
|
|
2444
|
-
const targetDayValue = safePop(stack);
|
|
2445
|
-
const nowValue = safePop(stack);
|
|
2446
|
-
const weekdayFault = faultedOperand(nowValue, targetDayValue);
|
|
2447
|
-
if (weekdayFault) {
|
|
2448
|
-
stack.push(weekdayFault);
|
|
2449
|
-
break;
|
|
2450
|
-
}
|
|
2451
|
-
const targetDay = targetDayValue.toNumber();
|
|
2452
|
-
const now = nowValue.toNumber();
|
|
2453
|
-
const currentDay = new Date(now).getDay();
|
|
2454
|
-
let diffDays = op === 93 /* DATE_NEXT_WEEKDAY */ ? (targetDay - currentDay + 7) % 7 : (currentDay - targetDay + 7) % 7;
|
|
2455
|
-
if (diffDays === 0) diffDays = 7;
|
|
2456
|
-
stack.push(datetimeValue(addCalendarDays(now, op === 93 /* DATE_NEXT_WEEKDAY */ ? diffDays : -diffDays)));
|
|
2457
|
-
break;
|
|
2458
|
-
}
|
|
2459
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2460
|
-
// §10 Matrix (OpCode 152+, see parser/OpCode.ts's Matrix band)
|
|
2461
|
-
// Replaces the old ARR_* vector-only opcodes (100-107), which
|
|
2462
|
-
// were never emitted by any registered parselet in production
|
|
2463
|
-
// confirmed dead code, deleted outright rather than repurposed.
|
|
2464
|
-
// See vm/Value.ts's MatrixData and vm/MatrixOps.ts's shared
|
|
2465
|
-
// column-major storage helpers.
|
|
2466
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2467
|
-
case 152 /* MAT_NEW */: {
|
|
2468
|
-
const rows = operandByte(opcodes, ip++, op, "row count");
|
|
2469
|
-
const cols = operandByte(opcodes, ip++, op, "column count");
|
|
2470
|
-
const count = rows * cols;
|
|
2471
|
-
const rowMajor = checkedArray(count, "matrix cells");
|
|
2472
|
-
let cellFault = null;
|
|
2473
|
-
for (let i = count - 1; i >= 0; i--) {
|
|
2474
|
-
const cellVal = safePop(stack);
|
|
2475
|
-
if (cellFault === null) cellFault = faultedOperand(cellVal);
|
|
2476
|
-
rowMajor[i] = cellVal.type === 10 /* Boolean */ ? cellVal.value : cellVal.type === 9 /* Symbolic */ ? cellVal.value : cellVal.toNumber();
|
|
2477
|
-
}
|
|
2478
|
-
if (cellFault) {
|
|
2479
|
-
stack.push(cellFault);
|
|
2480
|
-
break;
|
|
2481
|
-
}
|
|
2482
|
-
stack.push(matrixValue(rows, cols, rowMajorToColumnMajor(rows, cols, rowMajor)));
|
|
2483
|
-
break;
|
|
2484
|
-
}
|
|
2485
|
-
case 153 /* MAT_INDEX1 */: {
|
|
2486
|
-
const indexVal = safePop(stack), matrixVal = safePop(stack);
|
|
2487
|
-
const index1Fault = faultedOperand(matrixVal, indexVal);
|
|
2488
|
-
if (index1Fault) {
|
|
2489
|
-
stack.push(index1Fault);
|
|
2490
|
-
break;
|
|
2491
|
-
}
|
|
2492
|
-
if (matrixVal.type !== 7 /* Matrix */) {
|
|
2493
|
-
stack.push(errorValue("MATRIX_INDEX_NOT_A_MATRIX", `Cannot index a non-matrix value with "[...]".`));
|
|
2494
|
-
break;
|
|
2495
|
-
}
|
|
2496
|
-
const m = matrixVal.value;
|
|
2497
|
-
const index = Math.trunc(indexVal.toNumber());
|
|
2498
|
-
if (index < 0 || index >= m.data.length) {
|
|
2499
|
-
stack.push(errorValue(
|
|
2500
|
-
"MATRIX_INDEX_OUT_OF_BOUNDS",
|
|
2501
|
-
`Index ${index} is out of bounds for a ${m.rows}x${m.cols} matrix (valid range: 0-${m.data.length - 1}).`
|
|
2502
|
-
));
|
|
2503
|
-
break;
|
|
2504
|
-
}
|
|
2505
|
-
const cell = matIndex(m, index);
|
|
2506
|
-
stack.push(matrixEntryToValue(cell));
|
|
2507
|
-
break;
|
|
2508
|
-
}
|
|
2509
|
-
case 154 /* MAT_INDEX2 */: {
|
|
2510
|
-
const colVal = safePop(stack), rowVal = safePop(stack), matrixVal = safePop(stack);
|
|
2511
|
-
const index2Fault = faultedOperand(matrixVal, rowVal, colVal);
|
|
2512
|
-
if (index2Fault) {
|
|
2513
|
-
stack.push(index2Fault);
|
|
2514
|
-
break;
|
|
2515
|
-
}
|
|
2516
|
-
if (matrixVal.type !== 7 /* Matrix */) {
|
|
2517
|
-
stack.push(errorValue("MATRIX_INDEX_NOT_A_MATRIX", `Cannot index a non-matrix value with "[...]".`));
|
|
2518
|
-
break;
|
|
2519
|
-
}
|
|
2520
|
-
const m = matrixVal.value;
|
|
2521
|
-
const row = Math.trunc(rowVal.toNumber());
|
|
2522
|
-
const col = Math.trunc(colVal.toNumber());
|
|
2523
|
-
if (!inBounds(m, row, col)) {
|
|
2524
|
-
stack.push(errorValue(
|
|
2525
|
-
"MATRIX_INDEX_OUT_OF_BOUNDS",
|
|
2526
|
-
`[${row}, ${col}] is out of bounds for a ${m.rows}x${m.cols} matrix.`
|
|
2527
|
-
));
|
|
2528
|
-
break;
|
|
2529
|
-
}
|
|
2530
|
-
const cell = matAt(m, row, col);
|
|
2531
|
-
stack.push(matrixEntryToValue(cell));
|
|
2532
|
-
break;
|
|
2533
|
-
}
|
|
2534
|
-
case 156 /* RANGE_NEW */: {
|
|
2535
|
-
const maxVal = safePop(stack), minVal = safePop(stack);
|
|
2536
|
-
const rangeFault = faultedOperand(minVal, maxVal);
|
|
2537
|
-
if (rangeFault) {
|
|
2538
|
-
stack.push(rangeFault);
|
|
2539
|
-
break;
|
|
2540
|
-
}
|
|
2541
|
-
if (minVal.type !== 0 /* Number */ || maxVal.type !== 0 /* Number */) {
|
|
2542
|
-
stack.push(errorValue("INVALID_RANGE_BOUND", `A range's bounds must be plain numbers (e.g. "0:3").`));
|
|
2543
|
-
break;
|
|
2544
|
-
}
|
|
2545
|
-
const min = minVal.value;
|
|
2546
|
-
const max = maxVal.value;
|
|
2547
|
-
if (!Number.isInteger(min) || !Number.isInteger(max)) {
|
|
2548
|
-
stack.push(errorValue("NON_INTEGER_RANGE_BOUND", `A range's bounds must be whole numbers, got "${min}:${max}".`));
|
|
2549
|
-
break;
|
|
2550
|
-
}
|
|
2551
|
-
if (min > max) {
|
|
2552
|
-
stack.push(errorValue(
|
|
2553
|
-
"DESCENDING_RANGE",
|
|
2554
|
-
`A range's min (${min}) cannot be greater than its max (${max}) \u2014 did you mean "${max}:${min}"?`
|
|
2555
|
-
));
|
|
2556
|
-
break;
|
|
2557
|
-
}
|
|
2558
|
-
stack.push(rangeValue(min, max));
|
|
2559
|
-
break;
|
|
2560
|
-
}
|
|
2561
|
-
case 155 /* MAT_SLICE */: {
|
|
2562
|
-
const colRangeVal = safePop(stack), rowRangeVal = safePop(stack), matrixVal = safePop(stack);
|
|
2563
|
-
const sliceFault = faultedOperand(matrixVal, rowRangeVal, colRangeVal);
|
|
2564
|
-
if (sliceFault) {
|
|
2565
|
-
stack.push(sliceFault);
|
|
2566
|
-
break;
|
|
2567
|
-
}
|
|
2568
|
-
if (matrixVal.type !== 7 /* Matrix */) {
|
|
2569
|
-
stack.push(errorValue("MATRIX_INDEX_NOT_A_MATRIX", `Cannot slice a non-matrix value with "[...]".`));
|
|
2570
|
-
break;
|
|
2571
|
-
}
|
|
2572
|
-
if (rowRangeVal.type !== 8 /* Range */ || colRangeVal.type !== 8 /* Range */) {
|
|
2573
|
-
stack.push(errorValue("INVALID_MATRIX_SLICE_BOUND", `Matrix slicing needs range bounds (e.g. "a[0:1, 1:2]").`));
|
|
2574
|
-
break;
|
|
2575
|
-
}
|
|
2576
|
-
const m = matrixVal.value;
|
|
2577
|
-
const rowRange = rowRangeVal.value;
|
|
2578
|
-
const colRange = colRangeVal.value;
|
|
2579
|
-
if (rowRange.min < 0 || rowRange.max >= m.rows || colRange.min < 0 || colRange.max >= m.cols) {
|
|
2580
|
-
stack.push(errorValue(
|
|
2581
|
-
"MATRIX_INDEX_OUT_OF_BOUNDS",
|
|
2582
|
-
`Slice [${rowRange.min}:${rowRange.max}, ${colRange.min}:${colRange.max}] is out of bounds for a ${m.rows}x${m.cols} matrix.`
|
|
2583
|
-
));
|
|
2584
|
-
break;
|
|
2585
|
-
}
|
|
2586
|
-
const newRows = rowRange.max - rowRange.min + 1;
|
|
2587
|
-
const newCols = colRange.max - colRange.min + 1;
|
|
2588
|
-
const data = checkedArray(newRows * newCols, "matrix cells");
|
|
2589
|
-
for (let r = 0; r < newRows; r++) {
|
|
2590
|
-
for (let c = 0; c < newCols; c++) {
|
|
2591
|
-
data[r + c * newRows] = matAt(m, rowRange.min + r, colRange.min + c);
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
stack.push(matrixValue(newRows, newCols, data));
|
|
2595
|
-
break;
|
|
2596
|
-
}
|
|
2597
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2598
|
-
// §11 map/reduce (OpCode 157-158, see parser/OpCode.ts's Matrix
|
|
2599
|
-
// band). See packages/mapreduce/ for the parselets that decide
|
|
2600
|
-
// `kind` (0=inline anonymous body, 1=builtin function,
|
|
2601
|
-
// 2=user-defined function) at parse time, and
|
|
2602
|
-
// `BytecodeBuilder.ts`'s AnonymousBodyDef doc comment for why
|
|
2603
|
-
// kind-0 bodies are a SEPARATE side-table from userFunctionBodies.
|
|
2604
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2605
|
-
case 157 /* MAP_INVOKE */: {
|
|
2606
|
-
const kind = operandByte(opcodes, ip++, op, "body kind");
|
|
2607
|
-
const ref = operandByte(opcodes, ip++, op, "body reference");
|
|
2608
|
-
const collectionCount = operandByte(opcodes, ip++, op, "collection count");
|
|
2609
|
-
requireKnownBodyKind(kind, ref, op);
|
|
2610
|
-
const rawCollections = new Array(collectionCount);
|
|
2611
|
-
for (let i = collectionCount - 1; i >= 0; i--) rawCollections[i] = safePop(stack);
|
|
2612
|
-
let paramNames = [];
|
|
2613
|
-
let program;
|
|
2614
|
-
if (kind === 0) {
|
|
2615
|
-
const def = anonymousBodies?.[ref];
|
|
2616
|
-
if (!def) {
|
|
2617
|
-
throw ErrorFactory.internal(
|
|
2618
|
-
"INTERNAL_MISSING_ANONYMOUS_BODY",
|
|
2619
|
-
`Internal error: MAP_INVOKE referenced missing anonymous body index ${ref}`,
|
|
2620
|
-
{ ref }
|
|
2621
|
-
);
|
|
2622
|
-
}
|
|
2623
|
-
paramNames = def.params;
|
|
2624
|
-
program = def.program;
|
|
2625
|
-
} else if (kind === 2) {
|
|
2626
|
-
const name = pooledString(strings, ref, op);
|
|
2627
|
-
const fn = vm.getUserFunction(name);
|
|
2628
|
-
if (!fn) {
|
|
2629
|
-
stack.push(errorValue("UNDEFINED_FUNCTION", `map: undefined function "${name}"`));
|
|
2630
|
-
break;
|
|
2631
|
-
}
|
|
2632
|
-
if (fn.params.length !== collectionCount) {
|
|
2633
|
-
stack.push(errorValue(
|
|
2634
|
-
"FUNCTION_ARITY_MISMATCH",
|
|
2635
|
-
`map: "${name}" expects ${fn.params.length} argument(s) but ${collectionCount} collection(s) were given`
|
|
2636
|
-
));
|
|
2637
|
-
break;
|
|
2638
|
-
}
|
|
2639
|
-
paramNames = fn.params;
|
|
2640
|
-
program = fn.program;
|
|
2641
|
-
}
|
|
2642
|
-
let collectionLength = -1;
|
|
2643
|
-
const cellArrays = new Array(collectionCount);
|
|
2644
|
-
let mapEarlyError;
|
|
2645
|
-
for (let i = 0; i < collectionCount; i++) {
|
|
2646
|
-
const resolved = collectionToValues(rawCollections[i], vm.getMaxCollectionSize());
|
|
2647
|
-
if (!Array.isArray(resolved)) {
|
|
2648
|
-
mapEarlyError = resolved;
|
|
2649
|
-
break;
|
|
2650
|
-
}
|
|
2651
|
-
chargeAllocation(resolved.length, "collection elements");
|
|
2652
|
-
if (collectionLength === -1) {
|
|
2653
|
-
collectionLength = resolved.length;
|
|
2654
|
-
} else if (resolved.length !== collectionLength) {
|
|
2655
|
-
mapEarlyError = errorValue(
|
|
2656
|
-
"MAP_COLLECTION_LENGTH_MISMATCH",
|
|
2657
|
-
`map: all collections must have the same length (got ${collectionLength} and ${resolved.length}).`
|
|
2658
|
-
);
|
|
2659
|
-
break;
|
|
2660
|
-
}
|
|
2661
|
-
cellArrays[i] = resolved;
|
|
2662
|
-
}
|
|
2663
|
-
if (mapEarlyError) {
|
|
2664
|
-
stack.push(mapEarlyError);
|
|
2665
|
-
break;
|
|
2666
|
-
}
|
|
2667
|
-
const resultData = checkedArray(collectionLength, "matrix cells");
|
|
2668
|
-
let mapError;
|
|
2669
|
-
for (let i = 0; i < collectionLength; i++) {
|
|
2670
|
-
const args = new Array(collectionCount);
|
|
2671
|
-
for (let j = 0; j < collectionCount; j++) args[j] = cellArrays[j][i];
|
|
2672
|
-
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);
|
|
2673
|
-
const resultFault = faultedOperand(resultVal);
|
|
2674
|
-
if (resultFault) {
|
|
2675
|
-
mapError = resultFault;
|
|
2676
|
-
break;
|
|
2677
|
-
}
|
|
2678
|
-
resultData[i] = resultVal.type === 10 /* Boolean */ ? resultVal.value : resultVal.type === 9 /* Symbolic */ ? resultVal.value : resultVal.toNumber();
|
|
2679
|
-
}
|
|
2680
|
-
if (mapError) {
|
|
2681
|
-
stack.push(mapError);
|
|
2682
|
-
break;
|
|
2683
|
-
}
|
|
2684
|
-
const firstCollection = rawCollections[0];
|
|
2685
|
-
const sourceShape = firstCollection?.type === 7 /* Matrix */ ? firstCollection.value : void 0;
|
|
2686
|
-
if (sourceShape && sourceShape.rows * sourceShape.cols === collectionLength) {
|
|
2687
|
-
stack.push(matrixValue(sourceShape.rows, sourceShape.cols, resultData));
|
|
2688
|
-
} else {
|
|
2689
|
-
stack.push(matrixValue(1, collectionLength, resultData));
|
|
2690
|
-
}
|
|
2691
|
-
break;
|
|
2692
|
-
}
|
|
2693
|
-
case 158 /* REDUCE_INVOKE */: {
|
|
2694
|
-
const kind = operandByte(opcodes, ip++, op, "body kind");
|
|
2695
|
-
const ref = operandByte(opcodes, ip++, op, "body reference");
|
|
2696
|
-
const hasInitial = operandByte(opcodes, ip++, op, "initial-value flag");
|
|
2697
|
-
requireKnownBodyKind(kind, ref, op);
|
|
2698
|
-
const initialVal = hasInitial ? safePop(stack) : void 0;
|
|
2699
|
-
const collectionVal = safePop(stack);
|
|
2700
|
-
let paramNames = [];
|
|
2701
|
-
let program;
|
|
2702
|
-
if (kind === 0) {
|
|
2703
|
-
const def = anonymousBodies?.[ref];
|
|
2704
|
-
if (!def) {
|
|
2705
|
-
throw ErrorFactory.internal(
|
|
2706
|
-
"INTERNAL_MISSING_ANONYMOUS_BODY",
|
|
2707
|
-
`Internal error: REDUCE_INVOKE referenced missing anonymous body index ${ref}`,
|
|
2708
|
-
{ ref }
|
|
2709
|
-
);
|
|
2710
|
-
}
|
|
2711
|
-
paramNames = def.params;
|
|
2712
|
-
program = def.program;
|
|
2713
|
-
} else if (kind === 2) {
|
|
2714
|
-
const name = pooledString(strings, ref, op);
|
|
2715
|
-
const fn = vm.getUserFunction(name);
|
|
2716
|
-
if (!fn) {
|
|
2717
|
-
stack.push(errorValue("UNDEFINED_FUNCTION", `reduce: undefined function "${name}"`));
|
|
2718
|
-
break;
|
|
2719
|
-
}
|
|
2720
|
-
if (fn.params.length !== 2) {
|
|
2721
|
-
stack.push(errorValue(
|
|
2722
|
-
"FUNCTION_ARITY_MISMATCH",
|
|
2723
|
-
`reduce: "${name}" must take exactly 2 arguments (accumulator, element), got ${fn.params.length}`
|
|
2724
|
-
));
|
|
2725
|
-
break;
|
|
2726
|
-
}
|
|
2727
|
-
paramNames = fn.params;
|
|
2728
|
-
program = fn.program;
|
|
2729
|
-
}
|
|
2730
|
-
const cells = collectionToValues(collectionVal, vm.getMaxCollectionSize());
|
|
2731
|
-
if (!Array.isArray(cells)) {
|
|
2732
|
-
stack.push(cells);
|
|
2733
|
-
break;
|
|
2734
|
-
}
|
|
2735
|
-
chargeAllocation(cells.length, "collection elements");
|
|
2736
|
-
let acc;
|
|
2737
|
-
let startIdx;
|
|
2738
|
-
if (initialVal !== void 0) {
|
|
2739
|
-
const initialFault = faultedOperand(initialVal);
|
|
2740
|
-
if (initialFault) {
|
|
2741
|
-
stack.push(initialFault);
|
|
2742
|
-
break;
|
|
2743
|
-
}
|
|
2744
|
-
acc = initialVal;
|
|
2745
|
-
startIdx = 0;
|
|
2746
|
-
} else {
|
|
2747
|
-
if (cells.length === 0) {
|
|
2748
|
-
stack.push(errorValue("REDUCE_EMPTY_COLLECTION", `reduce: cannot reduce an empty collection without an initial value.`));
|
|
2749
|
-
break;
|
|
2750
|
-
}
|
|
2751
|
-
acc = cells[0];
|
|
2752
|
-
startIdx = 1;
|
|
2753
|
-
}
|
|
2754
|
-
let reduceError;
|
|
2755
|
-
for (let i = startIdx; i < cells.length; i++) {
|
|
2756
|
-
const args = [acc, cells[i]];
|
|
2757
|
-
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);
|
|
2758
|
-
const stepFault = faultedOperand(resultVal);
|
|
2759
|
-
if (stepFault) {
|
|
2760
|
-
reduceError = stepFault;
|
|
2761
|
-
break;
|
|
2762
|
-
}
|
|
2763
|
-
acc = resultVal;
|
|
2764
|
-
}
|
|
2765
|
-
if (reduceError) {
|
|
2766
|
-
stack.push(reduceError);
|
|
2767
|
-
break;
|
|
2768
|
-
}
|
|
2769
|
-
stack.push(acc);
|
|
2770
|
-
break;
|
|
2771
|
-
}
|
|
2772
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2773
|
-
// §12 Algebra verbs' bound unknown (OpCode 159)
|
|
2774
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2775
|
-
case 159 /* BIND_UNKNOWN */: {
|
|
2776
|
-
const ref = operandByte(opcodes, ip++, op, "body reference");
|
|
2777
|
-
const def = anonymousBodies?.[ref];
|
|
2778
|
-
if (!def) {
|
|
2779
|
-
throw ErrorFactory.internal(
|
|
2780
|
-
"INTERNAL_MISSING_ANONYMOUS_BODY",
|
|
2781
|
-
`Internal error: BIND_UNKNOWN referenced missing anonymous body index ${ref}`,
|
|
2782
|
-
{ ref }
|
|
2783
|
-
);
|
|
2784
|
-
}
|
|
2785
|
-
stack.push(invokeWithBoundUnknown(def.params[0], def.program, vm, pipeline, expression, context, !!symbolicTolerant));
|
|
2786
|
-
break;
|
|
2787
|
-
}
|
|
2788
|
-
}
|
|
2789
|
-
}
|
|
2790
|
-
const fallback = safePop(stack);
|
|
2791
|
-
return { type: "value", value: hasArena ? persistentValue(fallback) : fallback };
|
|
2792
|
-
} catch (e) {
|
|
2793
|
-
return { type: "error", error: normalizeUnknownError(e) };
|
|
2794
|
-
} finally {
|
|
2795
|
-
endEvaluation();
|
|
2796
|
-
}
|
|
2797
|
-
}
|
|
2798
|
-
|
|
2799
|
-
export { DependencyGraph, ScopeManager, createVM, executeBytecode };
|
|
2800
|
-
//# sourceMappingURL=chunk-FE4RC37D.js.map
|
|
2801
|
-
//# sourceMappingURL=chunk-FE4RC37D.js.map
|