diff --git a/package.json b/package.json index 4260435d..e9f5a4cb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@harmoniclabs/plu-ts", - "version": "0.4.4", + "version": "0.5.0", "description": "An embedded DSL for Cardano smart contracts creation coupled with a library for Cardano transactions, all in Typescript", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -56,14 +56,14 @@ "@harmoniclabs/uint8array-utils": "^1.0.0", "@harmoniclabs/pair": "^1.0.0", "@harmoniclabs/bytestring": "^1.0.0", - "@harmoniclabs/cbor": "^1.1.1", + "@harmoniclabs/cbor": "^1.1.2", "@harmoniclabs/plutus-data": "^1.0.0", "@harmoniclabs/cardano-costmodels-ts": "^1.0.0", - "@harmoniclabs/plutus-machine": "^1.0.1", - "@harmoniclabs/uplc": "^1.0.0", - "@harmoniclabs/cardano-ledger-ts": "^0.1.0", - "@harmoniclabs/plu-ts-onchain": "^0.1.1", - "@harmoniclabs/plu-ts-offchain": "^0.1.3" + "@harmoniclabs/plutus-machine": "^1.0.2", + "@harmoniclabs/uplc": "^1.1.0", + "@harmoniclabs/cardano-ledger-ts": "^0.1.2", + "@harmoniclabs/plu-ts-onchain": "^0.2.0", + "@harmoniclabs/plu-ts-offchain": "^0.1.5" }, "devDependencies": { "@babel/preset-env": "^7.18.6", diff --git a/packages/offchain/package.json b/packages/offchain/package.json index 0493dc93..e3c97147 100644 --- a/packages/offchain/package.json +++ b/packages/offchain/package.json @@ -1,6 +1,6 @@ { "name": "@harmoniclabs/plu-ts-offchain", - "version": "0.1.3", + "version": "0.1.5", "description": "An embedded DSL for Cardano smart contracts creation coupled with a library for Cardano transactions, all in Typescript", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -64,9 +64,9 @@ "@harmoniclabs/cbor": "^1.1.1", "@harmoniclabs/plutus-data": "^1.0.0", "@harmoniclabs/cardano-costmodels-ts": "^1.0.0", - "@harmoniclabs/plutus-machine": "^1.0.1", - "@harmoniclabs/uplc": "^1.0.0", - "@harmoniclabs/cardano-ledger-ts": "^0.1.1" + "@harmoniclabs/plutus-machine": "^1.0.2", + "@harmoniclabs/uplc": "^1.1.0", + "@harmoniclabs/cardano-ledger-ts": "^0.1.2" }, "devDependencies": { "@babel/preset-env": "^7.18.6", diff --git a/packages/offchain/src/TxBuilder/TxBuilder.ts b/packages/offchain/src/TxBuilder/TxBuilder.ts index 17b4d739..7b31aed9 100644 --- a/packages/offchain/src/TxBuilder/TxBuilder.ts +++ b/packages/offchain/src/TxBuilder/TxBuilder.ts @@ -541,11 +541,31 @@ function initTxBuild( const withdrawRedeemers: TxRedeemer[] = []; const scriptsToExec: ScriptToExecEntry[] = []; + + /** + * needed in `getScriptDataHash` to understand whoich cost model to transform in language view + */ + let _hasV1Scripts = false; + /** + * needed in `getScriptDataHash` to understand whoich cost model to transform in language view + */ + let _hasV2Scripts = false; function pushScriptToExec( idx: number, tag: TxRedeemerTag, script: Script, datum?: Data ) { if( script.type !== ScriptType.NativeScript ) { + + // keep track of exsisting csript versions + if( !_hasV1Scripts && script.type === "PlutusScriptV1" ) + { + _hasV1Scripts = true; + } + if( !_hasV2Scripts && script.type === "PlutusScriptV2" ) + { + _hasV2Scripts = true; + } + scriptsToExec.push({ index: idx, rdmrTag: tag, @@ -558,7 +578,6 @@ function initTxBuild( }) } } - function pushWitScript( script : Script ): void { const t = script.type; @@ -693,7 +712,6 @@ function initTxBuild( })); pushScriptToExec( i, TxRedeemerTag.Spend, refScript, dat ); - } if( inputScript !== undefined ) { @@ -720,7 +738,6 @@ function initTxBuild( })); pushScriptToExec( i, TxRedeemerTag.Spend, script, dat ); - } return new TxIn( utxo ) @@ -900,8 +917,8 @@ function initTxBuild( const languageViews = costModelsToLanguageViewCbor( this.protocolParamters.costModels, { - mustHaveV1: plutusV1ScriptsWitnesses.length > 0, - mustHaveV2: plutusV2ScriptsWitnesses.length > 0 + mustHaveV1: _hasV1Scripts, + mustHaveV2: _hasV2Scripts } ).toBuffer(); diff --git a/packages/offchain/src/__tests__/TxBuilder.build.test.ts b/packages/offchain/src/__tests__/TxBuilder.build.test.ts index 387d5894..9e9f511b 100644 --- a/packages/offchain/src/__tests__/TxBuilder.build.test.ts +++ b/packages/offchain/src/__tests__/TxBuilder.build.test.ts @@ -5,6 +5,8 @@ import { fromAscii } from "@harmoniclabs/uint8array-utils"; import { TxBuilder } from "../TxBuilder" test.todo("depends on onchain"); + + /* jest.setTimeout(2_000_000) diff --git a/packages/offchain/src/__tests__/TxBuilder.getScriptDataHash.test.ts b/packages/offchain/src/__tests__/TxBuilder.getScriptDataHash.test.ts index 1bf2e0e5..367b5304 100644 --- a/packages/offchain/src/__tests__/TxBuilder.getScriptDataHash.test.ts +++ b/packages/offchain/src/__tests__/TxBuilder.getScriptDataHash.test.ts @@ -1,7 +1,7 @@ -import { Tx, TxRedeemer, TxRedeemerTag, costModelsFromCborObj, costModelsToLanguageViewCbor, defaultV2Costs } from "@harmoniclabs/cardano-ledger-ts"; +import { CostModels, Tx, TxRedeemer, TxRedeemerTag, costModelsFromCborObj, costModelsToLanguageViewCbor, defaultV2Costs } from "@harmoniclabs/cardano-ledger-ts"; import { getScriptDataHash } from "../TxBuilder"; import { Cbor, CborArray, forceCborString, CborMap, CborBytes } from "@harmoniclabs/cbor"; -import { dataToCborObj, DataI } from "@harmoniclabs/plutus-data"; +import { dataToCborObj, DataI, DataConstr } from "@harmoniclabs/plutus-data"; import { ExBudget } from "@harmoniclabs/plutus-machine"; import { fromHex } from "@harmoniclabs/uint8array-utils"; @@ -209,5 +209,375 @@ describe("getScriptDataHash", () => { ).toEqual( "4415e6667e6d6bbd992af5092d48e3c2ba9825200d0234d2470068f7f0f178b3" ) + }); + + + test.skip("empowa", () => { + + const cstmdls: CostModels = { + "PlutusScriptV1": { + "addInteger-cpu-arguments-intercept": 205665n, + "addInteger-cpu-arguments-slope": 812n, + "addInteger-memory-arguments-intercept": 1n, + "addInteger-memory-arguments-slope": 1n, + "appendByteString-cpu-arguments-intercept": 1000n, + "appendByteString-cpu-arguments-slope": 571n, + "appendByteString-memory-arguments-intercept": 0n, + "appendByteString-memory-arguments-slope": 1n, + "appendString-cpu-arguments-intercept": 1000n, + "appendString-cpu-arguments-slope": 24177n, + "appendString-memory-arguments-intercept": 4n, + "appendString-memory-arguments-slope": 1n, + "bData-cpu-arguments": 1000n, + "bData-memory-arguments": 32n, + "blake2b_256-cpu-arguments-intercept": 117366n, + "blake2b_256-cpu-arguments-slope": 10475n, + "blake2b_256-memory-arguments": 4n, + "cekApplyCost-exBudgetCPU": 23000n, + "cekApplyCost-exBudgetMemory": 100n, + "cekBuiltinCost-exBudgetCPU": 23000n, + "cekBuiltinCost-exBudgetMemory": 100n, + "cekConstCost-exBudgetCPU": 23000n, + "cekConstCost-exBudgetMemory": 100n, + "cekDelayCost-exBudgetCPU": 23000n, + "cekDelayCost-exBudgetMemory": 100n, + "cekForceCost-exBudgetCPU": 23000n, + "cekForceCost-exBudgetMemory": 100n, + "cekLamCost-exBudgetCPU": 23000n, + "cekLamCost-exBudgetMemory": 100n, + "cekStartupCost-exBudgetCPU": 100n, + "cekStartupCost-exBudgetMemory": 100n, + "cekVarCost-exBudgetCPU": 23000n, + "cekVarCost-exBudgetMemory": 100n, + "chooseData-cpu-arguments": 19537n, + "chooseData-memory-arguments": 32n, + "chooseList-cpu-arguments": 175354n, + "chooseList-memory-arguments": 32n, + "chooseUnit-cpu-arguments": 46417n, + "chooseUnit-memory-arguments": 4n, + "consByteString-cpu-arguments-intercept": 221973n, + "consByteString-cpu-arguments-slope": 511n, + "consByteString-memory-arguments-intercept": 0n, + "consByteString-memory-arguments-slope": 1n, + "constrData-cpu-arguments": 89141n, + "constrData-memory-arguments": 32n, + "decodeUtf8-cpu-arguments-intercept": 497525n, + "decodeUtf8-cpu-arguments-slope": 14068n, + "decodeUtf8-memory-arguments-intercept": 4n, + "decodeUtf8-memory-arguments-slope": 2n, + "divideInteger-cpu-arguments-constant": 196500n, + "divideInteger-cpu-arguments-model-arguments-intercept": 453240n, + "divideInteger-cpu-arguments-model-arguments-slope": 220n, + "divideInteger-memory-arguments-intercept": 0n, + "divideInteger-memory-arguments-minimum": 1n, + "divideInteger-memory-arguments-slope": 1n, + "encodeUtf8-cpu-arguments-intercept": 1000n, + "encodeUtf8-cpu-arguments-slope": 28662n, + "encodeUtf8-memory-arguments-intercept": 4n, + "encodeUtf8-memory-arguments-slope": 2n, + "equalsByteString-cpu-arguments-constant": 245000n, + "equalsByteString-cpu-arguments-intercept": 216773n, + "equalsByteString-cpu-arguments-slope": 62n, + "equalsByteString-memory-arguments": 1n, + "equalsData-cpu-arguments-intercept": 1060367n, + "equalsData-cpu-arguments-slope": 12586n, + "equalsData-memory-arguments": 1n, + "equalsInteger-cpu-arguments-intercept": 208512n, + "equalsInteger-cpu-arguments-slope": 421n, + "equalsInteger-memory-arguments": 1n, + "equalsString-cpu-arguments-constant": 187000n, + "equalsString-cpu-arguments-intercept": 1000n, + "equalsString-cpu-arguments-slope": 52998n, + "equalsString-memory-arguments": 1n, + "fstPair-cpu-arguments": 80436n, + "fstPair-memory-arguments": 32n, + "headList-cpu-arguments": 43249n, + "headList-memory-arguments": 32n, + "iData-cpu-arguments": 1000n, + "iData-memory-arguments": 32n, + "ifThenElse-cpu-arguments": 80556n, + "ifThenElse-memory-arguments": 1n, + "indexByteString-cpu-arguments": 57667n, + "indexByteString-memory-arguments": 4n, + "lengthOfByteString-cpu-arguments": 1000n, + "lengthOfByteString-memory-arguments": 10n, + "lessThanByteString-cpu-arguments-intercept": 197145n, + "lessThanByteString-cpu-arguments-slope": 156n, + "lessThanByteString-memory-arguments": 1n, + "lessThanEqualsByteString-cpu-arguments-intercept": 197145n, + "lessThanEqualsByteString-cpu-arguments-slope": 156n, + "lessThanEqualsByteString-memory-arguments": 1n, + "lessThanEqualsInteger-cpu-arguments-intercept": 204924n, + "lessThanEqualsInteger-cpu-arguments-slope": 473n, + "lessThanEqualsInteger-memory-arguments": 1n, + "lessThanInteger-cpu-arguments-intercept": 208896n, + "lessThanInteger-cpu-arguments-slope": 511n, + "lessThanInteger-memory-arguments": 1n, + "listData-cpu-arguments": 52467n, + "listData-memory-arguments": 32n, + "mapData-cpu-arguments": 64832n, + "mapData-memory-arguments": 32n, + "mkCons-cpu-arguments": 65493n, + "mkCons-memory-arguments": 32n, + "mkNilData-cpu-arguments": 22558n, + "mkNilData-memory-arguments": 32n, + "mkNilPairData-cpu-arguments": 16563n, + "mkNilPairData-memory-arguments": 32n, + "mkPairData-cpu-arguments": 76511n, + "mkPairData-memory-arguments": 32n, + "modInteger-cpu-arguments-constant": 196500n, + "modInteger-cpu-arguments-model-arguments-intercept": 453240n, + "modInteger-cpu-arguments-model-arguments-slope": 220n, + "modInteger-memory-arguments-intercept": 0n, + "modInteger-memory-arguments-minimum": 1n, + "modInteger-memory-arguments-slope": 1n, + "multiplyInteger-cpu-arguments-intercept": 69522n, + "multiplyInteger-cpu-arguments-slope": 11687n, + "multiplyInteger-memory-arguments-intercept": 0n, + "multiplyInteger-memory-arguments-slope": 1n, + "nullList-cpu-arguments": 60091n, + "nullList-memory-arguments": 32n, + "quotientInteger-cpu-arguments-constant": 196500n, + "quotientInteger-cpu-arguments-model-arguments-intercept": 453240n, + "quotientInteger-cpu-arguments-model-arguments-slope": 220n, + "quotientInteger-memory-arguments-intercept": 0n, + "quotientInteger-memory-arguments-minimum": 1n, + "quotientInteger-memory-arguments-slope": 1n, + "remainderInteger-cpu-arguments-constant": 196500n, + "remainderInteger-cpu-arguments-model-arguments-intercept": 453240n, + "remainderInteger-cpu-arguments-model-arguments-slope": 220n, + "remainderInteger-memory-arguments-intercept": 0n, + "remainderInteger-memory-arguments-minimum": 1n, + "remainderInteger-memory-arguments-slope": 1n, + "sha2_256-cpu-arguments-intercept": 806990n, + "sha2_256-cpu-arguments-slope": 30482n, + "sha2_256-memory-arguments": 4n, + "sha3_256-cpu-arguments-intercept": 1927926n, + "sha3_256-cpu-arguments-slope": 82523n, + "sha3_256-memory-arguments": 4n, + "sliceByteString-cpu-arguments-intercept": 265318n, + "sliceByteString-cpu-arguments-slope": 0n, + "sliceByteString-memory-arguments-intercept": 4n, + "sliceByteString-memory-arguments-slope": 0n, + "sndPair-cpu-arguments": 85931n, + "sndPair-memory-arguments": 32n, + "subtractInteger-cpu-arguments-intercept": 205665n, + "subtractInteger-cpu-arguments-slope": 812n, + "subtractInteger-memory-arguments-intercept": 1n, + "subtractInteger-memory-arguments-slope": 1n, + "tailList-cpu-arguments": 41182n, + "tailList-memory-arguments": 32n, + "trace-cpu-arguments": 212342n, + "trace-memory-arguments": 32n, + "unBData-cpu-arguments": 31220n, + "unBData-memory-arguments": 32n, + "unConstrData-cpu-arguments": 32696n, + "unConstrData-memory-arguments": 32n, + "unIData-cpu-arguments": 43357n, + "unIData-memory-arguments": 32n, + "unListData-cpu-arguments": 32247n, + "unListData-memory-arguments": 32n, + "unMapData-cpu-arguments": 38314n, + "unMapData-memory-arguments": 32n, + "verifyEd25519Signature-cpu-arguments-intercept": 57996947n, + "verifyEd25519Signature-cpu-arguments-slope": 18975n, + "verifyEd25519Signature-memory-arguments": 10n, + }, + "PlutusScriptV2": { + "addInteger-cpu-arguments-intercept": 205665n, + "addInteger-cpu-arguments-slope": 812n, + "addInteger-memory-arguments-intercept": 1n, + "addInteger-memory-arguments-slope": 1n, + "appendByteString-cpu-arguments-intercept": 1000n, + "appendByteString-cpu-arguments-slope": 571n, + "appendByteString-memory-arguments-intercept": 0n, + "appendByteString-memory-arguments-slope": 1n, + "appendString-cpu-arguments-intercept": 1000n, + "appendString-cpu-arguments-slope": 24177n, + "appendString-memory-arguments-intercept": 4n, + "appendString-memory-arguments-slope": 1n, + "bData-cpu-arguments": 1000n, + "bData-memory-arguments": 32n, + "blake2b_256-cpu-arguments-intercept": 117366n, + "blake2b_256-cpu-arguments-slope": 10475n, + "blake2b_256-memory-arguments": 4n, + "cekApplyCost-exBudgetCPU": 23000n, + "cekApplyCost-exBudgetMemory": 100n, + "cekBuiltinCost-exBudgetCPU": 23000n, + "cekBuiltinCost-exBudgetMemory": 100n, + "cekConstCost-exBudgetCPU": 23000n, + "cekConstCost-exBudgetMemory": 100n, + "cekDelayCost-exBudgetCPU": 23000n, + "cekDelayCost-exBudgetMemory": 100n, + "cekForceCost-exBudgetCPU": 23000n, + "cekForceCost-exBudgetMemory": 100n, + "cekLamCost-exBudgetCPU": 23000n, + "cekLamCost-exBudgetMemory": 100n, + "cekStartupCost-exBudgetCPU": 100n, + "cekStartupCost-exBudgetMemory": 100n, + "cekVarCost-exBudgetCPU": 23000n, + "cekVarCost-exBudgetMemory": 100n, + "chooseData-cpu-arguments": 19537n, + "chooseData-memory-arguments": 32n, + "chooseList-cpu-arguments": 175354n, + "chooseList-memory-arguments": 32n, + "chooseUnit-cpu-arguments": 46417n, + "chooseUnit-memory-arguments": 4n, + "consByteString-cpu-arguments-intercept": 221973n, + "consByteString-cpu-arguments-slope": 511n, + "consByteString-memory-arguments-intercept": 0n, + "consByteString-memory-arguments-slope": 1n, + "constrData-cpu-arguments": 89141n, + "constrData-memory-arguments": 32n, + "decodeUtf8-cpu-arguments-intercept": 497525n, + "decodeUtf8-cpu-arguments-slope": 14068n, + "decodeUtf8-memory-arguments-intercept": 4n, + "decodeUtf8-memory-arguments-slope": 2n, + "divideInteger-cpu-arguments-constant": 196500n, + "divideInteger-cpu-arguments-model-arguments-intercept": 453240n, + "divideInteger-cpu-arguments-model-arguments-slope": 220n, + "divideInteger-memory-arguments-intercept": 0n, + "divideInteger-memory-arguments-minimum": 1n, + "divideInteger-memory-arguments-slope": 1n, + "encodeUtf8-cpu-arguments-intercept": 1000n, + "encodeUtf8-cpu-arguments-slope": 28662n, + "encodeUtf8-memory-arguments-intercept": 4n, + "encodeUtf8-memory-arguments-slope": 2n, + "equalsByteString-cpu-arguments-constant": 245000n, + "equalsByteString-cpu-arguments-intercept": 216773n, + "equalsByteString-cpu-arguments-slope": 62n, + "equalsByteString-memory-arguments": 1n, + "equalsData-cpu-arguments-intercept": 1060367n, + "equalsData-cpu-arguments-slope": 12586n, + "equalsData-memory-arguments": 1n, + "equalsInteger-cpu-arguments-intercept": 208512n, + "equalsInteger-cpu-arguments-slope": 421n, + "equalsInteger-memory-arguments": 1n, + "equalsString-cpu-arguments-constant": 187000n, + "equalsString-cpu-arguments-intercept": 1000n, + "equalsString-cpu-arguments-slope": 52998n, + "equalsString-memory-arguments": 1n, + "fstPair-cpu-arguments": 80436n, + "fstPair-memory-arguments": 32n, + "headList-cpu-arguments": 43249n, + "headList-memory-arguments": 32n, + "iData-cpu-arguments": 1000n, + "iData-memory-arguments": 32n, + "ifThenElse-cpu-arguments": 80556n, + "ifThenElse-memory-arguments": 1n, + "indexByteString-cpu-arguments": 57667n, + "indexByteString-memory-arguments": 4n, + "lengthOfByteString-cpu-arguments": 1000n, + "lengthOfByteString-memory-arguments": 10n, + "lessThanByteString-cpu-arguments-intercept": 197145n, + "lessThanByteString-cpu-arguments-slope": 156n, + "lessThanByteString-memory-arguments": 1n, + "lessThanEqualsByteString-cpu-arguments-intercept": 197145n, + "lessThanEqualsByteString-cpu-arguments-slope": 156n, + "lessThanEqualsByteString-memory-arguments": 1n, + "lessThanEqualsInteger-cpu-arguments-intercept": 204924n, + "lessThanEqualsInteger-cpu-arguments-slope": 473n, + "lessThanEqualsInteger-memory-arguments": 1n, + "lessThanInteger-cpu-arguments-intercept": 208896n, + "lessThanInteger-cpu-arguments-slope": 511n, + "lessThanInteger-memory-arguments": 1n, + "listData-cpu-arguments": 52467n, + "listData-memory-arguments": 32n, + "mapData-cpu-arguments": 64832n, + "mapData-memory-arguments": 32n, + "mkCons-cpu-arguments": 65493n, + "mkCons-memory-arguments": 32n, + "mkNilData-cpu-arguments": 22558n, + "mkNilData-memory-arguments": 32n, + "mkNilPairData-cpu-arguments": 16563n, + "mkNilPairData-memory-arguments": 32n, + "mkPairData-cpu-arguments": 76511n, + "mkPairData-memory-arguments": 32n, + "modInteger-cpu-arguments-constant": 196500n, + "modInteger-cpu-arguments-model-arguments-intercept": 453240n, + "modInteger-cpu-arguments-model-arguments-slope": 220n, + "modInteger-memory-arguments-intercept": 0n, + "modInteger-memory-arguments-minimum": 1n, + "modInteger-memory-arguments-slope": 1n, + "multiplyInteger-cpu-arguments-intercept": 69522n, + "multiplyInteger-cpu-arguments-slope": 11687n, + "multiplyInteger-memory-arguments-intercept": 0n, + "multiplyInteger-memory-arguments-slope": 1n, + "nullList-cpu-arguments": 60091n, + "nullList-memory-arguments": 32n, + "quotientInteger-cpu-arguments-constant": 196500n, + "quotientInteger-cpu-arguments-model-arguments-intercept": 453240n, + "quotientInteger-cpu-arguments-model-arguments-slope": 220n, + "quotientInteger-memory-arguments-intercept": 0n, + "quotientInteger-memory-arguments-minimum": 1n, + "quotientInteger-memory-arguments-slope": 1n, + "remainderInteger-cpu-arguments-constant": 196500n, + "remainderInteger-cpu-arguments-model-arguments-intercept": 453240n, + "remainderInteger-cpu-arguments-model-arguments-slope": 220n, + "remainderInteger-memory-arguments-intercept": 0n, + "remainderInteger-memory-arguments-minimum": 1n, + "remainderInteger-memory-arguments-slope": 1n, + "serialiseData-cpu-arguments-intercept": 1159724n, + "serialiseData-cpu-arguments-slope": 392670n, + "serialiseData-memory-arguments-intercept": 0n, + "serialiseData-memory-arguments-slope": 2n, + "sha2_256-cpu-arguments-intercept": 806990n, + "sha2_256-cpu-arguments-slope": 30482n, + "sha2_256-memory-arguments": 4n, + "sha3_256-cpu-arguments-intercept": 1927926n, + "sha3_256-cpu-arguments-slope": 82523n, + "sha3_256-memory-arguments": 4n, + "sliceByteString-cpu-arguments-intercept": 265318n, + "sliceByteString-cpu-arguments-slope": 0n, + "sliceByteString-memory-arguments-intercept": 4n, + "sliceByteString-memory-arguments-slope": 0n, + "sndPair-cpu-arguments": 85931n, + "sndPair-memory-arguments": 32n, + "subtractInteger-cpu-arguments-intercept": 205665n, + "subtractInteger-cpu-arguments-slope": 812n, + "subtractInteger-memory-arguments-intercept": 1n, + "subtractInteger-memory-arguments-slope": 1n, + "tailList-cpu-arguments": 41182n, + "tailList-memory-arguments": 32n, + "trace-cpu-arguments": 212342n, + "trace-memory-arguments": 32n, + "unBData-cpu-arguments": 31220n, + "unBData-memory-arguments": 32n, + "unConstrData-cpu-arguments": 32696n, + "unConstrData-memory-arguments": 32n, + "unIData-cpu-arguments": 43357n, + "unIData-memory-arguments": 32n, + "unListData-cpu-arguments": 32247n, + "unListData-memory-arguments": 32n, + "unMapData-cpu-arguments": 38314n, + "unMapData-memory-arguments": 32n, + "verifyEcdsaSecp256k1Signature-cpu-arguments": 35892428n, + "verifyEcdsaSecp256k1Signature-memory-arguments": 10n, + "verifyEd25519Signature-cpu-arguments-intercept": 57996947n, + "verifyEd25519Signature-cpu-arguments-slope": 18975n, + "verifyEd25519Signature-memory-arguments": 10n, + "verifySchnorrSecp256k1Signature-cpu-arguments-intercept": 38887044n, + "verifySchnorrSecp256k1Signature-cpu-arguments-slope": 32947n, + "verifySchnorrSecp256k1Signature-memory-arguments": 10n, + } + }; + + console.log( + getScriptDataHash( + [ + new TxRedeemer({ + tag: TxRedeemerTag.Spend, + index: 0, + data: new DataConstr( 0, [] ), + execUnits: new ExBudget({ + cpu: 129127102, + mem: 282101 + }) + }) + ], + [], + costModelsToLanguageViewCbor( cstmdls, { mustHaveV2: true } ).toBuffer() + )?.toString() + ); }) }); \ No newline at end of file diff --git a/packages/onchain/package.json b/packages/onchain/package.json index cf855819..b3fa17ea 100644 --- a/packages/onchain/package.json +++ b/packages/onchain/package.json @@ -1,6 +1,6 @@ { "name": "@harmoniclabs/plu-ts-onchain", - "version": "0.1.1", + "version": "0.2.0", "description": "An embedded DSL for Cardano smart contracts creation coupled with a library for Cardano transactions, all in Typescript", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -64,9 +64,9 @@ "@harmoniclabs/cbor": "^1.1.1", "@harmoniclabs/plutus-data": "^1.0.0", "@harmoniclabs/cardano-costmodels-ts": "^1.0.0", - "@harmoniclabs/plutus-machine": "^1.0.1", - "@harmoniclabs/uplc": "^1.0.0", - "@harmoniclabs/cardano-ledger-ts": "^0.1.0" + "@harmoniclabs/plutus-machine": "^1.0.2", + "@harmoniclabs/uplc": "^1.1.0", + "@harmoniclabs/cardano-ledger-ts": "^0.1.2" }, "devDependencies": { "@babel/preset-env": "^7.18.6", diff --git a/packages/onchain/src/IR/IRNodes/IRLetted.ts b/packages/onchain/src/IR/IRNodes/IRLetted.ts index 1334d9b0..a00e68b6 100644 --- a/packages/onchain/src/IR/IRNodes/IRLetted.ts +++ b/packages/onchain/src/IR/IRNodes/IRLetted.ts @@ -74,8 +74,9 @@ export class IRLetted readonly meta!: IRLettedMeta - constructor( DeBruijn: number, toLet: IRTerm, metadata: Partial = {} ) + constructor( DeBruijn: number | bigint, toLet: IRTerm, metadata: Partial = {} ) { + DeBruijn = typeof DeBruijn === "bigint" ? Number( DeBruijn ) : DeBruijn; if(!( Number.isSafeInteger( DeBruijn ) && DeBruijn >= 0 )) throw new BasePlutsError( diff --git a/packages/onchain/src/pluts/API/V1/Address/PCredential.ts b/packages/onchain/src/pluts/API/V1/Address/PCredential.ts index 1dfda711..e24a5194 100644 --- a/packages/onchain/src/pluts/API/V1/Address/PCredential.ts +++ b/packages/onchain/src/pluts/API/V1/Address/PCredential.ts @@ -1,8 +1,23 @@ import { pstruct } from "../../../PTypes/PStruct/pstruct"; +import { punBData } from "../../../lib/builtins/data"; +import { pfn } from "../../../lib/pfn"; +import { phoist } from "../../../lib/phoist"; +import { bs } from "../../../type_system/types"; import { PPubKeyHash } from "../PubKey/PPubKeyHash"; import { PValidatorHash } from "../ScriptsHashes/PValidatorHash"; export const PCredential = pstruct({ PPubKeyCredential: { pkh: PPubKeyHash.type }, PScriptCredential: { valHash: PValidatorHash.type }, +}, +( self_t ) => { + + const pcredHash = phoist( + pfn([ self_t ], bs ) + ( self => punBData.$( self.raw.fields.head ) ) + ) + + return { + hash: pcredHash + }; }); \ No newline at end of file diff --git a/packages/onchain/src/pluts/API/V1/Interval/PExtended.ts b/packages/onchain/src/pluts/API/V1/Interval/PExtended.ts index 106889ce..8f69252b 100644 --- a/packages/onchain/src/pluts/API/V1/Interval/PExtended.ts +++ b/packages/onchain/src/pluts/API/V1/Interval/PExtended.ts @@ -1,4 +1,6 @@ +import { palias } from "../../../PTypes/PAlias/palias"; import { pstruct } from "../../../PTypes/PStruct/pstruct"; +import { pInt } from "../../../lib/std/int"; import { int } from "../../../type_system"; export const PExtended = pstruct({ @@ -6,3 +8,8 @@ export const PExtended = pstruct({ PFinite: { _0: int }, PPosInf: {} }); + + +const PPOSIXTime = palias( int ) + +PPOSIXTime.from( pInt( 1 ) ) \ No newline at end of file diff --git a/packages/onchain/src/pluts/API/V1/Value/PValue/index.ts b/packages/onchain/src/pluts/API/V1/Value/PValue/index.ts index 3f8f10cb..86ee0124 100644 --- a/packages/onchain/src/pluts/API/V1/Value/PValue/index.ts +++ b/packages/onchain/src/pluts/API/V1/Value/PValue/index.ts @@ -1,18 +1,110 @@ import { palias } from "../../../../PTypes/PAlias/palias"; -import { asData, int, list, pair } from "../../../../type_system/types"; +import { pfstPair, psndPair } from "../../../../lib/builtins/pair"; +import { pif } from "../../../../lib/builtins/bool"; +import { pdelay } from "../../../../lib/pdelay"; +import { pfn } from "../../../../lib/pfn"; +import { phoist } from "../../../../lib/phoist"; +import { pInt } from "../../../../lib/std/int/pInt"; +import { _precursiveList } from "../../../../lib/std/list/precursiveList/minimal"; +import { delayed, fn, int, list, pair } from "../../../../type_system/types"; import { PCurrencySymbol } from "../PCurrencySymbol"; import { PTokenName } from "../PTokenName"; +import { _papp } from "../../../../lib/std/data/conversion/minimal_common"; +import { plam } from "../../../../lib/plam"; -export const PAssetsEntryT = pair( - PTokenName.type, - int -); +export const PAssetsEntry = palias( + pair( + PTokenName.type, + int + ), + ( _self_t ) => { -export const PValueEntryT = pair( - PCurrencySymbol.type, - list( PAssetsEntryT ) + return { + tokenName: pfstPair( PTokenName.type, int ), + quantity: psndPair( PTokenName.type, int ), + } + } ); +export const PValueEntry = palias( + pair( + PCurrencySymbol.type, + list( PAssetsEntry.type ) + ), + ( _self_t ) => { + + return { + policy: pfstPair( PCurrencySymbol.type, list( PAssetsEntry.type ) ), + assets: psndPair( PCurrencySymbol.type, list( PAssetsEntry.type ) ) + } + } +) + export const PValue = palias( - list( PValueEntryT ) -); \ No newline at end of file + list( PValueEntry.type ), + ( self_t ) => { + + const pvalueOf = phoist( + pfn([ + self_t, + PCurrencySymbol.type, + PTokenName.type + ], int) + (( value, currSym, tokenName ) => + + _papp( + _papp( + _papp( + _precursiveList( int, PValueEntry.type ), + plam( fn([ list(PValueEntry.type) ], int ), delayed( int ) )( _self => pdelay( pInt(0) ) ) + ), + pfn([ + fn([ list(PValueEntry.type) ], int ), + PValueEntry.type, + list( PValueEntry.type ) + ], int) + ((self, head, tail ) => + pif( int ).$( head.policy.eq( currSym ) ) + .then( + + _papp( + _papp( + _papp( + _precursiveList( int, PAssetsEntry.type ), + plam( fn([ list(PAssetsEntry.type) ], int ), delayed( int ) ) + ( _self => pdelay( pInt(0) ) ) + ), + pfn([ + fn([ list(PAssetsEntry.type) ], int ), + PAssetsEntry.type, + list( PAssetsEntry.type ) + ], int) + ( + (self, head, tail) => + pif( int ).$( head.fst.eq( tokenName ) ) + .then( head.snd ) + .else( self.$( tail ) as any ) + ) + ), + head.snd + ) + ) + .else( self.$( tail ) as any )) + ), + value + ) + ) + ); + + return { + amountOf: pvalueOf, + lovelaces: + pfn([ self_t ], int) + ( value => + pvalueOf.$( value ).$("").$("") + ) + } + } +); + + diff --git a/packages/onchain/src/pluts/API/V1/Value/__tests__/PValue.evalScript.test.ts b/packages/onchain/src/pluts/API/V1/Value/__tests__/PValue.evalScript.test.ts index b9fb26d8..0988407d 100644 --- a/packages/onchain/src/pluts/API/V1/Value/__tests__/PValue.evalScript.test.ts +++ b/packages/onchain/src/pluts/API/V1/Value/__tests__/PValue.evalScript.test.ts @@ -1,31 +1,33 @@ import { Machine } from "@harmoniclabs/plutus-machine"; import { UPLCConst, UPLCTerm, ErrorUPLC } from "@harmoniclabs/uplc"; -import { pmatch } from "../../../.."; -import { getHoistedTerms, getSortedHoistedSet } from "../../../../../IR/IRNodes/IRHoisted"; +import { Term, pmatch, termTypeToString, typeExtends } from "../../../.."; import { compileIRToUPLC } from "../../../../../IR/toUPLC/compileIRToUPLC"; import { PMaybe, fromData, pBool, pByteString, pInt, pPair, pdelay, pfn, phoist, pif, plam, precursiveList, ptoData, toData } from "../../../../lib"; import { pList } from "../../../../lib/std/list/const"; -import { termTypeToString } from "../../../../type_system"; import { bool, bs, fn, int, list } from "../../../../type_system/types"; import { PCurrencySymbol } from "../PCurrencySymbol"; import { PTokenName } from "../PTokenName"; -import { PAssetsEntryT, PValue, PValueEntryT } from "../PValue"; +import { PAssetsEntry, PValue, PValueEntry } from "../PValue"; +import { getFstT, getSndT } from "../../../../type_system/tyArgs"; const currSym = PCurrencySymbol.from( pByteString("ff".repeat(28)) ); const tn = PTokenName.from( pByteString("") ); const oneEntryValue = PValue.from( - pList( PValueEntryT )([ - pPair( PValueEntryT[1], PValueEntryT[2] ) - ( - currSym, - pList( PAssetsEntryT )([ - pPair( PAssetsEntryT[1], PAssetsEntryT[2] ) - ( - tn, - pInt(1_000_000) - ) - ]) + pList( PValueEntry.type )([ + PValueEntry.from( + pPair( PValueEntry.type[1][1], PValueEntry.type[1][2] ) + ( + currSym, + pList( PAssetsEntry.type )([ + PAssetsEntry.from( + pPair( getFstT( PAssetsEntry.type[1] ), getSndT( PAssetsEntry.type[1] ) )( + tn, + pInt(1_000_000) + ) + ) + ]) + ) ) ]) ); @@ -41,7 +43,7 @@ describe("Machine.evalSimple( PValue )", () => { test("empty value constructed correctly", () => { expect( Machine.evalSimple( - PValue.from( pList( PValueEntryT )([]) as any ) + PValue.from( pList( PValueEntry.type )([]) as any ) ) instanceof UPLCConst ).toBe( true ) @@ -95,8 +97,9 @@ const pvalueOf = phoist( bs, bs ], int) - (( value, currSym, tokenName ) => - pmatch( + (( value, currSym, tokenName ) => { + + return pmatch( value.find( entry => entry.fst.eq( currSym ) ) @@ -115,9 +118,8 @@ const pvalueOf = phoist( .onJust( just => just.val.snd ) .onNothing( _ => pInt( 0 ) ); }) - .onNothing( _ => pInt( 0 ) ) - - ) + .onNothing( _ => pInt( 0 ) ); + }) ); const pvalueOfBetter = phoist( @@ -127,36 +129,39 @@ const pvalueOfBetter = phoist( PTokenName.type ], int) (( value, currSym, tokenName ) => - precursiveList( int, PValueEntryT ) + precursiveList( int, PValueEntry.type ) .$( _self => pdelay( pInt(0) ) ) .$( pfn([ - fn([ list(PValueEntryT) ], int ), - PValueEntryT, - list( PValueEntryT ) + fn([ list(PValueEntry.type) ], int ), + PValueEntry.type, + list( PValueEntry.type ) ], int) - ((self, head, tail ) => - pif( int ).$( head.fst.eq( currSym ) ) - .then( - - precursiveList( int, PAssetsEntryT ) - .$( _self => pdelay( pInt(0) ) ) - .$( - pfn([ - fn([ list(PAssetsEntryT) ], int ), - PAssetsEntryT, - list( PAssetsEntryT ) - ], int) - ( - (self, head, tail) => - pif( int ).$( head.fst.eq( tokenName ) ) - .then( head.snd ) - .else( self.$( tail ) ) + ((self, head, tail ) => { + + return pif( int ).$( head.fst.eq( currSym ) ) + .then( + + precursiveList( int, PAssetsEntry.type ) + .$( _self => pdelay( pInt(0) ) ) + .$( + pfn([ + fn([ list(PAssetsEntry.type) ], int ), + PAssetsEntry.type, + list( PAssetsEntry.type ) + ], int) + ( + (self, head, tail) => + pif( int ).$( head.fst.eq( tokenName ) ) + .then( head.snd ) + .else( self.$( tail ) ) + ) ) + .$( head.snd ) ) - .$( head.snd ) + .else( self.$( tail ) ) + } ) - .else( self.$( tail ) )) ) .$( value ) ) @@ -190,6 +195,10 @@ describe("pvalueOf", () => { */ test("policy present but not token", () => { + oneEntryValue.amountOf( currSym, tn ); + + oneEntryValue.pamountOf.$( currSym ).$( tn ) + const expected = Machine.evalSimple( pInt(0) ); let received !: UPLCTerm; expect( diff --git a/packages/onchain/src/pluts/PTypes/PAlias/palias.ts b/packages/onchain/src/pluts/PTypes/PAlias/palias.ts index 919fd9ac..7fe5aff1 100644 --- a/packages/onchain/src/pluts/PTypes/PAlias/palias.ts +++ b/packages/onchain/src/pluts/PTypes/PAlias/palias.ts @@ -1,15 +1,19 @@ import type { Term } from "../../Term"; import { punsafeConvertType } from "../../lib/punsafeConvertType"; -import { AliasT, PrimType, TermType, alias, data } from "../../type_system/types"; +import { AliasT, Methods, PrimType, TermType, alias, data } from "../../type_system/types"; import { PDataRepresentable } from "../../PType/PDataRepresentable"; import { PData } from "../PData/PData"; -import { ToPType } from "../../type_system/ts-pluts-conversion"; +import { FromPType, ToPType } from "../../type_system/ts-pluts-conversion"; import { isWellFormedType } from "../../type_system/kinds/isWellFormedType"; import { typeExtends } from "../../type_system/typeExtends"; -import { PappArg, fromData, pappArgToTerm, toData } from "../../lib"; import { assert } from "../../../utils/assert"; import { defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; +import { PType } from "../../PType"; +import { PappArg, pappArgToTerm } from "../../lib/pappArg"; +import { TermAlias } from "../../lib/std/UtilityTerms/TermAlias"; +import { fromData } from "../../lib/std/data/conversion/fromData"; +import { toData } from "../../lib/std/data/conversion/toData"; /** @@ -25,35 +29,42 @@ class _PAlias extends PDataRepresentable } } -export type PAlias = +export type PAlias = { - new(): PClass + new(): _PAlias /** * @deprecated - */ - readonly termType: AliasT; - readonly type: AliasT; - readonly fromData: ( data: Term ) => Term; - readonly toData: ( data: Term ) => Term; - - readonly from: ( toAlias: PappArg> ) => Term> + */ + readonly termType: AliasT, AMethods>; + readonly type: AliasT, AMethods>; + readonly fromData: ( data: Term ) => Term>; + readonly toData: ( data: Term> ) => Term; + + readonly from: ( toAlias: PappArg ) => TermAlias } & PDataRepresentable -export function palias( +export function palias< + T extends TermType, + AMethods extends Methods +>( type: T, + getMethods?: ( self_t: AliasT ) => AMethods, fromDataConstraint: (( term: Term> ) => Term>) | undefined = undefined -) +): PAlias, AMethods> { assert( isWellFormedType( type ), "cannot construct 'PAlias' type; the type cannot be converted to an UPLC constant" ); + getMethods = typeof getMethods === "function" ? getMethods : _self_t => { return {} as AMethods }; + type ThisAliasT = AliasT; - type ThisAliasTerm = Term>; + type PT = ToPType; + type ThisAliasTerm = Term>; //@ts-ignore class PAliasExtension extends _PAlias @@ -72,10 +83,14 @@ export function palias( static fromData: ( data: Term ) => ThisAliasTerm; static toData: ( data: ThisAliasTerm ) => Term; - static from: ( toAlias: Term> ) => ThisAliasTerm; + static from: ( toAlias: Term ) => ThisAliasTerm; }; - const thisType: ThisAliasT = alias( type ) as any; + const thisTypeNoMethods = alias( type ); + + const methods = getMethods( thisTypeNoMethods as any ); + + const thisType: ThisAliasT = alias( type, methods ) as any; defineReadOnlyProperty( PAliasExtension, @@ -136,9 +151,10 @@ export function palias( defineReadOnlyProperty( PAliasExtension, "from", - ( toAlias: PappArg> ): ThisAliasTerm => - punsafeConvertType( pappArgToTerm( toAlias, type ), thisType ) as any + ( toAlias: PappArg ): ThisAliasTerm => { + return punsafeConvertType( pappArgToTerm( toAlias, type ), thisType ) as any + } ); - return PAliasExtension as unknown as PAlias; + return PAliasExtension as unknown as PAlias; } diff --git a/packages/onchain/src/pluts/PTypes/PStruct/pmatch/index.ts b/packages/onchain/src/pluts/PTypes/PStruct/pmatch/index.ts index 6908d98f..43fc1e5c 100644 --- a/packages/onchain/src/pluts/PTypes/PStruct/pmatch/index.ts +++ b/packages/onchain/src/pluts/PTypes/PStruct/pmatch/index.ts @@ -7,7 +7,7 @@ import { PLam } from "../../PFn/PLam"; import { PList } from "../../PList"; import { getFields } from "../matchSingleCtorStruct"; import { papp } from "../../../lib/papp"; -import { UtilityTermOf, addUtilityForType } from "../../../lib/addUtilityForType"; +import { UtilityTermOf, addUtilityForType } from "../../../lib/std/UtilityTerms/addUtilityForType"; import { TermList } from "../../../lib/std/UtilityTerms/TermList"; import { plam } from "../../../lib/plam"; import { TermFn } from "../../PFn"; @@ -29,6 +29,7 @@ import { IRConst } from "../../../../IR/IRNodes/IRConst"; import { IRForced } from "../../../../IR/IRNodes/IRForced"; import { IRError } from "../../../../IR/IRNodes/IRError"; import { IRDelayed } from "../../../../IR/IRNodes/IRDelayed"; +import { _punsafeConvertType } from "../../../lib/punsafeConvertType/minimal"; const elemAtCache: { [n: number]: TermFn<[ PList ], PData > } = {}; @@ -86,14 +87,19 @@ function getStructInstance for( let i = 0; i < fieldNames.length; i++ ) { const fieldName = fieldNames[i]; + const fieldType = ctorDef[fieldName]; + Object.defineProperty( instance, fieldName, { - value: addUtilityForType( ctorDef[ fieldName ] )( - _plet( - _fromData( ctorDef[ fieldName ] )( - getElemAtTerm( i ).$( fieldsList ) - ) + value: addUtilityForType( fieldType )( + _punsafeConvertType( + _plet( + _fromData( fieldType )( + getElemAtTerm( i ).$( fieldsList ) + ) + ), + fieldType ) ), writable: false, @@ -306,7 +312,7 @@ function getReturnTypeFromContinuation( * @returns the term that matches the ctor */ function hoistedMatchCtors( - structData: Term>, + structData: Term>, sDef: SDef, ctorCbs: (RawCtorCallback | Term, PType>>)[], ) : Term @@ -392,7 +398,7 @@ function hoistedMatchCtors( return result; } -export function pmatch( struct: Term> ): PMatchOptions +export function pmatch( struct: Term> ): PMatchOptions { const sDef = struct.type[1] as StructDefinition; if( !isStructDefinition( sDef ) ) diff --git a/packages/onchain/src/pluts/PTypes/PStruct/pstruct.ts b/packages/onchain/src/pluts/PTypes/PStruct/pstruct.ts index d1bbfea5..92d419a9 100644 --- a/packages/onchain/src/pluts/PTypes/PStruct/pstruct.ts +++ b/packages/onchain/src/pluts/PTypes/PStruct/pstruct.ts @@ -1,10 +1,10 @@ import type { TermFn } from "../PFn"; import { PAsData, PData } from "../PData/PData"; import { PDataRepresentable } from "../../PType/PDataRepresentable"; -import { UtilityTermOf, addUtilityForType } from "../../lib/addUtilityForType"; +import { UtilityTermOf, addUtilityForType } from "../../lib/std/UtilityTerms/addUtilityForType"; import { structDefToString, termTypeToString } from "../../type_system/utils"; -import { AliasT, GenericStructCtorDef, GenericStructDefinition, GenericTermType, PrimType, StructCtorDef, StructDefinition, StructT, TermType, alias, asData, data, int, struct, tyVar, unit } from "../../type_system/types"; +import { AliasT, GenericStructCtorDef, GenericStructDefinition, GenericTermType, Methods, PrimType, StructCtorDef, StructDefinition, StructT, TermType, alias, asData, data, int, struct, tyVar, unit } from "../../type_system/types"; import { ToPType } from "../../type_system/ts-pluts-conversion"; import { typeExtends, isStructDefinition, isStructType, isTaggedAsAlias } from "../../type_system"; import { Term } from "../../Term"; @@ -39,10 +39,10 @@ export type StructInstance = { } export type StructInstanceAsData = { - [Field in keyof SCtorDef]: Term> | Term> | Term | Term + [Field in keyof SCtorDef]: Term> | Term> | Term | Term } -export type PStruct = { +export type PStruct = { new(): _PStruct /** @@ -51,15 +51,15 @@ export type PStruct = { readonly termType: StructT; readonly type: StructT; - readonly fromDataTerm: TermFn<[PData],PStruct> - fromData: ( data: Term ) => Term>; + readonly fromDataTerm: TermFn<[PData],PStruct> + fromData: ( data: Term ) => Term>; - readonly toDataTerm: TermFn<[PStruct],PData> - toData: ( data: Term> ) => Term; + readonly toDataTerm: TermFn<[PStruct],PData> + toData: ( data: Term> ) => Term; } & PDataRepresentable & { [Ctor in keyof SDef]: - ( ctorFields: StructInstanceAsData ) => TermStruct + ( ctorFields: StructInstanceAsData ) => TermStruct } type Includes = @@ -176,19 +176,82 @@ function isStructInstanceOfDefinition const RESERVED_STRUCT_KEYS = Object.freeze([ "eq", - "eqTerm", + "peq", "extract", "in", "raw" ]); -export function pstruct( def: StructDef ): PStruct +/** + * + * @param {StructDef} def data-type definition of the struct + * + * each property of the object is a possible constructor for the struct; + * + * each constructor is defined by specifiying the fields that constructor expects and relative types + * + * @example + * ```ts + * const Shape = pstruct({ + * Circle: { + * radius: int + * }, + * Rectangle: { + * fstSide: int, + * sndSide: int + * } + * }); + * ``` + * + * @param {( self_t: StructT ) => Methods} getMethods (optional) function to implement arbitrary methods on a given struct. + * + * the function takes as first argument the type of this same struct and expects an object with various methods to be implemented on a struct instance + * + * @example + * ```ts + * const Shape = pstruct({ + * Circle: { + * radius: int + * }, + * Rectangle: { + * fstSide: int, + * sndSide: int + * } + * }, ( self_t ) => { + * + * return { + * largestSide: pfn([ self_t ], int ) + * ( self => + * pmatch( self ) + * .onCircle(({ radius }) => radius ) + * .onRectangle({ fstSide, sndSide } => + * pif( int ).$( fstSide.gt( sndSide ) ) + * .then( fstSide ) + * .else( sndSide ) + * ) + * ) + * }; + * }); + * + * const isLargeShape = pfn([ Shape.type ], int ) + * ( shape => shape.largestSide.gtEq( 100 ) ) + * ``` + */ +export function pstruct< + StructDef extends StructDefinition, + SMethods extends Methods +>( + def: StructDef, + getMethods?: ( self_t: StructT ) => SMethods +): PStruct { assert( isStructDefinition( def ), "cannot construct 'PStruct' type; struct definition is not constant: " + structDefToString( def ) ); + getMethods = typeof getMethods === "function" ? getMethods : _self_t => { return {} as SMethods; }; + class PStructExt extends _PStruct { static _isPType: true = true; @@ -200,13 +263,17 @@ export function pstruct( def: StructDef ): P super(); } - static termType: [ PrimType.Struct, StructDef ]; - static type: [ PrimType.Struct, StructDef ]; + static termType: [ PrimType.Struct, StructDef, SMethods ]; + static type: [ PrimType.Struct, StructDef, SMethods ]; static fromData: ( data: Term ) => Term static toData: ( data: Term ) => Term; } - const thisStructType = struct( def ); + const noMethodsType = struct( def ); + + const methods = getMethods( noMethodsType ); + + const thisStructType = struct( def, methods ); defineReadOnlyProperty( PStructExt, @@ -389,7 +456,6 @@ export function pstruct( def: StructDef ): P (PStructExt.prototype as any)[ctorName] ); } - /* Type 'typeof PStructExt' is not assignable to type 'PStruct' @@ -442,8 +508,8 @@ function replaceAliasesWith( export function typeofGenericStruct( genStruct: ( ...tyArgs: TermType[] ) - => PStruct -): StructT + => PStruct +): StructT { const nArgs = genStruct.length; const aliases: AliasT<[PrimType.Int]>[] = Array( nArgs ); @@ -482,9 +548,9 @@ export function typeofGenericStruct( */ export function pgenericStruct ( - getDescriptor: ( ...tyArgs: TypeArgs ) => PStruct + getDescriptor: ( ...tyArgs: TypeArgs ) => PStruct ): ( - (( ...tyArgs: TyArgs ) => PStruct) & + (( ...tyArgs: TyArgs ) => PStruct) & { type: [ PrimType.Struct, GenericStructDefinition ] } ) { @@ -501,10 +567,10 @@ export function pgenericStruct { - const tyArgsCache: Pair>[] = [] + const tyArgsCache: Pair>[] = [] return defineReadOnlyProperty( - ( ...tyArgs: TypeArgs ): PStruct => { + ( ...tyArgs: TypeArgs ): PStruct => { const thisTyArgsKey = tyArgs.map( termTypeToString ).join('|'); const keys = tyArgsCache.map( pair => pair.fst ); @@ -528,7 +594,7 @@ export function pgenericStruct>( thisTyArgsKey, result ) ); + tyArgsCache.push( new Pair>( thisTyArgsKey, result ) ); return result; }, diff --git a/packages/onchain/src/pluts/Script/Parametrized/__tests__/getFnTypes.test.ts b/packages/onchain/src/pluts/Script/Parametrized/__tests__/getFnTypes.test.ts new file mode 100644 index 00000000..76823a9e --- /dev/null +++ b/packages/onchain/src/pluts/Script/Parametrized/__tests__/getFnTypes.test.ts @@ -0,0 +1,30 @@ +import { bs, fn, int, lam, str } from "../../../type_system/types"; +import { getFnTypes } from "../getFnTypes"; + +describe("getFnTypes", () => { + + test("int", () => { + + expect( + getFnTypes( int ) + ).toEqual([ int ]); + + }); + + test("lam( int, bs )", () => { + + expect( + getFnTypes( lam( int, bs ) ) + ).toEqual([ int, bs ]); + + }); + + test("fn([ int, str ], bs )", () => { + + expect( + getFnTypes( fn([ int, str ], bs ) ) + ).toEqual([ int, str, bs ]); + + }); + +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/Script/Parametrized/getFnTypes.ts b/packages/onchain/src/pluts/Script/Parametrized/getFnTypes.ts index e08b8cc3..afe509ff 100644 --- a/packages/onchain/src/pluts/Script/Parametrized/getFnTypes.ts +++ b/packages/onchain/src/pluts/Script/Parametrized/getFnTypes.ts @@ -1,5 +1,22 @@ import { PrimType, TermType } from "../../type_system"; +/** + * given a term type of a function (lambda) + * extracts all the types from the inputs to the output + * + * the last type is the final output of the funciton. + * + * + * @example + * ```ts + * const notFn = getFnTypes( int ); // [ int ]; + * const simleLam = getFnTypes( lam( int, bs ) ); // [ int, bs ]; + * const twoIns = getFnTypes( fn([ int, str ], bs ) ); // [ int, str, bs ]; + * ``` + * + * @param {TermType} fnT function term type + * @returns {TermType[]} + */ export function getFnTypes( fnT: TermType ): TermType[] { const result: TermType[] = []; diff --git a/packages/onchain/src/pluts/Script/__tests__/Script.compile.NFTVendingMachine.test.ts b/packages/onchain/src/pluts/Script/__tests__/Script.compile.NFTVendingMachine.test.ts index 75d1cd97..62ee579c 100644 --- a/packages/onchain/src/pluts/Script/__tests__/Script.compile.NFTVendingMachine.test.ts +++ b/packages/onchain/src/pluts/Script/__tests__/Script.compile.NFTVendingMachine.test.ts @@ -15,10 +15,9 @@ import { pand } from "../../lib/builtins/bool"; import { Term } from "../../Term"; import { PBool } from "../../PTypes/PBool"; import { plet } from "../../lib/plet"; -import { _old_plet } from "../../lib/plet/old"; import { ByteString } from "@harmoniclabs/bytestring"; -describe("NFTVendingMachine", () => { +describe.skip("NFTVendingMachine", () => { test("it builds", () => { @@ -63,12 +62,12 @@ describe("NFTVendingMachine", () => { // and `1` as quantity pisEmpty.$( tx.mint.tail ) .and( - plet( tx.mint.head ).in( head => - + plet( tx.mint.head ).in( head => { + // `ownCurrSym` as policy, - head.fst.eq( ownCurrSym ) + return head.fst.eq( ownCurrSym ) .and( - _old_plet( head.snd ).in( assets => + plet( head.snd ).in( assets => pisEmpty.$( assets.tail ) .and( plet( assets.head ).in( asset => @@ -76,7 +75,7 @@ describe("NFTVendingMachine", () => { // `1` as quantity asset.snd.eq( 1 ) .and( - + // `Collection#` as asset name asset.fst.eq( pByteString( @@ -92,6 +91,8 @@ describe("NFTVendingMachine", () => { ) ) ) + } + ) ) )) diff --git a/packages/onchain/src/pluts/Script/__tests__/Script.compile.NFTVendingMachine_v05.test.ts b/packages/onchain/src/pluts/Script/__tests__/Script.compile.NFTVendingMachine_v05.test.ts new file mode 100644 index 00000000..722a681d --- /dev/null +++ b/packages/onchain/src/pluts/Script/__tests__/Script.compile.NFTVendingMachine_v05.test.ts @@ -0,0 +1,183 @@ +import { PPubKeyHash } from "../../API/V1/PubKey/PPubKeyHash"; +import { PScriptContext } from "../../API/V2/ScriptContext/PScriptContext"; +import { PCurrencySymbol } from "../../API/V1/Value/PCurrencySymbol"; +import { compile } from "../compile"; +import { bool, bs, data } from "../../type_system/types"; +import { pmatch } from "../../PTypes/PStruct/pmatch"; +import { pfn } from "../../lib/pfn"; +import { pisEmpty } from "../../lib/builtins/list"; +import { pByteString } from "../../lib/std/bs/pByteString"; +import { pdigitToBS } from "../../lib/std/int/pintToBS"; +import { punIData } from "../../lib/builtins/data"; +import { perror } from "../../lib/perror"; +import { pdelay } from "../../lib/pdelay"; +import { pand } from "../../lib/builtins/bool"; +import { Term } from "../../Term"; +import { PBool } from "../../PTypes/PBool"; +import { plet } from "../../lib/plet"; +import { ByteString } from "@harmoniclabs/bytestring"; +import { TermBool } from "@harmoniclabs/plu-ts-onchain"; + +describe("NFTVendingMachine", () => { + + test("it builds", () => { + + const nftPolicy = pfn([ + + bs, // owner public key hash + + bs, // counter thread identifier policy + bs, // price oracle thread identifier policy + + data, + PScriptContext.type + + ], bool) + (( ownerPkh, counterValId, priceOracleId, _rdmr, { tx, purpose } ) => { + + const ownCurrSym = plet( + pmatch( purpose ) + .onMinting( ({ currencySym }) => currencySym ) + ._( _ => perror( bs ) ) + ) + + return tx.inputs.some( ({ resolved: { value: inputValue, datum: _nftCounter } }) => + + // includes the **verified** input of the counter + // since the token that verifies the utxo is unique + // it makes no sense to check for the validator hash too + inputValue.some( entry => entry.policy.eq( counterValId ) ) + + // and delays the computation; in this case is not a detail + // because otherwhise it would have ran for each element of the list + .and( + + pmatch( _nftCounter ) + .onInlineDatum(({ datum: nftCounter }) => { + + const minted = plet( tx.mint.head ); + + const assets = plet( minted.snd ); + + const condition = pisEmpty.$( assets.tail ) + .and( + plet( assets.head ).in( asset => + + // `1` as quantity + asset.snd.eq( 1 ) + .and( + + // `Collection#` as asset name + asset.fst.eq( + pByteString( + ByteString.fromAscii( + "Collection#" + ) + ).concat( + pdigitToBS.$( punIData.$( nftCounter ) ) + ) + ) + ) + ) + ); + + // `ownCurrSym` as policy, + // checks that a SINGLE TOKEN is minted + // with `ownCurrSym` as policy, + // `Collection#` as asset name + // and `1` as quantity + return pisEmpty.$( tx.mint.tail ) + .and( minted.fst.eq( ownCurrSym ) ) + .and( condition ) + }) + ._( _ => perror( bool ) ) + + ) + ) + // finally checks for the price to be paid + .and( + pisEmpty.$( tx.refInputs.tail ) + .and( + (() => { + + return tx.refInputs.head.extract("resolved").in( ({ resolved: oracleRefInput }) => + oracleRefInput.extract("datum","value").in( oracle => + + // includes identifier + // safe if the token is unique (NFT) + oracle.value.some( valueEntry => valueEntry.fst.eq( priceOracleId ) ) + .and( + + tx.outputs.some( output => + output.extract("address","value").in( out => + out.address.extract("credential").in( outAddr => + + pand.$( + + //tx output going to owner + pmatch( outAddr.credential ) + .onPPubKeyCredential( _ => _.extract("pkh").in( ({ pkh }) => + pkh.eq( ownerPkh ) + )) + ._( _ => perror( bool ) ) + + ).$(pdelay( + + pmatch( + out.value.find( valueEntry => + valueEntry.fst.length.eq( 0 ) // empty bytestring (policy of ADA) + ) + ) + .onJust( _ => _.extract("val").in((({val}): Term => + + // list( pair( bs, int ) ) + val.snd + // pair( bs, int ) + .at( 0 ) + // int ( lovelaces ) + .snd.gtEq( + punIData.$( + pmatch( oracle.datum ) + .onInlineDatum( _ => _.extract("datum").in(({ datum }) => datum )) + ._( _ => perror( data ) ) + ) + ) + ))) + .onNothing( _ => perror( bool ) ) as Term + + ) as any ) + + ) + )) + + ) + + ) + ) + })() + ) + ) + }) + + function makeNFTweetPolicy( + owner: Term, + counterNFT: Term, + priceOracleNFT: Term, + ) + { + return nftPolicy + .$( owner as any ) + .$( counterNFT as any ) + .$( priceOracleNFT as any ); + } + + compile( + makeNFTweetPolicy( + PPubKeyHash.from( pByteString("") ), + PCurrencySymbol.from( pByteString("") ), + PCurrencySymbol.from( pByteString("") ) + ) + ) + + }) +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/Script/__tests__/Script.compile.nebula.test.ts b/packages/onchain/src/pluts/Script/__tests__/Script.compile.nebula.test.ts index 9dc68b07..8ce517dd 100644 --- a/packages/onchain/src/pluts/Script/__tests__/Script.compile.nebula.test.ts +++ b/packages/onchain/src/pluts/Script/__tests__/Script.compile.nebula.test.ts @@ -2,7 +2,7 @@ import { fromUtf8 } from "@harmoniclabs/uint8array-utils"; import { Script, ScriptType } from "@harmoniclabs/cardano-ledger-ts"; import { pByteString } from "../../lib/std/bs/pByteString"; import { pfn } from "../../lib/pfn"; -import { PAssetsEntryT, PValue, PValueEntryT } from "../../API/V1/Value/PValue"; +import { PAssetsEntry, PValue, PValueEntry } from "../../API/V1/Value/PValue"; import { bool, bs, data, fn, int, lam, list, map, unit } from "../../type_system/types"; import { plet } from "../../lib/plet"; import { phoist } from "../../lib/phoist"; @@ -31,7 +31,6 @@ import { pList } from "../../lib/std/list/const"; import { pBool } from "../../lib/std/bool/pBool"; import { PTxInInfo } from "../../API/V2/Tx/PTxInInfo"; import { pforce } from "../../lib/pforce"; -import { addUtilityForType } from "../../lib/addUtilityForType"; import { punsafeConvertType } from "../../lib/punsafeConvertType"; import { PTxOut } from "../../API/V2/Tx/PTxOut"; import { precursive } from "../../lib/precursive"; @@ -40,6 +39,7 @@ import { pchooseList, pisEmpty } from "../../lib/builtins/list"; import { ptraceError } from "../../lib/builtins/ptrace"; import { peqBs } from "../../lib/builtins/bs"; import { pmakeUnit } from "../../lib/std/unit/pmakeUnit"; +import { addUtilityForType } from "../../lib/std/UtilityTerms/addUtilityForType"; const pvalueOf = phoist( pfn([ @@ -50,13 +50,13 @@ const pvalueOf = phoist( // search currency symbol // 0 if not found - precursiveList( int, PValueEntryT ) + precursiveList( int, PValueEntry.type ) .$( _self => pdelay( pInt(0) ) ) .$( pfn([ - fn([ list(PValueEntryT) ], int ), - PValueEntryT, - list( PValueEntryT ) + fn([ list(PValueEntry.type) ], int ), + PValueEntry.type, + list( PValueEntry.type ) ], int) ((self, head, tail ) => @@ -65,13 +65,13 @@ const pvalueOf = phoist( // search token name // 0 if not found - precursiveList( int, PAssetsEntryT ) + precursiveList( int, PAssetsEntry.type ) .$( _self => pdelay( pInt(0) ) ) .$( pfn([ - fn([ list(PAssetsEntryT) ], int ), - PAssetsEntryT, - list( PAssetsEntryT ) + fn([ list(PAssetsEntry.type) ], int ), + PAssetsEntry.type, + list( PAssetsEntry.type ) ], int) ((self, head, tail) => @@ -269,7 +269,7 @@ const checkRoyalty = pfn([ // inlined const hasPaid = ( (fee: TermInt) => { - const isAddrToPay = plet( toPay.addr.eqTerm ); + const isAddrToPay = plet( toPay.addr.peq ); const isCorrectDatum = plet( peqData.$( paymentDatum ) ); @@ -474,13 +474,13 @@ const contract = ( params: ContractParams ) => pfn([ pmatch( metadata.find( entry => entry.fst.eq( fromUtf8("traits") ) ) ) .onJust( just => punListData.$( just.val.snd ) .map( punBData ) ) .onNothing( _ => perror( list( bs ) ) ) - .someTerm + .psome ).in( someTrait => traits.every( _trait => pmatch( punsafeConvertType( _trait, TraitOption.type ) ) - .onMustHave(({ trait }) => someTrait.$( trait.eqTerm ) ) + .onMustHave(({ trait }) => someTrait.$( trait.peq ) ) .onMustNotHave(({ trait }) => - pnot.$( someTrait.$( trait.eqTerm )) ) + pnot.$( someTrait.$( trait.peq )) ) ) ) ); @@ -527,7 +527,7 @@ const contract = ( params: ContractParams ) => pfn([ .onJust( ({ val: owner }) => pmatch( owner.credential ) - .onPPubKeyCredential( ({ pkh }) => tx.signatories.some( pkh.eqTerm as any ) ) + .onPPubKeyCredential( ({ pkh }) => tx.signatories.some( pkh.peq as any ) ) .onPScriptCredential( _ => txSignedByNebulaValidator.$( tx.mint ).$( ownInputValue as any ) ) ) @@ -576,13 +576,13 @@ const contract = ( params: ContractParams ) => pfn([ return pmatch( owner.credential ) .onPPubKeyCredential(({ pkh }) => - tx.signatories.some( pkh.eqTerm as any ) + tx.signatories.some( pkh.peq as any ) ) .onPScriptCredential( _ => txSignedByNebulaValidator.$( tx.mint ).$( ownInputValue as any ) ) }); }); -const untypedValidator = ( params: ContractParams ) => makeValidator( contract( params ) ); +const untypedValidator = ( params: ContractParams ) => makeValidator( contract( params ) as any ); const compiledContract = ( params: ContractParams ) => compile( untypedValidator( params ) ); @@ -788,13 +788,13 @@ test("compiles", () => { pmatch( metadata.find( entry => entry.fst.eq( fromUtf8("traits") ) ) ) .onJust( just => punListData.$( just.val.snd ) .map( punBData ) ) .onNothing( _ => perror( list( bs ) ) ) - .someTerm + .psome ).in( someTrait => traits.every( _trait => pmatch( punsafeConvertType( _trait, TraitOption.type ) ) - .onMustHave(({ trait }) => someTrait.$( trait.eqTerm ) ) + .onMustHave(({ trait }) => someTrait.$( trait.peq ) ) .onMustNotHave(({ trait }) => - pnot.$( someTrait.$( trait.eqTerm )) ) + pnot.$( someTrait.$( trait.peq )) ) ) ) ); @@ -834,7 +834,7 @@ test("compiles", () => { .onJust( ({ val: owner }) => pmatch( owner.credential ) - .onPPubKeyCredential( ({ pkh }) => tx.signatories.some( pkh.eqTerm as any ) ) + .onPPubKeyCredential( ({ pkh }) => tx.signatories.some( pkh.peq as any ) ) .onPScriptCredential( _ => txSignedByNebulaValidator.$( tx.mint ).$( ownInputValue as any ) ) ) @@ -877,7 +877,7 @@ test("compiles", () => { return pmatch( owner.credential ) .onPPubKeyCredential(({ pkh }) => - tx.signatories.some( pkh.eqTerm as any ) + tx.signatories.some( pkh.peq as any ) ) .onPScriptCredential( _ => txSignedByNebulaValidator.$( tx.mint ).$( ownInputValue as any ) ) }) diff --git a/packages/onchain/src/pluts/Script/__tests__/Script.compile.oneShotPolicy.test.ts b/packages/onchain/src/pluts/Script/__tests__/Script.compile.oneShotPolicy.test.ts index 63d0b81f..7d4ca4d5 100644 --- a/packages/onchain/src/pluts/Script/__tests__/Script.compile.oneShotPolicy.test.ts +++ b/packages/onchain/src/pluts/Script/__tests__/Script.compile.oneShotPolicy.test.ts @@ -1,7 +1,5 @@ -import { UtilityTermOf, bool, compile } from "../.."; import { PCurrencySymbol, PTxId, PTxOutRef, V2 } from "../../API"; import { pstruct, pmatch } from "../../PTypes"; -import { pData, pDataB, pDataI, perror, pfn, pisEmpty, plet, punsafeConvertType } from "../../lib"; import { fromHex } from "@harmoniclabs/uint8array-utils"; import { _old_plet } from "../../lib/plet/old"; import { Cbor, CborBytes } from "@harmoniclabs/cbor"; @@ -10,6 +8,15 @@ import { Machine } from "@harmoniclabs/plutus-machine"; import { ScriptType } from "@harmoniclabs/plutus-machine/dist/utils/ScriptType"; import { ErrorUPLC, UPLCTerm, UPLCDecoder, UPLCEncoder, UPLCProgram, showUPLC } from "@harmoniclabs/uplc"; import { Script } from "@harmoniclabs/cardano-ledger-ts" +import { pfn } from "../../lib/pfn"; +import { bool } from "../../type_system/types"; +import { plet } from "../../lib/plet"; +import { perror } from "../../lib/perror"; +import type { UtilityTermOf } from "../../lib/std/UtilityTerms/addUtilityForType"; +import { pisEmpty } from "../../lib/builtins/list"; +import { pData, pDataB, pDataI } from "../../lib/std/data/pData"; +import { punsafeConvertType } from "../../lib/punsafeConvertType"; +import { compile } from "../compile"; export const MintRdmr = pstruct({ Mint: {}, @@ -164,7 +171,7 @@ punsafeConvertType( describe("oneShotNFT", () => { - test("it compiles", () => { + test.only("it compiles", () => { // console.log( // showIR( diff --git a/packages/onchain/src/pluts/Term/index.ts b/packages/onchain/src/pluts/Term/index.ts index f68c1838..709f2e7d 100644 --- a/packages/onchain/src/pluts/Term/index.ts +++ b/packages/onchain/src/pluts/Term/index.ts @@ -7,7 +7,7 @@ import { IRTerm } from "../../IR/IRTerm"; import { ToIR } from "../../IR/interfaces/ToIR"; import { compileIRToUPLC } from "../../IR/toUPLC/compileIRToUPLC"; import { PType } from "../PType"; -import { FromPType, TermType, ToPType, isWellFormedGenericType } from "../type_system"; +import { FromPType, TermType, ToPType, isWellFormedGenericType, termTypeToString } from "../type_system"; import { cloneTermType } from "../type_system/cloneTermType"; import { defineReadOnlyHiddenProperty } from "@harmoniclabs/obj-utils"; import { Cloneable, isCloneable } from "../../utils/Cloneable"; @@ -46,7 +46,7 @@ export class Term { assert( isWellFormedGenericType( type ) || - Boolean(void console.log( type )), + Boolean(void console.log( termTypeToString( type ) )), "invalid type while constructing Term" ); diff --git a/packages/onchain/src/pluts/lib/addUtilityForType.ts b/packages/onchain/src/pluts/lib/addUtilityForType.ts deleted file mode 100644 index 9bf94988..00000000 --- a/packages/onchain/src/pluts/lib/addUtilityForType.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { PType } from "../PType"; -import type { PBool, PByteString, PInt, PList, PPair, PString, PStruct, PLam, PAlias } from "../PTypes"; -import { Term } from "../Term"; -import { isTaggedAsAlias } from "../type_system/kinds/isTaggedAsAlias"; -import { isStructType } from "../type_system/kinds/isWellFormedType"; -import { ToPType } from "../type_system/ts-pluts-conversion"; -import { typeExtends } from "../type_system/typeExtends"; -import { AliasT, StructDefinition, TermType, bool, bs, int, lam, list, pair, str, tyVar } from "../type_system/types"; -import { unwrapAlias } from "../type_system/tyArgs/unwrapAlias"; -import type { PappArg } from "./pappArg"; -import { papp } from "./papp"; -import { - TermAlias, - TermBool, addPBoolMethods, - TermBS, addPByteStringMethods, - TermInt, addPIntMethods, - TermList, addPListMethods, - TermPair, addPPairMethods, - TermStr, addPStringMethods, - TermStruct, addPStructMethods -} from "./std/UtilityTerms"; -import { defineNonDeletableNormalProperty } from "@harmoniclabs/obj-utils"; - - -// given the index returns the previous number ( PrevNum[2] -> 1; etc... ) -type PrevNum = [ never, 0, 1, 2, 3, 4, 5, 6 ]; - -// without the "finite" version typescript gets angry and says the type is too complex to be evaluated -type FiniteTermAlias = - MaxDepth extends never ? never : - T extends AliasT ? - // @ts-ignore - FiniteTermAlias : - TermAlias - -export type UtilityTermOf = - ( - PElem extends PBool ? TermBool : - PElem extends PByteString ? TermBS : - PElem extends PInt ? TermInt : - PElem extends PList ? TermList : - PElem extends PPair ? TermPair : - PElem extends PString ? TermStr : - PElem extends PStruct ? TermStruct : - PElem extends PLam ? - Term & { - $: ( input: PappArg ) => UtilityTermOf - } : - PElem extends PAlias ? FiniteTermAlias : - Term - ) & Term // needed because sometime typescript doesn't understands that the term is the same just extended - -export function addUtilityForType( t: T ) - : ( term: Term> ) => UtilityTermOf> -{ - // console.log("adding utility to ", termTypeToString( t ) ); - if( isTaggedAsAlias( t ) ) return addUtilityForType( unwrapAlias( t as any ) ) as any; - - if( typeExtends( t , bool ) ) return addPBoolMethods as any; - if( typeExtends( t , bs ) ) return addPByteStringMethods as any; - if( typeExtends( t , int ) ) return addPIntMethods as any; - if( typeExtends( t , list( tyVar() ) ) ) return addPListMethods as any; - if( typeExtends( t , pair( tyVar(), tyVar() ) ) ) return addPPairMethods as any; - if( typeExtends( t , str ) ) return addPStringMethods as any; - - if( typeExtends( t, lam( tyVar(), tyVar() )) ) - { - return (( term: any ) => defineNonDeletableNormalProperty( - term, - "$", - ( input: any ) => - papp( term, input ) - )) as any; - } - - if( isStructType( t ) ) - { - return addPStructMethods as any; - } - - // no utility - return ((x: any) => x) as any; -} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/builtins/bool/index.ts b/packages/onchain/src/pluts/lib/builtins/bool/index.ts index b072a447..80a62125 100644 --- a/packages/onchain/src/pluts/lib/builtins/bool/index.ts +++ b/packages/onchain/src/pluts/lib/builtins/bool/index.ts @@ -3,11 +3,10 @@ import { IRNative } from "../../../../IR/IRNodes/IRNative"; import { TermFn, PBool, PLam, PDelayed } from "../../../PTypes"; import { Term } from "../../../Term"; import { TermType, ToPType, tyVar, fn, bool, delayed, lam } from "../../../type_system"; -import { UtilityTermOf } from "../../addUtilityForType"; +import { UtilityTermOf } from "../../std/UtilityTerms/addUtilityForType"; import { papp } from "../../papp"; import { PappArg, pappArgToTerm } from "../../pappArg"; import { pdelay } from "../../pdelay"; -import { pfn } from "../../pfn"; import { pforce } from "../../pforce"; import { phoist } from "../../phoist"; import { plam } from "../../plam"; @@ -151,11 +150,9 @@ export const pnot phoist( plam( bool, bool ) ( b => - addPBoolMethods( - pstrictIf( bool ).$( b ) - .$( pBool( false ) ) - .$( pBool( true ) ) - ) + pstrictIf( bool ).$( b ) + .$( pBool( false ) ) + .$( pBool( true ) ) ) ) as any; @@ -243,3 +240,20 @@ export const por )) ) as any; +export const peqBool: Term>> +& { + $: ( bool: PappArg ) => + Term> + & { + $: ( bool: PappArg ) => TermBool + } +} += phoist( + plam( + bool, lam( bool, bool ) + )( a => plam( bool, bool ) + ( b => pstrictIf( bool ).$( a ) + .$( b ) + .$( pnot.$( b ) ) + )) +) as any; \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/builtins/data/index.ts b/packages/onchain/src/pluts/lib/builtins/data/index.ts index a09efe49..baa0cb00 100644 --- a/packages/onchain/src/pluts/lib/builtins/data/index.ts +++ b/packages/onchain/src/pluts/lib/builtins/data/index.ts @@ -3,7 +3,7 @@ import { PType } from "../../../PType"; import { TermFn, PData, PLam, PInt, PList, PPair, PByteString, PBool, PAsData } from "../../../PTypes"; import { Term } from "../../../Term"; import { TermType, ToPType, fn, data, delayed, int, list, lam, pair, asData, bs, bool, _pair } from "../../../type_system"; -import { UtilityTermOf } from "../../addUtilityForType"; +import { UtilityTermOf } from "../../std/UtilityTerms/addUtilityForType"; import { papp } from "../../papp"; import { PappArg } from "../../pappArg"; import { pdelay } from "../../pdelay"; diff --git a/packages/onchain/src/pluts/lib/builtins/int/intBinOpToBool.ts b/packages/onchain/src/pluts/lib/builtins/int/intBinOpToBool.ts index 7f2792f0..62ede7d3 100644 --- a/packages/onchain/src/pluts/lib/builtins/int/intBinOpToBool.ts +++ b/packages/onchain/src/pluts/lib/builtins/int/intBinOpToBool.ts @@ -11,7 +11,7 @@ import { IRNative } from "../../../../IR/IRNodes/IRNative"; import { IRApp } from "../../../../IR/IRNodes/IRApp"; import { IRHoisted } from "../../../../IR/IRNodes/IRHoisted"; -export type IntBinOPToBool = Term>> +export type IntBinOPToBool = Term>> & { $: ( input: PappArg ) => Term> diff --git a/packages/onchain/src/pluts/lib/builtins/pair/index.ts b/packages/onchain/src/pluts/lib/builtins/pair/index.ts index 5ec4e62e..96979828 100644 --- a/packages/onchain/src/pluts/lib/builtins/pair/index.ts +++ b/packages/onchain/src/pluts/lib/builtins/pair/index.ts @@ -4,7 +4,7 @@ import { TermFn, PPair, PLam } from "../../../PTypes"; import { Term } from "../../../Term"; import { PrimType, TermType, ToPType, data, lam, pair } from "../../../type_system"; import { unwrapAsData } from "../../../type_system/tyArgs"; -import { UtilityTermOf } from "../../addUtilityForType"; +import { UtilityTermOf } from "../../std/UtilityTerms/addUtilityForType"; import { papp } from "../../papp"; import { punsafeConvertType } from "../../punsafeConvertType"; import { fromData } from "../../std"; diff --git a/packages/onchain/src/pluts/lib/builtins/pair/noUnwrap.ts b/packages/onchain/src/pluts/lib/builtins/pair/noUnwrap.ts index 79150f96..c88f4f8d 100644 --- a/packages/onchain/src/pluts/lib/builtins/pair/noUnwrap.ts +++ b/packages/onchain/src/pluts/lib/builtins/pair/noUnwrap.ts @@ -3,10 +3,34 @@ import { IRNative } from "../../../../IR/IRNodes/IRNative"; import { TermFn, PPair, PLam } from "../../../PTypes"; import { Term } from "../../../Term"; import { TermType, ToPType, lam, pair } from "../../../type_system"; -import { UtilityTermOf } from "../../addUtilityForType"; +import { UtilityTermOf } from "../../std/UtilityTerms/addUtilityForType"; import { papp } from "../../papp"; +export function pfstPairNoUnwrap( fstType: A, sndType: B ) + : TermFn<[ PPair,ToPType> ], ToPType> +{ + const a = fstType; + const b = sndType; + + const outT = a; + + const bnTerm = new Term, ToPType>, ToPType>>( + lam( pair( a, b ), outT ) as any, + _dbn => IRNative.fstPair + ); + + defineReadOnlyProperty( + bnTerm, + "$", + ( _pair: Term,ToPType>> ): UtilityTermOf> => { + return papp( bnTerm, _pair ); + } + ); + + return bnTerm as any; +} + export function psndPairNoUnwrap( fstType: A, sndType: B ) : TermFn<[ PPair,ToPType> ], ToPType> { diff --git a/packages/onchain/src/pluts/lib/index.ts b/packages/onchain/src/pluts/lib/index.ts index c252f3a2..5514397b 100644 --- a/packages/onchain/src/pluts/lib/index.ts +++ b/packages/onchain/src/pluts/lib/index.ts @@ -8,7 +8,7 @@ export * from "./papp"; export * from "./plam"; export * from "./pfn"; export * from "./precursive"; -export * from "./addUtilityForType"; +export * from "./std/UtilityTerms/addUtilityForType"; export * from "./builtins"; export * from "./builtins/ptrace"; export * from "./plet"; diff --git a/packages/onchain/src/pluts/lib/papp.ts b/packages/onchain/src/pluts/lib/papp.ts index f30087cf..7269fd29 100644 --- a/packages/onchain/src/pluts/lib/papp.ts +++ b/packages/onchain/src/pluts/lib/papp.ts @@ -8,7 +8,7 @@ import { includesDynamicPairs } from "../type_system/includesDynamicPairs"; import { typeExtends } from "../type_system/typeExtends"; import { PrimType, TermType, data } from "../type_system/types"; import { termTypeToString } from "../type_system/utils"; -import { type UtilityTermOf, addUtilityForType } from "./addUtilityForType"; +import { type UtilityTermOf, addUtilityForType } from "./std/UtilityTerms/addUtilityForType"; import { PappArg, pappArgToTerm } from "./pappArg"; import { _fromData } from "./std/data/conversion/fromData_minimal"; import { _papp } from "./std/data/conversion/minimal_common"; diff --git a/packages/onchain/src/pluts/lib/pappArg.ts b/packages/onchain/src/pluts/lib/pappArg.ts index 0225f564..7e10996d 100644 --- a/packages/onchain/src/pluts/lib/pappArg.ts +++ b/packages/onchain/src/pluts/lib/pappArg.ts @@ -1,8 +1,8 @@ import type { PType } from "../PType"; -import type { PLam, PInt, PBool, PByteString, PString, PUnit, PPair, PList } from "../PTypes"; +import type { PLam, PInt, PBool, PByteString, PString, PUnit, PPair, PList, PAlias } from "../PTypes"; import { termTypeToString, getNRequiredLambdaArgs } from "../type_system/utils"; -import { UtilityTermOf } from "./addUtilityForType"; +import { UtilityTermOf } from "./std/UtilityTerms/addUtilityForType"; import { pfn } from "./pfn"; import { pList } from "./std/list/const"; import { pPair } from "./std/pair/pPair"; @@ -19,6 +19,7 @@ import { CborString } from "@harmoniclabs/cbor"; import { has_n_determined_keys } from "@harmoniclabs/obj-utils"; import { Pair } from "@harmoniclabs/pair"; import { assert } from "../../utils/assert"; +import { clearAsData } from "../type_system/tyArgs/clearAsData"; type _TsFunctionSatisfying[], POut extends PType> = @@ -34,6 +35,7 @@ export type TsFunctionSatisfying = export type PappArg = ( + PIn extends PAlias ? PappArg : PIn extends PInt ? bigint | number : PIn extends PBool ? boolean : PIn extends PByteString ? ByteString | Uint8Array | string : @@ -41,10 +43,14 @@ export type PappArg = PIn extends PUnit ? undefined | null : PIn extends PPair ? Pair, PappArg> | { fst: PappArg, snd: PappArg } | [ PappArg, PappArg ] : - PIn extends PList ? PappArg[] : + // PIn extends PList ? PappArg[] : PIn extends PLam ? TsFunctionSatisfying : Term ) | Term + // also the alias of the type is good + // (only works because we know `PIn` is not an alias if we are here) + // because of the initial chec + | Term> export function pappArgToTerm( arg: PappArg>, @@ -290,8 +296,12 @@ export function pappArgToTerm( const [ fst, snd ] = arg as PappArg[]; - const fstT = isWellFormedType( mustExtend[1] ) ? mustExtend[1] : tryGetConstantableType( fst ); - const sndT = isWellFormedType( mustExtend[2] ) ? mustExtend[2] : tryGetConstantableType( snd ); + let fstT = isWellFormedType( mustExtend[1] ) ? mustExtend[1] : tryGetConstantableType( fst ); + let sndT = isWellFormedType( mustExtend[2] ) ? mustExtend[2] : tryGetConstantableType( snd ); + + // maybe ???? + fstT = clearAsData( fstT ); + sndT = clearAsData( sndT ); return pPair( fstT, sndT )( pappArgToTerm( fst, fstT ), @@ -340,6 +350,7 @@ export function pappArgToTerm( } } + console.error( arg, arg.type ); throw new Error( "pappArgToTerm :: it was not possible to transform `arg` to a plu-ts value" + "; `arg` was " + arg + diff --git a/packages/onchain/src/pluts/lib/pfn.ts b/packages/onchain/src/pluts/lib/pfn.ts index a7672ccc..26e3a8a8 100644 --- a/packages/onchain/src/pluts/lib/pfn.ts +++ b/packages/onchain/src/pluts/lib/pfn.ts @@ -5,7 +5,7 @@ import { PLam } from "../PTypes"; import { Term, ToTermArrNonEmpty } from "../Term"; import { ToPType } from "../type_system/ts-pluts-conversion"; import { TermType, fn } from "../type_system/types"; -import { UtilityTermOf } from "./addUtilityForType"; +import { UtilityTermOf } from "./std/UtilityTerms/addUtilityForType"; import { PappArg } from "./pappArg"; import { plam } from "./plam"; import { CurriedFn, curry } from "../../utils/combinators"; @@ -26,17 +26,22 @@ type TermFnFromTypes> ) => TermFnFromTypes< RestIns, Out > } : never +/* type TsTermFuncArg = - ( PTy extends PLam ? + PTy extends PLam ? Term> & { $: ( input: Term ) => UtilityTermOf }: - UtilityTermOf ) extends infer PRes ? PRes & Term : never + UtilityTermOf +//*/ type TsTermFunctionArgs = InputsTypes extends [] ? never : - InputsTypes extends [ infer T extends TermType ] ? [ TsTermFuncArg> ] : - InputsTypes extends [ infer T extends TermType, ...infer RestTs extends [ TermType, ...TermType[] ] ] ? [ TsTermFuncArg>, ...TsTermFunctionArgs ] : + InputsTypes extends [ infer T extends TermType ] ? [ UtilityTermOf> ] : + InputsTypes extends [ + infer T extends TermType, + ...infer RestTs extends [ TermType, ...TermType[] ] + ] ? [ UtilityTermOf>, ...TsTermFunctionArgs ] : never; export type TsTermFunction = diff --git a/packages/onchain/src/pluts/lib/pforce/index.ts b/packages/onchain/src/pluts/lib/pforce/index.ts index 3e9800a4..f6397346 100644 --- a/packages/onchain/src/pluts/lib/pforce/index.ts +++ b/packages/onchain/src/pluts/lib/pforce/index.ts @@ -4,7 +4,7 @@ import { PType } from "../../PType"; import { PDelayed } from "../../PTypes"; import { Term } from "../../Term"; import { PrimType } from "../../type_system"; -import { UtilityTermOf, addUtilityForType } from "../addUtilityForType"; +import { UtilityTermOf, addUtilityForType } from "../std/UtilityTerms/addUtilityForType"; export function pforce( toForce: Term> ): UtilityTermOf export function pforce( toForce: Term ): UtilityTermOf diff --git a/packages/onchain/src/pluts/lib/plam.ts b/packages/onchain/src/pluts/lib/plam.ts index 5343dc6c..9ed802a3 100644 --- a/packages/onchain/src/pluts/lib/plam.ts +++ b/packages/onchain/src/pluts/lib/plam.ts @@ -4,7 +4,7 @@ import { Term } from "../Term"; import { includesDynamicPairs } from "../type_system/includesDynamicPairs"; import { ToPType } from "../type_system/ts-pluts-conversion"; import { TermType, lam } from "../type_system/types"; -import { UtilityTermOf, addUtilityForType } from "./addUtilityForType"; +import { UtilityTermOf, addUtilityForType } from "./std/UtilityTerms/addUtilityForType"; import { PappResult, papp } from "./papp"; import { IRVar } from "../../IR/IRNodes/IRVar"; import { IRFunc } from "../../IR/IRNodes/IRFunc"; diff --git a/packages/onchain/src/pluts/lib/plet/__tests__/plet.deps.test.ts b/packages/onchain/src/pluts/lib/plet/__tests__/plet.deps.test.ts new file mode 100644 index 00000000..c2468c3d --- /dev/null +++ b/packages/onchain/src/pluts/lib/plet/__tests__/plet.deps.test.ts @@ -0,0 +1,296 @@ +import { showUPLC } from "@harmoniclabs/uplc"; +import { PScriptContext } from "../../../API/V2"; +import { pmatch } from "../../../PTypes/PStruct/pmatch"; +import { pstruct } from "../../../PTypes/PStruct/pstruct"; +import { bs, int } from "../../../type_system/types"; +import { perror } from "../../perror"; +import { pfn } from "../../pfn"; +import { pInt } from "../../std"; + +import { plet } from "../index"; +import { _old_plet } from "../old"; +import { plam } from "../../plam"; +import { palias } from "../../../PTypes/PAlias/palias"; +import { PValue } from "../../../API/V1/Value/PValue"; + +describe("plet dependecies", () => { + + describe("structs", () => { + + const MyStruct = pstruct({ + New: { + hello: int, + there: bs + } + }); + + test("dot notation", () => { + + const term = pfn([ + MyStruct.type + ], int) + ( my => { + + const two = plet( pInt( 2 ) ); + + return my.hello.add( two ).mult( two ) + }); + + term.toIR(); + }); + + test("(deprecated) extract", () => { + + const term = pfn([ + MyStruct.type + ], int) + ( my => { + + return my.extract("hello").in(({ hello }) => { + + const two = plet( pInt( 2 ) ); + + return hello.add( two ).mult( two ) + }); + + }); + + term.toIR(); + }); + + test("(deprecated) extract with plet( ... ).in", () => { + + const term = pfn([ + MyStruct.type + ], int) + ( my => { + + return my.extract("hello").in(({ hello }) => { + + return plet( pInt( 2 ) ).in( two => + hello.add( two ).mult( two ) + ); + }) + }); + + term.toIR() + + }); + + }); + + test("plet().in ownCurrencySymbol" , () => { + + const doubleOwnCurrSym = pfn([ + PScriptContext.type + ], bs) + (( ctx ) => + + plet( ctx.purpose ).in( ( purpose ) => + + plet( + pmatch( purpose ) + .onMinting( mint => mint.currencySym ) + ._( _ => perror( bs ) ) + ).in( ownCurrSym => ownCurrSym.concat( ownCurrSym ) ) + + ) + ); + + doubleOwnCurrSym.toIR() + + }); + + test("(deprecated) extract().in ownCurrencySymbol" , () => { + + const doubleOwnCurrSym = pfn([ + PScriptContext.type + ], bs) + (( ctx ) => + + ctx.extract("purpose").in( ({ purpose }) => + + plet( + pmatch( purpose ) + .onMinting( mint => mint.currencySym ) + ._( _ => perror( bs ) ) + ).in( ownCurrSym => ownCurrSym.concat( ownCurrSym ) ) + + ) + ); + + doubleOwnCurrSym.toIR() + + }); + + test("(latest) ownCurrencySymbol" , () => { + + const doubleOwnCurrSym = pfn([ + PScriptContext.type + ], bs) + (( { purpose } ) => { + + const ownCurrSym = plet( + pmatch( purpose ) + .onMinting( mint => mint.currencySym ) + ._( _ => perror( bs ) ) + ); + + return ownCurrSym.concat( ownCurrSym ); + }); + + doubleOwnCurrSym.toIR() + + }); + + describe("plet(...).in( stuff => plet(...).in( thing => ... )", () => { + + test("dummy", () => { + + const dummy = plet( pInt( 42 ) ).in( stuff => + plet( pInt( 69 ) ).in( thing => thing.add( stuff ) ) + ); + + const uplc = dummy.toUPLC(); + + }); + + test("dummy in function", () => { + + const dummy = plam( int, int ) + ( n => + plet( pInt( 42 ) ).in( stuff => + plet( pInt( 69 ) ).in( thing => thing.add( stuff ).add( n ) ) + ) + ) + + const uplc = dummy.toUPLC(); + + }); + + test("dummy struct", () => { + + const Thing = pstruct({ + Thing: { + thing: int + } + }); + + const dummy = plam( Thing.type, int ) + ( n => + plet( pInt( 42 ) ).in( stuff => + plet( pInt( 69 ) ).in( thing => thing.add( stuff ).add( n.thing ) ) + ) + ) + + const uplc = dummy.toUPLC(); + + }); + + test("dummy struct w/ methods", () => { + + const Thing = pstruct({ + Thing: { + thing: int + } + }, + self_t => { + return { + num: plam( self_t, int ) + ( self => self.thing ) + } + }); + + const dummy = plam( Thing.type, int ) + ( n => + plet( pInt( 42 ) ).in( stuff => + plet( pInt( 69 ) ).in( thing => thing.add( stuff ).add( n.thing ) ) + ) + ) + + const uplc = dummy.toUPLC(); + + }); + + test("dummy val", () => { + + const dummy = plam( PValue.type, int ) + ( val => + plet( val.head.snd ).in( thing => + plet( val.head.fst.at(0) ).in( stuff => + stuff.add( 3 ) + ) + ) + ) + + const uplc = dummy.toUPLC(); + + }); + + test("plet( struct_with_methods ).in( thing => ... )", () => { + + const Thing = pstruct({ + Thing: { + thing: int + } + }, + self_t => { + return { + num: plam( self_t, int ) + ( self => self.thing ) + } + }); + + const dummy = plam( Thing.type, int ) + ( thing => + plet( thing ).in( t => + t.thing + ) + ) + + const uplc = dummy.toUPLC(); + + }); + + test("plet( alias_with_methods ).in( thing => ... )", () => { + + const Thing = palias( + bs, + self_t => { + return { + num: plam( self_t, int ) + ( self => self.at(0) ) + } + } + ); + + const dummy = plam( Thing.type, int ) + ( thing => + plet( thing ).in( t => + t.num + ) + ) + + const uplc = dummy.toUPLC(); + + }); + + test("value", () => { + + const thing = plam( PValue.type, int ) + ( val => + plet( val.head.policy ).in( policy => + plet( val.head.assets ).in( assets => + + pInt(0) + ) + ) + ); + + const uplc = thing.toUPLC(); + + }); + + }) + +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/plet/__tests__/plet.test.ts b/packages/onchain/src/pluts/lib/plet/__tests__/plet.test.ts index 9a993377..7fc10a09 100644 --- a/packages/onchain/src/pluts/lib/plet/__tests__/plet.test.ts +++ b/packages/onchain/src/pluts/lib/plet/__tests__/plet.test.ts @@ -1,6 +1,5 @@ import { pBool, pfn, plet } from "../.." import { compileIRToUPLC } from "../../../../IR/toUPLC/compileIRToUPLC"; -import { showUPLC } from "../../../../UPLC/UPLCTerm"; import { bool, int } from "../../../type_system" diff --git a/packages/onchain/src/pluts/lib/plet/index.ts b/packages/onchain/src/pluts/lib/plet/index.ts index 0125ed2f..527b1cbc 100644 --- a/packages/onchain/src/pluts/lib/plet/index.ts +++ b/packages/onchain/src/pluts/lib/plet/index.ts @@ -1,20 +1,26 @@ +import type { PType } from "../../PType"; +import type { PAlias } from "../../PTypes/PAlias/palias"; +import type { TermAlias } from "../std/UtilityTerms/TermAlias"; import { IRApp } from "../../../IR/IRNodes/IRApp"; import { IRFunc } from "../../../IR/IRNodes/IRFunc"; -import { IRHoisted } from "../../../IR/IRNodes/IRHoisted"; import { IRLetted } from "../../../IR/IRNodes/IRLetted"; import { IRVar } from "../../../IR/IRNodes/IRVar"; -import { isClosedIRTerm } from "../../../IR/utils/isClosedIRTerm"; -import type { PType } from "../../PType"; import { Term } from "../../Term"; import { PrimType } from "../../type_system/types"; -import { UtilityTermOf, addUtilityForType } from "../addUtilityForType"; import { _fromData } from "../std/data/conversion/fromData_minimal"; +import { IRHoisted } from "../../../IR/IRNodes/IRHoisted"; +import { isClosedIRTerm } from "../../../IR/utils/isClosedIRTerm"; +import { UtilityTermOf, addUtilityForType } from "../std/UtilityTerms/addUtilityForType"; +import { makeMockUtilityTerm } from "../std/UtilityTerms/mockUtilityTerms/makeMockUtilityTerm"; -export type LettedTerm = UtilityTermOf & { - in: ( expr: (value: UtilityTermOf) => Term ) => Term +export type LettedTerm = UtilityTermOf & { + in: + Term & SomeExtension extends Term> ? + ( expr: (value: TermAlias & SomeExtension) => Term ) => Term + : ( expr: (value: UtilityTermOf) => Term ) => Term } -export function plet( varValue: Term & SomeExtension ): LettedTerm +export function plet( varValue: Term & SomeExtension ): LettedTerm { type TermPVar = Term & SomeExtension; @@ -54,21 +60,16 @@ export function plet( varValu const continuation = ( expr: (value: TermPVar) => Term ): Term => { - const withUtility = addUtilityForType( varValue.type ); // only to extracts the type; never compiled - const outType = expr( - withUtility( - new Term( - varValue.type, - _varAccessDbn => new IRVar( 0 ) // mock variable - ) as any - ) as any - ).type; + const outType = expr( makeMockUtilityTerm( varValue.type ) as any ).type; // return papp( plam( varValue.type, outType )( expr as any ), varValue as any ) as any; const term = new Term( outType, dbn => { + + const withUtility = addUtilityForType( varValue.type ); + const arg = varValue.toIR( dbn ); if( @@ -99,8 +100,10 @@ export function plet( varValu return term; } + const lettedUtility = addUtilityForType( type )( letted ); + Object.defineProperty( - letted, "in", { + lettedUtility, "in", { value: continuation, writable: false, enumerable: false, @@ -108,5 +111,5 @@ export function plet( varValu } ); - return addUtilityForType( type )( letted ) as any; + return lettedUtility as any; } \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/plet/old.ts b/packages/onchain/src/pluts/lib/plet/old.ts index 0ef277c4..081747d0 100644 --- a/packages/onchain/src/pluts/lib/plet/old.ts +++ b/packages/onchain/src/pluts/lib/plet/old.ts @@ -4,8 +4,8 @@ import { IRLetted } from "../../../IR/IRNodes/IRLetted"; import { IRVar } from "../../../IR/IRNodes/IRVar"; import type { PType } from "../../PType"; import { Term } from "../../Term"; -import { PrimType } from "../../type_system"; -import { UtilityTermOf, addUtilityForType } from "../addUtilityForType"; +import { PrimType } from "../../type_system/types"; +import { UtilityTermOf, addUtilityForType } from "../std/UtilityTerms/addUtilityForType"; import { _fromData } from "../std/data/conversion/fromData_minimal"; diff --git a/packages/onchain/src/pluts/lib/precursive/index.ts b/packages/onchain/src/pluts/lib/precursive/index.ts new file mode 100644 index 00000000..0d715baf --- /dev/null +++ b/packages/onchain/src/pluts/lib/precursive/index.ts @@ -0,0 +1,40 @@ +import { IRApp } from "../../../IR/IRNodes/IRApp"; +import { IRNative } from "../../../IR/IRNodes/IRNative"; +import { assert } from "../../../utils/assert"; +import { PType } from "../../PType"; +import { PLam, TermFn } from "../../PTypes"; +import { Term } from "../../Term"; +import { TermType, lam, tyVar, typeExtends } from "../../type_system"; +import { addUtilityForType } from "../std/UtilityTerms/addUtilityForType"; +import { _precursive } from "./minimal"; + + +/** + * for reference the "Z combinator in js": https://medium.com/swlh/y-and-z-combinators-in-javascript-lambda-calculus-with-real-code-31f25be934ec + * + * ```js + * const Zcombinator = ( + * Z => ( + * toMakeRecursive => Z( value => toMakeRecursive(toMakeRecursive)(value) ) + * )( toMakeRecursive => Z( value => toMakeRecursive(toMakeRecursive)(value)) ) + * ); + * ``` + * of type + * ```js + * Z => toMakeRecursive => value => result + * ``` + * and ```toMakeRecursive``` has to be of type + * ```js + * self => value => result + * ``` + */ +export function precursive +( fnBody: + Term, // self + PLam> // the actual function + > +): TermFn<[ A ], B > +{ + return addUtilityForType( fnBody.type[2] as TermType )( _precursive( fnBody ) ) as any; +} diff --git a/packages/onchain/src/pluts/lib/precursive.ts b/packages/onchain/src/pluts/lib/precursive/minimal.ts similarity index 68% rename from packages/onchain/src/pluts/lib/precursive.ts rename to packages/onchain/src/pluts/lib/precursive/minimal.ts index d6891bab..deae0088 100644 --- a/packages/onchain/src/pluts/lib/precursive.ts +++ b/packages/onchain/src/pluts/lib/precursive/minimal.ts @@ -1,11 +1,10 @@ -import { IRApp } from "../../IR/IRNodes/IRApp"; -import { IRNative } from "../../IR/IRNodes/IRNative"; -import { assert } from "../../utils/assert"; -import { PType } from "../PType"; -import { PLam, TermFn } from "../PTypes"; -import { Term } from "../Term"; -import { TermType, lam, tyVar, typeExtends } from "../type_system"; -import { addUtilityForType } from "./addUtilityForType"; +import { IRApp } from "../../../IR/IRNodes/IRApp"; +import { IRNative } from "../../../IR/IRNodes/IRNative"; +import { assert } from "../../../utils/assert"; +import { PType } from "../../PType"; +import { PFn, PLam } from "../../PTypes"; +import { Term } from "../../Term"; +import { TermType, lam, tyVar, typeExtends } from "../../type_system"; /** @@ -27,13 +26,13 @@ import { addUtilityForType } from "./addUtilityForType"; * self => value => result * ``` */ -export function precursive +export function _precursive ( fnBody: Term, // self PLam> // the actual function > -): TermFn<[ A ], B > +): Term> { const a = tyVar("recursive_fn_a"); const b = tyVar("recursive_fn_b"); @@ -58,5 +57,5 @@ export function precursive ) ) - return addUtilityForType( fnBody.type[2] as TermType )( recursiveFn ) as any; + return ( recursiveFn ) as any; } diff --git a/packages/onchain/src/pluts/lib/punsafeConvertType/index.ts b/packages/onchain/src/pluts/lib/punsafeConvertType/index.ts index 3ca7b47a..2baf2be2 100644 --- a/packages/onchain/src/pluts/lib/punsafeConvertType/index.ts +++ b/packages/onchain/src/pluts/lib/punsafeConvertType/index.ts @@ -1,33 +1,40 @@ import type { PType } from "../../PType"; import type { TermType } from "../../type_system/types"; import type { ToPType } from "../../type_system/ts-pluts-conversion"; -import { type UtilityTermOf, addUtilityForType } from "../addUtilityForType"; +import { type UtilityTermOf, addUtilityForType } from "../std/UtilityTerms/addUtilityForType"; import { isWellFormedType } from "../../type_system/kinds/isWellFormedType"; import { Term } from "../../Term"; +import { termTypeToString } from "../../type_system"; export function punsafeConvertType -( someTerm: Term, toType: ToTermType ): UtilityTermOf> +( psome: Term, toType: ToTermType ): UtilityTermOf> { if( !isWellFormedType( toType ) ) - throw new Error("`punsafeConvertType` called with invalid type"); + { + throw new Error("`punsafeConvertType` called with invalid type"); + } const converted = new Term( toType, - someTerm.toIR, - Boolean((someTerm as any).isConstant) // isConstant + psome.toIR, + Boolean((psome as any).isConstant) // isConstant ) as any; - Object.keys( someTerm ).forEach( k => { + Object.keys( psome ).forEach( k => { // do not overwrite `type` and `toUPLC` properties - if( k === "type" || k === "toUPLC" || k === "toIR" ) return; + if( + k === "type" || + k === "toUPLC" || + k === "toIR" + ) return; Object.defineProperty( converted, k, Object.getOwnPropertyDescriptor( - someTerm, + psome, k ) ?? {} ) diff --git a/packages/onchain/src/pluts/lib/punsafeConvertType/minimal.ts b/packages/onchain/src/pluts/lib/punsafeConvertType/minimal.ts index 284bc2da..f69e886f 100644 --- a/packages/onchain/src/pluts/lib/punsafeConvertType/minimal.ts +++ b/packages/onchain/src/pluts/lib/punsafeConvertType/minimal.ts @@ -1,33 +1,36 @@ import type { PType } from "../../PType"; import type { TermType } from "../../type_system/types"; import type { ToPType } from "../../type_system/ts-pluts-conversion"; -import { type UtilityTermOf } from "../addUtilityForType"; import { isWellFormedType } from "../../type_system/kinds/isWellFormedType"; import { Term } from "../../Term"; export function _punsafeConvertType -( someTerm: Term, toType: ToTermType ): UtilityTermOf> +( psome: Term, toType: ToTermType ): Term> { if( !isWellFormedType( toType ) ) throw new Error(""); const converted = new Term( toType, - someTerm.toIR, - Boolean((someTerm as any).isConstant) // isConstant + psome.toIR, + Boolean((psome as any).isConstant) // isConstant ) as any; - Object.keys( someTerm ).forEach( k => { + Object.keys( psome ).forEach( k => { // do not overwrite `type` and `toUPLC` properties - if( k === "type" || k === "toUPLC" || k === "toIR") return; + if( + k === "type" || + k === "toUPLC" || + k === "toIR" + ) return; Object.defineProperty( converted, k, Object.getOwnPropertyDescriptor( - someTerm, + psome, k ) ?? {} ) diff --git a/packages/onchain/src/pluts/lib/std/PMaybe/PMaybe.ts b/packages/onchain/src/pluts/lib/std/PMaybe/PMaybe.ts index d1a0c0da..cbce1dbb 100644 --- a/packages/onchain/src/pluts/lib/std/PMaybe/PMaybe.ts +++ b/packages/onchain/src/pluts/lib/std/PMaybe/PMaybe.ts @@ -10,7 +10,7 @@ export type MaybeT = StructT<{ export type PMaybeT = PStruct<{ Just: { val: FromPType }, Nothing: {} -}> +}, any> export function PMaybe(tyArg: T) { diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermAlias.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermAlias.ts index 6b7369e3..a61ae61a 100644 --- a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermAlias.ts +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermAlias.ts @@ -1,18 +1,37 @@ -import { PAlias } from "../../../PTypes" -import { Term } from "../../../Term" -import { TermType, AliasT, ToPType } from "../../../type_system" -import { UtilityTermOf } from "../../addUtilityForType" +import type { PType } from "../../../PType" +import type { PAlias } from "../../../PTypes" +import type { Term } from "../../../Term" +import type { Methods } from "../../../type_system" +import type { UtilityTermOf } from "./addUtilityForType" +import type { FilterMethodsByInput, LiftMethods, MethodsAsTerms } from "./userMethods/methodsTypes" /** * basically unwraps the alias until it finds an actual type **/ -type NotUtilityOfAlias = - T extends AliasT ? - NotUtilityOfAlias : - UtilityTermOf> +type ActualTermUtility = + PT extends PAlias ? + ActualTermUtility : + UtilityTermOf -export type TermAlias = - T extends AliasT ? - Term> & NotUtilityOfAlias: - Term> & NotUtilityOfAlias \ No newline at end of file + +type ActualTermAlias = + Term> & + ActualTermUtility & + LiftMethods< + FilterMethodsByInput> + > & + MethodsAsTerms< + FilterMethodsByInput> + > + +export type TermAlias = + // if the type is already an alias + PT extends PAlias ? + // add utility of the actual type + ActualTermAlias : + // else add utility to this type (aliased) + ActualTermAlias + +// `addPAliasMethod` is (necessarily) mutually recursive with `addUtilityForType` +// so it is defined in "../addUtilityForType.ts" \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermBS.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermBS.ts index f73d4498..2229c6b5 100644 --- a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermBS.ts +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermBS.ts @@ -22,37 +22,37 @@ export type TermBS = Term & { readonly utf8Decoded: TermStr // pappendBs - readonly concatTerm: TermFn<[PByteString], PByteString> + readonly pconcat: TermFn<[PByteString], PByteString> readonly concat: ( other: PappArg) => TermBS // pconsBs - readonly prependTerm: TermFn<[PInt], PByteString> + readonly pprepend: TermFn<[PInt], PByteString> readonly prepend: ( byte: PappArg ) => TermBS // psliceBs - readonly subByteStringTerm: TermFn<[PInt, PInt], PByteString> + readonly psubByteString: TermFn<[PInt, PInt], PByteString> readonly subByteString: ( fromInclusive: PappArg, ofLength: PappArg ) => TermBS - readonly sliceTerm: TermFn<[PInt, PInt], PByteString> + readonly pslice: TermFn<[PInt, PInt], PByteString> readonly slice: ( fromInclusive: PappArg, toExclusive: PappArg ) => TermBS // pindexBs - readonly atTerm: TermFn<[PInt], PInt> + readonly pat: TermFn<[PInt], PInt> readonly at: ( index: PappArg ) => TermInt - readonly eqTerm: TermFn<[PByteString], PBool> + readonly peq: TermFn<[PByteString], PBool> readonly eq: ( other: PappArg ) => TermBool - readonly ltTerm: TermFn<[PByteString], PBool> + readonly plt: TermFn<[PByteString], PBool> readonly lt: ( other: PappArg ) => TermBool - readonly ltEqTerm: TermFn<[PByteString], PBool> + readonly pltEq: TermFn<[PByteString], PBool> readonly ltEq: ( other: PappArg ) => TermBool - readonly gtTerm: TermFn<[PByteString], PBool> + readonly pgt: TermFn<[PByteString], PBool> readonly gt: ( other: PappArg ) => TermBool - readonly gtEqTerm: TermFn<[PByteString], PBool> + readonly pgtEq: TermFn<[PByteString], PBool> readonly gtEq: ( other: PappArg ) => TermBool } @@ -106,7 +106,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "concatTerm", + "pconcat", { get: () => pappendBs.$( term ), ...getterOnly @@ -120,7 +120,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "prependTerm", + "pprepend", { get: () => flippedCons.$( term ), ...getterOnly @@ -134,7 +134,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "subByteStringTerm", + "psubByteString", { get: () => subByteString.$( term ), ...getterOnly @@ -148,7 +148,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "sliceTerm", + "pslice", { get: () => jsLikeSlice.$( term ), ...getterOnly @@ -162,7 +162,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "atTerm", + "pat", { get: () => pindexBs.$( term ), ...getterOnly @@ -176,7 +176,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "eqTerm", + "peq", { get: () => peqBs.$( term ), ...getterOnly @@ -190,7 +190,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "ltTerm", + "plt", { get: () => plessBs.$( term ), ...getterOnly @@ -204,7 +204,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "ltEqTerm", + "pltEq", { get: () => plessEqBs.$( term ), ...getterOnly @@ -218,7 +218,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "gtTerm", + "pgt", { get: () => pgreaterBS.$( term ), ...getterOnly @@ -232,7 +232,7 @@ export function addPByteStringMethods( term: Term ): TermBS definePropertyIfNotPresent( term, - "gtEqTerm", + "pgtEq", { get: () => pgreaterEqBS.$( term ), ...getterOnly diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermBool.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermBool.ts index ffc8d45b..dac7d49a 100644 --- a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermBool.ts +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermBool.ts @@ -3,7 +3,7 @@ import type { PBool, TermFn, PDelayed } from "../../../PTypes" import type { PappArg } from "../../pappArg" import { Term } from "../../../Term" import { pBool } from "../bool/pBool" -import { por, pstrictOr, pand, pstrictAnd } from "../../builtins/bool" +import { por, pstrictOr, pand, pstrictAnd, peqBool } from "../../builtins/bool" import { delayed } from "../../../type_system/types" import { IRDelayed } from "../../../../IR/IRNodes/IRDelayed" import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclabs/obj-utils" @@ -11,18 +11,20 @@ import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclab export type TermBool = Term & { - readonly orTerm: TermFn<[ PDelayed ], PBool> + readonly por: TermFn<[ PDelayed ], PBool> readonly or: ( other: PappArg ) => TermBool - readonly strictOrTerm: TermFn<[ PBool ], PBool> + readonly pstrictOr: TermFn<[ PBool ], PBool> readonly strictOr: ( other: PappArg ) => TermBool - readonly andTerm: TermFn<[ PDelayed ], PBool> + readonly pand: TermFn<[ PDelayed ], PBool> readonly and: ( other: PappArg ) => TermBool - readonly strictAndTerm: TermFn<[ PBool ], PBool> + readonly pstrictAnd: TermFn<[ PBool ], PBool> readonly strictAnd: ( other: PappArg ) => TermBool + readonly peq: TermFn<[ PBool ], PBool> + readonly eq: ( other: PappArg ) => TermBool } // avoid circular dependency @@ -49,7 +51,7 @@ export function addPBoolMethods( term: Term ): TermBool { definePropertyIfNotPresent( term, - "orTerm", + "por", { get: () => por.$( term ), ...getterOnly @@ -69,7 +71,7 @@ export function addPBoolMethods( term: Term ): TermBool definePropertyIfNotPresent( term, - "strictOrTerm", + "pstrictOr", { get: () => pstrictOr.$( term ), ...getterOnly @@ -84,7 +86,7 @@ export function addPBoolMethods( term: Term ): TermBool definePropertyIfNotPresent( term, - "andTerm", + "pand", { get: () => pand.$( term ), ...getterOnly @@ -104,7 +106,7 @@ export function addPBoolMethods( term: Term ): TermBool definePropertyIfNotPresent( term, - "strictAndTerm", + "pstrictAnd", { get: () => pstrictAnd.$( term ), ...getterOnly @@ -116,5 +118,19 @@ export function addPBoolMethods( term: Term ): TermBool ( other: PappArg ): TermBool => pstrictAnd.$( term ).$( other ) ); + definePropertyIfNotPresent( + term, + "peq", + { + get: () => peqBool.$( term ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "eq", + ( other: PappArg ): TermBool => peqBool.$( term ).$( other ) + ); + return term as any; } \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermInt.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermInt.ts index 0189746b..6ec4cc9a 100644 --- a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermInt.ts +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermInt.ts @@ -10,41 +10,41 @@ import { TermBool } from "./TermBool"; export type TermInt = Term & { - readonly addTerm: TermFn<[PInt], PInt> + readonly padd: TermFn<[PInt], PInt> readonly add: ( other: PappArg ) => TermInt - readonly subTerm: TermFn<[PInt], PInt> + readonly psub: TermFn<[PInt], PInt> readonly sub: ( other: PappArg ) => TermInt - readonly multTerm: TermFn<[PInt], PInt> + readonly pmult: TermFn<[PInt], PInt> readonly mult: ( other: PappArg ) => TermInt - readonly divTerm: TermFn<[PInt], PInt> + readonly pdiv: TermFn<[PInt], PInt> readonly div: ( other: PappArg ) => TermInt - readonly quotTerm: TermFn<[PInt], PInt> + readonly pquot: TermFn<[PInt], PInt> readonly quot: ( other: PappArg ) => TermInt - readonly remainderTerm: TermFn<[PInt], PInt> + readonly premainder: TermFn<[PInt], PInt> readonly remainder: ( other: PappArg ) => TermInt - readonly modTerm: TermFn<[PInt], PInt> + readonly pmod: TermFn<[PInt], PInt> readonly mod: ( other: PappArg ) => TermInt - readonly eqTerm: TermFn<[PInt], PBool> + readonly peq: TermFn<[PInt], PBool> readonly eq: ( other: PappArg ) => TermBool - readonly ltTerm: TermFn<[PInt], PBool> + readonly plt: TermFn<[PInt], PBool> readonly lt: ( other: PappArg ) => TermBool - readonly ltEqTerm: TermFn<[PInt], PBool> + readonly pltEq: TermFn<[PInt], PBool> readonly ltEq: ( other: PappArg ) => TermBool - readonly gtTerm: TermFn<[PInt], PBool> + readonly pgt: TermFn<[PInt], PBool> readonly gt: ( other: PappArg ) => TermBool - readonly gtEqTerm: TermFn<[PInt], PBool> + readonly pgtEq: TermFn<[PInt], PBool> readonly gtEq: ( other: PappArg ) => TermBool }; @@ -60,7 +60,7 @@ export function addPIntMethods( term: Term ) { definePropertyIfNotPresent( term, - "addTerm", + "padd", { get: () => padd.$( term ), ...getterOnly @@ -74,7 +74,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "subTerm", + "psub", { get: () => psub.$( term ), ...getterOnly @@ -88,7 +88,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "multTerm", + "pmult", { get: () => pmult.$( term ), ...getterOnly @@ -102,7 +102,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "divTerm", + "pdiv", { get: () => pdiv.$( term ), ...getterOnly @@ -116,7 +116,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "quotTerm", + "pquot", { get: () => pquot.$( term ), ...getterOnly @@ -130,7 +130,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "remainderTerm", + "premainder", { get: () => prem.$( term ), ...getterOnly @@ -144,7 +144,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "modTerm", + "pmod", { get: () => pmod.$( term ), ...getterOnly @@ -159,7 +159,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "eqTerm", + "peq", { get: () => peqInt.$( term ), ...getterOnly @@ -173,7 +173,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "ltTerm", + "plt", { get: () => plessInt.$( term ), ...getterOnly @@ -187,7 +187,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "ltEqTerm", + "pltEq", { get: () => plessEqInt.$( term ), ...getterOnly @@ -201,7 +201,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "gtTerm", + "pgt", { get: () => pgreaterInt.$( term ), ...getterOnly @@ -215,7 +215,7 @@ export function addPIntMethods( term: Term ) definePropertyIfNotPresent( term, - "gtEqTerm", + "pgtEq", { get: () => pgreaterEqInt.$( term ), ...getterOnly diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermList.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermList.ts index 0c92788b..03c5a858 100644 --- a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermList.ts +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermList.ts @@ -1,18 +1,18 @@ import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; import { PType } from "../../../PType"; import { PDataRepresentable } from "../../../PType/PDataRepresentable"; -import type { PList, TermFn, PInt, PLam, PBool } from "../../../PTypes"; +import type { PList, TermFn, PInt, PLam, PBool, PPair } from "../../../PTypes"; import { Term } from "../../../Term"; -import { ToPType, TermType, isWellFormedGenericType, PrimType, bool, lam, list, struct, typeExtends, tyVar } from "../../../type_system"; -import { getElemsT } from "../../../type_system/tyArgs"; +import { ToPType, TermType, isWellFormedGenericType, PrimType, bool, lam, list, struct, typeExtends, tyVar, pair } from "../../../type_system"; +import { getElemsT, getFstT, getSndT } from "../../../type_system/tyArgs"; import { termTypeToString } from "../../../type_system/utils"; -import { UtilityTermOf } from "../../addUtilityForType"; +import { UtilityTermOf } from "./addUtilityForType"; import { phead, ptail } from "../../builtins/list"; import { pprepend } from "../../builtins/pprepend"; -import { PappArg } from "../../pappArg"; +import { PappArg, pappArgToTerm } from "../../pappArg"; import { phoist } from "../../phoist"; import { plet } from "../../plet"; -import type { PMaybeT } from "../PMaybe/PMaybe"; +import { PMaybe, type PMaybeT } from "../PMaybe/PMaybe"; import { pflip } from "../combinators"; import { pevery } from "../list/pevery"; import { pfilter } from "../list/pfilter"; @@ -24,6 +24,7 @@ import { preverse } from "../list/preverse"; import { psome } from "../list/psome"; import { TermBool } from "./TermBool"; import { TermInt } from "./TermInt"; +import { peqList, pincludes, plookup } from "../list"; export type TermList = Term> & { @@ -76,34 +77,55 @@ export type TermList = Term> readonly reversed: TermList // indexing / query - readonly atTerm: TermFn<[PInt], PElemsT> + readonly pat: TermFn<[PInt], PElemsT> readonly at: ( index: PappArg ) => UtilityTermOf - readonly findTerm: TermFn<[PLam], PMaybeT> + readonly pfind: TermFn<[PLam], PMaybeT> readonly find: ( predicate: PappArg> ) => Term> // readonly includes: TermFn<[PElemsT], PBool> // readonly findIndex: TermFn<[PLam], PInt> - readonly filterTerm: TermFn<[PLam], PList> + readonly pfilter: TermFn<[PLam], PList> readonly filter: ( predicate: PappArg> ) => TermList // list creation - readonly prependTerm: TermFn<[PElemsT], PList> + readonly pprepend: TermFn<[PElemsT], PList> readonly prepend: ( elem: PappArg ) => TermList // readonly concat: TermFn<[PList], PList> // transform - readonly mapTerm: ( resultT: ResultT ) => TermFn<[PLam>], PList>> + readonly pmap: ( resultT: ResultT ) => TermFn<[PLam>], PList>> readonly map: ( f: PappArg> ) => TermList // readonly reduce: ( resultT: ResultT ) => TermFn<[PLam, PLam, ToPType>>], ToPType> // predicates - readonly everyTerm: TermFn<[PLam], PBool> + readonly pevery: TermFn<[PLam], PBool> readonly every: ( predicate: PappArg> ) => TermBool - readonly someTerm: TermFn<[PLam], PBool> + readonly psome: TermFn<[PLam], PBool> readonly some: ( predicate: PappArg> ) => TermBool -}; + + /** @since v0.5.0 */ + readonly pincludes: TermFn<[ PElemsT ], PBool> + /** @since v0.5.0 */ + readonly includes: ( elem: PappArg ) => TermBool + + /** @since v0.5.0 */ + readonly peq: TermFn<[ PList ], PBool> + /** @since v0.5.0 */ + readonly eq: ( other: PappArg> ) => TermBool + +} & ( + PElemsT extends PPair ? + { + + /** @since v0.5.0 */ + readonly plookup: TermFn<[ PK ], PMaybeT> + /** @since v0.5.0 */ + readonly lookup: ( predicate: PappArg ) => UtilityTermOf> + + } : {} +); const flippedPrepend = ( t: TermType ) => phoist( pflip( @@ -150,121 +172,129 @@ const getterOnly = { enumerable: true }; -export function addPListMethods( lst: Term> ) - : TermList +export function addPListMethods( _lst: Term> ) : TermList { - const elemsT = getElemsT( lst.type ); - const _lst = new Term( + const elemsT = getElemsT( _lst.type ); + + const lst = new Term( list( elemsT ), // needs to be wrapped to prevent the garbage collector to collect garbage (lst) - dbn => lst.toIR( dbn ), - (lst as any).isConstant + dbn => _lst.toIR( dbn ), + (_lst as any).isConstant ) as any; if(!isWellFormedGenericType( elemsT as any )) { throw new Error( - "`addPListMethods` can only be used on lists with concrete types; the type of the _lst was: " + termTypeToString( _lst.type ) + "`addPListMethods` can only be used on lists with concrete types; the type of the lst was: " + termTypeToString( lst.type ) ); } + _definePListMethods( lst, elemsT ); + _definePListMethods( _lst, elemsT ); + + return lst as any; +} + +function _definePListMethods( lst: Term>, elemsT: TermType ): void +{ definePropertyIfNotPresent( - _lst, + lst, "head", { get: () => { - return plet( phead( elemsT ).$( _lst ) ) + return plet( phead( elemsT ).$( lst ) ) }, ...getterOnly } ); definePropertyIfNotPresent( - _lst, + lst, "tail", { - get: () => plet( ptail( elemsT ).$( _lst ) ), + get: () => plet( ptail( elemsT ).$( lst ) ), ...getterOnly } ); definePropertyIfNotPresent( - _lst, + lst, "length", { - get: () => plet( plength( elemsT ).$( _lst ) ), + get: () => plet( plength( elemsT ).$( lst ) ), ...getterOnly } ); definePropertyIfNotPresent( - _lst, + lst, "reversed", { - get: () => plet( preverse( elemsT ).$( _lst ) ), + get: () => plet( preverse( elemsT ).$( lst ) ), ...getterOnly } ); definePropertyIfNotPresent( - _lst, - "atTerm", + lst, + "pat", { - get: () => pindexList( elemsT ).$( _lst ), + get: () => pindexList( elemsT ).$( lst ), ...getterOnly } ); defineReadOnlyProperty( - _lst, + lst, "at", - ( index: PappArg ): UtilityTermOf => pindexList( elemsT ).$( _lst ).$( index ) as any + ( index: PappArg ): UtilityTermOf => pindexList( elemsT ).$( lst ).$( index ) as any ); definePropertyIfNotPresent( - _lst, - "findTerm", + lst, + "pfind", { - get: () => flippedFind( elemsT ).$( _lst ), + get: () => flippedFind( elemsT ).$( lst ), ...getterOnly } ); defineReadOnlyProperty( - _lst, + lst, "find", ( predicate: PappArg> ): Term> => - pfind( elemsT ).$( predicate ).$( _lst ) as any + pfind( elemsT ).$( predicate ).$( lst ) as any ); definePropertyIfNotPresent( - _lst, - "filterTerm", + lst, + "pfilter", { - get: () => flippedFilter( elemsT ).$( _lst ), + get: () => flippedFilter( elemsT ).$( lst ), ...getterOnly } ); defineReadOnlyProperty( - _lst, + lst, "filter", ( predicate: PappArg> ): TermList => - pfilter( elemsT ).$( predicate as any ).$( _lst ) as any + pfilter( elemsT ).$( predicate as any ).$( lst ) as any ); definePropertyIfNotPresent( - _lst, - "prependTerm", + lst, + "pprepend", { - get: () => flippedPrepend( elemsT ).$( _lst ), + get: () => flippedPrepend( elemsT ).$( lst ), ...getterOnly } ); defineReadOnlyProperty( - _lst, + lst, "prepend", - ( elem: PappArg ): TermList => pprepend( elemsT ).$( elem ).$( _lst ) as any + ( elem: PappArg ): TermList => pprepend( elemsT ).$( elem ).$( lst ) as any ); defineReadOnlyProperty( - _lst, - "mapTerm", + lst, + "pmap", ( toType: TermType ) => phoist( pflip( @@ -273,56 +303,113 @@ export function addPListMethods( lst: Term list( toType ) ).$( pmap( elemsT, toType ) ) ) - .$( _lst ) + .$( lst ) ); defineReadOnlyProperty( - _lst, + lst, "map", - ( f: Term> ) => { + ( f: PappArg> ) => { + + f = pappArgToTerm( f as any, lam( elemsT, tyVar() ) ) as Term>; + const predicateTy = f.type; + if(!( predicateTy[0] === PrimType.Lambda && isWellFormedGenericType( predicateTy[2] ) )) throw new Error( - `can't map plu-ts fuction of type "${predicateTy}" over a _lst of type "_lst(${termTypeToString(elemsT)})"` + `can't map plu-ts fuction of type "${predicateTy}" over a lst of type "lst(${termTypeToString(elemsT)})"` ); - return pmap( elemsT, predicateTy[2] ).$( f as any ).$( _lst ); + return pmap( elemsT, predicateTy[2] ).$( f as any ).$( lst ); } ); definePropertyIfNotPresent( - _lst, - "everyTerm", + lst, + "pevery", { get: () => flippedEvery( elemsT ) - .$( _lst ), + .$( lst ), ...getterOnly } ); defineReadOnlyProperty( - _lst, + lst, "every", - ( predicate: PappArg> ): TermBool => pevery( elemsT ).$( predicate as any ).$( _lst ) + ( predicate: PappArg> ): TermBool => pevery( elemsT ).$( predicate as any ).$( lst ) ); definePropertyIfNotPresent( - _lst, - "someTerm", + lst, + "psome", { get: () => flippedSome( elemsT ) - .$( _lst ), + .$( lst ), ...getterOnly } ); defineReadOnlyProperty( - _lst, + lst, "some", - ( predicate: PappArg> ): TermBool => psome( elemsT ).$( predicate as any ).$( _lst ) + ( predicate: PappArg> ): TermBool => psome( elemsT ).$( predicate as any ).$( lst ) ); - - return _lst as any; -} + definePropertyIfNotPresent( + lst, + "pincludes", + { + get: () => pincludes( elemsT ).$( lst ), + ...getterOnly + } + + ); + defineReadOnlyProperty( + lst, + "includes", + ( elem: PappArg ): TermBool => pincludes( elemsT ).$( lst ).$( elem ) + ); + + definePropertyIfNotPresent( + lst, + "peq", + { + get: () => peqList( elemsT ).$( lst ), + ...getterOnly + } + + ); + defineReadOnlyProperty( + lst, + "eq", + ( other: PappArg> ): TermBool => peqList( elemsT ).$( lst ).$( other ) + ); + + if( typeExtends( elemsT, pair( tyVar(), tyVar() ) ) ) + { + + const kT = getFstT( elemsT ); + const vT = getSndT( elemsT ); + + const PMaybeVal = PMaybe( vT ); + + definePropertyIfNotPresent( + lst, + "plookup", + { + get: () => pflip( list(elemsT), kT, PMaybeVal.type ).$( plookup( kT, vT ) ).$( lst ), + ...getterOnly + } + + ); + defineReadOnlyProperty( + lst, + "lookup", + ( key: any ) => plookup( kT, vT ).$( key ).$( lst as any ) + ); + } + + return lst as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermPair.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermPair.ts index 342bda52..4af4eed4 100644 --- a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermPair.ts +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermPair.ts @@ -1,12 +1,15 @@ -import { definePropertyIfNotPresent } from "@harmoniclabs/obj-utils"; +import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; import { PType } from "../../../PType"; -import { PAsData, PPair } from "../../../PTypes"; +import { PAsData, PBool, PPair, TermFn } from "../../../PTypes"; import { Term } from "../../../Term"; -import { isWellFormedType, typeExtends } from "../../../type_system"; -import { tyVar, pair, TermType, PrimType } from "../../../type_system/types"; -import { UtilityTermOf } from "../../addUtilityForType"; +import { FromPType, isWellFormedType, typeExtends, unwrapAlias } from "../../../type_system"; +import { tyVar, pair, TermType, PrimType, PairT } from "../../../type_system/types"; +import { UtilityTermOf } from "./addUtilityForType"; import { pfstPair, psndPair } from "../../builtins"; import { plet } from "../../plet"; +import { PappArg } from "../../pappArg"; +import { TermBool } from "./TermBool"; +import { peqPair } from "../pair"; type UnwrapPAsData = PT extends PAsData ? PTy : @@ -17,7 +20,9 @@ export type TermPair = Term> readonly snd: UtilityTermOf> - + + readonly peq: TermFn<[ PPair ], PBool> + readonly eq: ( other: PappArg> ) => TermBool } const getterOnly = { @@ -26,9 +31,9 @@ const getterOnly = { enumerable: true }; -export function addPPairMethods( _pair: Term>) +export function addPPairMethods( _pair: Term>): TermPair { - const pairT = _pair.type; + const pairT = unwrapAlias( _pair.type ) as PairT,FromPType>; if( !typeExtends( pairT, pair( tyVar(), tyVar() ) ) ) { @@ -38,10 +43,12 @@ export function addPPairMethods( _pair: }; // MUST NOT unwrap `asData` + // (needed by pfst and psnd to understand if the result should be transformed) let fstT: TermType = pairT[1] as TermType; while( fstT[0] === PrimType.Alias ) fstT = fstT[1]; - // MUST NOT unwrap `asData` + // MUST NOT unwrap `asData` + // (needed by pfst and psnd to understand if the result should be transformed) let sndT: TermType = pairT[2] as TermType; while( sndT[0] === PrimType.Alias ) sndT = sndT[1]; @@ -65,5 +72,21 @@ export function addPPairMethods( _pair: } ); + definePropertyIfNotPresent( + _pair, + "peq", + { + get: () => peqPair( pairT ).$( _pair as any ), + set: () => {}, + enumerable: true, + configurable: false + } + ); + defineReadOnlyProperty( + _pair, + "eq", + ( other: any ): TermBool => peqPair( pairT ).$( _pair as any ).$( other ) + ); + return _pair as any; } \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermStr.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermStr.ts index d30916c3..befd3d8a 100644 --- a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermStr.ts +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermStr.ts @@ -12,10 +12,10 @@ export type TermStr = Term & { readonly utf8Encoded: TermBS // pappendStr - readonly concatTerm: TermFn<[ PString ], PString> + readonly pconcat: TermFn<[ PString ], PString> readonly concat: ( other: PappArg ) => TermStr - readonly eqTerm: TermFn<[ PString ], PBool > + readonly peq: TermFn<[ PString ], PBool > readonly eq: ( other: PappArg ) => TermBool } @@ -38,7 +38,7 @@ export function addPStringMethods( term: Term ): TermStr definePropertyIfNotPresent( term, - "concatTerm", + "pconcat", { get: () => plet( pappendStr.$( term ) ), ...getterOnly @@ -52,7 +52,7 @@ export function addPStringMethods( term: Term ): TermStr definePropertyIfNotPresent( term, - "eqTerm", + "peq", { get: () => plet( peqStr.$( term ) ), ...getterOnly diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermStruct.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermStruct.ts index 95520d5c..3e682db6 100644 --- a/packages/onchain/src/pluts/lib/std/UtilityTerms/TermStruct.ts +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/TermStruct.ts @@ -1,20 +1,17 @@ import { Term } from "../../../Term"; import type { PStruct, RestrictedStructInstance, StructInstance } from "../../../PTypes/PStruct/pstruct"; import type { PType } from "../../../PType"; +import type { TermFn } from "../../../PTypes/PFn/PFn"; // !!! IMPORTANT !!! // DO NOT change the order of imports // `../../../Term/Type/kinds` is also a dependecy of `pmatch` -import { getElemAtTerm, pmatch } from "../../../PTypes/PStruct/pmatch"; -import { StructDefinition, isStructType, isStructDefinition, data, list, int, pair } from "../../../type_system"; -import { peqData, punConstrData } from "../../builtins/data"; -import { TermFn } from "../../../PTypes/PFn/PFn"; +import { getElemAtTerm } from "../../../PTypes/PStruct/pmatch"; +import { StructDefinition, isStructType, isStructDefinition, data, list, int, pair, Methods } from "../../../type_system"; +import { peqData, } from "../../builtins/data"; import { PBool } from "../../../PTypes/PBool"; import { TermBool } from "./TermBool"; -import { PData, PInt, PList, PPair } from "../../../PTypes"; -import { _plet } from "../../plet/minimal"; import { _fromData } from "../data/conversion/fromData_minimal"; -import { plet } from "../../plet"; -import { UtilityTermOf } from "../../addUtilityForType"; +import { UtilityTermOf } from "./addUtilityForType"; import { punsafeConvertType } from "../../punsafeConvertType"; import { TermInt, addPIntMethods } from "./TermInt"; import { TermList, addPListMethods } from "./TermList"; @@ -26,16 +23,23 @@ import { IRApp } from "../../../../IR/IRNodes/IRApp"; import { IRNative } from "../../../../IR/IRNodes/IRNative"; import { IRVar } from "../../../../IR/IRNodes/IRVar"; import { IRLetted } from "../../../../IR/IRNodes/IRLetted"; +import type { PData } from "../../../PTypes/PData"; +import type { PList } from "../../../PTypes/PList"; +import type { PPair } from "../../../PTypes/PPair"; +import type { PInt } from "../../../PTypes/PInt"; +import { FilterMethodsByInput, LiftMethods, MethodsAsTerms } from "./userMethods/methodsTypes"; +import { addUserMethods } from "./userMethods/addUserMethods"; +import { plet } from "../../plet"; export type RawStruct = { readonly index: TermInt, readonly fields: TermList } -export type TermStruct = Term> & { +export type TermStruct = Term> & { - readonly eqTerm: TermFn<[PStruct], PBool> - readonly eq: ( other: Term> ) => TermBool + readonly peq: TermFn<[PStruct], PBool> + readonly eq: ( other: Term> ) => TermBool readonly raw: RawStruct @@ -53,13 +57,13 @@ export type TermStruct = Term> & { in: ( expr: ( extracted: RestrictedStructInstance ) => Term ) => UtilityTermOf } } : {} -); - -const getterOnly = { - set: () => {}, - configurable: false, - enumerable: true -}; +) & +LiftMethods< + FilterMethodsByInput> +> & +MethodsAsTerms< + FilterMethodsByInput> +> const hoisted_getFields = new IRHoisted( new IRFunc( 1, // struct @@ -73,7 +77,12 @@ const hoisted_getFields = new IRHoisted( ) ); -export function addPStructMethods( struct: Term> ): TermStruct +export function addPStructMethods< + SDef extends StructDefinition, + SMethods extends Methods +>( + struct: Term> +): TermStruct { const t = struct.type; if( !isStructType(t) ) return struct as any; @@ -146,7 +155,7 @@ export function addPStructMethods( struct: Term

plet( peqData.$( struct as any ) ), set: () => {}, @@ -156,7 +165,7 @@ export function addPStructMethods( struct: Term

> ) => peqData.$( struct as any ).$( other as any ) + struct, "eq", ( other: Term> ) => peqData.$( struct as any ).$( other as any ) ) const letted_unconstred = new Term>>( @@ -200,5 +209,7 @@ export function addPStructMethods( struct: Term

{ +describe("pprepend", () => { test("prepend something", () => { @@ -21,10 +21,10 @@ describe("prependTerm", () => { }); - test("prependTerm", () => { + test("pprepend", () => { expect( - () => pList(int)([ pInt(2) ]).prependTerm + () => pList(int)([ pInt(2) ]).pprepend ).not.toThrow() }) diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/addUtilityForType.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/addUtilityForType.ts new file mode 100644 index 00000000..1b6a17d3 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/addUtilityForType.ts @@ -0,0 +1,122 @@ +import { PType } from "../../../PType"; +import type { PBool, PByteString, PInt, PList, PPair, PString, PStruct, PLam, PAlias } from "../../../PTypes"; +import { Term } from "../../../Term"; +import { isTaggedAsAlias } from "../../../type_system/kinds/isTaggedAsAlias"; +import { isStructType } from "../../../type_system/kinds/isWellFormedType"; +import { ToPType } from "../../../type_system/ts-pluts-conversion"; +import { typeExtends } from "../../../type_system/typeExtends"; +import { Methods, PrimType, StructDefinition, TermType, bool, bs, int, lam, list, pair, str, tyVar } from "../../../type_system/types"; +import { unwrapAlias } from "../../../type_system/tyArgs/unwrapAlias"; +import type { PappArg } from "../../pappArg"; +import { papp } from "../../papp"; +import { + type TermAlias, + type TermBool, addPBoolMethods, + type TermBS, addPByteStringMethods, + type TermInt, addPIntMethods, + type TermList, addPListMethods, + type TermPair, addPPairMethods, + type TermStr, addPStringMethods, + type TermStruct, addPStructMethods +} from "."; +import { defineNonDeletableNormalProperty } from "@harmoniclabs/obj-utils"; +import { termTypeToString } from "../../../type_system/utils"; +import { addUserMethods } from "./userMethods/addUserMethods"; +import { _punsafeConvertType } from "../../punsafeConvertType/minimal"; + + +// given the index returns the previous number ( PrevNum[2] -> 1; etc... ) +type PrevNum = [ never, 0, 1, 2, 3, 4, 5, 6 ]; + +// without the "finite" version typescript gets angry and says the type is too complex to be evaluated +type FiniteTermAlias = + MaxDepth extends never ? never : + PT extends PAlias ? + FiniteTermAlias : + TermAlias + +export type UtilityTermOf = + ( + PElem extends PBool ? TermBool : + PElem extends PByteString ? TermBS : + PElem extends PInt ? TermInt : + PElem extends PList ? TermList : + PElem extends PPair ? TermPair : + PElem extends PString ? TermStr : + PElem extends PStruct ? TermStruct : + PElem extends PLam ? + Term & { + $: ( input: PappArg ) => UtilityTermOf + } : + // PElem extends PLam ? + // Term & { + // $: ( input: Term ) => Term + // } : + PElem extends PAlias ? FiniteTermAlias : + Term + ) & Term // needed because sometime typescript doesn't understands that the term is the same just extended + +export function addUtilityForType( t: T ) + : ( term: Term> ) => UtilityTermOf> +{ + if( isTaggedAsAlias( t ) ){ + return addPAliasMethods as any; + // return addUtilityForType( unwrapAlias( t ) ) as any; + }; + + if( typeExtends( t , bool ) ) return addPBoolMethods as any; + if( typeExtends( t , bs ) ) return addPByteStringMethods as any; + if( typeExtends( t , int ) ) return addPIntMethods as any; + if( typeExtends( t , list( tyVar() ) ) ) return addPListMethods as any; + if( typeExtends( t , pair( tyVar(), tyVar() ) ) ) return addPPairMethods as any; + if( typeExtends( t , str ) ) return addPStringMethods as any; + + if( typeExtends( t, lam( tyVar(), tyVar() )) ) + { + return (( term: any ) => defineNonDeletableNormalProperty( + term, + "$", + ( input: any ) => + papp( term, input ) + )) as any; + } + + if( isStructType( t ) ) + { + return addPStructMethods as any; + } + + // no utility + return ((x: any) => x) as any; +} + +// `addPAliasMethod` is (necessarily) mutually recursive with `addUtilityForType` +// so it is defined in this file instead of "./UtilityTerms/TermAlias.ts" +export function addPAliasMethods< + PAliased extends PType, + AMethods extends Methods +>( + aliasTerm: Term> +): TermAlias +{ + const originalType = aliasTerm.type; + + if( originalType[0] !== PrimType.Alias ) + { + console.error( originalType ); + try { + console.error( termTypeToString( originalType ) ) + } + catch {} + + throw new Error("addPAliasMethods used on non-alias type"); + } + + const aliasedType = unwrapAlias( originalType ); + + aliasTerm = addUtilityForType( aliasedType )( aliasTerm ) as any; + + aliasTerm = addUserMethods( aliasTerm, originalType[2] as AMethods ); + + return aliasTerm as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/makeMockTerm.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/makeMockTerm.ts new file mode 100644 index 00000000..411ccdad --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/makeMockTerm.ts @@ -0,0 +1,21 @@ +import { IRVar } from "../../../../../IR/IRNodes/IRVar"; +import { Term } from "../../../../Term"; +import { ToPType } from "../../../../type_system/ts-pluts-conversion"; +import { TermType } from "../../../../type_system/types"; +import { UtilityTermOf } from "../addUtilityForType"; +import { mockUtilityForType } from "./mockUtilityForType"; + +const mockTermIr = Object.freeze( new IRVar( 0 ) ); + +function genMockTermIr( _dbn: bigint ) +{ + return mockTermIr; +} + +export function makeMockTerm( t: T ): Term> +{ + return new Term( + t, + genMockTermIr + ); +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/makeMockUtilityTerm.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/makeMockUtilityTerm.ts new file mode 100644 index 00000000..c08de253 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/makeMockUtilityTerm.ts @@ -0,0 +1,10 @@ +import { ToPType } from "../../../../type_system/ts-pluts-conversion"; +import { TermType } from "../../../../type_system/types"; +import { UtilityTermOf } from "../addUtilityForType"; +import { makeMockTerm } from "./makeMockTerm"; +import { mockUtilityForType } from "./mockUtilityForType"; + +export function makeMockUtilityTerm( t: T ): UtilityTermOf> +{ + return mockUtilityForType( t )( makeMockTerm( t ) ); +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPBoolMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPBoolMethods.ts new file mode 100644 index 00000000..3c4769c7 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPBoolMethods.ts @@ -0,0 +1,96 @@ +import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclabs/obj-utils" +import { makeMockTerm } from "./makeMockTerm" +import { TermBool } from "../TermBool"; +import { Term } from "../../../../Term"; +import { PBool } from "../../../../PTypes/PBool"; +import { bool, delayed, lam } from "../../../../type_system/types"; + + +// export type TermBool = Term & { +// +// readonly por: TermFn<[ PDelayed ], PBool> +// readonly or: ( other: PappArg ) => TermBool +// +// readonly pstrictOr: TermFn<[ PBool ], PBool> +// readonly strictOr: ( other: PappArg ) => TermBool +// +// readonly pand: TermFn<[ PDelayed ], PBool> +// readonly and: ( other: PappArg ) => TermBool +// +// readonly pstrictAnd: TermFn<[ PBool ], PBool> +// readonly strictAnd: ( other: PappArg ) => TermBool +// +// } + +export function makeMockTermBool(): TermBool +{ + return mockPBoolMethods( makeMockTerm( bool ) ); +} + +const getterOnly = { + set: () => {}, + configurable: false, + enumerable: true +}; + +export function mockPBoolMethods( term: Term ): TermBool +{ + definePropertyIfNotPresent( + term, + "por", + { + get: () => makeMockTerm( lam( delayed( bool ), bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "or", + ( other: Term | boolean ): TermBool => mockPBoolMethods( makeMockTerm( bool ) ) + ); + + definePropertyIfNotPresent( + term, + "pstrictOr", + { + get: () => makeMockTerm( lam( bool, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "strictOr", + ( other: any ): TermBool => mockPBoolMethods( makeMockTerm( bool ) ) + ); + + + definePropertyIfNotPresent( + term, + "pand", + { + get: () => makeMockTerm( lam( delayed( bool ), bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "and", + ( other: Term | boolean ): TermBool => mockPBoolMethods( makeMockTerm( bool ) ) + ); + + definePropertyIfNotPresent( + term, + "pstrictAnd", + { + get: () => makeMockTerm( lam( bool, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "strictAnd", + ( other: any ): TermBool => mockPBoolMethods( makeMockTerm( bool ) ) + ); + + return term as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPByteStringMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPByteStringMethods.ts new file mode 100644 index 00000000..22a88dd4 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPByteStringMethods.ts @@ -0,0 +1,183 @@ +import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclabs/obj-utils" +import { PByteString } from "../../../../PTypes/PByteString"; +import { Term } from "../../../../Term"; +import { TermBS } from "../TermBS"; +import { makeMockTerm } from "./makeMockTerm"; +import { bool, bs, int, lam, str } from "../../../../type_system/types"; +import { makeMockUtilityTerm } from "./makeMockUtilityTerm"; +import { makeMockTermBool } from "./mockPBoolMethods"; +import { makeMockTermInt } from "./mockPIntMethods"; + +const getterOnly = { + set: () => {}, + configurable: false, + enumerable: true +}; + +export function makeMockTermBs(): TermBS +{ + return mockPByteStringMethods( makeMockTerm( bs ) ); +} + +export function mockPByteStringMethods( term: Term ): TermBS +{ + definePropertyIfNotPresent( + term, + "length", + { + get: () => makeMockUtilityTerm( int ), + ...getterOnly + } + ); + definePropertyIfNotPresent( + term, + "utf8Decoded", + { + get: () => makeMockUtilityTerm( str ), + ...getterOnly + } + ); + + definePropertyIfNotPresent( + term, + "pconcat", + { + get: () => makeMockUtilityTerm( lam( bs, bs ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "concat", + ( other: any ): TermBS => makeMockTermBs() + ); + + definePropertyIfNotPresent( + term, + "pprepend", + { + get: () => makeMockUtilityTerm( lam( int, bs ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "prepend", + ( byte: any ): TermBS => makeMockTermBs() + ); + + definePropertyIfNotPresent( + term, + "psubByteString", + { + get: () => makeMockUtilityTerm( lam( int, lam( int, bs ) ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "subByteString", + ( fromInclusive: any, ofLength: any ): TermBS => makeMockTermBs() + ); + + definePropertyIfNotPresent( + term, + "pslice", + { + get: () => makeMockUtilityTerm( lam( int, lam( int, bs ) ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "slice", + ( fromInclusive: any, toExclusive: any): TermBS => makeMockTermBs() + ); + + definePropertyIfNotPresent( + term, + "pat", + { + get: () => makeMockUtilityTerm( lam( int, bs ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "at", + ( index: any) /*: TermInt*/ => makeMockTermInt() + ); + + definePropertyIfNotPresent( + term, + "peq", + { + get: () => makeMockUtilityTerm( lam( bs, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "eq", + ( other: Term ) => makeMockTermBool() + ); + + definePropertyIfNotPresent( + term, + "plt", + { + get: () => makeMockUtilityTerm( lam( bs, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "lt", + ( other: Term ) => makeMockTermBool() + ); + + definePropertyIfNotPresent( + term, + "pltEq", + { + get: () => makeMockUtilityTerm( lam( bs, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "ltEq", + ( other: Term ) => makeMockTermBool() + ); + + definePropertyIfNotPresent( + term, + "pgt", + { + get: () => makeMockUtilityTerm( lam( bs, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "gt", + ( other: Term ) => makeMockTermBool() + ); + + definePropertyIfNotPresent( + term, + "pgtEq", + { + get: () => makeMockUtilityTerm( lam( bs, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "gtEq", + ( other: Term ) => makeMockTermBool() + ); + + + return term as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPIntMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPIntMethods.ts new file mode 100644 index 00000000..ca9c7bc0 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPIntMethods.ts @@ -0,0 +1,196 @@ +import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; +import { TermInt } from "../TermInt"; +import { bool, int, lam } from "../../../../type_system"; +import { makeMockTerm } from "./makeMockTerm"; +import { Term } from "../../../../Term"; +import { PInt } from "../../../../PTypes/PInt"; +import { makeMockUtilityTerm } from "./makeMockUtilityTerm"; +import { makeMockTermBool } from "./mockPBoolMethods"; + + +const getterOnly = { + set: () => {}, + configurable: false, + enumerable: true +}; + +export function makeMockTermInt(): TermInt +{ + return mockPIntMethods( makeMockTerm( int ) ); +} + +export function mockPIntMethods( term: Term ): TermInt +{ + definePropertyIfNotPresent( + term, + "padd", + { + get: () => makeMockUtilityTerm( lam( int, int ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "add", + ( other: Term ): TermInt => makeMockTermInt() + ); + + definePropertyIfNotPresent( + term, + "psub", + { + get: () => makeMockUtilityTerm( lam( int, int ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "sub", + ( other: Term ): TermInt => makeMockTermInt() + ); + + definePropertyIfNotPresent( + term, + "pmult", + { + get: () => makeMockUtilityTerm( lam( int, int ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "mult", + ( other: Term ): TermInt => makeMockTermInt() + ); + + definePropertyIfNotPresent( + term, + "pdiv", + { + get: () => makeMockUtilityTerm( lam( int, int ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "div", + ( other: Term ): TermInt => makeMockTermInt() + ); + + definePropertyIfNotPresent( + term, + "pquot", + { + get: () => makeMockUtilityTerm( lam( int, int ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "quot", + ( other: Term ): TermInt => makeMockTermInt() + ); + + definePropertyIfNotPresent( + term, + "premainder", + { + get: () => makeMockUtilityTerm( lam( int, int ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "remainder", + ( other: Term ): TermInt => makeMockTermInt() + ); + + definePropertyIfNotPresent( + term, + "pmod", + { + get: () => makeMockUtilityTerm( lam( int, int ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "mod", + ( other: Term ): TermInt => makeMockTermInt() + ); + + + definePropertyIfNotPresent( + term, + "peq", + { + get: () => makeMockUtilityTerm( lam( int, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "eq", + ( other: Term ) => makeMockTermBool() + ); + + definePropertyIfNotPresent( + term, + "plt", + { + get: () => makeMockUtilityTerm( lam( int, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "lt", + ( other: Term ) => makeMockTermBool() + ); + + definePropertyIfNotPresent( + term, + "pltEq", + { + get: () => makeMockUtilityTerm( lam( int, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "ltEq", + ( other: Term ) => makeMockTermBool() + ); + + definePropertyIfNotPresent( + term, + "pgt", + { + get: () => makeMockUtilityTerm( lam( int, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "gt", + ( other: Term ) => makeMockTermBool() + ); + + definePropertyIfNotPresent( + term, + "pgtEq", + { + get: () => makeMockUtilityTerm( lam( int, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "gtEq", + ( other: Term ) => makeMockTermBool() + ); + + + return term as any; +} + diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPListMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPListMethods.ts new file mode 100644 index 00000000..9bb47c98 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPListMethods.ts @@ -0,0 +1,187 @@ +import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; +import { PType } from "../../../../PType"; +import type { PList, TermFn, PInt, PLam, PBool } from "../../../../PTypes"; +import { Term } from "../../../../Term"; +import { ToPType, TermType, isWellFormedGenericType, PrimType, bool, lam, list, struct, typeExtends, tyVar, int } from "../../../../type_system"; +import { getElemsT } from "../../../../type_system/tyArgs"; +import { termTypeToString } from "../../../../type_system/utils"; +import { UtilityTermOf } from "../addUtilityForType"; +import { PMaybe, type PMaybeT } from "../../PMaybe/PMaybe"; +import { TermBool } from "../TermBool"; +import { TermList } from "../TermList"; +import { makeMockUtilityTerm } from "./makeMockUtilityTerm"; +import { makeMockTermInt } from "./mockPIntMethods"; +import { makeMockTermBool } from "./mockPBoolMethods"; + +const getterOnly = { + set: () => {}, + configurable: false, + enumerable: true +}; + +export function mockPListMethods( lst: Term> ) + : TermList +{ + const elemsT = getElemsT( lst.type ); + const _lst = new Term( + list( elemsT ), + // needs to be wrapped to prevent the garbage collector to collect garbage (lst) + dbn => lst.toIR( dbn ), + (lst as any).isConstant + ) as any; + + if(!isWellFormedGenericType( elemsT as any )) + { + throw new Error( + "`addPListMethods` can only be used on lists with concrete types; the type of the _lst was: " + termTypeToString( _lst.type ) + ); + } + + definePropertyIfNotPresent( + _lst, + "head", + { + get: () => makeMockUtilityTerm( elemsT ), + ...getterOnly + } + ); + definePropertyIfNotPresent( + _lst, + "tail", + { + get: () => makeMockUtilityTerm( list( elemsT ) ), + ...getterOnly + } + ); + definePropertyIfNotPresent( + _lst, + "length", + { + get: () => makeMockTermInt, + ...getterOnly + } + ); + definePropertyIfNotPresent( + _lst, + "reversed", + { + get: () => makeMockUtilityTerm( list( elemsT ) ), + ...getterOnly + } + ); + + + definePropertyIfNotPresent( + _lst, + "pat", + { + get: () => makeMockUtilityTerm( lam( int, elemsT ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + _lst, + "at", + ( index: Term ): UtilityTermOf => makeMockUtilityTerm( elemsT ) as any + ); + + definePropertyIfNotPresent( + _lst, + "pfind", + { + get: () => makeMockUtilityTerm( lam( lam( elemsT, bool ), PMaybe( elemsT ).type ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + _lst, + "find", + ( predicate: Term> ): Term> => makeMockUtilityTerm( PMaybe( elemsT ).type ) as any + ); + + definePropertyIfNotPresent( + _lst, + "pfilter", + { + get: () => makeMockUtilityTerm( lam( lam( elemsT, bool ), list( elemsT ) ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + _lst, + "filter", + ( predicate: Term> ): TermList => makeMockUtilityTerm( list( elemsT ) ) as any + ); + + definePropertyIfNotPresent( + _lst, + "pprepend", + { + get: () => makeMockUtilityTerm( lam( elemsT, list( elemsT ) ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + _lst, + "prepend", + ( elem: Term ): TermList => makeMockUtilityTerm( list( elemsT ) ) as any + ); + + defineReadOnlyProperty( + _lst, + "pmap", + ( toType: TermType ) => makeMockUtilityTerm( lam( lam( elemsT, toType ), list( toType ) ) ) + ); + defineReadOnlyProperty( + _lst, + "map", + ( f: Term> ) => { + + const predicateTy = f.type; + + if(!( + predicateTy[0] === PrimType.Lambda && + isWellFormedGenericType( predicateTy[2] ) + )) + throw new Error( + `can't map plu-ts fuction of type "${predicateTy}" over a _lst of type "_lst(${termTypeToString(elemsT)})"` + ); + + return makeMockUtilityTerm( list( predicateTy[2] ) ); + } + ); + + definePropertyIfNotPresent( + _lst, + "pevery", + { + get: () => makeMockUtilityTerm( lam( lam( elemsT, bool ), bool ) ) + .$( _lst ), + ...getterOnly + } + ); + defineReadOnlyProperty( + _lst, + "every", + ( predicate: Term> ): TermBool => makeMockTermBool() + ); + + definePropertyIfNotPresent( + _lst, + "psome", + { + get: () => makeMockUtilityTerm( lam( lam( elemsT, bool ), bool ) ) + .$( _lst ), + ...getterOnly + } + + ); + defineReadOnlyProperty( + _lst, + "some", + ( predicate: Term> ): TermBool => makeMockTermBool() + ); + + return _lst as any; +} + diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPPairMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPPairMethods.ts new file mode 100644 index 00000000..a1c6ab63 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPPairMethods.ts @@ -0,0 +1,69 @@ +import { definePropertyIfNotPresent } from "@harmoniclabs/obj-utils"; +import { PType } from "../../../../PType"; +import { PPair } from "../../../../PTypes/PPair"; +import { Term } from "../../../../Term"; +import { PrimType, TermType, isWellFormedType, pair, tyVar, typeExtends, unwrapAlias } from "../../../../type_system"; +import { TermPair } from "../TermPair"; +import { makeMockUtilityTerm } from "./makeMockUtilityTerm"; +import { unwrapAsData } from "../../../../type_system/tyArgs/unwrapAsData"; + + +const getterOnly = { + set: () => {}, + configurable: false, + enumerable: true +}; + +export function mockPPairMethods( _pair: Term>): TermPair +{ + const pairT = unwrapAlias( _pair.type ); + + if( !typeExtends( pairT, pair( tyVar(), tyVar() ) ) ) + { + throw new Error( + "can't add pair methods to a term that is not a pair" + ); + }; + + // MUST NOT unwrap `asData` + // (needed by pfst and psnd to understand if the result should be transformed) + let fstT: TermType = pairT[1] as TermType; + while( fstT[0] === PrimType.Alias ) fstT = fstT[1]; + + // MUST NOT unwrap `asData` + // (needed by pfst and psnd to understand if the result should be transformed) + let sndT: TermType = pairT[2] as TermType; + while( sndT[0] === PrimType.Alias ) sndT = sndT[1]; + + + if( isWellFormedType( fstT ) ) + definePropertyIfNotPresent( + _pair, + "fst", + { + get: () => makeMockUtilityTerm( + // pfst automatically unwraps data + fstT[0] === PrimType.AsData ? + unwrapAsData( fstT ) : + fstT + ), + ...getterOnly + } + ); + if( isWellFormedType( sndT ) ) + definePropertyIfNotPresent( + _pair, + "snd", + { + get: () => makeMockUtilityTerm( + // psnd automatically unwraps data + sndT[0] === PrimType.AsData ? + unwrapAsData( sndT ) : + sndT + ), + ...getterOnly + } + ); + + return _pair as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPStringMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPStringMethods.ts new file mode 100644 index 00000000..217aa321 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPStringMethods.ts @@ -0,0 +1,63 @@ +import { definePropertyIfNotPresent, defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; +import { PString } from "../../../../PTypes/PString"; +import { Term } from "../../../../Term"; +import { TermStr } from "../TermStr"; +import { makeMockTermBs } from "./mockPByteStringMethods"; +import { makeMockUtilityTerm } from "./makeMockUtilityTerm"; +import { bool, lam, str } from "../../../../type_system"; +import { makeMockTerm } from "./makeMockTerm"; +import { makeMockTermBool } from "./mockPBoolMethods"; + +const getterOnly = { + set: () => {}, + configurable: false, + enumerable: true +}; + +export function makeMockTermStr(): TermStr +{ + return mockPStringMethods( makeMockTerm( str ) ); +} + + +export function mockPStringMethods( term: Term ): TermStr +{ + definePropertyIfNotPresent( + term, + "utf8Encoded", + { + get: () => makeMockTermBs(), + ...getterOnly + } + ); + + definePropertyIfNotPresent( + term, + "pconcat", + { + get: () => makeMockUtilityTerm( lam( str, str ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "concat", + ( other: Term ) => makeMockTermStr() + ); + + definePropertyIfNotPresent( + term, + "peq", + { + get: () => makeMockUtilityTerm( lam( str, bool ) ), + ...getterOnly + } + ); + defineReadOnlyProperty( + term, + "eq", + ( other: Term ) => makeMockTermBool() + ); + + return term as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPStructMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPStructMethods.ts new file mode 100644 index 00000000..9205310c --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPStructMethods.ts @@ -0,0 +1,108 @@ +import { definePropertyIfNotPresent, defineReadOnlyProperty, hasOwn } from "@harmoniclabs/obj-utils"; +import { PStruct, RestrictedStructInstance } from "../../../../PTypes/PStruct/pstruct"; +import { Term } from "../../../../Term"; +import { isStructDefinition, isStructType } from "../../../../type_system/kinds/isWellFormedType"; +import { Methods, StructDefinition, bool, data, int, lam, list, pair } from "../../../../type_system/types"; +import { TermStruct } from "../TermStruct"; +import { makeMockTerm } from "./makeMockTerm"; +import { makeMockUtilityTerm } from "./makeMockUtilityTerm"; +import { PType } from "../../../../PType"; +import { makeMockTermBool } from "./mockPBoolMethods"; +import { mockPIntMethods } from "./mockPIntMethods"; +import { mockPListMethods } from "./mockPListMethods"; +import { mockUserMethods } from "./mockUserMethods"; + + +export function mockPStructMethods< + SDef extends StructDefinition, + SMethods extends Methods +>( + struct: Term> +): TermStruct +{ + const t = struct.type; + if( !isStructType(t) ) return struct as any; + + const sDef = t[1] as SDef; + if( typeof sDef === "symbol" || !isStructDefinition( sDef ) ) return struct as any; + + const ctors = Object.keys( sDef ); + + // shortcut for single ctors structs + if( ctors.length === 1 ) + { + const ctorName = ctors[0]; + const ctor = sDef[ ctorName ]; + + const fieldsNames = Object.keys( ctor ); + const nFields = fieldsNames.length + + const letted_fieldsListData = makeMockTerm( list( data ) ); + + for( let i = 0; i < nFields; i++ ) + { + const thisFieldName = fieldsNames[i]; + const thisFieldType = ctor[ thisFieldName ]; + + ( + !hasOwn( struct, thisFieldName ) + ) && Object.defineProperty( + struct, thisFieldName, + { + value: makeMockUtilityTerm( thisFieldType ), + writable: false, + enumerable: true, + configurable: false + } + ); + + } + + /** + * @deprecated + */ + defineReadOnlyProperty( + struct, + "extract", + ( ...fields: Fields ): { + in: ( expr: ( extracted: RestrictedStructInstance ) => Term ) => Term + } => { + return { + in: ( expr ) => expr( struct as any ) + } + } + ); + } + + definePropertyIfNotPresent( + struct, "peq", + { + get: () => makeMockUtilityTerm( lam( data, bool ) ), + set: () => {}, + configurable: false, + enumerable: true + } + ) + + defineReadOnlyProperty( + struct, "eq", ( other: Term> ) => makeMockTermBool() + ) + + const letted_unconstred = makeMockTerm( pair( int, list( data ) ) ); + + const letted_ctorIdx = makeMockTerm( int ); + + const letted_rawFields = makeMockTerm( list( data ) ); + + defineReadOnlyProperty( + struct, "raw", + Object.freeze({ + index: mockPIntMethods( letted_ctorIdx ), + fields: mockPListMethods( letted_rawFields ) + }) + ); + + struct = mockUserMethods( struct, t[2] ) + + return struct as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPapp.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPapp.ts new file mode 100644 index 00000000..34175f15 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockPapp.ts @@ -0,0 +1,14 @@ +import { PType } from "../../../../PType"; +import { PLam } from "../../../../PTypes/PFn/PLam"; +import { Term } from "../../../../Term"; +import { makeMockTerm } from "./makeMockTerm"; + +export function mockPapp( a: Term>, b: Term ): Term +{ + const outT = a.type[2]; + if( outT === undefined ) + { + console.log( a.type ); + } + return makeMockTerm( outT as any ) as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockUserMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockUserMethods.ts new file mode 100644 index 00000000..3aef4adf --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockUserMethods.ts @@ -0,0 +1,105 @@ +import { PType } from "../../../../PType"; +import type { Term } from "../../../../Term"; +import type { PLam } from "../../../../PTypes/PFn/PLam"; +import { defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; +import { getFnTypes } from "../../../../Script/Parametrized/getFnTypes"; +import { Methods, PrimType, TermType, typeExtends } from "../../../../type_system"; +// avoid potential dependecies and circular deps +// import { getMethodsWithFirstInputOfType } from "../UtilityTerms/userMethods/addUserMethods"; +import { LiftMethods, FilterMethodsByInput, MethodsAsTerms } from "../../UtilityTerms/userMethods/methodsTypes"; +import { mockPapp } from "./mockPapp"; +import { makeMockTerm } from "./makeMockTerm"; + + +// avoid potential dependecies and circular deps +function getMethodsWithFirstInputOfType( methods: Methods, type: TermType ): Methods +{ + const filtered: Methods = {}; + + let method: Term>; + + const methodNames = Object.keys( methods ); + + for( const methodName of methodNames ) + { + method = methods[methodName]; + + + if( method.type[0] !== PrimType.Lambda ) + throw new Error("user defined method is expected to be a funciton"); + + if( typeExtends( method.type[1], type ) ) + { + filtered[methodName] = method; + } + } + + return Object.freeze( filtered ); +} + +export function mockUserMethods< + PT extends PType, + M extends Methods +>( + term: Term, + methods: M +): Term & + LiftMethods< + FilterMethodsByInput + > & + MethodsAsTerms< + FilterMethodsByInput + > +{ + const t = term.type; + + const filtered = getMethodsWithFirstInputOfType( methods, t ); + + let method: Term>; + + for( const methodName in filtered ) + { + method = filtered[methodName]; + + const fnTypes = getFnTypes( method.type ); + + // don't add terms that do not accept `self_t` as first argument as methods + if( !typeExtends( fnTypes[0], t ) ) continue; + + // -1 (term application) + // -1 (the output type) + const missingArgsAfterApplication = fnTypes.length - 2; + + const appliedTerm = mockPapp( method, term ); + + if( missingArgsAfterApplication === 0 ) + { + defineReadOnlyProperty( + term, methodName, appliedTerm + ); + continue; + } + + defineReadOnlyProperty( + term, "p" + methodName, appliedTerm + ); + defineReadOnlyProperty( + term, methodName, ( ...other_terms: any[] ) => { + // let result: any = appliedTerm; + + if( other_terms.length < missingArgsAfterApplication ) + throw new Error("not enough arguments for method + '" + methodName + "'"); + + // for( let i = 0 ; i < missingArgsAfterApplication; i++ ) + // { + // result = mockPapp( result, other_terms[i] ); + // } + + // return result; + return makeMockTerm( fnTypes[ fnTypes.length - 1 ] ); + } + ) + } + + return term as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockUtilityForType.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockUtilityForType.ts new file mode 100644 index 00000000..7f6fa7a8 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/mockUtilityTerms/mockUtilityForType.ts @@ -0,0 +1,99 @@ +import { PType } from "../../../../PType"; +import type { PAlias } from "../../../../PTypes"; +import { Term } from "../../../../Term"; +import { isTaggedAsAlias } from "../../../../type_system/kinds/isTaggedAsAlias"; +import { isStructType } from "../../../../type_system/kinds/isWellFormedType"; +import { ToPType } from "../../../../type_system/ts-pluts-conversion"; +import { typeExtends } from "../../../../type_system/typeExtends"; +import { Methods, PrimType, TermType, bool, bs, int, lam, list, pair, str, tyVar } from "../../../../type_system/types"; +import { unwrapAlias } from "../../../../type_system/tyArgs/unwrapAlias"; +import { + TermAlias, +} from "../../UtilityTerms"; +import { defineNonDeletableNormalProperty } from "@harmoniclabs/obj-utils"; +import { _punsafeConvertType } from "../../../punsafeConvertType/minimal"; +import { termTypeToString } from "../../../../type_system/utils"; +import { UtilityTermOf } from "../../UtilityTerms/addUtilityForType"; +import { mockUserMethods } from "./mockUserMethods"; +import { mockPBoolMethods } from "./mockPBoolMethods"; +import { mockPapp } from "./mockPapp"; +import { mockPByteStringMethods } from "./mockPByteStringMethods"; +import { mockPIntMethods } from "./mockPIntMethods"; +import { mockPListMethods } from "./mockPListMethods"; +import { mockPPairMethods } from "./mockPPairMethods"; +import { mockPStringMethods } from "./mockPStringMethods"; +import { mockPStructMethods } from "./mockPStructMethods"; + +/** + * like `addUtilityForType` but it doesn't add real terms; + * + * the generated terms are not intended to end in the compilation result + * rather are useful to add the expected properties to the terms and their types + * + * `mockUtilityForType` is requires less work and less dependecies than `addUtilityForType` + */ +export function mockUtilityForType( t: T ) + : ( term: Term> ) => UtilityTermOf> +{ + if( isTaggedAsAlias( t ) ){ + return mockPAliasMethods as any + }; + + if( typeExtends( t , bool ) ) return mockPBoolMethods as any; + if( typeExtends( t , bs ) ) return mockPByteStringMethods as any; + if( typeExtends( t , int ) ) return mockPIntMethods as any; + if( typeExtends( t , list( tyVar() ) ) ) return mockPListMethods as any; + if( typeExtends( t , pair( tyVar(), tyVar() ) ) ) return mockPPairMethods as any; + if( typeExtends( t , str ) ) return mockPStringMethods as any; + + if( typeExtends( t, lam( tyVar(), tyVar() )) ) + { + return (( term: any ) => defineNonDeletableNormalProperty( + term, + "$", + ( input: any ) => + mockPapp( term, input ) + )) as any; + } + + if( isStructType( t ) ) + { + return mockPStructMethods as any; + } + + // no utility + return ((x: any) => x) as any; +} + +// `mockPAliasMethod` is (necessarily) mutually recursive with `mockUtilityForType` +// so it is defined in this file instead of "./UtilityTerms/TermAlias.ts" +export function mockPAliasMethods< + PAliased extends PType, + AMethods extends Methods +>( + aliasTerm: Term> +): TermAlias +{ + const originalType = aliasTerm.type; + + if( originalType[0] !== PrimType.Alias ) + { + console.error( originalType ); + try { + console.error( termTypeToString( originalType ) ) + } + catch {} + + throw new Error("mockPAliasMethods used on non-alias type"); + } + + const aliasedType = unwrapAlias( originalType ); + + aliasTerm = mockUtilityForType( aliasedType )( aliasTerm ) as any; + + aliasTerm = _punsafeConvertType( aliasTerm, originalType ) as any; + + aliasTerm = mockUserMethods( aliasTerm, aliasTerm.type[2] as AMethods ); + + return aliasTerm as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/__tests__/addUserMethods.instance.test.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/__tests__/addUserMethods.instance.test.ts new file mode 100644 index 00000000..3e8bd8d4 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/__tests__/addUserMethods.instance.test.ts @@ -0,0 +1,51 @@ +import { Methods, Term, int, lam, pInt, padd, pfn } from "../../../../.."; +import { addUserMethods } from "../addUserMethods"; + +describe("addUserMethod result", () => { + + + test("int", () => { + + const baseTerm = pInt(0); + + const methods = { + addOne : padd.$( 1 ), + doMultipleStuff: pfn([ int, int ], int) + (( self, other ) => self.add( other ) ) + }; + + const term = addUserMethods( baseTerm, methods ); + + const termKeys = Object.keys( term ); + + expect( termKeys.includes("addOne") ).toEqual( true ); + expect( termKeys.includes("paddOne") ).toEqual( false ); + + expect( term.addOne instanceof Term ).toBe( true ) + expect( term.paddOne ).toBe( undefined ) + + expect( termKeys.includes("doMultipleStuff") ).toEqual( true ); + expect( termKeys.includes("pdoMultipleStuff") ).toEqual( true ); + + expect( typeof term.doMultipleStuff ).toEqual("function"); + expect( term.pdoMultipleStuff instanceof Term ).toEqual( true ); + + expect( term.pdoMultipleStuff.type ).toEqual( lam( int, int ) ); + }); + + test("fail non well formed", () => { + + const baseTerm = pInt(0); + + const methods = { + addOne : padd.$( 1 ), + paddOne: pfn([ int, int ], int) + (( self, other ) => self.add( other ) ) + }; + + expect(() => { + addUserMethods( baseTerm, methods ); + }).toThrow(); + + }) +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/__tests__/isWellFormedMethods.test.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/__tests__/isWellFormedMethods.test.ts new file mode 100644 index 00000000..fc0f82b9 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/__tests__/isWellFormedMethods.test.ts @@ -0,0 +1,72 @@ +import { bool, int, pBool, pfn } from "../../../../.."; +import { isWellFormedMethods } from "../assertWellFormedMethods"; + +const fakeTerm = pfn([ int ], bool )(( n ) => pBool( true )); + +describe("isWellFormedMethods", () => { + + test("only foo", () => { + + expect( + isWellFormedMethods({ + foo: fakeTerm + }) + ).toBe( true ); + + }); + + test("foo and pfoo", () => { + + expect( + isWellFormedMethods({ + foo: fakeTerm, + pfoo: fakeTerm + }) + ).toBe( false ); + + }); + + test("foo and prop", () => { + + expect( + isWellFormedMethods({ + foo: fakeTerm, + prop: fakeTerm + }) + ).toBe( true ); + + }); + + test("prop and pprop", () => { + + expect( + isWellFormedMethods({ + pprop: fakeTerm, + prop: fakeTerm + }) + ).toBe( false ); + + }); + + test("prop and rop", () => { + + expect( + isWellFormedMethods({ + rop: fakeTerm, + prop: fakeTerm + }) + ).toBe( false ); + + }); + + test("only rop", () => { + + expect( + isWellFormedMethods({ + rop: fakeTerm + }) + ).toBe( true ); + + }); + +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/addUserMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/addUserMethods.ts new file mode 100644 index 00000000..9dbacb3e --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/addUserMethods.ts @@ -0,0 +1,114 @@ +import { defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; +import type { PType } from "../../../../PType"; +import type { PLam } from "../../../../PTypes/PFn/PLam"; +import type { Term } from "../../../../Term"; +import { typeExtends } from "../../../../type_system/typeExtends"; +import { PrimType, type Methods, type TermType } from "../../../../type_system/types"; +import type { FilterMethodsByInput, FilterOutSingleInputMethods, LiftMethods, MethodsAsTerms } from "./methodsTypes"; +import { papp } from "../../../papp"; +import { getFnTypes } from "../../../../Script/Parametrized/getFnTypes"; +import { _plet } from "../../../plet/minimal"; +import { addUtilityForType } from "../addUtilityForType"; +import { isWellFormedMethods } from "./assertWellFormedMethods"; + +function getMethodsWithFirstInputOfType( methods: Methods, type: TermType ): Methods +{ + const filtered: Methods = {}; + + let method: Term>; + + const methodNames = Object.keys( methods ); + + for( const methodName of methodNames ) + { + method = methods[methodName]; + + + if( method.type[0] !== PrimType.Lambda ) + throw new Error("user defined method is expected to be a funciton"); + + if( typeExtends( method.type[1], type ) ) + { + filtered[methodName] = method; + } + } + + return Object.freeze( filtered ); +} + +export function addUserMethods< + PT extends PType, + M extends Methods +>( + term: Term, + methods: M +): Term & + LiftMethods< + FilterMethodsByInput + > & + MethodsAsTerms< + FilterOutSingleInputMethods< + FilterMethodsByInput + > + > +{ + if( !isWellFormedMethods( methods ) ) + { + throw new Error( + "user-specified methods are not well formed, definition contains methods with ambigous names: " + + JSON.stringify( Object.keys( methods ), undefined, 2 ) + ); + } + + const t = term.type; + + const filtered = getMethodsWithFirstInputOfType( methods, t ); + + let method: Term>; + + for( const methodName in filtered ) + { + method = filtered[methodName]; + + const fnTypes = getFnTypes( method.type ); + + // don't add terms that do not accept `self_t` as first argument as methods + if( !typeExtends( fnTypes[0], t ) ) continue; + + // -1 (term application) + // -1 (the output type) + const missingArgsAfterApplication = fnTypes.length - 2; + + const _appliedTerm = papp( method, term ); + const appliedTerm = addUtilityForType( _appliedTerm.type )( _plet( _appliedTerm ) ); + + if( missingArgsAfterApplication === 0 ) + { + defineReadOnlyProperty( + term, methodName, appliedTerm + ); + continue; + } + + defineReadOnlyProperty( + term, "p" + methodName, appliedTerm + ); + defineReadOnlyProperty( + term, methodName, ( ...other_terms: any[] ) => { + let result: any = appliedTerm; + + if( other_terms.length < missingArgsAfterApplication ) + throw new Error("not enough arguments for method + '" + methodName + "'"); + + for( let i = 0 ; i < missingArgsAfterApplication; i++ ) + { + result = papp( result, other_terms[i] ); + } + + return result; + } + ) + } + + return term as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/assertWellFormedMethods.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/assertWellFormedMethods.ts new file mode 100644 index 00000000..17ad56fa --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/assertWellFormedMethods.ts @@ -0,0 +1,45 @@ +import type { Methods } from "../../../../type_system"; + +/** + * checks thatevery method name does not have an equivalent that starts with "p" + * (added by convention to indicate the term rather than the funciton) + * + * @example + * ```ts + * const notOk: Methods = { + * foo: pfn([ int ], bool)( ... ), + * bar: pfn([ int ], bool)( ... ), + * // ERROR: 'pfoo' is used to indicate the term counterpart of 'foo' + * pfoo: pfn([ int ], bool)( ... ) + * } + * const ok: Methods = { + * foo: pfn([ int ], bool)( ... ), + * bar: pfn([ int ], bool)( ... ), + * // no problem + * // this will generate 'prop' and 'pprop' + * // where 'prop' is the funciton and 'pprop' is the term + * prop: pfn([ int ], bool)( ... ) + * } + * ``` + */ +export function isWellFormedMethods( methods: Methods ): boolean +{ + const names = Object.keys( methods ); + const pnames: string[] = []; + + for( const name of names ) + { + if( name.length === 0 ) continue; + if( name[0] === "p" ) + { + pnames.push( name.slice(1) ); + } + } + + return !pnames.some( pname => names.includes( pname ) ) +} + +export function assertWellFormedMethods( methods: Methods ): void +{ + if( !isWellFormedMethods( methods ) ) throw new Error("methods are not well formed"); +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/methodsTypes.ts b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/methodsTypes.ts new file mode 100644 index 00000000..f4e1d57e --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/UtilityTerms/userMethods/methodsTypes.ts @@ -0,0 +1,136 @@ +import type { PType } from "../../../../PType"; +import { PAlias, PBool, PInt, PStruct } from "../../../../PTypes"; +import { PFn } from "../../../../PTypes/PFn/PFn"; +import type { PLam } from "../../../../PTypes/PFn/PLam"; +import type { Term } from "../../../../Term"; +import type { Methods, StructDefinition } from "../../../../type_system/types"; +import type { UtilityTermOf } from "../addUtilityForType"; + +export type LiftPMethod[] = []> + = PT extends PLam ? + ( + POut extends PLam ? + LiftPMethod ]> : + ( ...args: [ ...PrevPIns, Term ] ) => UtilityTermOf + ) : UtilityTermOf + +// type test_0 = LiftPMethod> +// type test_1 = LiftPMethod> +// type test_2 = LiftPMethod>> + +export type LiftTermMethod>> + // only infer `POut` because we assume `PIn` is the term using the method + // TODO: filter out `PIn`s that are not of type of the term using the method (requires additional type prameter) + = TFn extends Term> ? LiftPMethod : never; + +export type LiftMethods = { + readonly [ Method in keyof SMethods ]: LiftTermMethod +} + + +// type WithoutFirstPLam +// = PT extends PLam ? POut : PT; + +type TermToMethod>> + = T extends Term< + PLam< + PType /* The term with the methods */, + infer POut extends PType + > + > ? + ( + // Term is a function with at least two arguments + // (that means at least one other argument must be passed) + POut extends PLam ? + UtilityTermOf : + // else + // given the single input we have already somehting + // "forwarded" to `LiftMethods` as a "getter" (because we don't want the additional "p" as prefix) + never + + // UtilityTermOf> : + ) + : never; + +/** + * @requires - typescript@^4.1 + */ +export type MethodsAsTerms = { + readonly [ Method in keyof SMethods as `p${Method & string}` ]: TermToMethod +} + +// type test_3 = MethodsAsTerms<{ +// foo: Term> +// }> + +/** + * keeps only the methods that take the specified `InputFilter` as first input + */ +export type FilterMethodsByInput = { + [ M in keyof Ms ]: + Ms[M] extends Term> ? + ( + InputFilter extends PAlias ? + ( + MethodIn extends PAliased ? Ms[M] : + MethodIn extends PAlias ? Ms[M] : + never + ): + ( + InputFilter extends PStruct ? + ( + MethodIn extends PStruct ? Ms[M] : + never + ) : + ( + MethodIn extends PAlias ? + ( + InputFilter extends PAliased ? Ms[M] : + InputFilter extends PAlias ? Ms[M] : + never + ): + ( + MethodIn extends PStruct ? + ( + InputFilter extends PStruct ? Ms[M] : + never + ) : + // base case + ( MethodIn extends InputFilter ? Ms[M] : never ) + ) + ) + ) + ) : never +} & Methods; + +type test_4 = FilterMethodsByInput<{ + foo: Term> +}, PInt> +type test_5 = FilterMethodsByInput<{ + foo: Term> +}, PBool> +type test_6 = FilterMethodsByInput<{ + foo: Term, PBool>> +}, PInt> +type test_7 = FilterMethodsByInput<{ + foo: Term, PBool>> +}, PAlias> +type test_8 = FilterMethodsByInput<{ + foo: Term, PBool>> + baz: Term> + moo: Term> +}, PAlias> + +/** + * keeps only the methods that are a `PLam` AND take AT LEAST 2 INPUTS + */ +export type FilterOutSingleInputMethods = { + [ M in keyof Ms ]: + Ms[M] extends Term>> ? + Ms[M] : never +} & Methods; + +type test_9 = FilterOutSingleInputMethods<{ + foo: Term>, + bar: Term> +}> \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/data/conversion/__tests__/data.conversion.toData.test.ts b/packages/onchain/src/pluts/lib/std/data/conversion/__tests__/data.conversion.toData.test.ts index d62faa31..d5b79f68 100644 --- a/packages/onchain/src/pluts/lib/std/data/conversion/__tests__/data.conversion.toData.test.ts +++ b/packages/onchain/src/pluts/lib/std/data/conversion/__tests__/data.conversion.toData.test.ts @@ -1,5 +1,5 @@ import { Machine } from "@harmoniclabs/plutus-machine"; -import { PAssetsEntryT, PCurrencySymbol, PScriptPurpose, PTokenName, PValue, PValueEntryT } from "../../../../../API"; +import { PAssetsEntry, PCurrencySymbol, PScriptPurpose, PTokenName, PValue, PValueEntry } from "../../../../../API"; import { pair, data, asData, typeExtends, list, termTypeToString, int, bs } from "../../../../../type_system"; import { pByteString } from "../../../bs"; import { pInt } from "../../../int"; @@ -98,19 +98,16 @@ describe("_toData", () => { test("_toData( PValue.type )( beef32 )", () => { const beef32 = PValue.from( - pList( PValueEntryT )([ - pPair( PCurrencySymbol.type, - list( PAssetsEntryT ) ) - ( + pList( PValueEntry.type )([ + PValueEntry.from([ PCurrencySymbol.from( pByteString("deadbeef") ), - pList( PAssetsEntryT )([ - pPair( PTokenName.type, int ) - ( + pList( PAssetsEntry.type )([ + PAssetsEntry.from([ PTokenName.from( pByteString("beef") ), pInt( 32 ) - ) + ]) ]) - ) + ]) ]) ); diff --git a/packages/onchain/src/pluts/lib/std/data/conversion/fromData.ts b/packages/onchain/src/pluts/lib/std/data/conversion/fromData.ts index ac3763ba..68112304 100644 --- a/packages/onchain/src/pluts/lib/std/data/conversion/fromData.ts +++ b/packages/onchain/src/pluts/lib/std/data/conversion/fromData.ts @@ -4,7 +4,7 @@ import { TermFn } from "../../../../PTypes/PFn/PFn"; import { Term } from "../../../../Term"; import { TermType } from "../../../../type_system"; import { ToPType } from "../../../../type_system/ts-pluts-conversion"; -import { UtilityTermOf, addUtilityForType } from "../../../addUtilityForType"; +import { UtilityTermOf, addUtilityForType } from "../../UtilityTerms/addUtilityForType"; import { papp } from "../../../papp"; import { _fromData, _pfromData } from "./fromData_minimal"; diff --git a/packages/onchain/src/pluts/lib/std/data/conversion/fromData_minimal.ts b/packages/onchain/src/pluts/lib/std/data/conversion/fromData_minimal.ts index ae96120a..60e29365 100644 --- a/packages/onchain/src/pluts/lib/std/data/conversion/fromData_minimal.ts +++ b/packages/onchain/src/pluts/lib/std/data/conversion/fromData_minimal.ts @@ -78,7 +78,7 @@ const pPairFromData = ) ) .$( - _papp( punListData as any, assumedList ) + _papp( punListData as any, assumedList ) as any ) ) ); @@ -140,6 +140,7 @@ export function _fromData( t: T ): ( term: Term ) => const elemsT = getElemsT( t ) as PairT; const fstT = getFstT( elemsT ); const sndT = getSndT( elemsT ); + console.log( sndT, elemsT ); return ( ( term: Term ) => { @@ -188,7 +189,7 @@ export function _fromData( t: T ): ( term: Term ) => _papp( punListData as any, term - ) + ) as any ); (theTerm as any).isConstant = (term as any).isConstant; diff --git a/packages/onchain/src/pluts/lib/std/data/conversion/toData.ts b/packages/onchain/src/pluts/lib/std/data/conversion/toData.ts index 24e52b81..b8b1c5e6 100644 --- a/packages/onchain/src/pluts/lib/std/data/conversion/toData.ts +++ b/packages/onchain/src/pluts/lib/std/data/conversion/toData.ts @@ -4,7 +4,7 @@ import { TermFn } from "../../../../PTypes/PFn/PFn"; import { Term } from "../../../../Term"; import { TermType } from "../../../../type_system"; import { ToPType } from "../../../../type_system/ts-pluts-conversion"; -import { UtilityTermOf, addUtilityForType } from "../../../addUtilityForType"; +import { UtilityTermOf, addUtilityForType } from "../../UtilityTerms/addUtilityForType"; import { papp } from "../../../papp"; import { _ptoData, _toData } from "./toData_minimal"; diff --git a/packages/onchain/src/pluts/lib/std/data/conversion/toData_minimal.ts b/packages/onchain/src/pluts/lib/std/data/conversion/toData_minimal.ts index c7a9a5b5..dfcd37f4 100644 --- a/packages/onchain/src/pluts/lib/std/data/conversion/toData_minimal.ts +++ b/packages/onchain/src/pluts/lib/std/data/conversion/toData_minimal.ts @@ -241,7 +241,7 @@ function pid( fromT: T, toT: TT ): Term export function _ptoData( t: T ): Term, PAsData>>> { - if( isTaggedAsAlias( t ) ) return _ptoData( unwrapAlias( t as any ) ); + if( isTaggedAsAlias( t ) ) return _ptoData( unwrapAlias( t as any ) ) as any; if( typeExtends( t, data ) ) return pid( t, asData( t ) ); diff --git a/packages/onchain/src/pluts/lib/std/index.ts b/packages/onchain/src/pluts/lib/std/index.ts index f4537d39..ca35fa6d 100644 --- a/packages/onchain/src/pluts/lib/std/index.ts +++ b/packages/onchain/src/pluts/lib/std/index.ts @@ -6,6 +6,7 @@ export * from "./bool"; export * from "./data"; export * from "./list"; export * from "./pair"; +export * from "./stdEq"; export * from "./PMaybe"; export * from "./combinators"; export * from "./UtilityTerms"; \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/index.ts b/packages/onchain/src/pluts/lib/std/list/index.ts index dc0c3ec8..7e594024 100644 --- a/packages/onchain/src/pluts/lib/std/list/index.ts +++ b/packages/onchain/src/pluts/lib/std/list/index.ts @@ -10,4 +10,7 @@ export * from "./pfilter"; export * from "./pevery"; export * from "./psome"; export * from "./preverse"; -export * from "./const"; \ No newline at end of file +export * from "./const"; +export * from "./peqList"; +export * from "./plookup"; +export * from "./pincludes"; \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/pcompareList/__tests__/pcompareList.piterList.benchmark.test.ts b/packages/onchain/src/pluts/lib/std/list/pcompareList/__tests__/pcompareList.piterList.benchmark.test.ts new file mode 100644 index 00000000..a4e01a31 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/list/pcompareList/__tests__/pcompareList.piterList.benchmark.test.ts @@ -0,0 +1,69 @@ +import { bool, int } from "../../../../../type_system" +import { pisEmpty } from "../../../../builtins/list"; +import { pBool } from "../../../bool/pBool"; +import { piterLists } from "../../piterLists" +import { pcompareList } from ".."; +import { peqInt } from "../../../../builtins/int/intBinOpToBool"; +import { pList } from "../../const"; +import { pInt } from "../../../int"; +import { Machine } from "@harmoniclabs/plutus-machine"; +import { UPLCConst } from "@harmoniclabs/uplc"; + +describe("pcompareList vs piterLists", () => { + + const piterCompare = piterLists( int, int, bool ) + .$(( self, restSnd ) => pisEmpty.$( restSnd ) ) + .$(( self, restFst ) => pBool( false ) ) + .$(( self, fst, restFst, snd, restSnd ) => + fst.eq( snd ) + .and( self.$( restFst ).$( restSnd ) ) + ); + + const pcompare = pcompareList( int, int ) + .$(( restSnd ) => pisEmpty.$( restSnd ) ) + .$(( restFst ) => pBool( false ) ) + .$( peqInt ); + + const pListInt = ( ns: number[] ) => pList( int )( ns.map( pInt ) ); + + function bench( as: number[], bs: number[], log: boolean = false ) + { + const iterRes = Machine.eval( + piterCompare + .$(pListInt(as)) + .$(pListInt(bs)) + ); + + const compRes = Machine.eval( + pcompare + .$(pListInt(as)) + .$(pListInt(bs)) + ); + + const title = `${JSON.stringify(as)} - ${JSON.stringify(bs)}`; + if( log ) + { + console.log(` +test title: ${title} +result: ${(iterRes.result as UPLCConst).value} +iter: ${JSON.stringify(iterRes.budgetSpent.toJson())} +comp: ${JSON.stringify(compRes.budgetSpent.toJson())} + `); + } + + test(title, () => { + expect( iterRes.result ).toEqual( compRes.result ) + expect( iterRes.budgetSpent.mem ).toBeGreaterThanOrEqual( compRes.budgetSpent.mem ) + expect( iterRes.budgetSpent.cpu ).toBeGreaterThanOrEqual( compRes.budgetSpent.cpu ) + }) + + } + + bench([],[]); + bench([1],[]); + bench([],[1]); + bench([1],[1]); + bench([1,2,3],[1,2,3]); + bench([1,2,3,4,5,6,7],[1,2,3,4,5,6]); + +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/pcompareList/index.ts b/packages/onchain/src/pluts/lib/std/list/pcompareList/index.ts new file mode 100644 index 00000000..491b2ddb --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/list/pcompareList/index.ts @@ -0,0 +1,121 @@ +import { PBool } from "../../../../PTypes/PBool"; +import { PFn, TermFn } from "../../../../PTypes/PFn/PFn"; +import { PList } from "../../../../PTypes/PList"; +import { ToPType } from "../../../../type_system/ts-pluts-conversion"; +import { TermType, bool, fn, list } from "../../../../type_system/types"; +import { pif, pisEmpty } from "../../../builtins"; +import { papp } from "../../../papp"; +import { pfn } from "../../../pfn"; +import { phoist } from "../../../phoist"; +import { precursive } from "../../../precursive"; + +export function pcompareList< + FstElsT extends TermType, + SndElsT extends TermType +>( + fstListElems: FstElsT, + sndListElems: SndElsT +): TermFn<[ + + // matchFstNil + PFn<[ + + PList>, + + ], PBool>, + + // matchSndNil + PFn<[ + + PList>, + + ], PBool>, + + // matchCons + PFn<[ + + ToPType, + + ToPType, + + ], PBool>, + +], + // final self + PFn<[ + PList>, + PList>, + ], PBool> +> +{ + const finalSelfType = fn([ + list( fstListElems ), + list( sndListElems ) + ], bool ); + + const matchFstNil_t = fn([ + list( sndListElems ) + ], bool ); + + const matchSndNil_t = fn([ + list( fstListElems ) + ], bool ); + + const matchCons_t = fn([ + fstListElems, + sndListElems, + ], bool ); + + return phoist( + pfn([ + matchFstNil_t, + matchSndNil_t, + matchCons_t + ], finalSelfType) + (( matchFstNil, matchSndNil, matchCons ) => + precursive( + pfn([ + finalSelfType, + list( fstListElems ), + list( sndListElems ) + ], bool ) + ((self, fstList, sndList) => + + pif( bool ).$( pisEmpty.$( fstList ) ) + .then( + papp( + matchFstNil, + sndList + ) + ) + .else( + pif( bool ).$( pisEmpty.$( sndList ) ) + .$( + papp( + matchSndNil, + fstList + ) + ) + .$( + papp( + papp( + matchCons, + fstList.head + ), + sndList.head + ).and( + papp( + papp( + self, + fstList.tail + ), + sndList.tail + ) + ) + ) + ) + ) + ) as any + ) + ) as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/peqList.ts b/packages/onchain/src/pluts/lib/std/list/peqList.ts new file mode 100644 index 00000000..3f12aabd --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/list/peqList.ts @@ -0,0 +1,26 @@ +import { PBool, PList, TermFn } from "../../../PTypes"; +import { ToPType } from "../../../type_system"; +import { TermType, bool, lam, list } from "../../../type_system/types"; +import { pisEmpty } from "../../builtins/list"; +import { pfn } from "../../pfn"; +import { phoist } from "../../phoist"; +import { punsafeConvertType } from "../../punsafeConvertType"; +import { pBool } from "../bool/pBool"; +import { pstdEq } from "../stdEq/pstdEq"; +import { pcompareList } from "./pcompareList"; + +/** + * @since v0.5.0 + * @param {TermType} t type of the elements of the list + */ +export function peqList( t: ElemsT ) +: TermFn<[ PList>, PList> ], PBool> +{ + return phoist( + pcompareList( t, t ) + .$( punsafeConvertType( pisEmpty, lam( list( t ), bool ) ) ) + // if rest second is matched then restFst is not empty + .$(( _restFst ) => pBool( false ) ) + .$( pstdEq( t ) ) + ) +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/pfind.ts b/packages/onchain/src/pluts/lib/std/list/pfind.ts index eb287120..7baf07c5 100644 --- a/packages/onchain/src/pluts/lib/std/list/pfind.ts +++ b/packages/onchain/src/pluts/lib/std/list/pfind.ts @@ -1,10 +1,9 @@ -import { TermFn, PLam, PBool, PList } from "../../../PTypes"; +import type { TermFn, PLam, PBool, PList } from "../../../PTypes"; import { TermType, ToPType, lam, bool, list, asData } from "../../../type_system"; -import { pif, pisEmpty, phead, ptail } from "../../builtins"; +import { pif, pisEmpty, ptail } from "../../builtins"; import { papp } from "../../papp"; import { pfn } from "../../pfn"; import { phoist } from "../../phoist"; -import { plet } from "../../plet"; import { precursive } from "../../precursive"; import { PMaybeT, PMaybe } from "../PMaybe/PMaybe"; import { _ptoData } from "../data/conversion/toData_minimal"; @@ -41,20 +40,16 @@ export function pfind = ) .else( - plet( phead( elemsT ).$( _list ) ).in( head => - - pif( PMaybeElem.type ).$( papp( predicate, head ) ) - .then( - PMaybeElem.Just({ - // "as any" because of - // "Type 'Term>>' is not assignable to type 'Term>>>'" - val: papp( elemToData, head ) as any - }) - ) - .else( - papp( self, ptail( elemsT ).$( _list ) ) - ) - + pif( PMaybeElem.type ).$( papp( predicate, _list.head ) ) + .then( + PMaybeElem.Just({ + // "as any" because of + // "Type 'Term>>' is not assignable to type 'Term>>>'" + val: papp( elemToData, _list.head ) as any + }) + ) + .else( + papp( self, ptail( elemsT ).$( _list ) ) ) ) diff --git a/packages/onchain/src/pluts/lib/std/list/pincludes/index.ts b/packages/onchain/src/pluts/lib/std/list/pincludes/index.ts new file mode 100644 index 00000000..18a010d7 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/list/pincludes/index.ts @@ -0,0 +1,25 @@ +import { TermType, bool, fn, list } from "../../../../type_system"; +import { papp } from "../../../papp"; +import { pfn } from "../../../pfn"; +import { phoist } from "../../../phoist"; +import { pstdEq } from "../../stdEq"; + +export function pincludes< + ElemsT extends TermType +>( + elems_t: ElemsT +) +{ + return phoist( + phoist( + pfn([ + fn([ elems_t, elems_t ], bool ), + list( elems_t ), + elems_t + ], bool) + (( eqFn, lst, elem ) => lst.some( papp( eqFn, elem ) )) + ).$( + pstdEq( elems_t ) + ) + ) +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/piterLists/__tests__/piterLists.test.ts b/packages/onchain/src/pluts/lib/std/list/piterLists/__tests__/piterLists.test.ts new file mode 100644 index 00000000..47147b2a --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/list/piterLists/__tests__/piterLists.test.ts @@ -0,0 +1,190 @@ +import { Machine } from "@harmoniclabs/plutus-machine"; +import { piterLists } from ".."; +import { bool, int } from "../../../../../type_system/types"; +import { pBool } from "../../../bool"; +import { pList } from "../../const"; +import { pInt } from "../../../int"; +import { pisEmpty } from "../../../../builtins"; + +describe("piterLists", () => { + + test("only match", () => { + + const pBothNonEnpty = piterLists( int, int, bool ) + .$((self, restSnd) => pBool( false )) + .$((self, restFst) => pBool( false )) + .$((self, fst, restFst, snd, restSnd) => pBool( true )) + + expect( + Machine.evalSimple( + pBothNonEnpty + .$( pList( int )([]) ) + .$( pList( int )([]) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + expect( + Machine.evalSimple( + pBothNonEnpty + .$( pList( int )([ 1 ].map( pInt )) ) + .$( pList( int )([]) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + expect( + Machine.evalSimple( + pBothNonEnpty + .$( pList( int )([]) ) + .$( pList( int )([ 1 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + expect( + Machine.evalSimple( + pBothNonEnpty + .$( pList( int )([ 1 ].map( pInt )) ) + .$( pList( int )([ 1 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( true ) + ) + ); + + }) + + test.only("peqListInt", () => { + + const peqListInt = piterLists( int, int, bool ) + .$((self, restSnd) => pisEmpty.$( restSnd ) ) + .$((self, restFst) => pisEmpty.$( restFst ) ) + .$((self, fst, restFst, snd, restSnd) => + fst.eq( snd ) + .and( + self.$( restFst ).$( restSnd ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([]) ) + .$( pList( int )([]) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( true ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([ 1 ].map( pInt )) ) + .$( pList( int )([]) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([]) ) + .$( pList( int )([ 1 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([ 2 ].map( pInt )) ) + .$( pList( int )([ 1 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([ 1 ].map( pInt )) ) + .$( pList( int )([ 1 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( true ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([ 1 ].map( pInt )) ) + .$( pList( int )([ 1, 2 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([ 1 ].map( pInt )) ) + .$( pList( int )([ 2 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([ 1, 2 ].map( pInt )) ) + .$( pList( int )([ 1, 2 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( true ) + ) + ); + + expect( + Machine.evalSimple( + peqListInt + .$( pList( int )([ 1, 2 ].map( pInt )) ) + .$( pList( int )([ 1, 1 ].map( pInt )) ) + ) + ).toEqual( + Machine.evalSimple( + pBool( false ) + ) + ); + + }) + +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/piterLists/index.ts b/packages/onchain/src/pluts/lib/std/list/piterLists/index.ts new file mode 100644 index 00000000..d6c38b66 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/list/piterLists/index.ts @@ -0,0 +1,173 @@ +import { TermFn } from "../../../../PTypes/PFn"; +import { PFn } from "../../../../PTypes/PFn/PFn"; +import { PList } from "../../../../PTypes/PList"; +import { ToPType } from "../../../../type_system"; +import { TermType, delayed, fn, lam, list } from "../../../../type_system/types"; +import { papp } from "../../../papp"; +import { pdelay } from "../../../pdelay"; +import { pfn } from "../../../pfn"; +import { phoist } from "../../../phoist"; +import { precursive } from "../../../precursive"; +import { pmatchList } from "../pmatchList"; + +export function piterLists< + FstElsT extends TermType, + SndElsT extends TermType, + ResultT extends TermType +>( + fstListElems: FstElsT, + sndListElems: SndElsT, + resultT: ResultT +): TermFn<[ + + // matchFstNil + PFn<[ + + // final self + PFn<[ + PList>, + PList>, + ], ToPType>, + + PList>, + + ], ToPType>, + + // matchSndNil + PFn<[ + + // final self + PFn<[ + PList>, + PList>, + ], ToPType>, + + PList>, + + ], ToPType>, + + // matchCons + PFn<[ + + // final self + PFn<[ + PList>, + PList>, + ], ToPType>, + + ToPType, + PList>, + + ToPType, + PList>, + + ], ToPType>, + +], + // final self + PFn<[ + PList>, + PList>, + ], ToPType> +> +{ + const finalSelfType = fn([ + list( fstListElems ), + list( sndListElems ) + ], resultT ); + + const matchFstNil_t = fn([ + finalSelfType, + list( sndListElems ) + ], resultT ); + + const matchSndNil_t = fn([ + finalSelfType, + list( fstListElems ) + ], resultT ); + + const matchCons_t = fn([ + finalSelfType, + fstListElems, + list( fstListElems ), + sndListElems, + list( sndListElems ) + ], resultT ); + + return phoist( + pfn([ + matchFstNil_t, + matchSndNil_t, + matchCons_t + ], finalSelfType) + (( matchFstNil, matchSndNil, matchCons ) => + precursive( + pfn([ + finalSelfType, + list( fstListElems ), + list( sndListElems ) + ], resultT ) + ((self, fstList, sndList) => + pmatchList( resultT, fstListElems ) + .$( + pdelay( + papp( + papp( + matchFstNil, + self + ), + sndList + ) + ) + ) + .$( + pfn([ + fstListElems, + list( fstListElems ) + ], resultT) + (( fstEl, restFst ) => + pmatchList( resultT, sndListElems ) + .$( + pdelay( + papp( + papp( + matchSndNil, + self + ), + fstList + ) + ) + ) + .$( + pfn([ + sndListElems, + list( sndListElems ) + ], resultT) + (( sndEl, restSnd ) => + papp( + papp( + papp( + papp( + papp( + matchCons, + self + ), + fstEl + ), + restFst + ), + sndEl + ), + restSnd + ) + ) + ) + .$( sndList ) as any + ) + ) + .$( fstList ) as any + ) + ) as any + ) + ) as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/plookup/__tests__/plookup.test.ts b/packages/onchain/src/pluts/lib/std/list/plookup/__tests__/plookup.test.ts new file mode 100644 index 00000000..631e747e --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/list/plookup/__tests__/plookup.test.ts @@ -0,0 +1,73 @@ +import { UPLCConst, showUPLC } from "@harmoniclabs/uplc"; +import { data, int, pBool, pInt, pList, pPair, pair, plookup, pmatch } from "../../../../.." +import { Machine } from "@harmoniclabs/plutus-machine"; + +function pMapInts( entries: [ number, number ][] ) +{ + return pList( pair( int, int ) ) + ( entries.map( + ([ k, v ]) => pPair( int, int )( k, v ) + )) +} + +const trueUplc = UPLCConst.bool( true ); +const falseUplc = UPLCConst.bool( false ); + +const intsLookup = plookup( int, int ); +const dataLookup = plookup( data, data ); + +describe("plookup", () => { + + + function find4269( entries: [ number, number ][], _shouldFind?: boolean ) + { + const shouldFind = _shouldFind ?? entries.some( ([ k, v ]) => k === 42 && v === 69 ); + const expected = shouldFind ? trueUplc : falseUplc ; + + const term = pMapInts( entries ); + + test( JSON.stringify( entries ) + " " + shouldFind, () => { + expect( + Machine.evalSimple( + pmatch( + intsLookup.$( 42 ).$( term ) + ) + .onJust(({ val }) => val.eq( 69 )) + .onNothing( _ => pBool( false ) ) + ) + ).toEqual( expected ) + }) + } + + find4269([]); + find4269([[42, 69]]); + find4269([ + [2, 69], + [22, 69], + [69, 69], + [42, 69] + ]); + find4269([ + [2, 69], + [22, 69], + [69, 69] + ]); + find4269([ + [2, 69], + [22, 69], + [42, 42] + ]); + find4269([ + [2, 69], + [22, 69], + [42, 69], + [42, 42], + ]); + find4269([ + [2, 69], + [22, 69], + [42, 42], + [42, 69] + ], false); + +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/plookup/index.ts b/packages/onchain/src/pluts/lib/std/list/plookup/index.ts new file mode 100644 index 00000000..97ed36f0 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/list/plookup/index.ts @@ -0,0 +1,82 @@ +import { PList, PPair, TermFn } from "../../../../PTypes"; +import { TermType, ToPType, asData, bool, data, lam, list, pair, typeExtends } from "../../../../type_system"; +import { getDirectFstT } from "../../../../type_system/tyArgs/getDirectFstT"; +import { getDirectSndT } from "../../../../type_system/tyArgs/getDirectSndT"; +import { pif } from "../../../builtins/bool"; +import { peqData } from "../../../builtins/data"; +import { pfstPairNoUnwrap, psndPairNoUnwrap } from "../../../builtins/pair/noUnwrap"; +import { papp } from "../../../papp"; +import { pdelay } from "../../../pdelay"; +import { pfn } from "../../../pfn"; +import { phoist } from "../../../phoist"; +import { plet } from "../../../plet"; +import { punsafeConvertType } from "../../../punsafeConvertType"; +import { PMaybe, PMaybeT } from "../../PMaybe"; +import { toData } from "../../data"; +import { pstdEq } from "../../stdEq"; +import { precursiveList } from "../precursiveList"; + +export function plookup< + KT extends TermType, + VT extends TermType +>( + kT: KT, + vT: VT +): TermFn<[ + ToPType, + PList,ToPType>> +], PMaybeT>> +{ + const PMaybeVal = PMaybe( vT ); + + const elems_t = pair( kT, vT ); + + // pair call ususally adds `asData` + // taking args from pair type in case it ever changes + const fstT = getDirectFstT( elems_t ); + const sndT = getDirectSndT( elems_t ); + + const pfst = pfstPairNoUnwrap( fstT, sndT ); + const psnd = psndPairNoUnwrap( fstT, sndT ); + + const pvalueToData = toData( sndT ); + + const hoistedBody = phoist( + pfn([ + asData( kT ), + ], lam( list( elems_t ) ,PMaybeVal.type)) + ( searchElem => + plet( + peqData.$( searchElem ) + ).in( isKey => + precursiveList( PMaybeVal.type, elems_t ) + .$( _self => pdelay( PMaybeVal.Nothing({}) ) ) + .$(( self, el: any , rest ) => + pif( PMaybeVal.type ) + .$( isKey.$( pfst.$( el ) as any ) ) + .then( + PMaybeVal.Just({ + val: pvalueToData( psnd.$( el ) as any ) + }) + ) + .else( self.$( rest ) ) + ) + ) + ) + ); + + if( typeExtends( kT, data ) ) + { + return hoistedBody as any; + } + + return phoist( + pfn([ + kT + ], lam( list( elems_t ) ,PMaybeVal.type) ) + (( searchElem ) => + hoistedBody.$( toData( kT )( searchElem ) ) + ) + ) as any; + +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/list/precursiveList/index.ts b/packages/onchain/src/pluts/lib/std/list/precursiveList/index.ts index bf8d1ef7..97489957 100644 --- a/packages/onchain/src/pluts/lib/std/list/precursiveList/index.ts +++ b/packages/onchain/src/pluts/lib/std/list/precursiveList/index.ts @@ -9,7 +9,13 @@ import { _papp } from "../../data/conversion/minimal_common"; import { pmatchList } from "../pmatchList"; -export function precursiveList( returnT: ReturnT, elemsT: ElemtsT ) +export function precursiveList< + ReturnT extends TermType, + ElemtsT extends TermType +>( + returnT: ReturnT, + elemsT: ElemtsT +) : TermFn< [ PLam< // caseNil @@ -25,23 +31,27 @@ export function precursiveList // result > { - const finalType = lam( list( elemsT ), returnT ); + const finalSelfType = lam( list( elemsT ), returnT ); + + const matchNil_t = lam( finalSelfType, delayed( returnT ) ); + + const matchCons_t = fn([ finalSelfType, elemsT, list( elemsT ) ], returnT ); + + const originalSelf_t = fn([ + matchNil_t, + matchCons_t, + list( elemsT ) + ], returnT); return phoist( precursive( pfn([ - fn([ - lam( finalType, delayed( returnT ) ), - fn([ finalType, elemsT, list( elemsT ) ], returnT ), - list( elemsT ) - ], returnT ), - lam( finalType, delayed( returnT ) ), - fn([ finalType, elemsT, list( elemsT ) ], returnT ), - list( elemsT ) - ], - returnT - ) - ( ( self, matchNil, matchCons, lst ) => + originalSelf_t, + matchNil_t, + matchCons_t, + list( elemsT ) + ], returnT ) + (( self, matchNil, matchCons, lst ) => plet( papp( papp( diff --git a/packages/onchain/src/pluts/lib/std/list/precursiveList/minimal.ts b/packages/onchain/src/pluts/lib/std/list/precursiveList/minimal.ts index 9981a3b2..dce6aa53 100644 --- a/packages/onchain/src/pluts/lib/std/list/precursiveList/minimal.ts +++ b/packages/onchain/src/pluts/lib/std/list/precursiveList/minimal.ts @@ -1,15 +1,15 @@ -import { TermFn, PLam, PList, PDelayed } from "../../../../PTypes"; +import { PLam, PList, PDelayed, PFn } from "../../../../PTypes"; import { TermType, ToPType, lam, list, fn, delayed } from "../../../../type_system"; import { pfn } from "../../../pfn"; import { phoist } from "../../../phoist"; -import { plet } from "../../../plet"; -import { precursive } from "../../../precursive"; import { _papp } from "../../data/conversion/minimal_common"; import { _pmatchList } from "../pmatchList/minimal"; +import { Term } from "../../../../Term"; +import { _precursive } from "../../../precursive/minimal"; export function _precursiveList( returnT: ReturnT, elemsT: ElemtsT ) -: TermFn< +: Term>, ToPType>, // self @@ -22,12 +22,12 @@ export function _precursiveList> // list ], ToPType // result -> +>> { const finalType = lam( list( elemsT ), returnT ); return phoist( - precursive( + _precursive( pfn([ fn([ lam( finalType, delayed( returnT ) ), @@ -40,16 +40,18 @@ export function _precursiveList - plet( + ( ( self, matchNil, matchCons, lst ) => { + + const finalSelfTerm = _papp( _papp( - _papp( - self, - matchNil - ), - matchCons - ) - ).in( finalSelf => + self, + matchNil + ), + matchCons + ); + + const exprTerm = pfn([ finalSelfTerm.type ], returnT ) + ( finalSelf => _papp( _papp( _papp( @@ -67,7 +69,9 @@ export function _precursiveList( @@ -44,7 +45,7 @@ export function pPair( fst instanceof Term && ( typeExtends( fst.type, fstT ) || - typeExtends( fst.type, unwrapAsData( fstT as any ) ) || + typeExtends( fst.type, clearAsData( fstT as any ) ) || typeExtends( fst.type, data ) ), "first element of a constant pair was not a constant" @@ -53,14 +54,14 @@ export function pPair( snd instanceof Term && ( typeExtends( snd.type, sndT ) || - typeExtends( snd.type, unwrapAsData( sndT as any) ) || + typeExtends( snd.type, clearAsData( sndT as any ) ) || typeExtends( snd.type, data ) ), "second element of a constant pair was not a constant" ); - let _fst_ = _toData( unwrapAsData( fstT as any ) )( fst ); - let _snd_ = _toData( unwrapAsData( sndT as any ) )( snd ); + let _fst_ = _toData( clearAsData( fstT as any ) )( fst ); + let _snd_ = _toData( clearAsData( sndT as any ) )( snd ); if( !(fst as any).isConstant || diff --git a/packages/onchain/src/pluts/lib/std/pair/peqPair.ts b/packages/onchain/src/pluts/lib/std/pair/peqPair.ts new file mode 100644 index 00000000..f410f49b --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/pair/peqPair.ts @@ -0,0 +1,28 @@ +import { PBool, TermFn } from "../../../PTypes"; +import { PairT, TermType, ToPType, bool } from "../../../type_system"; +import { getDirectFstT } from "../../../type_system/tyArgs/getDirectFstT"; +import { getDirectSndT } from "../../../type_system/tyArgs/getDirectSndT"; +import { pfn } from "../../pfn"; +import { pfstPairNoUnwrap, psndPairNoUnwrap } from "../../builtins/pair/noUnwrap"; +import { pstdEq } from "../stdEq/pstdEq"; +import { phoist } from "../../phoist"; + +export function peqPair>( t: T ) +: TermFn<[ ToPType, ToPType ], PBool> +{ + const fstT = getDirectFstT( t ); + const sndT = getDirectSndT( t ); + + const pfst = pfstPairNoUnwrap( fstT, sndT ); + const psnd = psndPairNoUnwrap( fstT, sndT ); + + return phoist( + pfn([ t, t ], bool) + (( a, b ) => + pstdEq( fstT ).$( pfst.$( a ) ).$( pfst.$( b ) ) + .and( + pstdEq( sndT ).$( psnd.$( a ) ).$( psnd.$( b ) ) + ) + ) + ); +} diff --git a/packages/onchain/src/pluts/lib/std/stdEq/__tests__/pstdEq.test.ts b/packages/onchain/src/pluts/lib/std/stdEq/__tests__/pstdEq.test.ts new file mode 100644 index 00000000..08ec2ddc --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/stdEq/__tests__/pstdEq.test.ts @@ -0,0 +1,40 @@ +import { Machine } from "@harmoniclabs/plutus-machine"; +import { int, list } from "../../../../type_system"; +import { pstdEq } from "../pstdEq"; +import { pList } from "../../list"; +import { pInt } from "../../int"; +import { pBool } from "../../bool/pBool"; + +describe("pstdEq", () => { + + test("list(int)", () => { + + const eq = pstdEq( list( int ) ); + + // console.log( prettyUPLC( eq.toUPLC() ) ) + + const pListInt = ( ns: number[] ) => pList( int )( ns.map( pInt ) ); + + const eqTestExpected = Machine.evalSimple( + pBool( true ) + ); + + function eqTest( ns: number[] ) + { + const res = Machine.evalSimple( + eq + .$( pListInt(ns) ) + .$( pListInt(ns) ) + ); + + expect( res ).toEqual( eqTestExpected ) + } + + eqTest([]); + eqTest([1]); + eqTest([1,2,3,4]); + eqTest([ 0, 1, -1, 2, -2 ]); + + }); + +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/stdEq/index.ts b/packages/onchain/src/pluts/lib/std/stdEq/index.ts new file mode 100644 index 00000000..8a081f5b --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/stdEq/index.ts @@ -0,0 +1 @@ +export * from "./pstdEq"; \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/stdEq/pstdEq.ts b/packages/onchain/src/pluts/lib/std/stdEq/pstdEq.ts new file mode 100644 index 00000000..b7967635 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/stdEq/pstdEq.ts @@ -0,0 +1,28 @@ +import type { PBool } from "../../../PTypes/PBool"; +import type { PFn } from "../../../PTypes/PFn/PFn"; +import { int, typeExtends, type TermType, type ToPType, bs, str, unit, bool, data, list, tyVar, termTypeToString, pair } from "../../../type_system"; +import { getElemsT } from "../../../type_system/tyArgs"; +import { peqBool } from "../../builtins/bool"; +import { peqBs } from "../../builtins/bs"; +import { peqData } from "../../builtins/data"; +import { peqInt } from "../../builtins/int/intBinOpToBool"; +import { peqStr } from "../../builtins/str"; +import type { UtilityTermOf } from "../UtilityTerms/addUtilityForType"; +import { peqList } from "../list"; +import { peqPair } from "../pair/peqPair"; +import { peqUnit } from "../unit/peqUnit"; + +export function pstdEq( t: T ): UtilityTermOf, ToPType ], PBool>> +{ + if( typeExtends( t, int ) ) return peqInt as any; + if( typeExtends( t, bs ) ) return peqBs as any; + if( typeExtends( t, str ) ) return peqStr as any; + if( typeExtends( t, unit ) ) return peqUnit as any; + if( typeExtends( t, bool ) ) return peqBool as any; + if( typeExtends( t, data ) ) return peqData as any; + + if( typeExtends( t, list( tyVar() ) ) ) return peqList( getElemsT( t ) ) as any; + if( typeExtends( t, pair( tyVar(), tyVar() ) ) ) return peqPair( t as any ) as any; + + throw new Error("missing standard equality for type: " + termTypeToString( t )); +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/unit/index.ts b/packages/onchain/src/pluts/lib/std/unit/index.ts index 90a7b4d9..a66eeaf4 100644 --- a/packages/onchain/src/pluts/lib/std/unit/index.ts +++ b/packages/onchain/src/pluts/lib/std/unit/index.ts @@ -1,3 +1,4 @@ export * from "./pmakeUnit"; export * from "./pmakeUnitData"; -export * from "./pUnitFromData"; \ No newline at end of file +export * from "./pUnitFromData"; +export * from "./peqUnit"; \ No newline at end of file diff --git a/packages/onchain/src/pluts/lib/std/unit/peqUnit.ts b/packages/onchain/src/pluts/lib/std/unit/peqUnit.ts new file mode 100644 index 00000000..2f95ea31 --- /dev/null +++ b/packages/onchain/src/pluts/lib/std/unit/peqUnit.ts @@ -0,0 +1,14 @@ +import { bool, unit } from "../../../type_system"; +import { pfn } from "../../pfn"; +import { phoist } from "../../phoist"; +import { pBool } from "../bool"; + +/** + we could use `mkNilData` builtin to check if we actually have units + but we likely dont want the execution to fail by calling `peqUnit` + so this is just a dummy function that always retuns true. +*/ +export const peqUnit = phoist( + pfn([ unit, unit ], bool ) + (( _u1, _u2 ) => pBool( true ) ) +) \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/cloneTermType.ts b/packages/onchain/src/pluts/type_system/cloneTermType.ts index f78098fe..9be59827 100644 --- a/packages/onchain/src/pluts/type_system/cloneTermType.ts +++ b/packages/onchain/src/pluts/type_system/cloneTermType.ts @@ -4,7 +4,7 @@ import { GenericTermType, PrimType, lam, struct, pair, list, delayed, asData, al export function cloneTermType( t: T ): T { if( t[ 0 ] === PrimType.Struct ) - return struct( cloneStructDef( t[1] ) ) as any; + return struct( cloneStructDef( t[1] ), t[2] ) as any; if( t[ 0 ] === PrimType.Lambda ) return lam( cloneTermType( t[1] ), cloneTermType( t[2] ) ) as any; @@ -22,7 +22,7 @@ export function cloneTermType( t: T ): T return asData( cloneTermType( t[1] ) ) as any; if( t[ 0 ] === PrimType.Alias ) - return alias( cloneTermType( t[1] ) ) as any; + return alias( cloneTermType( t[1] ), t[2] ) as any; return [ t[0] ] as any; } \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/kinds/__tests__/typeSys.isWellFormedType.test.ts b/packages/onchain/src/pluts/type_system/kinds/__tests__/typeSys.isWellFormedType.test.ts index d54cb57c..d122daeb 100644 --- a/packages/onchain/src/pluts/type_system/kinds/__tests__/typeSys.isWellFormedType.test.ts +++ b/packages/onchain/src/pluts/type_system/kinds/__tests__/typeSys.isWellFormedType.test.ts @@ -1,9 +1,12 @@ -import { TermType, bs, fn, int, lam } from "../../types" +import { TermType, alias, bool, bs, fn, int, lam } from "../../types" import { isWellFormedType, isWellFormedGenericType } from "../../kinds"; import { termTypeToString } from "../../utils"; +import { pBool } from "../../../lib/std/bool/pBool"; +import { pfn } from "../../../lib/pfn"; +const fakeTerm = pfn([ int ], bool )(( n ) => pBool( true )); -describe("isWellFormedGenericType", () => { +describe("isWellFormedType", () => { function yes( t: TermType ) { @@ -20,4 +23,9 @@ describe("isWellFormedGenericType", () => { yes( lam( bs, int ) ); yes( fn([ bs, int ], int) ); + + yes( alias( int ) ); + yes( alias( int, {} ) ); + yes( alias( int, { foo: fakeTerm } ) ); + }) \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/kinds/isTaggedAsAlias.ts b/packages/onchain/src/pluts/type_system/kinds/isTaggedAsAlias.ts index bd66fae1..7e505851 100644 --- a/packages/onchain/src/pluts/type_system/kinds/isTaggedAsAlias.ts +++ b/packages/onchain/src/pluts/type_system/kinds/isTaggedAsAlias.ts @@ -1,6 +1,6 @@ import { GenericTermType, PrimType, TermType } from "../types"; -export function isTaggedAsAlias( t: GenericTermType ): t is [ PrimType.Alias, TermType ] +export function isTaggedAsAlias( t: GenericTermType ): t is [ PrimType.Alias, TermType, {} ] { return ( Array.isArray( t ) && t.length >= 2 && diff --git a/packages/onchain/src/pluts/type_system/kinds/isWellFormedType.ts b/packages/onchain/src/pluts/type_system/kinds/isWellFormedType.ts index 1d08a627..18bfc352 100644 --- a/packages/onchain/src/pluts/type_system/kinds/isWellFormedType.ts +++ b/packages/onchain/src/pluts/type_system/kinds/isWellFormedType.ts @@ -2,7 +2,8 @@ import { isObject } from "@harmoniclabs/obj-utils"; import { isPrimTypeTag } from "./isPrimTypeTag"; import { isTaggedAsAlias } from "./isTaggedAsAlias"; import { isTypeParam } from "./isTypePAram"; -import { GenericStructDefinition, GenericTermType, PrimType, StructCtorDef, StructDefinition, StructT, TermType } from "../types"; +import { GenericStructDefinition, GenericTermType, type Methods, PrimType, StructCtorDef, StructDefinition, StructT, TermType } from "../types"; +import { termTypeToString } from "../utils"; function getIsStructDefWithTermTypeCheck( termTypeCheck: ( t: TermType ) => boolean ) @@ -54,7 +55,7 @@ export const isGenericStructDefinition = getIsStructDefWithTermTypeCheck( ) as ( def: object ) => def is GenericStructDefinition; -export function isStructType( t: GenericTermType ): t is StructT +export function isStructType( t: GenericTermType ): t is StructT { return ( Array.isArray( t ) && @@ -98,11 +99,11 @@ export function isWellFormedType( t: GenericTermType ): t is TermType t[0] === PrimType.List || // ?? t[0] === PrimType.Alias - ) return t.length === 2 && isWellFormedType( t[1] as any ); + ) return t.length >= 2 && isWellFormedType( t[1] as any ); if( t[0] === PrimType.Struct ) { - return t.length === 2 && isStructDefinition( t[1] ); + return t.length >= 2 && isStructDefinition( t[1] ); } if( @@ -119,7 +120,7 @@ export function isWellFormedType( t: GenericTermType ): t is TermType export function isWellFormedGenericType( t: GenericTermType ): boolean { - if( isTaggedAsAlias( t ) ) return isWellFormedGenericType( t[1] as any ); + if( isTaggedAsAlias( t ) ) return isWellFormedGenericType( t[1] ); if(!( Array.isArray( t ) && @@ -141,21 +142,36 @@ export function isWellFormedGenericType( t: GenericTermType ): boolean t[0] === PrimType.List || // ?? t[0] === PrimType.Alias - ) return t.length === 2 && isWellFormedGenericType( t[1] as any ); + ) return t.length >= 2 && isWellFormedGenericType( t[1] ); if( t[0] === PrimType.Struct ) { - return t.length === 2 && isGenericStructDefinition( t[1] ); + return t.length >= 2 && isGenericStructDefinition( t[1] ); } if( t[0] === PrimType.Lambda || t[0] === PrimType.Pair - ) return ( - t.length === 3 && - isWellFormedGenericType( t[1] as any ) && - isWellFormedGenericType( t[2] as any ) - ); + ) + { + const fst = isWellFormedGenericType( t[1] ); + const snd = isWellFormedGenericType( t[2] ); + + if( !fst ) + { + console.log( "fst", termTypeToString( t[1] ) ); + } + if( !snd ) + { + console.log( "snd", termTypeToString( t[2] ) ); + } + + return ( + t.length === 3 && + fst && + snd + ); + } return false; } diff --git a/packages/onchain/src/pluts/type_system/ts-pluts-conversion.ts b/packages/onchain/src/pluts/type_system/ts-pluts-conversion.ts index 3d181b7b..5f3524e9 100644 --- a/packages/onchain/src/pluts/type_system/ts-pluts-conversion.ts +++ b/packages/onchain/src/pluts/type_system/ts-pluts-conversion.ts @@ -1,29 +1,28 @@ import { PType } from "../PType"; import { PInt, PByteString, PString, PUnit, PBool, PList, PPair, PDelayed, PLam, PAlias, PStruct, PData, PAsData } from "../PTypes"; -import { AliasT, GenericTermType, PrimType, StructDefinition, TermType, data, fn } from "./types"; - +import { AliasT, GenericTermType, Methods, NonAliasTermType, PrimType, StructDefinition, TermType, data, fn } from "./types"; export type ToPType = +T extends [ PrimType.Alias, infer T extends NonAliasTermType, infer AMethods extends Methods ] ? PAlias, AMethods> : +T extends [ PrimType.Int ] ? PInt : +T extends [ PrimType.BS ] ? PByteString : +T extends [ PrimType.Str ] ? PString : +T extends [ PrimType.Unit ] ? PUnit : +T extends [ PrimType.Bool ] ? PBool : +T extends [ PrimType.Data ] ? PData : T extends [ PrimType.List, infer ListTyArg extends TermType ] ? PList> : T extends [ PrimType.Pair, infer FstTyArg extends TermType, infer SndTyArg extends TermType ] ? PPair, ToPType> : T extends [ PrimType.Delayed, infer TyArg extends TermType ] ? PDelayed> : T extends [ PrimType.Lambda, infer InputTyArg extends TermType, infer OutputTyArg extends TermType ] ? PLam, ToPType> : -T extends [ PrimType.Struct, infer SDef extends StructDefinition ] ? PStruct : -T extends [ PrimType.Alias, infer T extends TermType ] ? PAlias : +T extends [ PrimType.Struct, infer SDef extends StructDefinition, infer SMethods extends Methods ] ? PStruct : // asData elements should NOT be assignable to normal elements T extends [ PrimType.AsData, infer ExpectedT extends TermType ] ? PAsData> : -T extends [ PrimType.Int ] ? PInt : -T extends [ PrimType.BS ] ? PByteString : -T extends [ PrimType.Str ] ? PString : -T extends [ PrimType.Unit ] ? PUnit : -T extends [ PrimType.Bool ] ? PBool : -T extends [ PrimType.Data ] ? PData : T extends GenericTermType ? PType : // T extends FromPType ? PT : // !!! IMPORTANT !!! can only be present in one of the two types; breaks TypeScript LSP otherwise never; -export type FromPType | PStruct | PAlias> = +export type FromPType | PStruct | PAlias> = PT extends PInt ? [ PrimType.Int ] : PT extends PByteString ? [ PrimType.BS ] : PT extends PString ? [ PrimType.Str ] : @@ -37,8 +36,8 @@ PT extends PPair ? [ PrimType.Pair, FromPType, FromPType ] : PT extends PDelayed ? [ PrimType.Delayed, FromPType ] : PT extends PLam ? [ PrimType.Lambda, FromPType, FromPType ] : -PT extends PStruct ? [ PrimType.Struct, SDef ] : -PT extends PAlias ? AliasT : +PT extends PStruct ? [ PrimType.Struct, SDef, SMethods ] : +PT extends PAlias ? AliasT, AMethods> : PT extends ToPType ? T : // !!! IMPORTANT !!! can only be present in one of the two types; breaks TypeScript LSP otherwise PT extends PType ? GenericTermType : // PT extends ToPType ? T : diff --git a/packages/onchain/src/pluts/type_system/tyArgs/__tests__/clearAsData.test.ts b/packages/onchain/src/pluts/type_system/tyArgs/__tests__/clearAsData.test.ts new file mode 100644 index 00000000..6d0f02e5 --- /dev/null +++ b/packages/onchain/src/pluts/type_system/tyArgs/__tests__/clearAsData.test.ts @@ -0,0 +1,19 @@ +import { TermType, int, asData } from "../../types" +import { termTypeToString } from "../../utils" +import { clearAsData } from "../clearAsData" +import { unwrapAsData } from "../unwrapAsData" + +describe("clearAsData", () => { + + function testType( t: TermType, expected: TermType ): void + { + test( termTypeToString( t ), () => { + + expect( clearAsData( t ) ).toEqual( expected ); + expect( clearAsData( t )[0] ).toEqual( unwrapAsData( t )[0] ); + + }) + } + + testType( asData( int ), int ); +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/tyArgs/__tests__/getElemsT.test.ts b/packages/onchain/src/pluts/type_system/tyArgs/__tests__/getElemsT.test.ts new file mode 100644 index 00000000..59c0ff64 --- /dev/null +++ b/packages/onchain/src/pluts/type_system/tyArgs/__tests__/getElemsT.test.ts @@ -0,0 +1,30 @@ +import { TermType, alias, int, list, termTypeToString } from "../../.." +import { getElemsT } from "../getElemsT" + +describe("getElemsT", () => { + + describe("just list", () => { + + test("list(int) -> int", () => { + expect( getElemsT( list( int ) ) ).toEqual( int ); + }); + + test("list(list(int)) -> list(int)", () => { + expect( getElemsT( list( list( int ) ) ) ).toEqual( list( int ) ); + }); + + test("list(list(list(int))) -> list(list(int))", () => { + expect( getElemsT( list( list( list( int ) ) ) ) ).toEqual( list( list( int ) ) ); + }); + + }); + + describe("alias list", () => { + + test("alias(list(int)) -> int", () => { + expect( getElemsT( alias( list( int ) ) ) ).toEqual( int ); + }); + + }); + +}) \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/tyArgs/clearAsData.ts b/packages/onchain/src/pluts/type_system/tyArgs/clearAsData.ts new file mode 100644 index 00000000..65e2f7e4 --- /dev/null +++ b/packages/onchain/src/pluts/type_system/tyArgs/clearAsData.ts @@ -0,0 +1,47 @@ +import { PrimType, TermType, alias, asData, list, pair } from "../types"; +import { getElemsT } from "./getElemsT"; +import { getFstT } from "./getFstT"; +import { getSndT } from "./getSndT"; +import { unwrapAsData } from "./unwrapAsData"; + +/** + * undoes the `asData` call + */ +export function clearAsData( t: TermType ): TermType +{ + // invalid asData type but not worth to rise an error + if( + t[0] === PrimType.Lambda || + t[0] === PrimType.Delayed + ) return t; + + // already data + if( + t[0] === PrimType.Struct || + t[0] === PrimType.Data + ) return t; + + t = unwrapAsData( t ); + + if( t[0] === PrimType.Alias ) + { + t = alias( clearAsData( t[1] ), t[2] ); + } + else if( t[0] === PrimType.List ) + { + const listElemsT = getElemsT( t ); + + if( listElemsT[ 0 ] === PrimType.Pair ) + { + const fstT = getFstT( listElemsT ); + const sndT = getSndT( listElemsT ); + t = list( pair( clearAsData( fstT ), clearAsData( sndT ) ) ) + } + else + { + t = list( clearAsData( listElemsT ) ) + } + } + + return t; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/tyArgs/getDirectFstT.ts b/packages/onchain/src/pluts/type_system/tyArgs/getDirectFstT.ts new file mode 100644 index 00000000..b513c65d --- /dev/null +++ b/packages/onchain/src/pluts/type_system/tyArgs/getDirectFstT.ts @@ -0,0 +1,17 @@ +import { GenericTermType, PrimType, TermType } from "../types"; + +export function getDirectFstT( t: [ PrimType.Pair, T, GenericTermType ] ): T +export function getDirectFstT( t: TermType ): TermType +export function getDirectFstT( t: GenericTermType ): GenericTermType +export function getDirectFstT( t: GenericTermType ): GenericTermType +{ + // !!! IMPORTANT !!! + // **this unwrapping is assumed to happen here** by `typeExtends` + // if this ever changes please reflect the change to `typeExtends` too + // !!! IMPORTANT !!! + while( + t[0] !== PrimType.Pair + ) t = t[1] as any; + + return t[1] as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/tyArgs/getDirectSndT.ts b/packages/onchain/src/pluts/type_system/tyArgs/getDirectSndT.ts new file mode 100644 index 00000000..ca98b909 --- /dev/null +++ b/packages/onchain/src/pluts/type_system/tyArgs/getDirectSndT.ts @@ -0,0 +1,17 @@ +import { GenericTermType, PrimType, TermType } from "../types"; + +export function getDirectSndT( t: [ PrimType.Pair, GenericTermType, T ] ): T +export function getDirectSndT( t: TermType ): TermType +export function getDirectSndT( t: GenericTermType ): GenericTermType +export function getDirectSndT( t: GenericTermType ): GenericTermType +{ + // !!! IMPORTANT !!! + // **this unwrapping is assumed to happen here** by `typeExtends` + // if this ever changes please reflect the change to `typeExtends` too + // !!! IMPORTANT !!! + while( + t[0] !== PrimType.Pair + ) t = t[1] as any; + + return t[2] as any; +} \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/tyArgs/getElemsT.ts b/packages/onchain/src/pluts/type_system/tyArgs/getElemsT.ts index d895d2b6..1148de23 100644 --- a/packages/onchain/src/pluts/type_system/tyArgs/getElemsT.ts +++ b/packages/onchain/src/pluts/type_system/tyArgs/getElemsT.ts @@ -1,6 +1,6 @@ import { GenericTermType, PrimType, TermType } from "../types"; -export function getElemsT( t: [ PrimType.Alias, [ PrimType.List, T ] ] ): T +export function getElemsT( t: [ PrimType.Alias, [ PrimType.List, T ], any ] ): T export function getElemsT( t: [ PrimType.AsData, [ PrimType.List, T ] ] ): T export function getElemsT( t: [ PrimType.List, T ] ): T export function getElemsT( t: TermType ): TermType @@ -8,6 +8,7 @@ export function getElemsT( t: GenericTermType ): GenericTermType export function getElemsT( t: GenericTermType ): GenericTermType { // get to the first list type + // aka, skips `alias` `asData` etc. while( t[0] !== PrimType.List ) t = t[1] as any; return t[1] as any; diff --git a/packages/onchain/src/pluts/type_system/tyArgs/getFstT.ts b/packages/onchain/src/pluts/type_system/tyArgs/getFstT.ts index b7e8ba2c..2e3dedbc 100644 --- a/packages/onchain/src/pluts/type_system/tyArgs/getFstT.ts +++ b/packages/onchain/src/pluts/type_system/tyArgs/getFstT.ts @@ -1,19 +1,25 @@ import { GenericTermType, PrimType, TermType } from "../types"; -export function getFstT( t: [ PrimType.Alias, [ PrimType.Pair, T, GenericTermType ] ] ): T +export function getFstT( t: [ PrimType.Alias, [ PrimType.Pair, T, GenericTermType ], any ] ): T export function getFstT( t: [ PrimType.AsData, [ PrimType.Pair, T, GenericTermType ] ] ): T export function getFstT( t: [ PrimType.Pair, T, GenericTermType ] ): T export function getFstT( t: TermType ): TermType export function getFstT( t: GenericTermType ): GenericTermType export function getFstT( t: GenericTermType ): GenericTermType { - t = t[1] as any; - // this unwrapping is assumed in `typeExtends` + // !!! IMPORTANT !!! + // **this unwrapping is assumed to happen here** by `typeExtends` // if this ever changes please reflect the change to `typeExtends` too + // !!! IMPORTANT !!! while( t[0] === PrimType.AsData || t[0] === PrimType.Alias ) t = t[1]; + if( t[0] !== PrimType.Pair ) + throw new Error("getSndT used on non-pair type"); + + t = t[1] as any; + return t as any; } \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/tyArgs/getSndT.ts b/packages/onchain/src/pluts/type_system/tyArgs/getSndT.ts index 8de3bfdd..61aa7752 100644 --- a/packages/onchain/src/pluts/type_system/tyArgs/getSndT.ts +++ b/packages/onchain/src/pluts/type_system/tyArgs/getSndT.ts @@ -1,19 +1,24 @@ import { GenericTermType, PrimType, TermType } from "../types"; -export function getSndT( t: [ PrimType.Alias, [ PrimType.Pair, GenericTermType, T ] ] ): T +export function getSndT( t: [ PrimType.Alias, [ PrimType.Pair, GenericTermType, T ], any ] ): T export function getSndT( t: [ PrimType.AsData, [ PrimType.Pair, GenericTermType, T ] ] ): T export function getSndT( t: [ PrimType.Pair, GenericTermType, T ] ): T export function getSndT( t: TermType ): TermType export function getSndT( t: GenericTermType ): GenericTermType export function getSndT( t: GenericTermType ): GenericTermType -{ - t = t[2] as any; - // this unwrapping is assumed in `typeExtends` +{ + // !!! IMPORTANT !!! + // **this unwrapping is assumed to happen here** by `typeExtends` // if this ever changes please reflect the change to `typeExtends` too + // !!! IMPORTANT !!! while( t[0] === PrimType.AsData || t[0] === PrimType.Alias ) t = t[1]; + if( t[0] !== PrimType.Pair ) + throw new Error("getSndT used on non-pair type"); + + t = t[2]; return t; } \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/tyArgs/unwrapAlias.ts b/packages/onchain/src/pluts/type_system/tyArgs/unwrapAlias.ts index c95a43e8..bd140318 100644 --- a/packages/onchain/src/pluts/type_system/tyArgs/unwrapAlias.ts +++ b/packages/onchain/src/pluts/type_system/tyArgs/unwrapAlias.ts @@ -1,6 +1,6 @@ import { PrimType, TermType } from "../types"; -export function unwrapAlias( t: [ PrimType.Alias, T ] ): T +export function unwrapAlias( t: [ PrimType.Alias, T, any ] | T ): T { while( t[0] === PrimType.Alias ) t = t[1] as any; return t as any; diff --git a/packages/onchain/src/pluts/type_system/tyArgs/unwrapAsData.ts b/packages/onchain/src/pluts/type_system/tyArgs/unwrapAsData.ts index 950bec34..c72c0f68 100644 --- a/packages/onchain/src/pluts/type_system/tyArgs/unwrapAsData.ts +++ b/packages/onchain/src/pluts/type_system/tyArgs/unwrapAsData.ts @@ -1,6 +1,8 @@ import { PrimType, TermType } from "../types"; export function unwrapAsData( t: [ PrimType.AsData, T ] ): T +export function unwrapAsData( t: T ): T +export function unwrapAsData( t: TermType ): T { while( t[0] === PrimType.AsData ) t = t[1] as any; return t as any; diff --git a/packages/onchain/src/pluts/type_system/typeExtends.ts b/packages/onchain/src/pluts/type_system/typeExtends.ts index 71768325..660d0800 100644 --- a/packages/onchain/src/pluts/type_system/typeExtends.ts +++ b/packages/onchain/src/pluts/type_system/typeExtends.ts @@ -3,6 +3,7 @@ import { isGenericStructType, isStructType, isWellFormedGenericType, isWellForme import { getFstT, getSndT } from "./tyArgs"; import { unwrapAlias } from "./tyArgs/unwrapAlias"; import { GenericStructCtorDef, GenericStructDefinition, GenericTermType, PrimType, StructCtorDef, StructDefinition, TermType, data } from "./types"; +import { termTypeToString } from "./utils"; /** diff --git a/packages/onchain/src/pluts/type_system/types.ts b/packages/onchain/src/pluts/type_system/types.ts index 5c4e43ba..32935d0b 100644 --- a/packages/onchain/src/pluts/type_system/types.ts +++ b/packages/onchain/src/pluts/type_system/types.ts @@ -1,6 +1,9 @@ import { defineReadOnlyProperty } from "@harmoniclabs/obj-utils"; import { assert } from "../../utils/assert"; import { getElemsT } from "./tyArgs"; +import { PLam } from "../PTypes"; +import { PType } from "../PType"; +import { Term } from "../Term"; export const enum PrimType { Int = "int", @@ -26,15 +29,17 @@ type BaseT | PrimType.Bool | PrimType.Data +export type Methods = { [method: string]: Term> } + export type ListT = [ PrimType.List, T ]; export type DelayedT = [ PrimType.Delayed, T ]; export type PairT = [ PrimType.Pair , FstT, SndT ]; -export type StructT = [ PrimType.Struct, GSDef ]; +export type StructT = [ PrimType.Struct, GSDef, SMethods ]; -export type AliasT = T[0] extends PrimType.Alias ? T : [ PrimType.Alias, T ]; +export type AliasT = T[0] extends PrimType.Alias ? T : [ PrimType.Alias, T, AMethods ]; export type AsDataT = T[0] extends PrimType.AsData ? T : [ PrimType.AsData, T ]; @@ -57,7 +62,7 @@ type NonStructTag | PrimType.Pair | PrimType.Delayed | PrimType.Lambda - | PrimType.Alias + // | PrimType.Alias | PrimType.AsData; /* @@ -65,19 +70,22 @@ type NonStructTag export type TermType = readonly [ BaseT ] - | readonly [ PrimType.Struct, StructDefinition ] + | readonly [ PrimType.Struct, StructDefinition, Methods ] | readonly [ PrimType.List, TermType ] | readonly [ PrimType.Delayed, TermType ] | readonly [ PrimType.Pair , TermType, TermType ] | readonly [ PrimType.Lambda , TermType, TermType ] - | readonly [ PrimType.Alias, TermType ] + | readonly [ PrimType.Alias, TermType, Methods ] | readonly [ PrimType.AsData, TermType ] //*/ //* export type TermType - = [ NonStructTag, ...TermType[] ] | [ PrimType.Struct, StructDefinition ] + = [ NonStructTag, ...TermType[] ] | [ PrimType.Struct, StructDefinition, Methods ] | [ PrimType.Alias, TermType, Methods ] //*/ +export type NonAliasTermType + = [ NonStructTag, ...TermType[] ] | [ PrimType.Struct, StructDefinition, Methods ] + export type StructCtorDef = { [field: string | number]: TermType } @@ -164,19 +172,21 @@ export const delayed = ( toDelay: T ): [ PrimType.Delayed, T ] => Object.freeze([ PrimType.Delayed, toDelay ]) as any; -export const struct = ( def: GSDef ): StructT => +export const struct = ( def: GSDef, methods?: SMethods ): StructT => Object.freeze([ PrimType.Struct, - Object.freeze( cloneStructDef( def ) ) + Object.freeze( cloneStructDef( def ) ), + Object.freeze( methods ?? {} ) ]) as any; export function alias>( toAlias: T ): T -export function alias( toAlias: T ): [ PrimType.Alias, T ] -export function alias( toAlias: T ): [ PrimType.Alias, T ] +export function alias( toAlias: T ): [ PrimType.Alias, T, {} ] +export function alias( toAlias: T, methods: AMethods ): [ PrimType.Alias, T, AMethods ] +export function alias( toAlias: T, methods?: AMethods ): [ PrimType.Alias, T, AMethods ] { if( toAlias[0] === PrimType.Alias ) return toAlias as any; - return Object.freeze([ PrimType.Alias, toAlias ]) as any; + return Object.freeze([ PrimType.Alias, toAlias, Object.freeze( methods ?? {} ) ]) as any; } export function asData( someT: [PrimType.Data] ): [ PrimType.Data ] @@ -201,10 +211,10 @@ export function asData( someT: T ): [ PrimType.AsData // if the type is an alias temporarely unwrap; // this to prevent blocking the mapping of `asData` - let wasAlias = false; + let exAliasMethods: Methods | undefined = undefined; if( someT[0] === PrimType.Alias ) { - wasAlias = true; + exAliasMethods = someT[2]; someT = someT[1] as any; } @@ -216,6 +226,14 @@ export function asData( someT: T ): [ PrimType.AsData { someT = list( pair( asData( elemsT[1] as any ), asData( elemsT[2] as any ) ) ) as any; } + else if( + elemsT[0] === PrimType.Alias && + elemsT[1][0] === PrimType.Pair + ) + { + someT = list( pair( asData( elemsT[1][1] ), asData( elemsT[1][2] ) ) ) as any; + someT = alias( someT, elemsT[2] ) as any; + } else { someT = list( asData( elemsT ) ) as any @@ -224,7 +242,7 @@ export function asData( someT: T ): [ PrimType.AsData // re-wrap in alias if it was infact an alias // before finally wrapping everything in `asData` - if( wasAlias ) someT = alias( someT ) as any; + if( typeof exAliasMethods !== "undefined" ) someT = alias( someT, exAliasMethods ) as any; return Object.freeze([ PrimType.AsData, someT ]) as any; } @@ -245,10 +263,11 @@ export const tyVar = ( ( description?: any ) => Object.freeze([ Symbol( des export type GenericTermType = TermType | [ TyParam ] -| [ PrimType.Struct, GenericStructDefinition ] -| [ PrimType.List, GenericTermType ] -| [ PrimType.Delayed, GenericTermType ] -| [ PrimType.Pair , GenericTermType, GenericTermType ] -| [ PrimType.Lambda , GenericTermType, GenericTermType ] -| [ PrimType.Alias, GenericTermType ] -| [ PrimType.AsData, GenericTermType ] \ No newline at end of file +| [ NonStructTag, ...GenericTermType[] ] +| [ PrimType.Struct, GenericStructDefinition, Methods ] +| [ PrimType.Alias, GenericTermType, Methods ] +// | [ PrimType.List, GenericTermType ] +// | [ PrimType.Delayed, GenericTermType ] +// | [ PrimType.Pair , GenericTermType, GenericTermType ] +// | [ PrimType.Lambda , GenericTermType, GenericTermType ] +// | [ PrimType.AsData, GenericTermType ] \ No newline at end of file diff --git a/packages/onchain/src/pluts/type_system/utils.ts b/packages/onchain/src/pluts/type_system/utils.ts index e5ca2234..9c7dde98 100644 --- a/packages/onchain/src/pluts/type_system/utils.ts +++ b/packages/onchain/src/pluts/type_system/utils.ts @@ -57,34 +57,43 @@ export function termTypeToString( t: GenericTermType ): string } if( isTaggedAsAlias( t ) ) { - return "alias(" + ( - termTypeToString( unwrapAlias( t as any ) ) - ) + ")"; + const aliased = termTypeToString( unwrapAlias( t as any ) ); + return "alias(" + aliased + ")"; } if( tag === PrimType.AsData ) { return "asData(" + ( - termTypeToString( t[1] as any ) + termTypeToString( t[1] ) ) + ")"; } if( tag === PrimType.List ) { return "list(" + ( - termTypeToString( t[1] as any ) + termTypeToString( t[1] ) ) + ")"; } if( tag === PrimType.Pair ) { return "pair(" + ( - termTypeToString( t[1] as any ) + termTypeToString( t[1] ) ) + "," + ( - termTypeToString( t[2] as any ) + termTypeToString( t[2] ) ) + ")"; } - if( typeof t[0] === "symbol" ) return "tyParam("+ ((t[0] as any).description ?? "") +")"; + if( tag === PrimType.Lambda ) + { + return termTypeToString( t[1] ) + " -> " + termTypeToString( t[2] ); + } + + if( typeof t[0] === "symbol" ) return "tyParam("+ ((t[0] ).description ?? "") +")"; + + if( !t.slice ) { + console.log( t ); + // return ""; + } const tyArgs = t.slice(1) as TermType[]; - return ( t[0] + (tyArgs.length > 0 ? ',': "") + tyArgs.map( termTypeToString ).toString() ); + return ( t[0] + (tyArgs.length > 0 ? ',': "") + tyArgs.map( t => termTypeToString( t ) ).toString() ); } export interface CtorDefJson { diff --git a/packages/onchain/src/utils/IsSingleKey.ts b/packages/onchain/src/utils/IsSingleKey.ts index 4f4f2a5c..f51db09a 100644 --- a/packages/onchain/src/utils/IsSingleKey.ts +++ b/packages/onchain/src/utils/IsSingleKey.ts @@ -1,6 +1,6 @@ type SingleKeyObj = { [Prop in K]: (Record & - // infer here is just binding + // `infer O` here is just binding Record, never>) extends infer O ? { [Q in keyof O]: O[Q] } : never diff --git a/packages/onchain/src/utils/test_utils.ts b/packages/onchain/src/utils/test_utils.ts index 219c34f4..519f27c2 100644 --- a/packages/onchain/src/utils/test_utils.ts +++ b/packages/onchain/src/utils/test_utils.ts @@ -3,7 +3,6 @@ import { pByteString, PValue, pList, - PValueEntryT, toData, PTxOutRef, PTxId, @@ -12,7 +11,6 @@ import { pPair, PCurrencySymbol, list, - PAssetsEntryT, PTokenName, int, pInt, @@ -39,10 +37,12 @@ import { PTxOut, addUtilityForType, V2, + PValueEntry, + PAssetsEntry } from "../pluts"; export const unitDatumHash = PDatumHash.from( pByteString("923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec") ); -export const emptyValue = PValue.from( pList( PValueEntryT )([]) as any ); +export const emptyValue = PValue.from( pList( PValueEntry.type )([]) as any ); export const emptyValueAsData = toData( PValue.type )( emptyValue ); @@ -58,19 +58,16 @@ export const validatorSpendingUtxo = PTxOutRef.PTxOutRef({ export const validatorSpendingUtxoAsData = toData( PTxOutRef.type )( validatorSpendingUtxo ); export const beef32 = PValue.from( - pList( PValueEntryT )([ - pPair( PCurrencySymbol.type, - list( PAssetsEntryT ) ) - ( + pList( PValueEntry.type )([ + PValueEntry.from([ PCurrencySymbol.from( pByteString("deadbeef") ), - pList( PAssetsEntryT )([ - pPair( PTokenName.type, int ) - ( + pList( PAssetsEntry.type )([ + PAssetsEntry.from([ PTokenName.from( pByteString("beef") ), pInt( 32 ) - ) + ]) ]) - ) + ]) ]) );