diff --git a/.github/workflows/comment.yml b/.github/workflows/comment.yml new file mode 100644 index 000000000..f70ba2a9f --- /dev/null +++ b/.github/workflows/comment.yml @@ -0,0 +1,23 @@ +name: Comment +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + +jobs: + build: + name: Telegram + runs-on: ubuntu-latest + if: ${{ github.event.issue.pull_request }} + steps: + - name: Send telegram message on push + uses: appleboy/telegram-action@v1.0.0 + with: + to: ${{ secrets.TELEGRAM_ISSUE_CHAT_ID }} + token: ${{ secrets.TELEGRAM_ISSUE_TOKEN }} + format: Markdown + # this might go wrong, because `title` and `body` can have arbitrary markdown + message: | + ✏️ [${{ github.event.comment.user.login }}](${{ github.event.comment.html_url }}): ${{ github.event.comment.body }} + πŸ”€ ${{ github.event.issue.title }} diff --git a/.github/workflows/pull.yml b/.github/workflows/pull.yml new file mode 100644 index 000000000..f42f44769 --- /dev/null +++ b/.github/workflows/pull.yml @@ -0,0 +1,23 @@ +name: Pull request +on: + pull_request: + types: [opened, reopened] + +jobs: + build: + name: Telegram + runs-on: ubuntu-latest + steps: + - name: Send telegram message on push + uses: appleboy/telegram-action@v1.0.0 + continue-on-error: true + with: + to: ${{ secrets.TELEGRAM_ISSUE_CHAT_ID }} + token: ${{ secrets.TELEGRAM_ISSUE_TOKEN }} + format: Markdown + # this might go wrong, because `title` can have arbitrary markdown + message: | + 🍰 [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }}) + 🌲 ${{ github.repository }} + πŸ‘€ ${{ github.actor }} + πŸ”€ ${{ github.event.pull_request.base.label }} <- ${{ github.event.pull_request.head.label }} diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 81733e3ed..9bdc67ca9 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -22,6 +22,29 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 + - name: Setup Node.js 18 for backwards compat tests + uses: actions/setup-node@v3 + with: + node-version: 18 + # without caching + + - name: Backwards compat tests + run: | + # Temporarily ignore engines + yarn config set ignore-engines true + # Install dependencies, gen and build the compiler + yarn install + yarn clean + yarn gen + yarn build + # Test some specific things for backwards compatibility. + # It's important to restrain from using too much of Node.js 22+ features + # until it goes into maintenance LTS state and majority of users catches up + yarn jest -t 'isSubsetOf' + # Clean-up + yarn cleanall + yarn config delete ignore-engines + - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index f463d8443..221a9b688 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,14 +13,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New CSpell dictionaries: TVM instructions and adjusted list of Fift words: PR [#881](https://github.com/tact-lang/tact/pull/881) - Docs: the `description` property to the frontmatter of the each page for better SEO: PR [#916](https://github.com/tact-lang/tact/pull/916) - Docs: Google Analytics tags per every page: PR [#921](https://github.com/tact-lang/tact/pull/921) -- Docs: Added Ston.fi cookbook: PR [#956](https://github.com/tact-lang/tact/pull/956) -- Docs: Added NFTs cookbook: PR [#958](https://github.com/tact-lang/tact/pull/958) +- Docs: added Ston.fi cookbook: PR [#956](https://github.com/tact-lang/tact/pull/956) +- Docs: added NFTs cookbook: PR [#958](https://github.com/tact-lang/tact/pull/958) - Ability to specify a compile-time method ID expression for getters: PR [#922](https://github.com/tact-lang/tact/pull/922) and PR [#932](https://github.com/tact-lang/tact/pull/932) -- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856) +- Destructuring of structs and messages: PR [#856](https://github.com/tact-lang/tact/pull/856), PR [#964](https://github.com/tact-lang/tact/pull/964), PR [#969](https://github.com/tact-lang/tact/pull/969) - Docs: automatic links to Web IDE from all code blocks: PR [#994](https://github.com/tact-lang/tact/pull/994) - The `SendDefaultMode` send mode constant to the standard library: PR [#1010](https://github.com/tact-lang/tact/pull/1010) - Docs: initial semi-automated Chinese translation of the documentation: PR [#942](https://github.com/tact-lang/tact/pull/942) - The `replace` and `replaceGet` methods for the `Map` type: PR [#941](https://github.com/tact-lang/tact/pull/941) +- Utility for logging errors in code that was supposed to be unreachable: PR [#991](https://github.com/tact-lang/tact/pull/991) +- Docs: `preloadRef` method for the `Slice` type: PR [#1044](https://github.com/tact-lang/tact/pull/1044) +- Docs: added DeDust cookbook: PR [#954](https://github.com/tact-lang/tact/pull/954) ### Changed @@ -28,15 +31,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Optional types for `self` argument in `extends mutates` functions are now allowed: PR [#854](https://github.com/tact-lang/tact/pull/854) - Docs: complete overhaul of the exit codes page: PR [#978](https://github.com/tact-lang/tact/pull/978) - Docs: enhanced Jettons Cookbook page: PR [#944](https://github.com/tact-lang/tact/pull/944) +- Error codes in the report are now formatted as a list: PR [#1051](https://github.com/tact-lang/tact/pull/1051) ### Fixed -- Collisions in getter method ids are now handled and reported properly: PR [#875](https://github.com/tact-lang/tact/pull/875) +- Collisions in getter method ids are now handled and reported properly: PR [#875](https://github.com/tact-lang/tact/pull/875), PR [#1052](https://github.com/tact-lang/tact/pull/1052) - Docs: layout of tables, syntax highlighting, Chinese translations of sidebar separators: PR [#916](https://github.com/tact-lang/tact/pull/916) - Non-null struct fields after null ones are treated correctly in Sandbox tests after updating `@ton/core` to 0.59.0: PR [#933](https://github.com/tact-lang/tact/pull/933) - Prevent inline code snippets from changing their background color: PR [#935](https://github.com/tact-lang/tact/pull/935) - Docs: correctly handle next and previous page links at the bottom of the pages when there's a separator item in the sidebar: PR [#949](https://github.com/tact-lang/tact/pull/949) - Docs: compilation of examples in `data-structures.mdx` and across Cookbook: PR [#917](https://github.com/tact-lang/tact/pull/917) +- `as coins` map value serialization type is now handled correctly: PR [#987](https://github.com/tact-lang/tact/pull/987) +- Type checking for `foreach` loops in trait methods: PR [#1017](https://github.com/tact-lang/tact/pull/1017) +- The `sha256()` function no longer throws on statically known strings of any length: PR [#907](https://github.com/tact-lang/tact/pull/907) ### Release contributors diff --git a/cspell.json b/cspell.json index 69c9a2136..45bdb11fa 100644 --- a/cspell.json +++ b/cspell.json @@ -115,6 +115,7 @@ "uintptr", "uninit", "unixfs", + "varuint", "workchain", "xffff", "ΠΏΡ€ΠΈΠ²Π΅Ρ‚" diff --git a/docs/links-to-web-ide.js b/docs/links-to-web-ide.js index 4903ff7e6..1e5794995 100644 --- a/docs/links-to-web-ide.js +++ b/docs/links-to-web-ide.js @@ -75,7 +75,7 @@ export default function remarkLinksToWebIDE() { // Constructing opening tag [ // Open the tag - '

+ +The destructuring assignment is a concise way to unpack [Structs][s] and [Messages][m] into distinct variables. It mirrors the [instantiation syntax](/book/expressions#instantiation), but instead of creating a new [Struct][s] or [Message][m] it binds every field or some of the fields to their respective variables. + +The syntax is derived from the [`let` statement](#let), and instead of specifying the variable name directly it involves specifying the structure type on the left side of the [assignment operator `={:tact}`](/book/operators#assignment), which corresponds to the structure type of the value on the right side. + +```tact {9} +// Definition of Example +struct Example { number: Int } + +// An arbitrary helper function +fun get42(): Example { return Example { number: 42 } } + +fun basic() { + // Basic syntax of destructuring assignment (to the left of "="): + let Example { number } = get42(); + // ------- ------ ------- + // ↑ ↑ ↑ + // | | gives the Example Struct + // | definition of "number" variable, derived + // | from the field "number" in Example Struct + // target structure type "Example" + // to destructure fields from + + // Same as above, but with an instantiation + // to showcase how destructuring syntax mirrors it: + let Example { number } = Example { number: 42 }; + // ---------------------- + // ↑ + // instantiation of Example Struct + + // Above examples of syntax are roughly equivalent + // to the following series of statements: + let example = Example { number: 42 }; + let number = example.number; +} +``` + +Just like in [instantiation](/book/expressions#instantiation), the trailing comma is allowed. + +```tact +struct Example { number: Int } + +fun trailblazing() { + let Example { + number, // trailing comma inside variable list + } = Example { + number: 42, // trailing comma inside field list + }; +} +``` + +:::note + + [Augmented assignment operators](/book/operators#augmented-assignment) do not make sense for such assignments and will therefore be reported as parsing errors: + + ```tact + struct Example { number: Int } + fun get42(): Example { return Example { number: 42 } } + + fun basic() { + let Example { number } += get42(); + // ^ this will result in the parse error: + // expected "=" + } + ``` + +::: + +To create a binding under a different variable name, specify it after the semicolon `:{:tact}`. + +```tact +// Similar definition, but this time field is called "field", not "number" +struct Example { field: Int } + +fun naming(s: Example) { + let Example { field: varFromField } = s; + // ------------ ↑ + // ↑ | + // | instance of Example Struct, received + // | as a parameter of the function "naming" + // definition of "varFromField" variable, derived + // from the field "field" in Example Struct +} +``` + +Note, that the order of bindings doesn't matter β€” all the fields retain their values and types under their names no matter the order in which they stand in their definition in the respective [Struct][s] or [Message][m]. + +```tact +// "first" goes first, then goes "second" +struct Two { first: Int; second: String } + +fun order(s: Two) { + let Two { second, first } = s; + // ------ ----- + // ↑ ↑ + // | this variable will be of type Int, + // | same as the "first" field on Struct Two + // this variable will be of type String, + // same as the "second" field in Struct Two +} +``` + +Destructuring assignment is exhaustive and requires specifying all the fields as variables. To deliberately ignore some of the fields, use an underscore `_{:tact}`, which discards the considered field's value. Note, that such wildcard variable name `_{:tact}` cannot be accessed: + +```tact +// "first" goes first, then goes "second" +struct Two { first: Int; second: String } + +fun discard(s: Two) { + let Two { second: _, first } = s; + // --- + // ↑ + // discards the "second" field, only taking the "first" +} +``` + +To completely ignore the rest of the fields, use `..` at the end of the list: + +```tact +struct Many { one: Int; two: Int; three: Int; fans: Int } + +fun ignore(s: Many) { + let Many { fans, .. } = s; + // -- + // ↑ + // ignores all the unspecified fields, + // defining only "fans" +} +``` + +:::caution + + At the moment, destructuring of nested [Structs][s] or [Messages][m] isn't allowed. That is, the following won't work: + + ```tact + struct First { nested: Second } + struct Second { field: Int } + + fun example() { + let prep = First { nested: Second { field: 42 } }; + let First { nested: { field: thing } } = prep; + // ^ this will result in the parse error: + // expected "_", "A".."Z", or "a".."z" + } + ``` + +::: + ## Branches Control the flow of the code. @@ -415,3 +568,5 @@ foreach (_, _ in quartiles) { ::: [int]: /book/integers +[s]: /book/structs-and-messages#structs +[m]: /book/structs-and-messages#messages diff --git a/docs/src/content/docs/cookbook/dexes/dedust.mdx b/docs/src/content/docs/cookbook/dexes/dedust.mdx index 9c95527f3..b55d6739a 100644 --- a/docs/src/content/docs/cookbook/dexes/dedust.mdx +++ b/docs/src/content/docs/cookbook/dexes/dedust.mdx @@ -7,8 +7,438 @@ sidebar: [DeDust](https://dedust.io) is a decentralized exchange (DEX) and automated market maker (AMM) built natively on [TON Blockchain](https://ton.org) and [DeDust Protocol 2.0](https://docs.dedust.io/reference/tlb-schemes). DeDust is designed with a meticulous attention to user experience (UX), gas efficiency, and extensibility. -:::danger[Not implemented] +Before going further, familiarize yourself with the following: - This page is a stub until it gets new content in [#146](https://github.com/tact-lang/tact-docs/issues/146). +* [Receiving messages](/book/receive/) +* [Sending messages](/book/send/) +* [Fungible Tokens (Jettons)](/cookbook/jettons/) +* [DeDust Docs: Concepts](https://docs.dedust.io/docs/concepts) + +## Swaps + +Read more about swaps in the [DeDust documentation](https://docs.dedust.io/docs/swaps). + +:::caution + + It's important to ensure that contracts are deployed. Sending funds to an inactive contract could result in irretrievable loss. + +::: + +All kinds of swaps use `SwapStep{:tact}` and `SwapParams{:tact}` structures: + +```tact +/// https://docs.dedust.io/reference/tlb-schemes#swapstep +struct SwapStep { + // The pool that will do the swapping, i.e. pairs like TON/USDT or USDT/DUST + poolAddress: Address; + + // A kind of swap to make, can only be 0 as of now + kind: Int as uint1 = 0; + + // Minimum output of the swap + // If the actual value is less than specified, the swap will be rejected + limit: Int as coins = 0; + + // Reference to the next step, which can be used for multi-hop swaps + // The type here is actually `SwapStep?`, + // but specifying recursive types isn't allowed in Tact yet + nextStep: Cell?; +} + +/// https://docs.dedust.io/reference/tlb-schemes#swapparams +struct SwapParams { + // Specifies a deadline for the swap to reject the swap coming to the pool late + // Accepts the number of seconds passed since the UNIX Epoch + // Defaults to 0, which removes the deadline + deadline: Int as uint32 = 0; + + // Specifies an address where funds will be sent after the swap + // Defaults to `null`, which makes the swap use the sender's address + recipientAddress: Address? = null; + + // Referral address, required for the referral program of DeDust + // Defaults to `null` + referralAddress: Address? = null; + + // Custom payload that will be attached to the fund transfer upon a successful swap + // Defaults to `null` + fulfillPayload: Cell? = null; + + // Custom payload that will be attached to the fund transfer upon a rejected swap + // Defaults to `null` + rejectPayload: Cell? = null; +} +``` + +### Swap Toncoin for any Jetton + +:::note +The guides below use the [Jetton vault](https://docs.dedust.io/docs/concepts#vault). To obtain its address for your Jetton, refer to [this guide](https://docs.dedust.io/docs/swaps#step-1-find-the-vault-scale). +::: + +```tact +/// https://docs.dedust.io/reference/tlb-schemes#message-swap +message(0xea06185d) NativeSwap { + // Unique identifier used to trace transactions across multiple contracts + // Defaults to 0, which means we don't mark messages to trace their chains + queryId: Int as uint64 = 0; + + // Toncoin amount for the swap + amount: Int as coins; + + // Inlined fields of SwapStep Struct + poolAddress: Address; + kind: Int as uint1 = 0; + limit: Int as coins = 0; + nextStep: SwapStep? = null; + + // Set of parameters relevant for the whole swap + swapParams: SwapParams; +} + +// Let's say `swapAmount` is `ton("0.1")`, which is 10000000 nanoToncoins +fun swapToncoinForUSDT(swapAmount: Int) { + send(SendParameters{ + // Address of TON vault to send the message to + to: address("EQDa4VOnTYlLvDJ0gZjNYm5PXfSmmtL6Vs6A_CZEtXCNICq_"), + // Amount to swap plus a trade fee + value: swapAmount + ton("0.2"), + body: NativeSwap{ + amount: swapAmount, + // Address of the swap pool, which is the TON/USDT pair in this case + poolAddress: address("EQA-X_yo3fzzbDbJ_0bzFWKqtRuZFIRa1sJsveZJ1YpViO3r"), + // Set of parameters relevant for the whole swap + swapParams: SwapParams{}, // use defaults + }.toCell(), + }); +} + +// +// Helper Structs described earlier on this page +// + +struct SwapStep { + poolAddress: Address; + kind: Int as uint1 = 0; + limit: Int as coins = 0; + nextStep: Cell?; +} + +struct SwapParams { + deadline: Int as uint32 = 0; + recipientAddress: Address? = null; + referralAddress: Address? = null; + fulfillPayload: Cell? = null; + rejectPayload: Cell? = null; +} +``` + +### Swap a Jetton for another Jetton or Toncoin + +```tact +/// https://docs.dedust.io/reference/tlb-schemes#message-swap-1 +message(0xe3a0d482) JettonSwapPayload { + // Inlined fields of SwapStep Struct + poolAddress: Address; + kind: Int as uint1 = 0; + limit: Int as coins = 0; + nextStep: SwapStep? = null; + + // Set of parameters relevant for the whole swap + swapParams: SwapParams; +} + +/// NOTE: To calculate and provide Jetton wallet address for the target user, +/// make sure to check links after this code snippet +fun swapJetton(targetJettonWalletAddress: Address) { + send(SendParameters{ + to: targetJettonWalletAddress, + value: ton("0.3"), + body: JettonTransfer{ + // Unique identifier used to trace transactions across multiple contracts + // Set to 0, which means we don't mark messages to trace their chains + queryId: 0, + // Jetton amount for the swap + amount: 10, // NOTE: change to yours + // Address of the Jetton vault to the send message to + destination: address("EQAYqo4u7VF0fa4DPAebk4g9lBytj2VFny7pzXR0trjtXQaO"), + // Where to return the exceeding funds + responseDestination: myAddress(), + forwardTonAmount: ton("0.25"), + forwardPayload: JettonSwapPayload{ + // Address of the swap pool, which is the TON/USDT pair in this case + poolAddress: address("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs"), + // Set of parameters relevant for the whole swap + swapParams: SwapParams{}, // use defaults + }.toCell(), + }.toCell(), + }); +} + +// +// Helper Structs described earlier on this page +// + +struct SwapStep { + poolAddress: Address; + kind: Int as uint1 = 0; + limit: Int as coins = 0; + nextStep: Cell?; +} + +struct SwapParams { + deadline: Int as uint32 = 0; + recipientAddress: Address? = null; + referralAddress: Address? = null; + fulfillPayload: Cell? = null; + rejectPayload: Cell? = null; +} + +// +// Messages from the Jetton standard +// + +message(0xf8a7ea5) JettonTransfer { + queryId: Int as uint64; + amount: Int as coins; + destination: Address; + responseDestination: Address; + customPayload: Cell? = null; + forwardTonAmount: Int as coins; + forwardPayload: Cell?; // slightly adjusted +} +``` + +:::note[Useful links:] + + [Retrieving Jetton wallet address in TON Docs](https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user)\ + [How to calculate user's Jetton wallet address (offline)?](https://docs.ton.org/v3/guidelines/dapps/cookbook#how-to-calculate-users-jetton-wallet-address-offline) + +::: + +## Liquidity Provisioning + +To provide liquidity to a particular DeDust pool, you must provide both assets. The pool will then issue special _LP tokens_ to the depositor's address. + +Read more about liquidity provisioning in the [DeDust documentation](https://docs.dedust.io/docs/liquidity-provisioning). + +```tact +import "@stdlib/deploy"; + +/// https://docs.dedust.io/reference/tlb-schemes#message-deposit_liquidity-1 +message(0x40e108d6) JettonDepositLiquidity { + // Pool type: 0 for volatile, 1 for stable + // Volatile pool is based on the "Constant Product" formula + // Stable-swap pool is optimized for assets of near-equal value, + // e.g. USDT/USDC, TON/stTON, etc. + poolType: Int as uint1; + + // Provided assets + asset0: Asset; + asset1: Asset; + + // Minimal amount of LP tokens to be received + // If there's less liquidity provided, the provisioning will be rejected + // Defaults to 0, makes this value ignored + minimalLpAmount: Int as coins = 0; + + // Target amount of the first asset + targetBalances0: Int as coins; + + // Target amount of the second asset + targetBalances1: Int as coins; + + // Custom payload attached to the transaction if the provisioning is successful + // Defaults to `null`, which means no payload + fulfillPayload: Cell? = null; + + // Custom payload attached to the transaction if the provisioning is rejected + // Defaults to `null`, which means no payload + rejectPayload: Cell? = null; +} + +/// https://docs.dedust.io/reference/tlb-schemes#message-deposit_liquidity +message(0xd55e4686) NativeDepositLiquidity { + // Unique identifier used to trace transactions across multiple contracts + // Defaults to 0, which means we don't mark messages to trace their chains + queryId: Int as uint64 = 0; + + // Toncoin amount for the deposit + amount: Int as coins; + + // Inlined fields of JettonDepositLiquidity Message without the opcode prefix + poolType: Int as uint1; + asset0: Asset; + asset1: Asset; + minimalLpAmount: Int as coins = 0; + targetBalances0: Int as coins; + targetBalances1: Int as coins; + fulfillPayload: Cell? = null; + rejectPayload: Cell? = null; +} + +/// https://docs.dedust.io/reference/tlb-schemes#asset +struct Asset { + // Specify 0 for native (TON) and omit all following fields + // Specify 1 for Jetton and then you must set non-null values for the following fields + type: Int as uint4; + + workchain: Int as uint8 = 0; // Both this zeroes will be removed during .build() function. Only type will remain. + address: Int as uint256 = 0; +} + +const PoolTypeVolatile: Int = 0; +const PoolTypeStable: Int = 1; + +const AssetTypeNative: Int = 0b0000; +const AssetTypeJetton: Int = 0b0001; + +const JettonProvideLpGas: Int = ton("0.5"); +const JettonProvideLpGasFwd: Int = ton("0.4"); +const TonProvideLpGas: Int = ton("0.15"); + +// This example directly uses the provided `myJettonWalletAddress` +// In real-world scenarios, it's more reliable to calculate this address on-chain or save it during initialization to prevent any issues +fun provideLiquidity(myJettonWalletAddress: Address) { + let jettonMasterRaw = parseStdAddress( + address("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs") + .asSlice() + ); + + // Step 1. Prepare input + let jettonAmount = ton("1"); + let tonAmount = ton("1"); + + let asset0 = Asset{ + type: AssetTypeNative, + }; + let asset1 = Asset{ + type: AssetTypeJetton, + workchain: jettonMasterRaw.workchain, + address: jettonMasterRaw.address, + }; + + // Step 2. Deposit Jetton to Vault + let jettonDepositBody = JettonDepositLiquidity{ + poolType: PoolTypeVolatile, + asset0, + asset1, + targetBalances0: tonAmount, + targetBalances1: jettonAmount, + }.build(); // notice the .build() and not .toCell(), + // since we want some custom serialization logic! + + send(SendParameters{ + to: myJettonWalletAddress, + value: JettonProvideLpGas, + body: JettonTransfer{ + queryId: 42, + amount: jettonAmount, + // Jetton Vault + destination: address("EQAYqo4u7VF0fa4DPAebk4g9lBytj2VFny7pzXR0trjtXQaO"), + responseDestination: myAddress(), + forwardTonAmount: JettonProvideLpGasFwd, + forwardPayload: jettonDepositBody, + }.toCell() + }); + + // Step 3. Deposit TON to Vault + let nativeDepositBody = NativeDepositLiquidity{ + queryId: 42, + amount: tonAmount, + poolType: PoolTypeVolatile, + asset0, + asset1, + targetBalances0: tonAmount, + targetBalances1: jettonAmount, + }.build(); // notice the .build() and not .toCell(), + // since we want some custom serialization logic! + + send(SendParameters{ + to: address("EQDa4VOnTYlLvDJ0gZjNYm5PXfSmmtL6Vs6A_CZEtXCNICq_"), + value: tonAmount + TonProvideLpGas, + body: nativeDepositBody, + }); +} + +// +// Helper extension functions to build respective Structs and Messages +// + +extends fun build(self: Asset): Cell { + let assetBuilder = beginCell() + .storeUint(self.type, 4); + + if (self.type == AssetTypeNative) { + return assetBuilder.endCell(); + } + + if (self.type == AssetTypeJetton) { + return assetBuilder + .storeUint(self.workchain, 8) + .storeUint(self.address, 256) + .endCell(); + } + + // Unknown asset type + return beginCell().endCell(); +} + +extends fun build(self: JettonDepositLiquidity): Cell { + return beginCell() + .storeUint(0x40e108d6, 32) + .storeUint(self.poolType, 1) + .storeSlice(self.asset0.build().asSlice()) + .storeSlice(self.asset1.build().asSlice()) + .storeCoins(self.minimalLpAmount) + .storeCoins(self.targetBalances0) + .storeCoins(self.targetBalances1) + .storeMaybeRef(self.fulfillPayload) + .storeMaybeRef(self.rejectPayload) + .endCell(); +} + +extends fun build(self: NativeDepositLiquidity): Cell { + return beginCell() + .storeUint(0xd55e4686, 32) + .storeUint(self.queryId, 64) + .storeCoins(self.amount) + .storeUint(self.poolType, 1) + .storeSlice(self.asset0.build().asSlice()) + .storeSlice(self.asset1.build().asSlice()) + .storeRef( + beginCell() + .storeCoins(self.minimalLpAmount) + .storeCoins(self.targetBalances0) + .storeCoins(self.targetBalances1) + .endCell() + ) + .storeMaybeRef(self.fulfillPayload) + .storeMaybeRef(self.rejectPayload) + .endCell(); +} + +// +// Messages from the Jetton standard +// + +message(0xf8a7ea5) JettonTransfer { + queryId: Int as uint64; + amount: Int as coins; + destination: Address; + responseDestination: Address?; + customPayload: Cell? = null; + forwardTonAmount: Int as coins; + forwardPayload: Cell?; // slightly adjusted +} +``` + +### Withdraw liquidity + +To withdraw liquidity, burning LP tokens is required. You can refer to examples of Jetton burning in the [respective section of Jettons Cookbook page](/cookbook/jettons#burning-jetton). However, more Toncoins should be added than for the normal burn, since adding too few may result in LP tokens being burned, but no (or only partial) liquidity being sent from the pool. Therefore, consider attaching at least $0.5$ Toncoin β€” excess amount will be returned. + +:::tip[Hey there!] + +Didn't find your favorite example of DeDust interaction? Have cool implementations in mind? [Contributions are welcome!](https://github.com/tact-lang/tact/issues) ::: diff --git a/docs/src/content/docs/ref/core-cells.mdx b/docs/src/content/docs/ref/core-cells.mdx index f9bd67e71..f29d38650 100644 --- a/docs/src/content/docs/ref/core-cells.mdx +++ b/docs/src/content/docs/ref/core-cells.mdx @@ -634,15 +634,45 @@ Attempts to load more data than [`Slice{:tact}`][slice] contains throw an except Usage examples: ```tact -let s: Slice = beginCell().storeRef(emptyCell()).asSlice(); -let fizz: Cell = s.loadRef(); +let s1: Slice = beginCell().storeRef(emptyCell()).asSlice(); +let fizz: Cell = s1.loadRef(); -let s: Slice = beginCell() +let s2: Slice = beginCell() .storeRef(emptyCell()) + .storeRef(s1.asCell()) + .asSlice(); +let ref1: Cell = s2.loadRef(); +let ref2: Cell = s2.loadRef(); +ref1 == ref2; // false +``` + +## Slice.preloadRef + +```tact +extends fun preloadRef(self: Slice): Cell; +``` + +Extension function for the [`Slice{:tact}`][slice]. + +Preloads the next reference from the [`Slice{:tact}`][slice] as a [`Cell{:tact}`][cell]. Doesn't modify the original [`Slice{:tact}`][slice]. + +Attempts to preload such reference [`Cell{:tact}`][cell] when [`Slice{:tact}`][slice] doesn't contain it throw an exception with [exit code 8](/book/exit-codes#8): `Cell overflow`. + +Attempts to preload more data than [`Slice{:tact}`][slice] contains throw an exception with [exit code 9](/book/exit-codes#9): `Cell underflow`. + +Usage examples: + +```tact +let s1: Slice = beginCell().storeRef(emptyCell()).asSlice(); +let fizz: Cell = s1.preloadRef(); // didn't modify s1 + +let s2: Slice = beginCell() .storeRef(emptyCell()) + .storeRef(s1.asCell()) .asSlice(); -let ref1: Cell = s.loadRef(); -let ref2: Cell = s.loadRef(); +let ref1: Cell = s2.preloadRef(); +let ref2: Cell = s2.preloadRef(); +ref1 == ref2; // true ``` ## Slice.refs diff --git a/docs/src/content/docs/ref/core-math.mdx b/docs/src/content/docs/ref/core-math.mdx index 403eefd5c..e4ccb1f81 100644 --- a/docs/src/content/docs/ref/core-math.mdx +++ b/docs/src/content/docs/ref/core-math.mdx @@ -257,11 +257,17 @@ fun sha256(data: Slice): Int; fun sha256(data: String): Int; ``` -Computes and returns the [SHA-256][sha-2] hash as an $256$-bit unsigned [`Int{:tact}`][int] from a passed [`Slice{:tact}`][slice] or [`String{:tact}`][p] `data`. +Computes and returns the [SHA-256][sha-2] hash as a $256$-bit unsigned [`Int{:tact}`][int] from a passed [`Slice{:tact}`][slice] or [`String{:tact}`][p] `data`. -In case `data` is a [`String{:tact}`][p] it should have a number of bits divisible by $8$, and in case it's a [`Slice{:tact}`][slice] it must **also** have no references (i.e. only up to 1023 bits of data in total). +In case `data` is a [`String{:tact}`][p] it should have a number of bits divisible by $8$, and in case it's a [`Slice{:tact}`][slice] it must **also** have no references (i.e. only up to $1023$ bits of data in total). This function tries to resolve constant string values at [compile-time](/ref/core-comptime) whenever possible. -This function tries to resolve constant string values in [compile-time](/ref/core-comptime) whenever possible. +:::caution + + If the [`String{:tact}`][p] value cannot be resolved during [compilation time](/ref/core-comptime), then the hash is calculated at runtime by the [TVM][tvm] itself. Note, that hashing strings with more than $128$ bytes by the [TVM][tvm] can cause collisions if their first $128$ bytes are the same. + + Therefore, prefer using statically known strings whenever possible. When in doubt, use strings of up to $128$ bytes long. + +::: Usage examples: @@ -277,5 +283,6 @@ sha256(someVariableElsewhere); // will try to resolve at compile-time, [int]: /book/integers [slice]: /book/cells#slices +[tvm]: https://docs.ton.org/learn/tvm-instructions/tvm-overview [ed]: https://en.wikipedia.org/wiki/EdDSA#Ed25519 [sha-2]: https://en.wikipedia.org/wiki/SHA-2#Hash_standard diff --git a/docs/src/content/docs/ref/stdlib-dns.mdx b/docs/src/content/docs/ref/stdlib-dns.mdx index 39ba1cbc0..fb6abc131 100644 --- a/docs/src/content/docs/ref/stdlib-dns.mdx +++ b/docs/src/content/docs/ref/stdlib-dns.mdx @@ -42,7 +42,7 @@ Source code (FunC): [dns.fc#L1](https://github.com/tact-lang/tact/blob/e69c7fc99 native dnsInternalNormalize(src: Slice): Slice; ``` -Normalizes the internal DNS representation of the [`Slice{:tact}`][slice]. The passed [`Slice{:tact}`][slice] must not have any references, otherwise an exception wit [exit code 134](/book/exit-codes#134) will be thrown: `Invalid argument`. +Normalizes the internal DNS representation of the [`Slice{:tact}`][slice]. The passed [`Slice{:tact}`][slice] must not have any references, otherwise an exception with [exit code 134](/book/exit-codes#134) will be thrown: `Invalid argument`. Source code (FunC): [dns.fc#L125](https://github.com/tact-lang/tact/blob/e69c7fc99dc9be3fa5ff984456c03ffe8fed3677/stdlib/libs/dns.fc#L125) diff --git a/package.json b/package.json index 11947028a..4db5dec06 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,10 @@ "test": "jest", "coverage": "cross-env COVERAGE=true jest", "release": "yarn clean && yarn build && yarn coverage && yarn release-it --npm.yarn1", + "type": "tsc --noEmit", "lint": "yarn eslint .", "lint:schema": "ajv validate -s schemas/configSchema.json -d tact.config.json", - "fmt": "yarn prettier -w .", + "fmt": "yarn prettier -l -w .", "fmt:check": "yarn prettier --check .", "spell": "yarn cspell --no-progress \"**\"", "knip": "knip", diff --git a/scripts/copy-files.ts b/scripts/copy-files.ts index 4859b57c9..ae69bcd37 100644 --- a/scripts/copy-files.ts +++ b/scripts/copy-files.ts @@ -1,8 +1,10 @@ import * as fs from "node:fs/promises"; import * as path from "node:path"; +import * as glob from "glob"; const cp = async (fromGlob: string, toPath: string) => { - for await (const file of fs.glob(fromGlob)) { + const files = glob.sync(fromGlob); + for (const file of files) { await fs.copyFile(file, path.join(toPath, path.basename(file))); } }; diff --git a/src/abi/global.ts b/src/abi/global.ts index 82f3f4520..86025f260 100644 --- a/src/abi/global.ts +++ b/src/abi/global.ts @@ -9,7 +9,7 @@ import { writeExpression, writeValue, } from "../generator/writers/writeExpression"; -import { throwCompilationError } from "../errors"; +import { TactConstEvalError, throwCompilationError } from "../errors"; import { evalConstantExpression } from "../constEval"; import { getErrorId } from "../types/resolveErrors"; import { AbiFunction } from "./AbiFunction"; @@ -368,23 +368,30 @@ export const GlobalFunctions: Map = new Map([ // String case if (arg0.name === "String") { + let str: string | undefined; + + // Try const-eval try { - const str = evalConstantExpression( + str = evalConstantExpression( resolved[0]!, ctx.ctx, ) as string; - if (Buffer.from(str).length > 128) { - throwCompilationError( - "sha256 expects string argument with byte length <= 128", - ref, - ); - } + } catch (error) { + if ( + !(error instanceof TactConstEvalError) || + error.fatal + ) + throw error; + } + + // If const-eval did succeed + if (str !== undefined) { return BigInt( "0x" + sha256_sync(str).toString("hex"), ).toString(10); - } catch (e) { - // Not a constant } + + // Otherwise, revert back to runtime hash through SHA256U const exp = writeExpression(resolved[0]!, ctx); return `string_hash(${exp})`; } diff --git a/src/abi/map.ts b/src/abi/map.ts index bc44095cc..23370bcb1 100644 --- a/src/abi/map.ts +++ b/src/abi/map.ts @@ -160,6 +160,10 @@ export const MapFunctions: Map = new Map([ } else if (self.valueAs?.startsWith("uint")) { vBits = parseInt(self.valueAs.slice(4), 10); vKind = "uint"; + } else if (self.valueAs?.startsWith("coins")) { + vKind = "coins"; + ctx.used(`__tact_dict_set_${kind}_${vKind}`); + return `${resolved[0]}~__tact_dict_set_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]})`; } ctx.used(`__tact_dict_set_${kind}_${vKind}`); return `${resolved[0]}~__tact_dict_set_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]}, ${vBits})`; @@ -226,6 +230,10 @@ export const MapFunctions: Map = new Map([ } else if (self.valueAs?.startsWith("uint")) { vBits = parseInt(self.valueAs.slice(4), 10); vKind = "uint"; + } else if (self.valueAs?.startsWith("coins")) { + vKind = "coins"; + ctx.used(`__tact_dict_get_${kind}_${vKind}`); + return `__tact_dict_get_${kind}_${vKind}(${resolved[0]}, ${bits}, ${resolved[1]})`; } ctx.used(`__tact_dict_get_${kind}_${vKind}`); return `__tact_dict_get_${kind}_${vKind}(${resolved[0]}, ${bits}, ${resolved[1]}, ${vBits})`; @@ -571,6 +579,10 @@ export const MapFunctions: Map = new Map([ } else if (self.valueAs?.startsWith("uint")) { vBits = parseInt(self.valueAs.slice(4), 10); vKind = "uint"; + } else if (self.valueAs?.startsWith("coins")) { + vKind = "coins"; + ctx.used(`__tact_dict_replace_${kind}_${vKind}`); + return `${resolved[0]}~__tact_dict_replace_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]})`; } ctx.used(`__tact_dict_replace_${kind}_${vKind}`); return `${resolved[0]}~__tact_dict_replace_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]}, ${vBits})`; @@ -649,6 +661,10 @@ export const MapFunctions: Map = new Map([ } else if (self.valueAs?.startsWith("uint")) { vBits = parseInt(self.valueAs.slice(4), 10); vKind = "uint"; + } else if (self.valueAs?.startsWith("coins")) { + vKind = "coins"; + ctx.used(`__tact_dict_replaceget_${kind}_${vKind}`); + return `${resolved[0]}~__tact_dict_replaceget_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]})`; } ctx.used(`__tact_dict_replaceget_${kind}_${vKind}`); return `${resolved[0]}~__tact_dict_replaceget_${kind}_${vKind}(${bits}, ${resolved[1]}, ${resolved[2]}, ${vBits})`; diff --git a/src/bindings/typescript/serializers.ts b/src/bindings/typescript/serializers.ts index ef9c46311..2f23a8583 100644 --- a/src/bindings/typescript/serializers.ts +++ b/src/bindings/typescript/serializers.ts @@ -620,6 +620,7 @@ type MapSerializerDescrKey = | { kind: "address" }; type MapSerializerDescrValue = | { kind: "int" | "uint"; bits: number } + | { kind: "varuint"; length: number } | { kind: "boolean" } | { kind: "address" } | { kind: "cell" } @@ -665,6 +666,9 @@ function getValueParser(src: MapSerializerDescrValue) { return `Dictionary.Values.BigUint(${src.bits})`; } } + case "varuint": { + return `Dictionary.Values.BigVarUint(${src.length})`; + } case "address": { return "Dictionary.Values.Address()"; } @@ -739,7 +743,7 @@ const map: Serializer = { ) { value = { kind: "uint", bits: 256 }; } else if (src.valueFormat === "coins") { - value = { kind: "uint", bits: 124 }; + value = { kind: "varuint", length: 4 }; } } if (src.value === "address") { @@ -809,6 +813,10 @@ const map: Serializer = { } } break; + case "varuint": { + valueT = `bigint`; + break; + } case "boolean": { valueT = `boolean`; diff --git a/src/generator/writeReport.ts b/src/generator/writeReport.ts index b6f580a85..62250f53d 100644 --- a/src/generator/writeReport.ts +++ b/src/generator/writeReport.ts @@ -45,7 +45,7 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) { // Error Codes w.write(`# Error Codes`); Object.entries(abi.errors!).forEach(([t, abiError]) => { - w.write(`${t}: ${abiError.message}`); + w.write(`* ${t}: ${abiError.message}`); }); w.append(); diff --git a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap index fbf26704e..30db3674f 100644 --- a/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap +++ b/src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap @@ -2243,6 +2243,132 @@ if (ok) { "name": "__tact_dict_replaceget_slice_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_slice_cell(cell d, int kl, slice k, cell v)", }, + { + "code": { + "code": "var (r, ok) = __tact_dict_get(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_slice_coins", + "signature": "int __tact_dict_get_slice_coins(cell d, int kl, slice k)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_min(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_min", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_slice_coins", + "signature": "(slice, int, int) __tact_dict_min_slice_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_next", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_slice_coins", + "signature": "(slice, int, int) __tact_dict_next_slice_coins(cell d, int kl, slice pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, ()); +} else { + return (dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_slice_coins", + "signature": "(cell, ()) __tact_dict_set_slice_coins(cell d, int kl, slice k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, (ok)); +} else { + return dict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_slice_coins", + "signature": "(cell, (int)) __tact_dict_replace_slice_coins(cell d, int kl, slice k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_slice_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_slice_coins(cell d, int kl, slice k, int v)", + }, { "code": { "code": "var (r, ok) = udict_get?(d, kl, k); @@ -2699,6 +2825,120 @@ if (ok) { "name": "__tact_dict_replaceget_uint_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_uint_cell(cell d, int kl, int k, cell v)", }, + { + "code": { + "code": "var (r, ok) = udict_get?(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_uint_coins", + "signature": "int __tact_dict_get_uint_coins(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_min?(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_uint_coins", + "signature": "(int, int, int) __tact_dict_min_uint_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_uint_coins", + "signature": "(int, int, int) __tact_dict_next_uint_coins(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, ()); +} else { + return (udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_uint_coins", + "signature": "(cell, ()) __tact_dict_set_uint_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, (ok)); +} else { + return udict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_uint_coins", + "signature": "(cell, (int)) __tact_dict_replace_uint_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_uint_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_uint_coins(cell d, int kl, int k, int v)", + }, { "code": { "code": "var (r, ok) = idict_get?(d, kl, k); @@ -3019,14 +3259,128 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_replace_int_uint", - "signature": "(cell, (int)) __tact_dict_replace_int_uint(cell d, int kl, int k, int v, int vl)", + "name": "__tact_dict_replace_int_uint", + "signature": "(cell, (int)) __tact_dict_replace_int_uint(cell d, int kl, int k, int v, int vl)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); +if (ok) { + return (d, old~load_uint(vl)); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_int_uint", + "signature": "(cell, (int)) __tact_dict_replaceget_int_uint(cell d, int kl, int k, int v, int vl)", + }, + { + "code": { + "code": "var (r, ok) = idict_get_ref?(d, kl, k); +if (ok) { + return r; +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_int_cell", + "signature": "cell __tact_dict_get_int_cell(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_min_ref?(d, kl); +if (flag) { + return (key, value, flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_int_cell", + "signature": "(int, cell, int) __tact_dict_min_int_cell(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_ref(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_int_cell", + "signature": "(int, cell, int) __tact_dict_next_int_cell(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, ()); +} else { + return (idict_set_ref(d, kl, k, v), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_int_cell", + "signature": "(cell, ()) __tact_dict_set_int_cell(cell d, int kl, int k, cell v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, (ok)); +} else { + return idict_replace_ref?(d, kl, k, v); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_int_cell", + "signature": "(cell, (int)) __tact_dict_replace_int_cell(cell d, int kl, int k, cell v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); + "code": "var (old, ok) = null?(v) ? d~idict_delete_get_ref?(kl, k) : d~idict_replaceget_ref?(kl, k, v); if (ok) { - return (d, old~load_uint(vl)); + return (d, old); } else { return (d, null()); }", @@ -3038,14 +3392,14 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_int_uint", - "signature": "(cell, (int)) __tact_dict_replaceget_int_uint(cell d, int kl, int k, int v, int vl)", + "name": "__tact_dict_replaceget_int_cell", + "signature": "(cell, (cell)) __tact_dict_replaceget_int_cell(cell d, int kl, int k, cell v)", }, { "code": { - "code": "var (r, ok) = idict_get_ref?(d, kl, k); + "code": "var (r, ok) = idict_get?(d, kl, k); if (ok) { - return r; + return r~load_coins(); } else { return null(); }", @@ -3057,14 +3411,14 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_get_int_cell", - "signature": "cell __tact_dict_get_int_cell(cell d, int kl, int k)", + "name": "__tact_dict_get_int_coins", + "signature": "int __tact_dict_get_int_coins(cell d, int kl, int k)", }, { "code": { - "code": "var (key, value, flag) = idict_get_min_ref?(d, kl); + "code": "var (key, value, flag) = idict_get_min?(d, kl); if (flag) { - return (key, value, flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -3076,14 +3430,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_min_int_cell", - "signature": "(int, cell, int) __tact_dict_min_int_cell(cell d, int kl)", + "name": "__tact_dict_min_int_coins", + "signature": "(int, int, int) __tact_dict_min_int_coins(cell d, int kl)", }, { "code": { "code": "var (key, value, flag) = idict_get_next?(d, kl, pivot); if (flag) { - return (key, value~load_ref(), flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -3095,8 +3449,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_next_int_cell", - "signature": "(int, cell, int) __tact_dict_next_int_cell(cell d, int kl, int pivot)", + "name": "__tact_dict_next_int_coins", + "signature": "(int, int, int) __tact_dict_next_int_coins(cell d, int kl, int pivot)", }, { "code": { @@ -3104,7 +3458,7 @@ if (flag) { var (r, ok) = idict_delete?(d, kl, k); return (r, ()); } else { - return (idict_set_ref(d, kl, k, v), ()); + return (idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); }", "kind": "generic", }, @@ -3114,8 +3468,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_set_int_cell", - "signature": "(cell, ()) __tact_dict_set_int_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_set_int_coins", + "signature": "(cell, ()) __tact_dict_set_int_coins(cell d, int kl, int k, int v)", }, { "code": { @@ -3123,7 +3477,7 @@ if (flag) { var (r, ok) = idict_delete?(d, kl, k); return (r, (ok)); } else { - return idict_replace_ref?(d, kl, k, v); + return idict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); }", "kind": "generic", }, @@ -3133,14 +3487,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_replace_int_cell", - "signature": "(cell, (int)) __tact_dict_replace_int_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_replace_int_coins", + "signature": "(cell, (int)) __tact_dict_replace_int_coins(cell d, int kl, int k, int v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~idict_delete_get_ref?(kl, k) : d~idict_replaceget_ref?(kl, k, v); + "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); if (ok) { - return (d, old); + return (d, old~load_coins()); } else { return (d, null()); }", @@ -3152,8 +3506,8 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_int_cell", - "signature": "(cell, (cell)) __tact_dict_replaceget_int_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_replaceget_int_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_int_coins(cell d, int kl, int k, int v)", }, { "code": { @@ -6315,6 +6669,132 @@ if (ok) { "name": "__tact_dict_replaceget_slice_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_slice_cell(cell d, int kl, slice k, cell v)", }, + { + "code": { + "code": "var (r, ok) = __tact_dict_get(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_slice_coins", + "signature": "int __tact_dict_get_slice_coins(cell d, int kl, slice k)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_min(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_min", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_slice_coins", + "signature": "(slice, int, int) __tact_dict_min_slice_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_next", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_slice_coins", + "signature": "(slice, int, int) __tact_dict_next_slice_coins(cell d, int kl, slice pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, ()); +} else { + return (dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_slice_coins", + "signature": "(cell, ()) __tact_dict_set_slice_coins(cell d, int kl, slice k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, (ok)); +} else { + return dict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_slice_coins", + "signature": "(cell, (int)) __tact_dict_replace_slice_coins(cell d, int kl, slice k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_slice_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_slice_coins(cell d, int kl, slice k, int v)", + }, { "code": { "code": "var (r, ok) = udict_get?(d, kl, k); @@ -6625,7 +7105,121 @@ if (flag) { var (r, ok) = udict_delete?(d, kl, k); return (r, (ok)); } else { - return udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl)); + return udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_uint_uint", + "signature": "(cell, (int)) __tact_dict_replace_uint_uint(cell d, int kl, int k, int v, int vl)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); +if (ok) { + return (d, old~load_uint(vl)); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_uint_uint", + "signature": "(cell, (int)) __tact_dict_replaceget_uint_uint(cell d, int kl, int k, int v, int vl)", + }, + { + "code": { + "code": "var (r, ok) = udict_get_ref?(d, kl, k); +if (ok) { + return r; +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_uint_cell", + "signature": "cell __tact_dict_get_uint_cell(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_min_ref?(d, kl); +if (flag) { + return (key, value, flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_uint_cell", + "signature": "(int, cell, int) __tact_dict_min_uint_cell(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_ref(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_uint_cell", + "signature": "(int, cell, int) __tact_dict_next_uint_cell(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, ()); +} else { + return (udict_set_ref(d, kl, k, v), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_uint_cell", + "signature": "(cell, ()) __tact_dict_set_uint_cell(cell d, int kl, int k, cell v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, (ok)); +} else { + return udict_replace_ref?(d, kl, k, v); }", "kind": "generic", }, @@ -6635,14 +7229,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_replace_uint_uint", - "signature": "(cell, (int)) __tact_dict_replace_uint_uint(cell d, int kl, int k, int v, int vl)", + "name": "__tact_dict_replace_uint_cell", + "signature": "(cell, (int)) __tact_dict_replace_uint_cell(cell d, int kl, int k, cell v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); + "code": "var (old, ok) = null?(v) ? d~udict_delete_get_ref?(kl, k) : d~udict_replaceget_ref?(kl, k, v); if (ok) { - return (d, old~load_uint(vl)); + return (d, old); } else { return (d, null()); }", @@ -6654,14 +7248,14 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_uint_uint", - "signature": "(cell, (int)) __tact_dict_replaceget_uint_uint(cell d, int kl, int k, int v, int vl)", + "name": "__tact_dict_replaceget_uint_cell", + "signature": "(cell, (cell)) __tact_dict_replaceget_uint_cell(cell d, int kl, int k, cell v)", }, { "code": { - "code": "var (r, ok) = udict_get_ref?(d, kl, k); + "code": "var (r, ok) = udict_get?(d, kl, k); if (ok) { - return r; + return r~load_coins(); } else { return null(); }", @@ -6673,14 +7267,14 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_get_uint_cell", - "signature": "cell __tact_dict_get_uint_cell(cell d, int kl, int k)", + "name": "__tact_dict_get_uint_coins", + "signature": "int __tact_dict_get_uint_coins(cell d, int kl, int k)", }, { "code": { - "code": "var (key, value, flag) = udict_get_min_ref?(d, kl); + "code": "var (key, value, flag) = udict_get_min?(d, kl); if (flag) { - return (key, value, flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -6692,14 +7286,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_min_uint_cell", - "signature": "(int, cell, int) __tact_dict_min_uint_cell(cell d, int kl)", + "name": "__tact_dict_min_uint_coins", + "signature": "(int, int, int) __tact_dict_min_uint_coins(cell d, int kl)", }, { "code": { "code": "var (key, value, flag) = udict_get_next?(d, kl, pivot); if (flag) { - return (key, value~load_ref(), flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -6711,8 +7305,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_next_uint_cell", - "signature": "(int, cell, int) __tact_dict_next_uint_cell(cell d, int kl, int pivot)", + "name": "__tact_dict_next_uint_coins", + "signature": "(int, int, int) __tact_dict_next_uint_coins(cell d, int kl, int pivot)", }, { "code": { @@ -6720,7 +7314,7 @@ if (flag) { var (r, ok) = udict_delete?(d, kl, k); return (r, ()); } else { - return (udict_set_ref(d, kl, k, v), ()); + return (udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); }", "kind": "generic", }, @@ -6730,8 +7324,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_set_uint_cell", - "signature": "(cell, ()) __tact_dict_set_uint_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_set_uint_coins", + "signature": "(cell, ()) __tact_dict_set_uint_coins(cell d, int kl, int k, int v)", }, { "code": { @@ -6739,7 +7333,7 @@ if (flag) { var (r, ok) = udict_delete?(d, kl, k); return (r, (ok)); } else { - return udict_replace_ref?(d, kl, k, v); + return udict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); }", "kind": "generic", }, @@ -6749,14 +7343,14 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_replace_uint_cell", - "signature": "(cell, (int)) __tact_dict_replace_uint_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_replace_uint_coins", + "signature": "(cell, (int)) __tact_dict_replace_uint_coins(cell d, int kl, int k, int v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~udict_delete_get_ref?(kl, k) : d~udict_replaceget_ref?(kl, k, v); + "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); if (ok) { - return (d, old); + return (d, old~load_coins()); } else { return (d, null()); }", @@ -6768,8 +7362,8 @@ if (ok) { "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_uint_cell", - "signature": "(cell, (cell)) __tact_dict_replaceget_uint_cell(cell d, int kl, int k, cell v)", + "name": "__tact_dict_replaceget_uint_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_uint_coins(cell d, int kl, int k, int v)", }, { "code": { @@ -7227,6 +7821,120 @@ if (ok) { "name": "__tact_dict_replaceget_int_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_int_cell(cell d, int kl, int k, cell v)", }, + { + "code": { + "code": "var (r, ok) = idict_get?(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_int_coins", + "signature": "int __tact_dict_get_int_coins(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_min?(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_int_coins", + "signature": "(int, int, int) __tact_dict_min_int_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_int_coins", + "signature": "(int, int, int) __tact_dict_next_int_coins(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, ()); +} else { + return (idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_int_coins", + "signature": "(cell, ()) __tact_dict_set_int_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, (ok)); +} else { + return idict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_int_coins", + "signature": "(cell, (int)) __tact_dict_replace_int_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_int_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_int_coins(cell d, int kl, int k, int v)", + }, { "code": { "code": "var (r, ok) = __tact_dict_get(d, kl, k); @@ -10178,7 +10886,133 @@ if (flag) { "code": { "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); if (flag) { - return (key, value~load_uint(vl), flag); + return (key, value~load_uint(vl), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_next", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_slice_uint", + "signature": "(slice, int, int) __tact_dict_next_slice_uint(cell d, int kl, slice pivot, int vl)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, ()); +} else { + return (dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_slice_uint", + "signature": "(cell, ()) __tact_dict_set_slice_uint(cell d, int kl, slice k, int v, int vl)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = __tact_dict_delete(d, kl, k); + return (r, (ok)); +} else { + return dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_slice_uint", + "signature": "(cell, (int)) __tact_dict_replace_slice_uint(cell d, int kl, slice k, int v, int vl)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); +if (ok) { + return (d, old~load_uint(vl)); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_delete_get", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_slice_uint", + "signature": "(cell, (int)) __tact_dict_replaceget_slice_uint(cell d, int kl, slice k, int v, int vl)", + }, + { + "code": { + "code": "var (r, ok) = __tact_dict_get_ref(d, kl, k); +if (ok) { + return r; +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_get_ref", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_slice_cell", + "signature": "cell __tact_dict_get_slice_cell(cell d, int kl, slice k)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_min_ref(d, kl); +if (flag) { + return (key, value, flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set { + "__tact_dict_min_ref", + }, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_slice_cell", + "signature": "(slice, cell, int) __tact_dict_min_slice_cell(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); +if (flag) { + return (key, value~load_ref(), flag); } else { return (null(), null(), flag); }", @@ -10192,8 +11026,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_next_slice_uint", - "signature": "(slice, int, int) __tact_dict_next_slice_uint(cell d, int kl, slice pivot, int vl)", + "name": "__tact_dict_next_slice_cell", + "signature": "(slice, cell, int) __tact_dict_next_slice_cell(cell d, int kl, slice pivot)", }, { "code": { @@ -10201,7 +11035,7 @@ if (flag) { var (r, ok) = __tact_dict_delete(d, kl, k); return (r, ()); } else { - return (dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ()); + return __tact_dict_set_ref(d, kl, k, v); }", "kind": "generic", }, @@ -10209,12 +11043,13 @@ if (flag) { "context": "stdlib", "depends": Set { "__tact_dict_delete", + "__tact_dict_set_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_set_slice_uint", - "signature": "(cell, ()) __tact_dict_set_slice_uint(cell d, int kl, slice k, int v, int vl)", + "name": "__tact_dict_set_slice_cell", + "signature": "(cell, ()) __tact_dict_set_slice_cell(cell d, int kl, slice k, cell v)", }, { "code": { @@ -10222,7 +11057,7 @@ if (flag) { var (r, ok) = __tact_dict_delete(d, kl, k); return (r, (ok)); } else { - return dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl)); + return __tact_dict_replace_ref(d, kl, k, v); }", "kind": "generic", }, @@ -10230,18 +11065,19 @@ if (flag) { "context": "stdlib", "depends": Set { "__tact_dict_delete", + "__tact_dict_replace_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_replace_slice_uint", - "signature": "(cell, (int)) __tact_dict_replace_slice_uint(cell d, int kl, slice k, int v, int vl)", + "name": "__tact_dict_replace_slice_cell", + "signature": "(cell, (int)) __tact_dict_replace_slice_cell(cell d, int kl, slice k, cell v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse()); + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get_ref(kl, k) : d~__tact_dict_replaceget_ref(kl, k, v); if (ok) { - return (d, old~load_uint(vl)); + return (d, old); } else { return (d, null()); }", @@ -10250,19 +11086,20 @@ if (ok) { "comment": null, "context": "stdlib", "depends": Set { - "__tact_dict_delete_get", + "__tact_dict_delete_get_ref", + "__tact_dict_replaceget_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_slice_uint", - "signature": "(cell, (int)) __tact_dict_replaceget_slice_uint(cell d, int kl, slice k, int v, int vl)", + "name": "__tact_dict_replaceget_slice_cell", + "signature": "(cell, (cell)) __tact_dict_replaceget_slice_cell(cell d, int kl, slice k, cell v)", }, { "code": { - "code": "var (r, ok) = __tact_dict_get_ref(d, kl, k); + "code": "var (r, ok) = __tact_dict_get(d, kl, k); if (ok) { - return r; + return r~load_coins(); } else { return null(); }", @@ -10271,19 +11108,19 @@ if (ok) { "comment": null, "context": "stdlib", "depends": Set { - "__tact_dict_get_ref", + "__tact_dict_get", }, "flags": Set { "inline", }, - "name": "__tact_dict_get_slice_cell", - "signature": "cell __tact_dict_get_slice_cell(cell d, int kl, slice k)", + "name": "__tact_dict_get_slice_coins", + "signature": "int __tact_dict_get_slice_coins(cell d, int kl, slice k)", }, { "code": { - "code": "var (key, value, flag) = __tact_dict_min_ref(d, kl); + "code": "var (key, value, flag) = __tact_dict_min(d, kl); if (flag) { - return (key, value, flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -10292,19 +11129,19 @@ if (flag) { "comment": null, "context": "stdlib", "depends": Set { - "__tact_dict_min_ref", + "__tact_dict_min", }, "flags": Set { "inline", }, - "name": "__tact_dict_min_slice_cell", - "signature": "(slice, cell, int) __tact_dict_min_slice_cell(cell d, int kl)", + "name": "__tact_dict_min_slice_coins", + "signature": "(slice, int, int) __tact_dict_min_slice_coins(cell d, int kl)", }, { "code": { "code": "var (key, value, flag) = __tact_dict_next(d, kl, pivot); if (flag) { - return (key, value~load_ref(), flag); + return (key, value~load_coins(), flag); } else { return (null(), null(), flag); }", @@ -10318,8 +11155,8 @@ if (flag) { "flags": Set { "inline", }, - "name": "__tact_dict_next_slice_cell", - "signature": "(slice, cell, int) __tact_dict_next_slice_cell(cell d, int kl, slice pivot)", + "name": "__tact_dict_next_slice_coins", + "signature": "(slice, int, int) __tact_dict_next_slice_coins(cell d, int kl, slice pivot)", }, { "code": { @@ -10327,7 +11164,7 @@ if (flag) { var (r, ok) = __tact_dict_delete(d, kl, k); return (r, ()); } else { - return __tact_dict_set_ref(d, kl, k, v); + return (dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); }", "kind": "generic", }, @@ -10335,13 +11172,12 @@ if (flag) { "context": "stdlib", "depends": Set { "__tact_dict_delete", - "__tact_dict_set_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_set_slice_cell", - "signature": "(cell, ()) __tact_dict_set_slice_cell(cell d, int kl, slice k, cell v)", + "name": "__tact_dict_set_slice_coins", + "signature": "(cell, ()) __tact_dict_set_slice_coins(cell d, int kl, slice k, int v)", }, { "code": { @@ -10349,7 +11185,7 @@ if (flag) { var (r, ok) = __tact_dict_delete(d, kl, k); return (r, (ok)); } else { - return __tact_dict_replace_ref(d, kl, k, v); + return dict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); }", "kind": "generic", }, @@ -10357,19 +11193,18 @@ if (flag) { "context": "stdlib", "depends": Set { "__tact_dict_delete", - "__tact_dict_replace_ref", }, "flags": Set { "inline", }, - "name": "__tact_dict_replace_slice_cell", - "signature": "(cell, (int)) __tact_dict_replace_slice_cell(cell d, int kl, slice k, cell v)", + "name": "__tact_dict_replace_slice_coins", + "signature": "(cell, (int)) __tact_dict_replace_slice_coins(cell d, int kl, slice k, int v)", }, { "code": { - "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get_ref(kl, k) : d~__tact_dict_replaceget_ref(kl, k, v); + "code": "var (old, ok) = null?(v) ? d~__tact_dict_delete_get(kl, k) : d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); if (ok) { - return (d, old); + return (d, old~load_coins()); } else { return (d, null()); }", @@ -10378,14 +11213,13 @@ if (ok) { "comment": null, "context": "stdlib", "depends": Set { - "__tact_dict_delete_get_ref", - "__tact_dict_replaceget_ref", + "__tact_dict_delete_get", }, "flags": Set { "inline", }, - "name": "__tact_dict_replaceget_slice_cell", - "signature": "(cell, (cell)) __tact_dict_replaceget_slice_cell(cell d, int kl, slice k, cell v)", + "name": "__tact_dict_replaceget_slice_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_slice_coins(cell d, int kl, slice k, int v)", }, { "code": { @@ -10843,6 +11677,120 @@ if (ok) { "name": "__tact_dict_replaceget_uint_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_uint_cell(cell d, int kl, int k, cell v)", }, + { + "code": { + "code": "var (r, ok) = udict_get?(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_uint_coins", + "signature": "int __tact_dict_get_uint_coins(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_min?(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_uint_coins", + "signature": "(int, int, int) __tact_dict_min_uint_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = udict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_uint_coins", + "signature": "(int, int, int) __tact_dict_next_uint_coins(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, ()); +} else { + return (udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_uint_coins", + "signature": "(cell, ()) __tact_dict_set_uint_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = udict_delete?(d, kl, k); + return (r, (ok)); +} else { + return udict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_uint_coins", + "signature": "(cell, (int)) __tact_dict_replace_uint_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~udict_delete_get?(kl, k) : d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_uint_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_uint_coins(cell d, int kl, int k, int v)", + }, { "code": { "code": "var (r, ok) = idict_get?(d, kl, k); @@ -11299,6 +12247,120 @@ if (ok) { "name": "__tact_dict_replaceget_int_cell", "signature": "(cell, (cell)) __tact_dict_replaceget_int_cell(cell d, int kl, int k, cell v)", }, + { + "code": { + "code": "var (r, ok) = idict_get?(d, kl, k); +if (ok) { + return r~load_coins(); +} else { + return null(); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_get_int_coins", + "signature": "int __tact_dict_get_int_coins(cell d, int kl, int k)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_min?(d, kl); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_min_int_coins", + "signature": "(int, int, int) __tact_dict_min_int_coins(cell d, int kl)", + }, + { + "code": { + "code": "var (key, value, flag) = idict_get_next?(d, kl, pivot); +if (flag) { + return (key, value~load_coins(), flag); +} else { + return (null(), null(), flag); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_next_int_coins", + "signature": "(int, int, int) __tact_dict_next_int_coins(cell d, int kl, int pivot)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, ()); +} else { + return (idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_set_int_coins", + "signature": "(cell, ()) __tact_dict_set_int_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "if (null?(v)) { + var (r, ok) = idict_delete?(d, kl, k); + return (r, (ok)); +} else { + return idict_replace_builder?(d, kl, k, begin_cell().store_coins(v)); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replace_int_coins", + "signature": "(cell, (int)) __tact_dict_replace_int_coins(cell d, int kl, int k, int v)", + }, + { + "code": { + "code": "var (old, ok) = null?(v) ? d~idict_delete_get?(kl, k) : d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse()); +if (ok) { + return (d, old~load_coins()); +} else { + return (d, null()); +}", + "kind": "generic", + }, + "comment": null, + "context": "stdlib", + "depends": Set {}, + "flags": Set { + "inline", + }, + "name": "__tact_dict_replaceget_int_coins", + "signature": "(cell, (int)) __tact_dict_replaceget_int_coins(cell d, int kl, int k, int v)", + }, { "code": { "code": "var (r, ok) = __tact_dict_get(d, kl, k); diff --git a/src/generator/writers/writeFunction.ts b/src/generator/writers/writeFunction.ts index 25cc63cf6..88c3fc51c 100644 --- a/src/generator/writers/writeFunction.ts +++ b/src/generator/writers/writeFunction.ts @@ -23,7 +23,7 @@ import { resolveFuncTupleType } from "./resolveFuncTupleType"; import { ops } from "./ops"; import { freshIdentifier } from "./freshIdentifier"; import { idTextErr, throwInternalCompilerError } from "../../errors"; -import { prettyPrintAsmShuffle } from "../../prettyPrinter"; +import { ppAsmShuffle } from "../../prettyPrinter"; export function writeCastedExpression( expression: AstExpression, @@ -580,7 +580,7 @@ export function writeFunction(f: FunctionDescription, ctx: WriterContext) { args: fAst.shuffle.args.map((id) => idOfText(funcIdOf(id))), }; ctx.asm( - prettyPrintAsmShuffle(asmShuffleEscaped), + ppAsmShuffle(asmShuffleEscaped), fAst.instructions.join(" "), ); }); diff --git a/src/generator/writers/writeStdlib.ts b/src/generator/writers/writeStdlib.ts index 5ca630c6d..4bd365603 100644 --- a/src/generator/writers/writeStdlib.ts +++ b/src/generator/writers/writeStdlib.ts @@ -1,7 +1,7 @@ import { contractErrors } from "../../abi/errors"; import { maxTupleSize } from "../../bindings/typescript/writeStruct"; import { enabledMasterchain } from "../../config/features"; -import { throwInternalCompilerError } from "../../errors"; +import { match } from "../../utils/tricks"; import { WriterContext } from "../Writer"; export function writeStdlib(ctx: WriterContext): void { @@ -1152,16 +1152,24 @@ export function writeStdlib(ctx: WriterContext): void { const keyTypes = ["slice", "uint", "int"] as const; type KeyType = (typeof keyTypes)[number]; -const valTypes = ["slice", "int", "uint", "cell"] as const; +const valTypes = ["slice", "int", "uint", "cell", "coins"] as const; type ValType = (typeof valTypes)[number]; +function getSignatureKeyType(key: KeyType): KeyType { + return key === "uint" ? "int" : key; +} + +function getSignatureValueType(value: ValType): ValType { + return value === "uint" || value === "coins" ? "int" : value; +} + function genTactDictGet( ctx: WriterContext, key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureRetType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const dictGet = () => { const cellSuffix = value === "cell" ? "_ref" : ""; switch (key) { @@ -1182,12 +1190,15 @@ function genTactDictGet( return "r~load_uint(vl)"; case "int": return "r~load_int(vl)"; + case "coins": + return "r~load_coins()"; } }; const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1196,7 +1207,7 @@ function genTactDictGet( }; ctx.fun(`__tact_dict_get_${key}_${value}`, () => { ctx.signature( - `${signatureRetType} __tact_dict_get_${key}_${value}(cell d, int kl, ${signatureKeyType} k${valBitsArg()})`, + `${signatureValueType} __tact_dict_get_${key}_${value}(cell d, int kl, ${signatureKeyType} k${valBitsArg()})`, ); ctx.flag("inline"); ctx.context("stdlib"); @@ -1214,7 +1225,7 @@ function genTactDictGet( } function genTactDictExists(ctx: WriterContext, key: KeyType): void { - const signatureKeyType = key === "uint" ? "int" : key; + const signatureKeyType = getSignatureKeyType(key); const dictGet = () => { switch (key) { case "slice": @@ -1245,12 +1256,13 @@ function genTactDictSet( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValueType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1267,38 +1279,24 @@ function genTactDictSet( return "idict_delete?"; } }; - const returnExpr = () => { - switch (`${key}:${value}`) { - case "int:int": - return "(idict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; - case "int:uint": - return "(idict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; - case "uint:int": - return "(udict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; - case "uint:uint": - return "(udict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; - case "slice:int": - return "(dict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())"; - case "slice:uint": - return "(dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())"; - case "int:cell": - return "(idict_set_ref(d, kl, k, v), ())"; - case "uint:cell": - return "(udict_set_ref(d, kl, k, v), ())"; - case "int:slice": - return "(idict_set(d, kl, k, v), ())"; - case "uint:slice": - return "(udict_set(d, kl, k, v), ())"; - case "slice:cell": - return `${ctx.used("__tact_dict_set_ref")}(d, kl, k, v)`; - case "slice:slice": - return "(dict_set_builder(d, kl, k, begin_cell().store_slice(v)), ())"; - default: - throwInternalCompilerError( - `Unprocessed combination of key/value types ${key}/${value} in dict set operation`, - ); - } - }; + // prettier-ignore + const returnExpr = () => match(key, value) + .on("int", "int")(() => "(idict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())") + .on("int", "uint")(() => "(idict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())") + .on("int", "coins")(() => "(idict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())") + .on("uint", "int")(() => "(udict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())") + .on("uint", "uint")(() => "(udict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())") + .on("uint", "coins")(() => "(udict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())") + .on("slice", "int")(() => "(dict_set_builder(d, kl, k, begin_cell().store_int(v, vl)), ())") + .on("slice", "uint")(() => "(dict_set_builder(d, kl, k, begin_cell().store_uint(v, vl)), ())") + .on("slice", "coins")(() => "(dict_set_builder(d, kl, k, begin_cell().store_coins(v)), ())") + .on("int", "cell")(() => "(idict_set_ref(d, kl, k, v), ())") + .on("uint", "cell")(() => "(udict_set_ref(d, kl, k, v), ())") + .on("int", "slice")(() => "(idict_set(d, kl, k, v), ())") + .on("uint", "slice")(() => "(udict_set(d, kl, k, v), ())") + .on("slice", "cell")(() => `${ctx.used("__tact_dict_set_ref")}(d, kl, k, v)`) + .on("slice", "slice")(() => "(dict_set_builder(d, kl, k, begin_cell().store_slice(v)), ())") + .end(); ctx.fun(`__tact_dict_set_${key}_${value}`, () => { ctx.signature( `(cell, ()) __tact_dict_set_${key}_${value}(cell d, int kl, ${signatureKeyType} k, ${signatureValueType} v${valBitsArg()})`, @@ -1323,50 +1321,45 @@ function genTactDictGetMin( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": return ", int vl"; } }; - const dictGetMin = () => { - switch (`${key}:${value}`) { - case "int:int": - case "int:uint": - case "int:slice": - return "idict_get_min?"; - case "uint:int": - case "uint:uint": - case "uint:slice": - return "udict_get_min?"; - case "slice:int": - case "slice:uint": - case "slice:slice": - return ctx.used("__tact_dict_min"); - case "int:cell": - return "idict_get_min_ref?"; - case "uint:cell": - return "udict_get_min_ref?"; - case "slice:cell": - return ctx.used("__tact_dict_min_ref"); - default: - throwInternalCompilerError( - `Unprocessed combination of key/value types ${key}/${value} in dict get min operation`, - ); - } - }; + // prettier-ignore + const dictGetMin = () => match(key, value) + .on("int", "int")(() => "idict_get_min?") + .on("int", "uint")(() => "idict_get_min?") + .on("int", "coins")(() => "idict_get_min?") + .on("int", "slice")(() => "idict_get_min?") + .on("uint", "int")(() => "udict_get_min?") + .on("uint", "uint")(() => "udict_get_min?") + .on("uint", "coins")(() => "udict_get_min?") + .on("uint", "slice")(() => "udict_get_min?") + .on("slice", "int")(() => ctx.used("__tact_dict_min")) + .on("slice", "uint")(() => ctx.used("__tact_dict_min")) + .on("slice", "coins")(() => ctx.used("__tact_dict_min")) + .on("slice", "slice")(() => ctx.used("__tact_dict_min")) + .on("int", "cell")(() => "idict_get_min_ref?") + .on("uint", "cell")(() => "udict_get_min_ref?") + .on("slice", "cell")(() => ctx.used("__tact_dict_min_ref")) + .end(); const returnValExpr = () => { switch (value) { case "int": return "value~load_int(vl)"; case "uint": return "value~load_uint(vl)"; + case "coins": + return "value~load_coins()"; case "slice": case "cell": return "value"; @@ -1374,7 +1367,7 @@ function genTactDictGetMin( }; ctx.fun(`__tact_dict_min_${key}_${value}`, () => { ctx.signature( - `(${signatureKeyType}, ${signatureValType}, int) __tact_dict_min_${key}_${value}(cell d, int kl${valBitsArg()})`, + `(${signatureKeyType}, ${signatureValueType}, int) __tact_dict_min_${key}_${value}(cell d, int kl${valBitsArg()})`, ); ctx.flag("inline"); ctx.context("stdlib"); @@ -1396,12 +1389,13 @@ function genTactDictGetNext( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1424,6 +1418,8 @@ function genTactDictGetNext( return "value~load_int(vl)"; case "uint": return "value~load_uint(vl)"; + case "coins": + return "value~load_coins()"; case "slice": return "value"; case "cell": @@ -1432,7 +1428,7 @@ function genTactDictGetNext( }; ctx.fun(`__tact_dict_next_${key}_${value}`, () => { ctx.signature( - `(${signatureKeyType}, ${signatureValType}, int) __tact_dict_next_${key}_${value}(cell d, int kl, ${signatureKeyType} pivot${valBitsArg()})`, + `(${signatureKeyType}, ${signatureValueType}, int) __tact_dict_next_${key}_${value}(cell d, int kl, ${signatureKeyType} pivot${valBitsArg()})`, ); ctx.flag("inline"); ctx.context("stdlib"); @@ -1460,12 +1456,13 @@ function genTactDictReplace( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValueType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1482,38 +1479,24 @@ function genTactDictReplace( return "idict_delete?"; } }; - const returnExpr = () => { - switch (`${key}:${value}`) { - case "int:int": - return "idict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; - case "int:uint": - return "idict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; - case "uint:int": - return "udict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; - case "uint:uint": - return "udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; - case "slice:int": - return "dict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))"; - case "slice:uint": - return "dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))"; - case "int:cell": - return "idict_replace_ref?(d, kl, k, v)"; - case "uint:cell": - return "udict_replace_ref?(d, kl, k, v)"; - case "int:slice": - return "idict_replace?(d, kl, k, v)"; - case "uint:slice": - return "udict_replace?(d, kl, k, v)"; - case "slice:cell": - return `${ctx.used("__tact_dict_replace_ref")}(d, kl, k, v)`; - case "slice:slice": - return "dict_replace_builder?(d, kl, k, begin_cell().store_slice(v))"; - default: - throwInternalCompilerError( - `Unprocessed combination of key/value types ${key}/${value} in dict replace operation`, - ); - } - }; + // prettier-ignore + const returnExpr = () => match(key, value) + .on("int", "int")(() => "idict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))") + .on("int", "uint")(() => "idict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))") + .on("int", "coins")(() => "idict_replace_builder?(d, kl, k, begin_cell().store_coins(v))") + .on("uint", "int")(() => "udict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))") + .on("uint", "uint")(() => "udict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))") + .on("uint", "coins")(() => "udict_replace_builder?(d, kl, k, begin_cell().store_coins(v))") + .on("slice", "int")(() => "dict_replace_builder?(d, kl, k, begin_cell().store_int(v, vl))") + .on("slice", "uint")(() => "dict_replace_builder?(d, kl, k, begin_cell().store_uint(v, vl))") + .on("slice", "coins")(() => "dict_replace_builder?(d, kl, k, begin_cell().store_coins(v))") + .on("int", "cell")(() => "idict_replace_ref?(d, kl, k, v)") + .on("uint", "cell")(() => "udict_replace_ref?(d, kl, k, v)") + .on("int", "slice")(() => "idict_replace?(d, kl, k, v)") + .on("uint", "slice")(() => "udict_replace?(d, kl, k, v)") + .on("slice", "cell")(() => `${ctx.used("__tact_dict_replace_ref")}(d, kl, k, v)`) + .on("slice", "slice")(() => "dict_replace_builder?(d, kl, k, begin_cell().store_slice(v))") + .end(); ctx.fun(`__tact_dict_replace_${key}_${value}`, () => { ctx.signature( `(cell, (int)) __tact_dict_replace_${key}_${value}(cell d, int kl, ${signatureKeyType} k, ${signatureValueType} v${valBitsArg()})`, @@ -1538,12 +1521,13 @@ function genTactDictReplaceGet( key: KeyType, value: ValType, ): void { - const signatureKeyType = key === "uint" ? "int" : key; - const signatureValueType = value === "uint" ? "int" : value; + const signatureKeyType = getSignatureKeyType(key); + const signatureValueType = getSignatureValueType(value); const valBitsArg = () => { switch (value) { case "slice": case "cell": + case "coins": return ""; case "uint": case "int": @@ -1561,38 +1545,24 @@ function genTactDictReplaceGet( return `idict_delete_get${cellSuffix}?`; } }; - const returnExpr = () => { - switch (`${key}:${value}`) { - case "int:int": - return "d~idict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; - case "int:uint": - return "d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; - case "uint:int": - return "d~udict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; - case "uint:uint": - return "d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; - case "slice:int": - return "d~dict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())"; - case "slice:uint": - return "d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())"; - case "int:cell": - return "d~idict_replaceget_ref?(kl, k, v)"; - case "uint:cell": - return "d~udict_replaceget_ref?(kl, k, v)"; - case "int:slice": - return "d~idict_replaceget?(kl, k, v)"; - case "uint:slice": - return "d~udict_replaceget?(kl, k, v)"; - case "slice:cell": - return `d~${ctx.used("__tact_dict_replaceget_ref")}(kl, k, v)`; - case "slice:slice": - return "d~dict_replaceget?(kl, k, begin_cell().store_slice(v).end_cell().begin_parse())"; - default: - throwInternalCompilerError( - `Unprocessed combination of key/value types ${key}/${value} in dict replaceGet operation`, - ); - } - }; + // prettier-ignore + const returnExpr = () => match(key, value) + .on("int", "int")(() => "d~idict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())") + .on("int", "uint")(() => "d~idict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())") + .on("int", "coins")(() => "d~idict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())") + .on("uint", "int")(() => "d~udict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())") + .on("uint", "uint")(() => "d~udict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())") + .on("uint", "coins")(() => "d~udict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())") + .on("slice", "int")(() => "d~dict_replaceget?(kl, k, begin_cell().store_int(v, vl).end_cell().begin_parse())") + .on("slice", "uint")(() => "d~dict_replaceget?(kl, k, begin_cell().store_uint(v, vl).end_cell().begin_parse())") + .on("slice", "coins")(() => "d~dict_replaceget?(kl, k, begin_cell().store_coins(v).end_cell().begin_parse())") + .on("int", "cell")(() => "d~idict_replaceget_ref?(kl, k, v)") + .on("uint", "cell")(() => "d~udict_replaceget_ref?(kl, k, v)") + .on("int", "slice")(() => "d~idict_replaceget?(kl, k, v)") + .on("uint", "slice")(() => "d~udict_replaceget?(kl, k, v)") + .on("slice", "cell")(() => `d~${ctx.used("__tact_dict_replaceget_ref")}(kl, k, v)`) + .on("slice", "slice")(() => "d~dict_replaceget?(kl, k, begin_cell().store_slice(v).end_cell().begin_parse())") + .end(); const parseExpr = () => { switch (value) { case "slice": @@ -1602,6 +1572,8 @@ function genTactDictReplaceGet( return "old~load_uint(vl)"; case "int": return "old~load_int(vl)"; + case "coins": + return "old~load_coins()"; } }; ctx.fun(`__tact_dict_replaceget_${key}_${value}`, () => { diff --git a/src/grammar/__snapshots__/grammar.spec.ts.snap b/src/grammar/__snapshots__/grammar.spec.ts.snap index 333d3dc1a..060ae17e9 100644 --- a/src/grammar/__snapshots__/grammar.spec.ts.snap +++ b/src/grammar/__snapshots__/grammar.spec.ts.snap @@ -8075,7 +8075,7 @@ exports[`grammar should parse stmt-augmented-assign-logic 1`] = ` exports[`grammar should parse stmt-destructuring 1`] = ` { - "id": 140, + "id": 148, "imports": [], "items": [ { @@ -8209,17 +8209,17 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "attributes": [], - "id": 139, + "id": 147, "kind": "function_def", "loc": fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; let S { a, b, c } = s; - let S { a: a1 } = s; - let S { b: b1 } = s; - let S { c: c1 } = s; - let S { a: a2, b: b2 } = s; - let S { a: a3, c: c3 } = s; - let S { b: b4, c: c4 } = s; + let S { a: a1, .. } = s; + let S { b: b1, .. } = s; + let S { c: c1, .. } = s; + let S { a: a2, b: b2, .. } = s; + let S { a: a3, c: c3, .. } = s; + let S { b: b4, c: c4, .. } = s; let m = M{ a: 1, b: 2 }; let M { a: a_m, b: b_m } = m; @@ -8321,12 +8321,12 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 45, + "id": 46, "kind": "id", "loc": s, "text": "s", }, - "id": 46, + "id": 47, "identifiers": Map { "a" => [ { @@ -8371,6 +8371,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, ], }, + "ignoreUnspecifiedFields": false, "kind": "statement_destruct", "loc": let S { a, b, c } = s;, "type": { @@ -8382,32 +8383,33 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 51, + "id": 53, "kind": "id", "loc": s, "text": "s", }, - "id": 52, + "id": 54, "identifiers": Map { "a" => [ { - "id": 48, + "id": 49, "kind": "id", "loc": a, "text": "a", }, { - "id": 49, + "id": 50, "kind": "id", "loc": a1, "text": "a1", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { a: a1 } = s;, + "loc": let S { a: a1, .. } = s;, "type": { - "id": 47, + "id": 48, "kind": "type_id", "loc": S, "text": "S", @@ -8415,32 +8417,33 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 57, + "id": 60, "kind": "id", "loc": s, "text": "s", }, - "id": 58, + "id": 61, "identifiers": Map { "b" => [ { - "id": 54, + "id": 56, "kind": "id", "loc": b, "text": "b", }, { - "id": 55, + "id": 57, "kind": "id", "loc": b1, "text": "b1", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { b: b1 } = s;, + "loc": let S { b: b1, .. } = s;, "type": { - "id": 53, + "id": 55, "kind": "type_id", "loc": S, "text": "S", @@ -8448,32 +8451,33 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 63, + "id": 67, "kind": "id", "loc": s, "text": "s", }, - "id": 64, + "id": 68, "identifiers": Map { "c" => [ { - "id": 60, + "id": 63, "kind": "id", "loc": c, "text": "c", }, { - "id": 61, + "id": 64, "kind": "id", "loc": c1, "text": "c1", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { c: c1 } = s;, + "loc": let S { c: c1, .. } = s;, "type": { - "id": 59, + "id": 62, "kind": "type_id", "loc": S, "text": "S", @@ -8481,22 +8485,22 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 72, + "id": 77, "kind": "id", "loc": s, "text": "s", }, - "id": 73, + "id": 78, "identifiers": Map { "a" => [ { - "id": 66, + "id": 70, "kind": "id", "loc": a, "text": "a", }, { - "id": 67, + "id": 71, "kind": "id", "loc": a2, "text": "a2", @@ -8504,23 +8508,24 @@ exports[`grammar should parse stmt-destructuring 1`] = ` ], "b" => [ { - "id": 69, + "id": 73, "kind": "id", "loc": b, "text": "b", }, { - "id": 70, + "id": 74, "kind": "id", "loc": b2, "text": "b2", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { a: a2, b: b2 } = s;, + "loc": let S { a: a2, b: b2, .. } = s;, "type": { - "id": 65, + "id": 69, "kind": "type_id", "loc": S, "text": "S", @@ -8528,22 +8533,22 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 81, + "id": 87, "kind": "id", "loc": s, "text": "s", }, - "id": 82, + "id": 88, "identifiers": Map { "a" => [ { - "id": 75, + "id": 80, "kind": "id", "loc": a, "text": "a", }, { - "id": 76, + "id": 81, "kind": "id", "loc": a3, "text": "a3", @@ -8551,23 +8556,24 @@ exports[`grammar should parse stmt-destructuring 1`] = ` ], "c" => [ { - "id": 78, + "id": 83, "kind": "id", "loc": c, "text": "c", }, { - "id": 79, + "id": 84, "kind": "id", "loc": c3, "text": "c3", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { a: a3, c: c3 } = s;, + "loc": let S { a: a3, c: c3, .. } = s;, "type": { - "id": 74, + "id": 79, "kind": "type_id", "loc": S, "text": "S", @@ -8575,22 +8581,22 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 90, + "id": 97, "kind": "id", "loc": s, "text": "s", }, - "id": 91, + "id": 98, "identifiers": Map { "b" => [ { - "id": 84, + "id": 90, "kind": "id", "loc": b, "text": "b", }, { - "id": 85, + "id": 91, "kind": "id", "loc": b4, "text": "b4", @@ -8598,23 +8604,24 @@ exports[`grammar should parse stmt-destructuring 1`] = ` ], "c" => [ { - "id": 87, + "id": 93, "kind": "id", "loc": c, "text": "c", }, { - "id": 88, + "id": 94, "kind": "id", "loc": c4, "text": "c4", }, ], }, + "ignoreUnspecifiedFields": true, "kind": "statement_destruct", - "loc": let S { b: b4, c: c4 } = s;, + "loc": let S { b: b4, c: c4, .. } = s;, "type": { - "id": 83, + "id": 89, "kind": "type_id", "loc": S, "text": "S", @@ -8625,15 +8632,15 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "args": [ { "field": { - "id": 94, + "id": 101, "kind": "id", "loc": a, "text": "a", }, - "id": 96, + "id": 103, "initializer": { "base": 10, - "id": 95, + "id": 102, "kind": "number", "loc": 1, "value": 1n, @@ -8643,15 +8650,15 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "field": { - "id": 97, + "id": 104, "kind": "id", "loc": b, "text": "b", }, - "id": 99, + "id": 106, "initializer": { "base": 10, - "id": 98, + "id": 105, "kind": "number", "loc": 2, "value": 2n, @@ -8660,21 +8667,21 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": b: 2, }, ], - "id": 100, + "id": 107, "kind": "struct_instance", "loc": M{ a: 1, b: 2 }, "type": { - "id": 93, + "id": 100, "kind": "type_id", "loc": M, "text": "M", }, }, - "id": 101, + "id": 108, "kind": "statement_let", "loc": let m = M{ a: 1, b: 2 };, "name": { - "id": 92, + "id": 99, "kind": "id", "loc": m, "text": "m", @@ -8683,22 +8690,22 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 109, + "id": 117, "kind": "id", "loc": m, "text": "m", }, - "id": 110, + "id": 118, "identifiers": Map { "a" => [ { - "id": 103, + "id": 110, "kind": "id", "loc": a, "text": "a", }, { - "id": 104, + "id": 111, "kind": "id", "loc": a_m, "text": "a_m", @@ -8706,23 +8713,24 @@ exports[`grammar should parse stmt-destructuring 1`] = ` ], "b" => [ { - "id": 106, + "id": 113, "kind": "id", "loc": b, "text": "b", }, { - "id": 107, + "id": 114, "kind": "id", "loc": b_m, "text": "b_m", }, ], }, + "ignoreUnspecifiedFields": false, "kind": "statement_destruct", "loc": let M { a: a_m, b: b_m } = m;, "type": { - "id": 102, + "id": 109, "kind": "type_id", "loc": M, "text": "M", @@ -8730,46 +8738,46 @@ exports[`grammar should parse stmt-destructuring 1`] = ` }, { "expression": { - "id": 137, + "id": 145, "kind": "op_binary", "left": { - "id": 135, + "id": 143, "kind": "op_binary", "left": { - "id": 133, + "id": 141, "kind": "op_binary", "left": { - "id": 131, + "id": 139, "kind": "op_binary", "left": { - "id": 129, + "id": 137, "kind": "op_binary", "left": { - "id": 127, + "id": 135, "kind": "op_binary", "left": { - "id": 125, + "id": 133, "kind": "op_binary", "left": { - "id": 123, + "id": 131, "kind": "op_binary", "left": { - "id": 121, + "id": 129, "kind": "op_binary", "left": { - "id": 119, + "id": 127, "kind": "op_binary", "left": { - "id": 117, + "id": 125, "kind": "op_binary", "left": { - "id": 115, + "id": 123, "kind": "op_binary", "left": { - "id": 113, + "id": 121, "kind": "op_binary", "left": { - "id": 111, + "id": 119, "kind": "id", "loc": a, "text": "a", @@ -8777,7 +8785,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b, "op": "+", "right": { - "id": 112, + "id": 120, "kind": "id", "loc": b, "text": "b", @@ -8786,7 +8794,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c, "op": "+", "right": { - "id": 114, + "id": 122, "kind": "id", "loc": c, "text": "c", @@ -8795,7 +8803,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1, "op": "+", "right": { - "id": 116, + "id": 124, "kind": "id", "loc": a1, "text": "a1", @@ -8804,7 +8812,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1, "op": "+", "right": { - "id": 118, + "id": 126, "kind": "id", "loc": b1, "text": "b1", @@ -8813,7 +8821,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1, "op": "+", "right": { - "id": 120, + "id": 128, "kind": "id", "loc": c1, "text": "c1", @@ -8822,7 +8830,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2, "op": "+", "right": { - "id": 122, + "id": 130, "kind": "id", "loc": a2, "text": "a2", @@ -8831,7 +8839,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2, "op": "+", "right": { - "id": 124, + "id": 132, "kind": "id", "loc": b2, "text": "b2", @@ -8840,7 +8848,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3, "op": "+", "right": { - "id": 126, + "id": 134, "kind": "id", "loc": a3, "text": "a3", @@ -8849,7 +8857,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3, "op": "+", "right": { - "id": 128, + "id": 136, "kind": "id", "loc": c3, "text": "c3", @@ -8858,7 +8866,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4, "op": "+", "right": { - "id": 130, + "id": 138, "kind": "id", "loc": b4, "text": "b4", @@ -8867,7 +8875,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4, "op": "+", "right": { - "id": 132, + "id": 140, "kind": "id", "loc": c4, "text": "c4", @@ -8876,7 +8884,7 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m, "op": "+", "right": { - "id": 134, + "id": 142, "kind": "id", "loc": a_m, "text": "a_m", @@ -8885,13 +8893,13 @@ exports[`grammar should parse stmt-destructuring 1`] = ` "loc": a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m, "op": "+", "right": { - "id": 136, + "id": 144, "kind": "id", "loc": b_m, "text": "b_m", }, }, - "id": 138, + "id": 146, "kind": "statement_return", "loc": return a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m;, }, diff --git a/src/grammar/ast.ts b/src/grammar/ast.ts index cc75370b2..536d31346 100644 --- a/src/grammar/ast.ts +++ b/src/grammar/ast.ts @@ -326,6 +326,7 @@ export type AstStatementDestruct = { type: AstTypeId; /** field name -> [field id, local id] */ identifiers: Map; + ignoreUnspecifiedFields: boolean; expression: AstExpression; id: number; loc: SrcInfo; @@ -377,19 +378,22 @@ export type AstBouncedMessageType = { // export type AstExpression = + | AstExpressionPrimary | AstOpBinary | AstOpUnary - | AstFieldAccess - | AstNumber - | AstId - | AstBoolean + | AstConditional; + +export type AstExpressionPrimary = | AstMethodCall + | AstFieldAccess | AstStaticCall | AstStructInstance + | AstNumber + | AstBoolean + | AstId | AstNull | AstInitOf - | AstString - | AstConditional; + | AstString; export type AstBinaryOperation = | "+" @@ -592,6 +596,13 @@ export type AstDestructMapping = { loc: SrcInfo; }; +export type AstDestructEnd = { + kind: "destruct_end"; + ignoreUnspecifiedFields: boolean; + id: number; + loc: SrcInfo; +}; + export type AstNumber = { kind: "number"; base: AstNumberBase; @@ -704,6 +715,7 @@ export type AstReceiverKind = export type AstNode = | AstFuncId | AstDestructMapping + | AstDestructEnd | AstExpression | AstStatement | AstTypeDecl diff --git a/src/grammar/clone.ts b/src/grammar/clone.ts index 693f40972..a63a8f25d 100644 --- a/src/grammar/clone.ts +++ b/src/grammar/clone.ts @@ -126,6 +126,7 @@ export function cloneNode(src: T): T { } else if (src.kind === "statement_foreach") { return cloneAstNode({ ...src, + map: cloneNode(src.map), statements: src.statements.map(cloneNode), }); } else if (src.kind === "function_def") { diff --git a/src/grammar/grammar.ohm b/src/grammar/grammar.ohm index 09a5cf2fe..2f17501e4 100644 --- a/src/grammar/grammar.ohm +++ b/src/grammar/grammar.ohm @@ -158,7 +158,9 @@ Tact { StatementForEach = foreach "(" id "," id "in" Expression ")" "{" Statement* "}" - StatementDestruct = let typeId "{" ListOf ","? "}" "=" Expression (";" | &"}") + StatementDestruct = let typeId "{" ListOf EndOfIdentifiers "}" "=" Expression (";" | &"}") + EndOfIdentifiers = "," ".." --ignoreUnspecifiedFields + | ","? --regular DestructItem = id ":" id --regular | id --punned @@ -210,13 +212,22 @@ Tact { | "+" ExpressionUnary --plus | "!" ExpressionUnary --not | "~" ExpressionUnary --bitwiseNot - | ExpressionPrimary + | ExpressionPostfix // Order is important - ExpressionPrimary = ExpressionUnboxNotNull + ExpressionPostfix = ExpressionUnboxNotNull | ExpressionMethodCall | ExpressionFieldAccess - | ExpressionStaticCall + | ExpressionPrimary + + ExpressionUnboxNotNull = ExpressionPostfix "!!" + + ExpressionFieldAccess = ExpressionPostfix "." id ~"(" + + ExpressionMethodCall = ExpressionPostfix "." id Arguments + + // Order is important + ExpressionPrimary = ExpressionStaticCall | ExpressionParens | ExpressionStructInstance | integerLiteral @@ -228,12 +239,6 @@ Tact { ExpressionParens = "(" Expression ")" - ExpressionUnboxNotNull = ExpressionPrimary "!!" - - ExpressionFieldAccess = ExpressionPrimary "." id ~"(" - - ExpressionMethodCall = ExpressionPrimary "." id Arguments - ExpressionStructInstance = typeId "{" ListOf ","? "}" ExpressionStaticCall = id Arguments diff --git a/src/grammar/grammar.ts b/src/grammar/grammar.ts index d33aa28ca..2048e5139 100644 --- a/src/grammar/grammar.ts +++ b/src/grammar/grammar.ts @@ -1018,7 +1018,7 @@ semantics.addOperation("astOfStatement", { typeId, _lparen, identifiers, - _optTrailingComma, + endOfIdentifiers, _rparen, _equals, expression, @@ -1043,6 +1043,8 @@ semantics.addOperation("astOfStatement", { ]); return map; }, new Map()), + ignoreUnspecifiedFields: + endOfIdentifiers.astOfExpression().ignoreUnspecifiedFields, expression: expression.astOfExpression(), loc: createRef(this), }); @@ -1191,6 +1193,20 @@ semantics.addOperation("astOfExpression", { loc: createRef(this), }); }, + EndOfIdentifiers_regular(_comma) { + return createAstNode({ + kind: "destruct_end", + ignoreUnspecifiedFields: false, + loc: createRef(this), + }); + }, + EndOfIdentifiers_ignoreUnspecifiedFields(_comma, _dotDot) { + return createAstNode({ + kind: "destruct_end", + ignoreUnspecifiedFields: true, + loc: createRef(this), + }); + }, ExpressionAdd_add(left, _plus, right) { return createAstNode({ kind: "op_binary", diff --git a/src/grammar/iterators.ts b/src/grammar/iterators.ts index 8db047100..bc473a918 100644 --- a/src/grammar/iterators.ts +++ b/src/grammar/iterators.ts @@ -141,6 +141,8 @@ export function traverse(node: AstNode, callback: (node: AstNode) => void) { }); traverse(node.expression, callback); break; + case "destruct_end": + break; case "statement_return": if (node.expression) traverse(node.expression, callback); break; diff --git a/src/grammar/test/stmt-destructuring.tact b/src/grammar/test/stmt-destructuring.tact index a61c86668..7c7316826 100644 --- a/src/grammar/test/stmt-destructuring.tact +++ b/src/grammar/test/stmt-destructuring.tact @@ -12,12 +12,12 @@ message M { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; let S { a, b, c } = s; - let S { a: a1 } = s; - let S { b: b1 } = s; - let S { c: c1 } = s; - let S { a: a2, b: b2 } = s; - let S { a: a3, c: c3 } = s; - let S { b: b4, c: c4 } = s; + let S { a: a1, .. } = s; + let S { b: b1, .. } = s; + let S { c: c1, .. } = s; + let S { a: a2, b: b2, .. } = s; + let S { a: a3, c: c3, .. } = s; + let S { b: b4, c: c4, .. } = s; let m = M{ a: 1, b: 2 }; let M { a: a_m, b: b_m } = m; diff --git a/src/imports/stdlib.ts b/src/imports/stdlib.ts index 516eacf2e..a79113eed 100644 --- a/src/imports/stdlib.ts +++ b/src/imports/stdlib.ts @@ -148,33 +148,42 @@ files['std/cells.tact'] = 'T1BUUkVGIH0KCmFzbSBleHRlbmRzIGZ1biBlbmRDZWxsKHNlbGY6IEJ1aWxkZXIpOiBDZWxsIHsgRU5EQyB9Cgphc20gZXh0ZW5kcyBmdW4gcmVmcyhzZWxmOiBCdWls' + 'ZGVyKTogSW50IHsgQlJFRlMgfQoKYXNtIGV4dGVuZHMgZnVuIGJpdHMoc2VsZjogQnVpbGRlcik6IEludCB7IEJCSVRTIH0KCi8vCi8vIFNsaWNlCi8vCgphc20gZXh0' + 'ZW5kcyBmdW4gYmVnaW5QYXJzZShzZWxmOiBDZWxsKTogU2xpY2UgeyBDVE9TIH0KCmFzbSgtPiAxIDApIGV4dGVuZHMgbXV0YXRlcyBmdW4gbG9hZFJlZihzZWxmOiBT' + - 'bGljZSk6IENlbGwgeyBMRFJFRiB9Cgphc20gZXh0ZW5kcyBmdW4gcHJlbG9hZFJlZihzZWxmOiBTbGljZSk6IENlbGwgeyBQTERSRUYgfQoKLy8gc3BlY2lhbCB0cmVh' + - 'dG1lbnQgaW4gRnVuYyBjb21waWxlciwgc28gbm90IHJlcGxhY2VkIHdpdGggYXNtICJMRFNMSUNFWCIKQG5hbWUobG9hZF9iaXRzKQpleHRlbmRzIG11dGF0ZXMgbmF0' + - 'aXZlIGxvYWRCaXRzKHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBTbGljZTsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNl' + - 'ZCB3aXRoIGFzbSAiUExEU0xJQ0VYIgpAbmFtZShwcmVsb2FkX2JpdHMpCmV4dGVuZHMgbmF0aXZlIHByZWxvYWRCaXRzKHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBTbGlj' + - 'ZTsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3aXRoIGFzbSAiTERJWCIKQG5hbWUobG9hZF9pbnQpCmV4dGVu' + - 'ZHMgbXV0YXRlcyBuYXRpdmUgbG9hZEludChzZWxmOiBTbGljZSwgbDogSW50KTogSW50OwoKLy8gc3BlY2lhbCB0cmVhdG1lbnQgaW4gRnVuYyBjb21waWxlciwgc28g' + - 'bm90IHJlcGxhY2VkIHdpdGggYXNtICJQTERJWCIKQG5hbWUocHJlbG9hZF9pbnQpCmV4dGVuZHMgbmF0aXZlIHByZWxvYWRJbnQoc2VsZjogU2xpY2UsIGw6IEludCk6' + - 'IEludDsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3aXRoIGFzbSAiTERVWCIKQG5hbWUobG9hZF91aW50KQpl' + - 'eHRlbmRzIG11dGF0ZXMgbmF0aXZlIGxvYWRVaW50KHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBJbnQ7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVy' + - 'LCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIlBMRFVYIgpAbmFtZShwcmVsb2FkX3VpbnQpCmV4dGVuZHMgbmF0aXZlIHByZWxvYWRVaW50KHNlbGY6IFNsaWNlLCBs' + - 'OiBJbnQpOiBJbnQ7Cgphc20oLT4gMSAwKSBleHRlbmRzIG11dGF0ZXMgZnVuIGxvYWRCb29sKHNlbGY6IFNsaWNlKTogQm9vbCB7IDEgTERJIH0KCi8vLyBFeHRlbnNp' + - 'b24gbXV0YXRpb24gZnVuY3Rpb24gZm9yIHRoZSBgU2xpY2VgLiBBbGlhcyB0byBgU2xpY2UubG9hZEJvb2woKWAuCi8vLwovLy8gYGBgdGFjdAovLy8gbGV0IHM6IFNs' + - 'aWNlID0gYmVnaW5DZWxsKCkuc3RvcmVCb29sKHRydWUpLmFzU2xpY2UoKTsKLy8vIGxldCBmaXp6OiBCb29sID0gcy5sb2FkQml0KCk7IC8vIHRydWUKLy8vIGBgYAov' + - 'Ly8KLy8vIEBzaW5jZSBUYWN0IDEuNS4wCi8vLyBAc2VlIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL3JlZi9jb3JlLWNlbGxzI3NsaWNlbG9hZGJpdAovLy8KYXNt' + - 'KC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVzIGZ1biBsb2FkQml0KHNlbGY6IFNsaWNlKTogQm9vbCB7IDEgTERJIH0KCmFzbSggLT4gMSAwKSBleHRlbmRzIG11dGF0ZXMg' + - 'ZnVuIGxvYWRDb2lucyhzZWxmOiBTbGljZSk6IEludCB7IExEVkFSVUlOVDE2IH0KCkBuYW1lKF9fdGFjdF9sb2FkX2FkZHJlc3MpCmV4dGVuZHMgbXV0YXRlcyBuYXRp' + - 'dmUgbG9hZEFkZHJlc3Moc2VsZjogU2xpY2UpOiBBZGRyZXNzOwoKYXNtIGV4dGVuZHMgbXV0YXRlcyBmdW4gc2tpcEJpdHMoc2VsZjogU2xpY2UsIGw6IEludCkgeyBT' + - 'RFNLSVBGSVJTVCB9Cgphc20gZXh0ZW5kcyBmdW4gZW5kUGFyc2Uoc2VsZjogU2xpY2UpIHsgRU5EUyB9CgovLwovLyBTbGljZSBzaXplCi8vCgphc20gZXh0ZW5kcyBm' + - 'dW4gcmVmcyhzZWxmOiBTbGljZSk6IEludCB7IFNSRUZTIH0KCmFzbSBleHRlbmRzIGZ1biBiaXRzKHNlbGY6IFNsaWNlKTogSW50IHsgU0JJVFMgfQoKYXNtIGV4dGVu' + - 'ZHMgZnVuIGVtcHR5KHNlbGY6IFNsaWNlKTogQm9vbCB7IFNFTVBUWSB9Cgphc20gZXh0ZW5kcyBmdW4gZGF0YUVtcHR5KHNlbGY6IFNsaWNlKTogQm9vbCB7IFNERU1Q' + - 'VFkgfQoKYXNtIGV4dGVuZHMgZnVuIHJlZnNFbXB0eShzZWxmOiBTbGljZSk6IEJvb2wgeyBTUkVNUFRZIH0KCi8vCi8vIENvbnZlcnNpb25zCi8vCgppbmxpbmUgZXh0' + - 'ZW5kcyBmdW4gYXNTbGljZShzZWxmOiBCdWlsZGVyKTogU2xpY2UgewogICAgcmV0dXJuIHNlbGYuZW5kQ2VsbCgpLmJlZ2luUGFyc2UoKTsKfQoKaW5saW5lIGV4dGVu' + - 'ZHMgZnVuIGFzU2xpY2Uoc2VsZjogQ2VsbCk6IFNsaWNlIHsKICAgIHJldHVybiBzZWxmLmJlZ2luUGFyc2UoKTsKfQoKaW5saW5lIGV4dGVuZHMgZnVuIGFzQ2VsbChz' + - 'ZWxmOiBTbGljZSk6IENlbGwgewogICAgcmV0dXJuIGJlZ2luQ2VsbCgpCiAgICAgICAgLnN0b3JlU2xpY2Uoc2VsZikKICAgICAgICAuZW5kQ2VsbCgpOwp9Cgppbmxp' + - 'bmUgZXh0ZW5kcyBmdW4gYXNDZWxsKHNlbGY6IEJ1aWxkZXIpOiBDZWxsIHsKICAgIHJldHVybiBzZWxmLmVuZENlbGwoKTsKfQoKaW5saW5lIGZ1biBlbXB0eUNlbGwo' + - 'KTogQ2VsbCB7CiAgICByZXR1cm4gYmVnaW5DZWxsKCkuZW5kQ2VsbCgpOwp9CgppbmxpbmUgZnVuIGVtcHR5U2xpY2UoKTogU2xpY2UgewogICAgcmV0dXJuIGVtcHR5' + - 'Q2VsbCgpLmFzU2xpY2UoKTsKfQo='; + 'bGljZSk6IENlbGwgeyBMRFJFRiB9CgovLy8gRXh0ZW5zaW9uIGZ1bmN0aW9uIGZvciB0aGUgYFNsaWNlYC4gQXZhaWxhYmxlIHNpbmNlIFRhY3QgMS41LjAuCi8vLwov' + + 'Ly8gUHJlbG9hZHMgdGhlIG5leHQgcmVmZXJlbmNlIGZyb20gdGhlIGBTbGljZWAgYXMgYSBgQ2VsbGAuIERvZXNuJ3QgbW9kaWZ5IHRoZSBvcmlnaW5hbCBgU2xpY2Vg' + + 'LgovLy8KLy8vIEF0dGVtcHRzIHRvIHByZWxvYWQgc3VjaCByZWZlcmVuY2UgYENlbGxgIHdoZW4gYFNsaWNlYCBkb2Vzbid0IGNvbnRhaW4gaXQgdGhyb3cgYW4gZXhj' + + 'ZXB0aW9uIHdpdGggZXhpdCBjb2RlIDg6IGBDZWxsIG92ZXJmbG93YC4KLy8vCi8vLyBBdHRlbXB0cyB0byBwcmVsb2FkIG1vcmUgZGF0YSB0aGFuIGBTbGljZWAgY29u' + + 'dGFpbnMgdGhyb3cgYW4gZXhjZXB0aW9uIHdpdGggZXhpdCBjb2RlIDk6IGBDZWxsIHVuZGVyZmxvd2AuCi8vLwovLy8gYGBgdGFjdAovLy8gbGV0IHMxOiBTbGljZSA9' + + 'IGJlZ2luQ2VsbCgpLnN0b3JlUmVmKGVtcHR5Q2VsbCgpKS5hc1NsaWNlKCk7Ci8vLyBsZXQgZml6ejogQ2VsbCA9IHMxLnByZWxvYWRSZWYoKTsgLy8gZGlkbid0IG1v' + + 'ZGlmeSBzMQovLy8KLy8vIGxldCBzMjogU2xpY2UgPSBiZWdpbkNlbGwoKQovLy8gICAgIC5zdG9yZVJlZihlbXB0eUNlbGwoKSkKLy8vICAgICAuc3RvcmVSZWYoczEu' + + 'YXNDZWxsKCkpCi8vLyAgICAgLmFzU2xpY2UoKTsKLy8vIGxldCByZWYxOiBDZWxsID0gczIucHJlbG9hZFJlZigpOwovLy8gbGV0IHJlZjI6IENlbGwgPSBzMi5wcmVs' + + 'b2FkUmVmKCk7Ci8vLyByZWYxID09IHJlZjI7IC8vIHRydWUKLy8vIGBgYAovLy8KLy8vIFNlZToKLy8vICogaHR0cHM6Ly9kb2NzLnRhY3QtbGFuZy5vcmcvcmVmL2Nv' + + 'cmUtY2VsbHMjc2xpY2VwcmVsb2FkcmVmCi8vLyAqIGh0dHBzOi8vZG9jcy50YWN0LWxhbmcub3JnL2Jvb2svZXhpdC1jb2RlcwovLy8KYXNtIGV4dGVuZHMgZnVuIHBy' + + 'ZWxvYWRSZWYoc2VsZjogU2xpY2UpOiBDZWxsIHsgUExEUkVGIH0KCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3' + + 'aXRoIGFzbSAiTERTTElDRVgiCkBuYW1lKGxvYWRfYml0cykKZXh0ZW5kcyBtdXRhdGVzIG5hdGl2ZSBsb2FkQml0cyhzZWxmOiBTbGljZSwgbDogSW50KTogU2xpY2U7' + + 'CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIlBMRFNMSUNFWCIKQG5hbWUocHJlbG9hZF9iaXRz' + + 'KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkQml0cyhzZWxmOiBTbGljZSwgbDogSW50KTogU2xpY2U7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBpbGVy' + + 'LCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIkxESVgiCkBuYW1lKGxvYWRfaW50KQpleHRlbmRzIG11dGF0ZXMgbmF0aXZlIGxvYWRJbnQoc2VsZjogU2xpY2UsIGw6' + + 'IEludCk6IEludDsKCi8vIHNwZWNpYWwgdHJlYXRtZW50IGluIEZ1bmMgY29tcGlsZXIsIHNvIG5vdCByZXBsYWNlZCB3aXRoIGFzbSAiUExESVgiCkBuYW1lKHByZWxv' + + 'YWRfaW50KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkSW50KHNlbGY6IFNsaWNlLCBsOiBJbnQpOiBJbnQ7CgovLyBzcGVjaWFsIHRyZWF0bWVudCBpbiBGdW5jIGNvbXBp' + + 'bGVyLCBzbyBub3QgcmVwbGFjZWQgd2l0aCBhc20gIkxEVVgiCkBuYW1lKGxvYWRfdWludCkKZXh0ZW5kcyBtdXRhdGVzIG5hdGl2ZSBsb2FkVWludChzZWxmOiBTbGlj' + + 'ZSwgbDogSW50KTogSW50OwoKLy8gc3BlY2lhbCB0cmVhdG1lbnQgaW4gRnVuYyBjb21waWxlciwgc28gbm90IHJlcGxhY2VkIHdpdGggYXNtICJQTERVWCIKQG5hbWUo' + + 'cHJlbG9hZF91aW50KQpleHRlbmRzIG5hdGl2ZSBwcmVsb2FkVWludChzZWxmOiBTbGljZSwgbDogSW50KTogSW50OwoKYXNtKC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVz' + + 'IGZ1biBsb2FkQm9vbChzZWxmOiBTbGljZSk6IEJvb2wgeyAxIExESSB9CgovLy8gRXh0ZW5zaW9uIG11dGF0aW9uIGZ1bmN0aW9uIGZvciB0aGUgYFNsaWNlYC4gQWxp' + + 'YXMgdG8gYFNsaWNlLmxvYWRCb29sKClgLgovLy8KLy8vIGBgYHRhY3QKLy8vIGxldCBzOiBTbGljZSA9IGJlZ2luQ2VsbCgpLnN0b3JlQm9vbCh0cnVlKS5hc1NsaWNl' + + 'KCk7Ci8vLyBsZXQgZml6ejogQm9vbCA9IHMubG9hZEJpdCgpOyAvLyB0cnVlCi8vLyBgYGAKLy8vCi8vLyBAc2luY2UgVGFjdCAxLjUuMAovLy8gQHNlZSBodHRwczov' + + 'L2RvY3MudGFjdC1sYW5nLm9yZy9yZWYvY29yZS1jZWxscyNzbGljZWxvYWRiaXQKLy8vCmFzbSgtPiAxIDApIGV4dGVuZHMgbXV0YXRlcyBmdW4gbG9hZEJpdChzZWxm' + + 'OiBTbGljZSk6IEJvb2wgeyAxIExESSB9Cgphc20oIC0+IDEgMCkgZXh0ZW5kcyBtdXRhdGVzIGZ1biBsb2FkQ29pbnMoc2VsZjogU2xpY2UpOiBJbnQgeyBMRFZBUlVJ' + + 'TlQxNiB9CgpAbmFtZShfX3RhY3RfbG9hZF9hZGRyZXNzKQpleHRlbmRzIG11dGF0ZXMgbmF0aXZlIGxvYWRBZGRyZXNzKHNlbGY6IFNsaWNlKTogQWRkcmVzczsKCmFz' + + 'bSBleHRlbmRzIG11dGF0ZXMgZnVuIHNraXBCaXRzKHNlbGY6IFNsaWNlLCBsOiBJbnQpIHsgU0RTS0lQRklSU1QgfQoKYXNtIGV4dGVuZHMgZnVuIGVuZFBhcnNlKHNl' + + 'bGY6IFNsaWNlKSB7IEVORFMgfQoKLy8KLy8gU2xpY2Ugc2l6ZQovLwoKYXNtIGV4dGVuZHMgZnVuIHJlZnMoc2VsZjogU2xpY2UpOiBJbnQgeyBTUkVGUyB9Cgphc20g' + + 'ZXh0ZW5kcyBmdW4gYml0cyhzZWxmOiBTbGljZSk6IEludCB7IFNCSVRTIH0KCmFzbSBleHRlbmRzIGZ1biBlbXB0eShzZWxmOiBTbGljZSk6IEJvb2wgeyBTRU1QVFkg' + + 'fQoKYXNtIGV4dGVuZHMgZnVuIGRhdGFFbXB0eShzZWxmOiBTbGljZSk6IEJvb2wgeyBTREVNUFRZIH0KCmFzbSBleHRlbmRzIGZ1biByZWZzRW1wdHkoc2VsZjogU2xp' + + 'Y2UpOiBCb29sIHsgU1JFTVBUWSB9CgovLwovLyBDb252ZXJzaW9ucwovLwoKaW5saW5lIGV4dGVuZHMgZnVuIGFzU2xpY2Uoc2VsZjogQnVpbGRlcik6IFNsaWNlIHsK' + + 'ICAgIHJldHVybiBzZWxmLmVuZENlbGwoKS5iZWdpblBhcnNlKCk7Cn0KCmlubGluZSBleHRlbmRzIGZ1biBhc1NsaWNlKHNlbGY6IENlbGwpOiBTbGljZSB7CiAgICBy' + + 'ZXR1cm4gc2VsZi5iZWdpblBhcnNlKCk7Cn0KCmlubGluZSBleHRlbmRzIGZ1biBhc0NlbGwoc2VsZjogU2xpY2UpOiBDZWxsIHsKICAgIHJldHVybiBiZWdpbkNlbGwo' + + 'KQogICAgICAgIC5zdG9yZVNsaWNlKHNlbGYpCiAgICAgICAgLmVuZENlbGwoKTsKfQoKaW5saW5lIGV4dGVuZHMgZnVuIGFzQ2VsbChzZWxmOiBCdWlsZGVyKTogQ2Vs' + + 'bCB7CiAgICByZXR1cm4gc2VsZi5lbmRDZWxsKCk7Cn0KCmlubGluZSBmdW4gZW1wdHlDZWxsKCk6IENlbGwgewogICAgcmV0dXJuIGJlZ2luQ2VsbCgpLmVuZENlbGwo' + + 'KTsKfQoKaW5saW5lIGZ1biBlbXB0eVNsaWNlKCk6IFNsaWNlIHsKICAgIHJldHVybiBlbXB0eUNlbGwoKS5hc1NsaWNlKCk7Cn0K'; files['std/config.tact'] = 'YXNtIGZ1biBnZXRDb25maWdQYXJhbShpZDogSW50KTogQ2VsbD8geyBDT05GSUdPUFRQQVJBTSB9Cg=='; files['std/context.tact'] = diff --git a/src/index.ts b/src/index.ts index 7469a0101..da47c4368 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export { enableFeatures, build } from "./pipeline/build"; +export { precompile } from "./pipeline/precompile"; export { TactError, TactParseError, diff --git a/src/interpreter.ts b/src/interpreter.ts index 66213c44b..8a40737c3 100644 --- a/src/interpreter.ts +++ b/src/interpreter.ts @@ -1,4 +1,4 @@ -import { Address, beginCell, BitString, Cell, toNano } from "@ton/core"; +import { Address, beginCell, BitString, Cell, Slice, toNano } from "@ton/core"; import { paddedBufferToBits } from "@ton/core/dist/boc/utils/paddedBits"; import * as crc32 from "crc-32"; import { evalConstantExpression } from "./constEval"; @@ -1068,17 +1068,14 @@ export class Interpreter { } case "sha256": { ensureFunArity(1, ast.args, ast.loc); - const str = ensureString( - this.interpretExpression(ast.args[0]!), - ast.args[0]!.loc, - ); - const dataSize = Buffer.from(str).length; - if (dataSize > 128) { - throwErrorConstEval( - `data is too large for sha256 hash, expected up to 128 bytes, got ${dataSize}`, + const expr = this.interpretExpression(ast.args[0]!); + if (expr instanceof Slice) { + throwNonFatalErrorConstEval( + "slice argument is currently not supported", ast.loc, ); } + const str = ensureString(expr, ast.args[0]!.loc); return BigInt("0x" + sha256_sync(str).toString("hex")); } case "emptyMap": { @@ -1465,14 +1462,6 @@ export class Interpreter { ast.expression.loc, ); } - if (ast.identifiers.size !== Object.keys(val).length - 1) { - throwErrorConstEval( - `destructuring assignment expected ${Object.keys(val).length - 1} fields, but got ${ - ast.identifiers.size - }`, - ast.loc, - ); - } for (const [field, name] of ast.identifiers.values()) { if (name.text === "_") { diff --git a/src/pipeline/precompile.ts b/src/pipeline/precompile.ts index eca8f3533..8ada419da 100644 --- a/src/pipeline/precompile.ts +++ b/src/pipeline/precompile.ts @@ -7,18 +7,20 @@ import { resolveErrors } from "../types/resolveErrors"; import { resolveSignatures } from "../types/resolveSignatures"; import { resolveImports } from "../imports/resolveImports"; import { VirtualFileSystem } from "../vfs/VirtualFileSystem"; +import { AstModule } from "../grammar/ast"; export function precompile( ctx: CompilerContext, project: VirtualFileSystem, stdlib: VirtualFileSystem, entrypoint: string, + parsedModules?: AstModule[], ) { // Load all sources const imported = resolveImports({ entrypoint, project, stdlib }); // Add information about all the source code entries to the context - ctx = openContext(ctx, imported.tact, imported.func); + ctx = openContext(ctx, imported.tact, imported.func, parsedModules); // First load type descriptors and check that // they all have valid signatures diff --git a/src/prettyPrinter.ts b/src/prettyPrinter.ts index 4c7685802..a825ec5f0 100644 --- a/src/prettyPrinter.ts +++ b/src/prettyPrinter.ts @@ -1,803 +1,897 @@ -import { - AstConstantDef, - AstImport, - AstNativeFunctionDecl, - AstReceiver, - AstStatementRepeat, - AstStatementUntil, - AstStatementWhile, - AstStatementForEach, - AstStatementTry, - AstStatementTryCatch, - AstCondition, - AstStatementAugmentedAssign, - AstStatementAssign, - AstStatementExpression, - AstStatementReturn, - AstStatementLet, - AstFunctionDef, - AstType, - AstStatement, - AstExpression, - AstContract, - AstTrait, - AstId, - AstModule, - AstModuleItem, - AstStructDecl, - AstMessageDecl, - AstTraitDeclaration, - AstFunctionDecl, - AstConstantDecl, - AstContractDeclaration, - AstContractInit, - AstStructFieldInitializer, - AstPrimitiveTypeDecl, - AstTypeId, - AstMapType, - AstBouncedMessageType, - AstFieldDecl, - AstOptionalType, - AstNode, - AstFuncId, - idText, - AstAsmFunctionDef, - AstFunctionAttribute, - AstTypedParameter, - AstAsmInstruction, - AstAsmShuffle, - astNumToString, - AstStatementDestruct, -} from "./grammar/ast"; -import { throwInternalCompilerError } from "./errors"; -import JSONbig from "json-bigint"; +import * as A from "./grammar/ast"; +import { groupBy, intercalate, isUndefined } from "./utils/array"; +import { makeVisitor } from "./utils/tricks"; + +// +// Types +// + +export const ppAstTypeId = A.idText; + +export const ppAstTypeIdWithStorage = ( + type: A.AstTypeId, + storageType: A.AstId | null, +): string => { + const alias = storageType ? ` as ${ppAstId(storageType)}` : ""; + return `${ppAstTypeId(type)}${alias}`; +}; + +export const ppAstMapType = ({ + keyType, + keyStorageType, + valueType, + valueStorageType, +}: A.AstMapType): string => { + const key = ppAstTypeIdWithStorage(keyType, keyStorageType); + const value = ppAstTypeIdWithStorage(valueType, valueStorageType); + return `map<${key}, ${value}>`; +}; + +export const ppAstBouncedMessageType = ({ + messageType, +}: A.AstBouncedMessageType): string => `bounced<${ppAstTypeId(messageType)}>`; + +export const ppAstOptionalType = ({ typeArg }: A.AstOptionalType): string => + `${ppAstType(typeArg)}?`; + +export const ppAstType = makeVisitor()({ + type_id: ppAstTypeId, + map_type: ppAstMapType, + bounced_message_type: ppAstBouncedMessageType, + optional_type: ppAstOptionalType, +}); + +// +// Expressions +// + +export const unaryOperatorType: Record = { + "+": "pre", + "-": "pre", + "!": "pre", + "~": "pre", + + "!!": "post", +}; + +export const checkPostfix = (operator: A.AstUnaryOperation) => + unaryOperatorType[operator] === "post"; /** - * Provides methods to format and indent Tact code. + * Description of precedence of certain type of AST node */ -export class PrettyPrinter { +export type Precedence = { /** - * @param indentLevel Initial level of indentation. - * @param indentSpaces Number of spaces per indentation level. + * Add parentheses around `code` if in this `parent` position we need brackets + * @param check Position-checking function from parent + * @param code Code to put parentheses around + * @returns */ - constructor( - private indentLevel: number = 0, - private readonly indentSpaces: number = 4, - ) {} + brace: ( + position: (childPrecedence: number) => boolean, + code: string, + ) => string; + /** + * Used in positions where grammar rule mentions itself + * + * Passed down when a position allows same unparenthesized operator + * For example, on left side of addition we can use another addition without + * parentheses: `1 + 2 + 3` means `(1 + 2) + 3`. Thus for left-associative + * operators we pass `self` to their left argument printer. + */ + self: (childPrecedence: number) => boolean; + /** + * Used in positions where grammar rule mentions other rule + * + * Passed down when a position disallows same unparenthesized operator + * For example, on the right side of subtraction we can't use another subtraction + * without parentheses: `1 - (2 - 3)` is not the same as `(1 - 2) - 3`. Thus for + * left-associative operators we pass `child` to their right argument printer. + */ + child: (childPrecedence: number) => boolean; +}; - private increaseIndent() { - this.indentLevel += 1; - } +/** + * Given numeric value of precedence, where higher values stand for higher binding power, + * create a helper object for precedence checking + */ +export const makePrecedence = (myPrecedence: number): Precedence => ({ + brace: (position, code) => (position(myPrecedence) ? `(${code})` : code), + self: (childPrecedence) => childPrecedence < myPrecedence, + child: (childPrecedence) => childPrecedence <= myPrecedence, +}); - private decreaseIndent() { - this.indentLevel -= 1; - } +// Least binding operator +export const lowestPrecedence = makePrecedence(0); - private indent(): string { - return " ".repeat(this.indentLevel * this.indentSpaces); - } +export const conditionalPrecedence = makePrecedence(20); - ppAstPrimitiveTypeDecl(primitive: AstPrimitiveTypeDecl): string { - return `${this.indent()}primitive ${this.ppAstId(primitive.name)};`; - } +export const binaryPrecedence: Readonly< + Record +> = { + "||": makePrecedence(30), - // - // Types - // - - ppAstType(typeRef: AstType): string { - switch (typeRef.kind) { - case "type_id": - return this.ppAstTypeId(typeRef); - case "map_type": - return this.ppAstMapType(typeRef); - case "bounced_message_type": - return this.ppAstBouncedMessageType(typeRef); - case "optional_type": - return this.ppAstOptionalType(typeRef); - } - } + "&&": makePrecedence(40), - ppAstTypeId(typeRef: AstTypeId): string { - return idText(typeRef); - } + "|": makePrecedence(50), - ppAstOptionalType(typeRef: AstOptionalType): string { - return `${this.ppAstType(typeRef.typeArg)}?`; - } + "^": makePrecedence(60), - ppAstMapType(typeRef: AstMapType): string { - const keyAlias = typeRef.keyStorageType - ? ` as ${this.ppAstId(typeRef.keyStorageType)}` - : ""; - const valueAlias = typeRef.valueStorageType - ? ` as ${this.ppAstId(typeRef.valueStorageType)}` - : ""; - return `map<${this.ppAstTypeId(typeRef.keyType)}${keyAlias}, ${this.ppAstTypeId(typeRef.valueType)}${valueAlias}>`; - } + "&": makePrecedence(70), - ppAstBouncedMessageType(typeRef: AstBouncedMessageType): string { - return `bounced<${this.ppAstTypeId(typeRef.messageType)}>`; - } + "==": makePrecedence(80), + "!=": makePrecedence(80), - // - // Expressions - // + "<": makePrecedence(90), + ">": makePrecedence(90), + "<=": makePrecedence(90), + ">=": makePrecedence(90), - /** - * Returns precedence used in unary/binary operations. - * Lower number means higher precedence - */ - getPrecedence(kind: string, op?: string): number { - switch (kind) { - case "op_binary": - switch (op) { - case "||": - return 1; - case "&&": - return 2; - case "|": - return 3; - case "^": - return 4; - case "&": - return 5; - case "==": - case "!=": - return 6; - case "<": - case ">": - case "<=": - case ">=": - return 7; - case "+": - case "-": - return 8; - case "*": - case "/": - case "%": - return 9; - default: - return 11; - } - case "conditional": - case "static_call": - case "method_call": - return 0; - case "op_unary": - return 10; - default: - return 11; - } - } + "<<": makePrecedence(100), + ">>": makePrecedence(100), - ppAstExpression(expr: AstExpression, parentPrecedence: number = 0): string { - let result; - let currentPrecedence = this.getPrecedence(expr.kind); - - switch (expr.kind) { - case "op_binary": - currentPrecedence = this.getPrecedence(expr.kind, expr.op); - result = `${this.ppAstExpression(expr.left, currentPrecedence)} ${expr.op} ${this.ppAstExpression(expr.right, currentPrecedence)}`; - break; - case "op_unary": - currentPrecedence = this.getPrecedence(expr.kind, expr.op); - result = `${expr.op}${this.ppAstExpression(expr.operand, currentPrecedence)}`; - break; - case "field_access": - result = `${this.ppAstExpression(expr.aggregate, currentPrecedence)}.${this.ppAstId(expr.field)}`; - break; - case "method_call": - result = `${this.ppAstExpression(expr.self, currentPrecedence)}.${this.ppAstId(expr.method)}(${expr.args.map((arg) => this.ppAstExpression(arg, currentPrecedence)).join(", ")})`; - break; - case "static_call": - result = `${this.ppAstId(expr.function)}(${expr.args.map((arg) => this.ppAstExpression(arg, currentPrecedence)).join(", ")})`; - break; - case "struct_instance": - result = `${this.ppAstId(expr.type)}{${expr.args.map((x) => this.ppAstStructFieldInit(x)).join(", ")}}`; - break; - case "init_of": - result = `initOf ${this.ppAstId(expr.contract)}(${expr.args.map((arg) => this.ppAstExpression(arg, currentPrecedence)).join(", ")})`; - break; - case "conditional": - result = `${this.ppAstExpression(expr.condition, currentPrecedence)} ? ${this.ppAstExpression(expr.thenBranch, currentPrecedence)} : ${this.ppAstExpression(expr.elseBranch, currentPrecedence)}`; - break; - case "number": - result = astNumToString(expr); - break; - case "id": - result = expr.text; - break; - case "boolean": - result = expr.value.toString(); - break; - case "string": - result = `"${expr.value}"`; - break; - case "null": - result = "null"; - break; - } + "+": makePrecedence(110), + "-": makePrecedence(110), - // Set parens when needed - if ( - parentPrecedence > 0 && - currentPrecedence > 0 && - currentPrecedence < parentPrecedence - ) { - result = `(${result})`; - } - - return result; - } + "*": makePrecedence(120), + "/": makePrecedence(120), + "%": makePrecedence(120), +}; - ppAstStructFieldInit(param: AstStructFieldInitializer): string { - return `${this.ppAstId(param.field)}: ${this.ppAstExpression(param.initializer)}`; - } +export const prefixPrecedence = makePrecedence(140); - // - // Program - // - - ppAstModule(program: AstModule): string { - const importsFormatted = - program.imports.length > 0 - ? `${program.imports - .map((entry) => this.ppAstImport(entry)) - .join("\n")}\n\n` - : ""; - const entriesFormatted = program.items - .map((entry, index, array) => { - const formattedEntry = this.ppModuleItem(entry); - const nextEntry = array[index + 1]; - if ( - entry.kind === "constant_def" && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - nextEntry?.kind === "constant_def" - ) { - return formattedEntry; - } - return formattedEntry + "\n"; - }) - .join("\n"); - return `${importsFormatted}${entriesFormatted.trim()}`; - } - - ppModuleItem(item: AstModuleItem): string { - switch (item.kind) { - case "struct_decl": - return this.ppAstStruct(item); - case "contract": - return this.ppAstContract(item); - case "primitive_type_decl": - return this.ppAstPrimitiveTypeDecl(item); - case "function_def": - return this.ppAstFunctionDef(item); - case "asm_function_def": - return this.ppAstAsmFunctionDef(item); - case "native_function_decl": - return this.ppAstNativeFunction(item); - case "trait": - return this.ppAstTrait(item); - // case "program_import": - // return this.ppASTProgramImport(item); - case "constant_def": - return this.ppAstConstant(item); - case "message_decl": - return this.ppAstMessage(item); - } - } - - ppAstImport(importItem: AstImport): string { - return `${this.indent()}import "${importItem.path.value}";`; - } - - ppAstStruct(struct: AstStructDecl): string { - this.increaseIndent(); - const fieldsFormatted = struct.fields - .map((field) => this.ppAstFieldDecl(field)) - .join("\n"); - this.decreaseIndent(); - return `${this.indent()}struct ${this.ppAstId(struct.name)} {\n${fieldsFormatted}\n}`; - } +// Used by postfix unary !!, method calls and field accesses +export const postfixPrecedence = makePrecedence(150); - ppAstMessage(struct: AstMessageDecl): string { - const prefixFormatted = - struct.opcode !== null ? `(${astNumToString(struct.opcode)})` : ""; - this.increaseIndent(); - const fieldsFormatted = struct.fields - .map((field) => this.ppAstFieldDecl(field)) - .join("\n"); - this.decreaseIndent(); - return `${this.indent()}message${prefixFormatted} ${this.ppAstId(struct.name)} {\n${fieldsFormatted}\n}`; - } - - ppAstTrait(trait: AstTrait): string { - const traitsFormatted = trait.traits - .map((t) => this.ppAstId(t)) - .join(", "); - const attrsRaw = trait.attributes - .map((attr) => `@${attr.type}("${attr.name.value}")`) - .join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - this.increaseIndent(); - const bodyFormatted = trait.declarations - .map((dec, index, array) => { - const formattedDec = this.ppTraitBody(dec); - const nextDec = array[index + 1]; - /* eslint-disable @typescript-eslint/no-unnecessary-condition */ - if ( - ((dec.kind === "constant_def" || - dec.kind === "constant_decl") && - (nextDec?.kind === "constant_def" || - nextDec?.kind === "constant_decl")) || - (dec.kind === "field_decl" && - nextDec?.kind === "field_decl") - ) { - return formattedDec; - } - /* eslint-enable @typescript-eslint/no-unnecessary-condition */ - return formattedDec + "\n"; - }) - .join("\n"); - const header = traitsFormatted - ? `trait ${this.ppAstId(trait.name)} with ${traitsFormatted}` - : `trait ${this.ppAstId(trait.name)}`; - this.decreaseIndent(); - return `${this.indent()}${attrsFormatted}${header} {\n${bodyFormatted}${this.indent()}}`; - } - - ppTraitBody(item: AstTraitDeclaration): string { - switch (item.kind) { - case "field_decl": - return this.ppAstFieldDecl(item); - case "function_def": - return this.ppAstFunctionDef(item); - case "asm_function_def": - return this.ppAstAsmFunctionDef(item); - case "receiver": - return this.ppAstReceiver(item); - case "constant_def": - return this.ppAstConstant(item); - case "function_decl": - return this.ppAstFunctionDecl(item); - case "constant_decl": - return this.ppAstConstDecl(item); - } - } - - ppAstFieldDecl(field: AstFieldDecl): string { - const typeFormatted = this.ppAstType(field.type); - const initializer = field.initializer - ? ` = ${this.ppAstExpression(field.initializer)}` - : ""; - const asAlias = field.as ? ` as ${this.ppAstId(field.as)}` : ""; - return `${this.indent()}${this.ppAstId(field.name)}: ${typeFormatted}${asAlias}${initializer};`; - } +/** + * Expression printer takes an expression and a function from parent AST node printer that checks + * whether expressions with given precedence should be parenthesized in parent context + */ +export type ExprPrinter = ( + expr: T, +) => (check: (childPrecedence: number) => boolean) => string; - ppAstConstant(constant: AstConstantDef): string { - const valueFormatted = ` = ${this.ppAstExpression(constant.initializer)}`; - const attrsRaw = constant.attributes.map((attr) => attr.type).join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - return `${this.indent()}${attrsFormatted}const ${this.ppAstId(constant.name)}: ${this.ppAstType(constant.type)}${valueFormatted};`; - } +/** + * Wrapper for AST nodes that should never be parenthesized, and thus do not require information + * about the position they're printed in + * + * Takes a regular printer function and returns corresponding ExprPrinter that ignores all + * position and precedence information + */ +export const ppLeaf = + (printer: (t: T) => string): ExprPrinter => + (node) => + () => + printer(node); + +export const ppExprArgs = (args: A.AstExpression[]) => + args.map((arg) => ppAstExpression(arg)).join(", "); + +export const ppAstStructFieldInit = ( + param: A.AstStructFieldInitializer, +): string => `${ppAstId(param.field)}: ${ppAstExpression(param.initializer)}`; + +export const ppAstStructInstance = ({ type, args }: A.AstStructInstance) => + `${ppAstId(type)}{${args.map((x) => ppAstStructFieldInit(x)).join(", ")}}`; + +export const ppAstInitOf = ({ contract, args }: A.AstInitOf) => + `initOf ${ppAstId(contract)}(${ppExprArgs(args)})`; + +export const ppAstNumber = A.astNumToString; +export const ppAstBoolean = ({ value }: A.AstBoolean) => value.toString(); +export const ppAstId = ({ text }: A.AstId) => text; +export const ppAstNull = (_expr: A.AstNull) => "null"; +export const ppAstString = ({ value }: A.AstString) => `"${value}"`; + +export const ppAstStaticCall = ({ function: func, args }: A.AstStaticCall) => { + return `${ppAstId(func)}(${ppExprArgs(args)})`; +}; + +export const ppAstMethodCall: ExprPrinter = + ({ self: object, method, args }) => + (position) => { + const { brace, self } = postfixPrecedence; + return brace( + position, + `${ppAstExpressionNested(object)(self)}.${ppAstId(method)}(${ppExprArgs(args)})`, + ); + }; + +export const ppAstFieldAccess: ExprPrinter = + ({ aggregate, field }) => + (position) => { + const { brace, self } = postfixPrecedence; + return brace( + position, + `${ppAstExpressionNested(aggregate)(self)}.${ppAstId(field)}`, + ); + }; + +export const ppAstOpUnary: ExprPrinter = + ({ op, operand }) => + (position) => { + const isPostfix = checkPostfix(op); + const { brace, self } = isPostfix + ? postfixPrecedence + : prefixPrecedence; + const code = ppAstExpressionNested(operand)(self); + return brace(position, isPostfix ? `${code}${op}` : `${op}${code}`); + }; + +export const ppAstOpBinary: ExprPrinter = + ({ left, op, right }) => + (position) => { + const { brace, self, child } = binaryPrecedence[op]; + const leftCode = ppAstExpressionNested(left)(self); + const rightCode = ppAstExpressionNested(right)(child); + return brace(position, `${leftCode} ${op} ${rightCode}`); + }; + +export const ppAstConditional: ExprPrinter = + ({ condition, thenBranch, elseBranch }) => + (position) => { + const { brace, self, child } = conditionalPrecedence; + const conditionCode = ppAstExpressionNested(condition)(child); + const thenCode = ppAstExpressionNested(thenBranch)(child); + const elseCode = ppAstExpressionNested(elseBranch)(self); + return brace(position, `${conditionCode} ? ${thenCode} : ${elseCode}`); + }; + +export const ppAstExpressionNested = makeVisitor()({ + struct_instance: ppLeaf(ppAstStructInstance), + number: ppLeaf(ppAstNumber), + boolean: ppLeaf(ppAstBoolean), + id: ppLeaf(ppAstId), + null: ppLeaf(ppAstNull), + init_of: ppLeaf(ppAstInitOf), + string: ppLeaf(ppAstString), + static_call: ppLeaf(ppAstStaticCall), + + method_call: ppAstMethodCall, + field_access: ppAstFieldAccess, + + op_unary: ppAstOpUnary, + + op_binary: ppAstOpBinary, + + conditional: ppAstConditional, +}); + +export const ppAstExpression = (expr: A.AstExpression): string => { + return ppAstExpressionNested(expr)(lowestPrecedence.child); +}; - ppAstConstDecl(constant: AstConstantDecl): string { - const attrsRaw = constant.attributes.map((attr) => attr.type).join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - return `${this.indent()}${attrsFormatted}const ${this.ppAstId(constant.name)}: ${this.ppAstType(constant.type)};`; - } +/** + * An intermediate language that is only concerned of spacing and indentation + */ +type Context = { + /** + * Line of code with \n implied + */ + row: (s: string) => U; - ppAstContract(contract: AstContract): string { - const traitsFormatted = contract.traits - .map((trait) => trait.text) - .join(", "); - this.increaseIndent(); - const bodyFormatted = contract.declarations - .map((dec, index, array) => { - const formattedDec = this.ppContractBody(dec); - const nextDec = array[index + 1]; - if ( - (dec.kind === "constant_def" && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - nextDec?.kind === "constant_def") || - (dec.kind === "field_decl" && - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - nextDec?.kind === "field_decl") - ) { - return formattedDec; - } - return formattedDec + "\n"; - }) - .join("\n"); - this.decreaseIndent(); - const header = traitsFormatted - ? `contract ${this.ppAstId(contract.name)} with ${traitsFormatted}` - : `contract ${this.ppAstId(contract.name)}`; - const attrsRaw = contract.attributes - .map((attr) => `@interface("${attr.name.value}")`) - .join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - return `${this.indent()}${attrsFormatted}${header} {\n${bodyFormatted}${this.indent()}}`; - } + /** + * Stacks lines after each other + */ + block: (rows: readonly U[]) => U; - ppContractBody(declaration: AstContractDeclaration): string { - switch (declaration.kind) { - case "field_decl": - return this.ppAstFieldDecl(declaration); - case "function_def": - return this.ppAstFunctionDef(declaration); - case "asm_function_def": - return this.ppAstAsmFunctionDef(declaration); - case "contract_init": - return this.ppAstInitFunction(declaration); - case "receiver": - return this.ppAstReceiver(declaration); - case "constant_def": - return this.ppAstConstant(declaration); - } - } + /** + * Similar to `block`, but adjacent lines of groups get concatenated + * [a, b] + [c, d] = [a, bc, d] + */ + concat: (rows: readonly U[]) => U; - public ppAstFunctionDef(f: AstFunctionDef): string { - const body = this.ppStatementBlock(f.statements); - return `${this.indent()}${this.ppAstFunctionSignature(f.attributes, f.name, f.return, f.params)} ${body}`; - } + /** + * Same as `indent`, but indents `rows` 1 level deeper and adds `{` and `}` + */ + braced: (rows: readonly U[]) => U; - public ppAstAsmFunctionDef(f: AstAsmFunctionDef): string { - const asmAttr = `asm${prettyPrintAsmShuffle(f.shuffle)}`; - const body = this.ppAsmInstructionsBlock(f.instructions); - return `${this.indent()}${asmAttr} ${this.ppAstFunctionSignature(f.attributes, f.name, f.return, f.params)} ${body}`; - } + /** + * Print a list of `items` with `print` + */ + list: (items: readonly T[], print: Printer) => readonly U[]; - ppAstFunctionDecl(f: AstFunctionDecl): string { - return `${this.indent()}${this.ppAstFunctionSignature(f.attributes, f.name, f.return, f.params)};`; - } + /** + * Display `items` with `print` in groups distinguished by return value of `getTag` + */ + grouped: (options: { + items: readonly T[]; + /** + * Items with the same tag are displayed without extra empty line between them + * + * Use NaN for tag whenever items should always be displayed with empty line, + * because NaN !== NaN + */ + getTag: (t: T) => V; + print: Printer; + }) => readonly U[]; +}; - ppAstFunctionSignature( - attributes: AstFunctionAttribute[], - name: AstId, - retTy: AstType | null, - params: AstTypedParameter[], - ): string { - const argsFormatted = params - .map( - (arg) => - `${this.ppAstId(arg.name)}: ${this.ppAstType(arg.type)}`, - ) - .join(", "); - const attrsRaw = attributes - .map((attr) => this.ppAstFunctionAttribute(attr)) - .join(" "); - const attrsFormatted = attrsRaw ? `${attrsRaw} ` : ""; - const returnType = retTy ? `: ${this.ppAstType(retTy)}` : ""; - return `${attrsFormatted}fun ${this.ppAstId(name)}(${argsFormatted})${returnType}`; - } +/** + * Generates line of code with indentation, given desired indentation level of outer scope + */ +type LevelFn = (level: number) => string; - ppAstFunctionAttribute(attr: AstFunctionAttribute): string { - if (attr.type === "get" && attr.methodId !== null) { - return `get(${this.ppAstExpression(attr.methodId)})`; - } else { - return attr.type; - } - } +/** + * Result of printing an expression is an array of rows, parameterized over indentation + * of outer scope + */ +type ContextModel = readonly LevelFn[]; - ppAstReceiver(receive: AstReceiver): string { - const header = this.ppAstReceiverHeader(receive); - const stmtsFormatted = this.ppStatementBlock(receive.statements); - return `${this.indent()}${header} ${stmtsFormatted}`; - } +/** + * Concatenates an array of printing results, so that last line of each expression is merged + * with first line of next expression + * + * Typically used to generate multiline indented code as part of single-line expression + * + * Roughly, `concat(["while (true)"], [" "], ["{", "...", "}"]) = ["while (true) {", "...", "}"]` + */ +const concat = ([head, ...tail]: readonly ContextModel[]): ContextModel => { + // If we're concatenating an empty array, the result is always empty + if (isUndefined(head)) { + return []; + } + // Create a copy of first printing result, where we'll accumulate other results + const init = [...head]; + // Recursively concatenate all printing results except for first + const next = concat(tail); + // Take last line of first printing result + const last = init.pop(); + // If first printing result has no lines, return concatenation result of all others + if (isUndefined(last)) { + return next; + } + // Get first line on concatenated printing results starting with second + const [nextHead, ...nextTail] = next; + // If they all concatenated into an array of 0 lines, just return first printing result + if (isUndefined(nextHead)) { + return head; + } + // Otherwise concatenate results, leaving indent only in front of the merged line + return [...init, (level) => last(level) + nextHead(0), ...nextTail]; +}; + +const createContext = (spaces: number): Context => { + const row = (s: string) => [ + // Empty lines are not indented + (level: number) => (s === "" ? s : " ".repeat(level * spaces) + s), + ]; + const block = (rows: readonly ContextModel[]) => rows.flat(); + const indent = (rows: readonly ContextModel[]) => + block(rows).map((f) => (level: number) => f(level + 1)); + const braced = (rows: readonly ContextModel[]) => + block([row(`{`), indent(rows), row(`}`)]); + const list = (items: readonly T[], print: Printer) => + items.map((node) => print(node)(ctx)); + const grouped = ({ + items, + getTag, + print, + }: { + items: readonly T[]; + getTag: (t: T) => V; + print: Printer; + }) => { + return intercalate( + groupBy(items, getTag).map((group) => list(group, print)), + row(""), + ); + }; + const ctx: Context = { + row, + concat, + block, + braced, + list, + grouped, + }; + return ctx; +}; - ppAstReceiverHeader(receive: AstReceiver): string { - switch (receive.selector.kind) { - case "internal-simple": - return `receive(${this.ppAstId(receive.selector.param.name)}: ${this.ppAstType(receive.selector.param.type)})`; - case "internal-fallback": - return `receive()`; - case "internal-comment": - return `receive("${receive.selector.comment.value}")`; - case "bounce": - return `bounced(${this.ppAstId(receive.selector.param.name)}: ${this.ppAstType(receive.selector.param.type)})`; - case "external-simple": - return `external(${this.ppAstId(receive.selector.param.name)}: ${this.ppAstType(receive.selector.param.type)})`; - case "external-fallback": - return `external()`; - case "external-comment": - return `external("${receive.selector.comment.value}")`; +/** + * Prints AST node of type `T` into an intermediate language of row of type `U` + * + * We enforce `U` to be a generic argument so that no implementation can (ab)use + * the fact it's a string and generate some indentation without resorting to + * methods of `Context`. + */ +type Printer = (item: T) => (ctx: Context) => U; + +export const ppAstModule: Printer = + ({ imports, items }) => + (c) => { + const itemsCode = c.grouped({ + items, + getTag: ({ kind }) => (kind === "constant_def" ? 1 : NaN), + print: ppModuleItem, + }); + if (imports.length === 0) { + return c.block(itemsCode); } - } - - ppAstNativeFunction(func: AstNativeFunctionDecl): string { - const argsFormatted = func.params - .map( - (arg) => - `${this.ppAstId(arg.name)}: ${this.ppAstType(arg.type)}`, - ) + return c.block([ + ...c.list(imports, ppAstImport), + c.row(""), + ...itemsCode, + ]); + }; + +export const ppAstStruct: Printer = + ({ name, fields }) => + (c) => + c.concat([ + c.row(`struct ${ppAstId(name)} `), + c.braced(c.list(fields, ppAstFieldDecl)), + ]); + +export const ppAstContract: Printer = + ({ name, traits, declarations, attributes }) => + (c) => { + const attrsCode = attributes + .map(({ name: { value } }) => `@interface("${value}") `) + .join(""); + const traitsCode = traits.map((trait) => trait.text).join(", "); + const header = traitsCode + ? `contract ${ppAstId(name)} with ${traitsCode}` + : `contract ${ppAstId(name)}`; + return c.concat([ + c.row(`${attrsCode}${header} `), + c.braced( + c.grouped({ + items: declarations, + getTag: ({ kind }) => + kind === "constant_def" + ? 1 + : kind === "field_decl" + ? 2 + : NaN, + print: ppContractBody, + }), + ), + ]); + }; + +export const ppAstPrimitiveTypeDecl: Printer = + ({ name }) => + (c) => + c.row(`primitive ${ppAstId(name)};`); + +export const ppAstFunctionDef: Printer = (node) => (c) => + c.concat([ + c.row(ppAstFunctionSignature(node)), + c.row(" "), + ppStatementBlock(node.statements)(c), + ]); + +export const ppAsmShuffle = ({ args, ret }: A.AstAsmShuffle): string => { + if (args.length === 0 && ret.length === 0) { + return ""; + } + const argsCode = args.map(({ text }) => text).join(" "); + if (ret.length === 0) { + return `(${argsCode})`; + } + const retCode = ret.map(({ value }) => value.toString()).join(" "); + return `(${argsCode} -> ${retCode})`; +}; + +export const ppAstAsmFunctionDef: Printer = + (node) => (c) => + c.concat([ + c.row( + `asm${ppAsmShuffle(node.shuffle)} ${ppAstFunctionSignature(node)} `, + ), + ppAsmInstructionsBlock(node.instructions)(c), + ]); + +export const ppAstNativeFunction: Printer = + ({ name, nativeName, params, return: retTy, attributes }) => + (c) => { + const attrs = attributes.map(({ type }) => type + " ").join(""); + const argsCode = params + .map(({ name, type }) => `${ppAstId(name)}: ${ppAstType(type)}`) .join(", "); - const returnType = func.return - ? `: ${this.ppAstType(func.return)}` + const returnType = retTy ? `: ${ppAstType(retTy)}` : ""; + return c.block([ + c.row(`@name(${ppAstFuncId(nativeName)})`), + c.row(`${attrs}native ${ppAstId(name)}(${argsCode})${returnType};`), + ]); + }; + +export const ppAstTrait: Printer = + ({ name, traits, attributes, declarations }) => + (c) => { + const attrsCode = attributes + .map((attr) => `@${attr.type}("${attr.name.value}") `) + .join(""); + const traitsCode = traits.map((t) => ppAstId(t)).join(", "); + const header = traitsCode + ? `trait ${ppAstId(name)} with ${traitsCode}` + : `trait ${ppAstId(name)}`; + return c.concat([ + c.row(`${attrsCode}${header} `), + c.braced( + c.grouped({ + items: declarations, + getTag: ({ kind }) => + kind === "constant_def" || kind === "constant_decl" + ? 1 + : kind === "field_decl" + ? 2 + : NaN, + print: ppTraitBody, + }), + ), + ]); + }; + +export const ppAstConstant: Printer = + ({ attributes, initializer, name, type }) => + (c) => { + const attrsCode = attributes.map(({ type }) => type + " ").join(""); + return c.row( + `${attrsCode}const ${ppAstId(name)}: ${ppAstType(type)} = ${ppAstExpression(initializer)};`, + ); + }; + +export const ppAstMessage: Printer = + ({ name, opcode, fields }) => + (c) => { + const prefixCode = + opcode !== null ? `(${A.astNumToString(opcode)})` : ""; + + return c.concat([ + c.row(`message${prefixCode} ${ppAstId(name)} `), + c.braced(c.list(fields, ppAstFieldDecl)), + ]); + }; + +export const ppModuleItem: Printer = + makeVisitor()({ + struct_decl: ppAstStruct, + contract: ppAstContract, + primitive_type_decl: ppAstPrimitiveTypeDecl, + function_def: ppAstFunctionDef, + asm_function_def: ppAstAsmFunctionDef, + native_function_decl: ppAstNativeFunction, + trait: ppAstTrait, + constant_def: ppAstConstant, + message_decl: ppAstMessage, + }); + +export const ppAstFieldDecl: Printer = + ({ type, initializer, as, name }) => + (c) => { + const asAlias = as ? ` as ${ppAstId(as)}` : ""; + const initializerCode = initializer + ? ` = ${ppAstExpression(initializer)}` : ""; - let attrs = func.attributes.map((attr) => attr.type).join(" "); - attrs = attrs ? attrs + " " : ""; - return `${this.indent()}@name(${this.ppAstFuncId(func.nativeName)})\n${this.indent()}${attrs}native ${this.ppAstId(func.name)}(${argsFormatted})${returnType};`; - } - - ppAstFuncId(func: AstFuncId): string { - return func.text; - } - - ppAstInitFunction(initFunc: AstContractInit): string { - const argsFormatted = initFunc.params - .map( - (arg) => - `${this.ppAstId(arg.name)}: ${this.ppAstType(arg.type)}`, - ) + return c.row( + `${ppAstId(name)}: ${ppAstType(type)}${asAlias}${initializerCode};`, + ); + }; + +export const ppAstReceiver: Printer = + ({ selector, statements }) => + (c) => + c.concat([ + c.row(`${ppAstReceiverHeader(selector)} `), + ppStatementBlock(statements)(c), + ]); + +export const ppAstFunctionDecl: Printer = (f) => (c) => + c.row(`${ppAstFunctionSignature(f)};`); + +export const ppAstConstDecl: Printer = + ({ attributes, name, type }) => + (c) => { + const attrsCode = attributes.map(({ type }) => type + " ").join(""); + return c.row(`${attrsCode}const ${ppAstId(name)}: ${ppAstType(type)};`); + }; + +export const ppTraitBody: Printer = + makeVisitor()({ + function_def: ppAstFunctionDef, + asm_function_def: ppAstAsmFunctionDef, + constant_def: ppAstConstant, + field_decl: ppAstFieldDecl, + receiver: ppAstReceiver, + function_decl: ppAstFunctionDecl, + constant_decl: ppAstConstDecl, + }); + +export const ppAstInitFunction: Printer = + ({ params, statements }) => + (c) => { + const argsCode = params + .map(({ name, type }) => `${ppAstId(name)}: ${ppAstType(type)}`) .join(", "); - - this.increaseIndent(); - const stmtsFormatted = initFunc.statements - .map((stmt) => this.ppAstStatement(stmt)) - .join("\n"); - this.decreaseIndent(); - - return `${this.indent()}init(${argsFormatted}) {${stmtsFormatted == "" ? "" : "\n"}${stmtsFormatted}${stmtsFormatted == "" ? "" : "\n" + this.indent()}}`; - } - - // - // Statements - // - - ppAstStatement(stmt: AstStatement): string { - switch (stmt.kind) { - case "statement_let": - return this.ppAstStatementLet(stmt as AstStatementLet); - case "statement_return": - return this.ppAstStatementReturn(stmt as AstStatementReturn); - case "statement_expression": - return this.ppAstStatementExpression( - stmt as AstStatementExpression, - ); - case "statement_assign": - return this.ppAstStatementAssign(stmt as AstStatementAssign); - case "statement_augmentedassign": - return this.ppAstStatementAugmentedAssign( - stmt as AstStatementAugmentedAssign, - ); - case "statement_condition": - return this.ppAstCondition(stmt as AstCondition); - case "statement_while": - return this.ppAstStatementWhile(stmt as AstStatementWhile); - case "statement_until": - return this.ppAstStatementUntil(stmt as AstStatementUntil); - case "statement_repeat": - return this.ppAstStatementRepeat(stmt as AstStatementRepeat); - case "statement_foreach": - return this.ppAstStatementForEach(stmt as AstStatementForEach); - case "statement_try": - return this.ppAstStatementTry(stmt as AstStatementTry); - case "statement_try_catch": - return this.ppAstStatementTryCatch( - stmt as AstStatementTryCatch, - ); - case "statement_destruct": - return this.ppAstStatementDestruct( - stmt as AstStatementDestruct, - ); + if (statements.length === 0) { + return c.row(`init(${argsCode}) {}`); } - } - - ppStatementBlock(stmts: AstStatement[]): string { - this.increaseIndent(); - const stmtsFormatted = stmts - .map((stmt) => this.ppAstStatement(stmt)) - .join("\n"); - this.decreaseIndent(); - const result = `{\n${stmtsFormatted}\n${this.indent()}}`; - return result; - } - - ppAsmInstructionsBlock(instructions: AstAsmInstruction[]): string { - this.increaseIndent(); - const instructionsFormatted = instructions - .map((instr) => this.ppAstAsmInstruction(instr)) - .join("\n"); - this.decreaseIndent(); - return `{\n${instructionsFormatted}\n${this.indent()}}`; - } - - ppAstAsmInstruction(instruction: AstAsmInstruction): string { - return `${this.indent()}${instruction}`; - } - - ppAstStatementLet(statement: AstStatementLet): string { - const expression = this.ppAstExpression(statement.expression); - const tyAnnotation = - statement.type === null - ? "" - : `: ${this.ppAstType(statement.type)}`; - return `${this.indent()}let ${this.ppAstId(statement.name)}${tyAnnotation} = ${expression};`; - } - - ppAstStatementReturn(statement: AstStatementReturn): string { - const expression = statement.expression - ? this.ppAstExpression(statement.expression) - : ""; - return `${this.indent()}return ${expression};`; - } - - ppAstStatementExpression(statement: AstStatementExpression): string { - return `${this.indent()}${this.ppAstExpression(statement.expression)};`; - } - - ppAstId(id: AstId) { - return id.text; - } - - ppAstStatementAssign(statement: AstStatementAssign): string { - return `${this.indent()}${this.ppAstExpression(statement.path)} = ${this.ppAstExpression(statement.expression)};`; - } - - ppAstStatementAugmentedAssign( - statement: AstStatementAugmentedAssign, - ): string { - return `${this.indent()}${this.ppAstExpression(statement.path)} ${statement.op}= ${this.ppAstExpression(statement.expression)};`; - } - - ppAstCondition(statement: AstCondition): string { - const condition = this.ppAstExpression(statement.condition); - const trueBranch = this.ppStatementBlock(statement.trueStatements); - const falseBranch = statement.falseStatements - ? ` else ${this.ppStatementBlock(statement.falseStatements)}` - : ""; - return `${this.indent()}if (${condition}) ${trueBranch}${falseBranch}`; - } - - ppAstStatementWhile(statement: AstStatementWhile): string { - const condition = this.ppAstExpression(statement.condition); - const stmts = this.ppStatementBlock(statement.statements); - return `${this.indent()}while (${condition}) ${stmts}`; - } - - ppAstStatementRepeat(statement: AstStatementRepeat): string { - const condition = this.ppAstExpression(statement.iterations); - const stmts = this.ppStatementBlock(statement.statements); - return `${this.indent()}repeat (${condition}) ${stmts}`; - } - - ppAstStatementUntil(statement: AstStatementUntil): string { - const condition = this.ppAstExpression(statement.condition); - const stmts = this.ppStatementBlock(statement.statements); - return `${this.indent()}do ${stmts} until (${condition});`; - } - - ppAstStatementForEach(statement: AstStatementForEach): string { - const header = `foreach (${this.ppAstId(statement.keyName)}, ${this.ppAstId(statement.valueName)} in ${this.ppAstExpression(statement.map)})`; - const body = this.ppStatementBlock(statement.statements); - return `${this.indent()}${header} ${body}`; - } - - ppAstStatementTry(statement: AstStatementTry): string { - const body = this.ppStatementBlock(statement.statements); - return `${this.indent()}try ${body}`; - } - - ppAstStatementTryCatch(statement: AstStatementTryCatch): string { - const tryBody = this.ppStatementBlock(statement.statements); - const catchBody = this.ppStatementBlock(statement.catchStatements); - return `${this.indent()}try ${tryBody} catch (${this.ppAstId(statement.catchName)}) ${catchBody}`; - } - - ppAstStatementDestruct(statement: AstStatementDestruct): string { - const ids = statement.identifiers - .values() - .reduce((acc: string[], [field, name]) => { - const id = - field.text === name.text - ? this.ppAstId(name) - : `${this.ppAstId(field)}: ${this.ppAstId(name)}`; - acc.push(id); - return acc; - }, []); - return `${this.indent()}let ${this.ppAstTypeId(statement.type)} {${ids.join(", ")}} = ${this.ppAstExpression(statement.expression)};`; - } -} + return c.concat([ + c.row(`init(${argsCode}) `), + c.braced(c.list(statements, ppAstStatement)), + ]); + }; + +export const ppContractBody: Printer = + makeVisitor()({ + field_decl: ppAstFieldDecl, + function_def: ppAstFunctionDef, + asm_function_def: ppAstAsmFunctionDef, + contract_init: ppAstInitFunction, + receiver: ppAstReceiver, + constant_def: ppAstConstant, + }); + +export const ppAstImport: Printer = + ({ path }) => + (c) => + c.row(`import "${path.value}";`); + +export const ppAstFunctionSignature = ({ + name, + attributes, + return: retTy, + params, +}: A.AstFunctionDef | A.AstAsmFunctionDef | A.AstFunctionDecl): string => { + const argsCode = params + .map(({ name, type }) => `${ppAstId(name)}: ${ppAstType(type)}`) + .join(", "); + const attrsCode = attributes + .map((attr) => ppAstFunctionAttribute(attr) + " ") + .join(""); + const returnType = retTy ? `: ${ppAstType(retTy)}` : ""; + return `${attrsCode}fun ${ppAstId(name)}(${argsCode})${returnType}`; +}; + +export const ppAstFunctionAttribute = ( + attr: A.AstFunctionAttribute, +): string => { + if (attr.type === "get" && attr.methodId !== null) { + return `get(${ppAstExpression(attr.methodId)})`; + } else { + return attr.type; + } +}; + +export const ppAstReceiverHeader = makeVisitor()({ + bounce: ({ param: { name, type } }) => + `bounced(${ppAstId(name)}: ${ppAstType(type)})`, + "internal-simple": ({ param: { name, type } }) => + `receive(${ppAstId(name)}: ${ppAstType(type)})`, + "external-simple": ({ param: { name, type } }) => + `external(${ppAstId(name)}: ${ppAstType(type)})`, + "internal-fallback": () => `receive()`, + "external-fallback": () => `external()`, + "internal-comment": ({ comment: { value } }) => `receive("${value}")`, + "external-comment": ({ comment: { value } }) => `external("${value}")`, +}); + +export const ppAstFuncId = (func: A.AstFuncId): string => func.text; + +// +// Statements +// + +export const ppStatementBlock: Printer = (stmts) => (c) => + c.braced(stmts.length === 0 ? [c.row("")] : c.list(stmts, ppAstStatement)); + +export const ppAsmInstructionsBlock: Printer = + (instructions) => (c) => + c.braced(instructions.map(c.row)); + +export const ppAstStatementLet: Printer = + ({ type, name, expression }) => + (c) => { + const tyAnnotation = type === null ? "" : `: ${ppAstType(type)}`; + return c.row( + `let ${ppAstId(name)}${tyAnnotation} = ${ppAstExpression(expression)};`, + ); + }; + +export const ppAstStatementReturn: Printer = + ({ expression }) => + (c) => + c.row(`return ${expression ? ppAstExpression(expression) : ""};`); + +export const ppAstStatementExpression: Printer = + ({ expression }) => + (c) => + c.row(`${ppAstExpression(expression)};`); + +export const ppAstStatementAssign: Printer = + ({ path, expression }) => + (c) => + c.row(`${ppAstExpression(path)} = ${ppAstExpression(expression)};`); + +export const ppAstStatementAugmentedAssign: Printer< + A.AstStatementAugmentedAssign +> = + ({ path, op, expression }) => + (c) => + c.row( + `${ppAstExpression(path)} ${op}= ${ppAstExpression(expression)};`, + ); + +export const ppAstCondition: Printer = + ({ condition, trueStatements, falseStatements }) => + (c) => { + if (falseStatements) { + return c.concat([ + c.row(`if (${ppAstExpression(condition)}) `), + ppStatementBlock(trueStatements)(c), + c.row(" else "), + ppStatementBlock(falseStatements)(c), + ]); + } else { + return c.concat([ + c.row(`if (${ppAstExpression(condition)}) `), + ppStatementBlock(trueStatements)(c), + ]); + } + }; + +export const ppAstStatementWhile: Printer = + ({ condition, statements }) => + (c) => + c.concat([ + c.row(`while (${ppAstExpression(condition)}) `), + ppStatementBlock(statements)(c), + ]); + +export const ppAstStatementRepeat: Printer = + ({ iterations, statements }) => + (c) => + c.concat([ + c.row(`repeat (${ppAstExpression(iterations)}) `), + ppStatementBlock(statements)(c), + ]); + +export const ppAstStatementUntil: Printer = + ({ condition, statements }) => + (c) => + c.concat([ + c.row(`do `), + ppStatementBlock(statements)(c), + c.row(` until (${ppAstExpression(condition)});`), + ]); + +export const ppAstStatementForEach: Printer = + ({ keyName, valueName, map, statements }) => + (c) => + c.concat([ + c.row( + `foreach (${ppAstId(keyName)}, ${ppAstId(valueName)} in ${ppAstExpression(map)}) `, + ), + ppStatementBlock(statements)(c), + ]); + +export const ppAstStatementTry: Printer = + ({ statements }) => + (c) => + c.concat([c.row(`try `), ppStatementBlock(statements)(c)]); + +export const ppAstStatementTryCatch: Printer = + ({ statements, catchName, catchStatements }) => + (c) => + c.concat([ + c.row(`try `), + ppStatementBlock(statements)(c), + c.row(` catch (${ppAstId(catchName)}) `), + ppStatementBlock(catchStatements)(c), + ]); + +export const ppAstStatementDestruct: Printer = + ({ type, identifiers, ignoreUnspecifiedFields, expression }) => + (c) => { + const ids: string[] = []; + for (const [field, name] of identifiers.values()) { + const id = + field.text === name.text + ? ppAstId(name) + : `${ppAstId(field)}: ${ppAstId(name)}`; + ids.push(id); + } + const restPattern = ignoreUnspecifiedFields ? ", .." : ""; + return c.row( + `let ${ppAstTypeId(type)} {${ids.join(", ")}${restPattern}} = ${ppAstExpression(expression)};`, + ); + }; + +export const ppAstStatement: Printer = + makeVisitor()({ + statement_let: ppAstStatementLet, + statement_return: ppAstStatementReturn, + statement_expression: ppAstStatementExpression, + statement_assign: ppAstStatementAssign, + statement_augmentedassign: ppAstStatementAugmentedAssign, + statement_condition: ppAstCondition, + statement_while: ppAstStatementWhile, + statement_until: ppAstStatementUntil, + statement_repeat: ppAstStatementRepeat, + statement_foreach: ppAstStatementForEach, + statement_try: ppAstStatementTry, + statement_try_catch: ppAstStatementTryCatch, + statement_destruct: ppAstStatementDestruct, + }); + +export const exprNode = + (exprPrinter: (expr: T) => string): Printer => + (node) => + (c) => + c.row(exprPrinter(node)); + +export const ppAstNode: Printer = makeVisitor()({ + op_binary: exprNode(ppAstExpression), + op_unary: exprNode(ppAstExpression), + field_access: exprNode(ppAstExpression), + method_call: exprNode(ppAstExpression), + static_call: exprNode(ppAstExpression), + struct_instance: exprNode(ppAstExpression), + init_of: exprNode(ppAstExpression), + conditional: exprNode(ppAstExpression), + number: exprNode(ppAstExpression), + id: exprNode(ppAstExpression), + boolean: exprNode(ppAstExpression), + string: exprNode(ppAstExpression), + null: exprNode(ppAstExpression), + type_id: exprNode(ppAstType), + optional_type: exprNode(ppAstType), + map_type: exprNode(ppAstType), + bounced_message_type: exprNode(ppAstType), + struct_field_initializer: exprNode(ppAstStructFieldInit), + destruct_mapping: () => { + throw new Error("Not implemented"); + }, + typed_parameter: () => { + throw new Error("Not implemented"); + }, + destruct_end: () => { + throw new Error("Not implemented"); + }, + + module: ppAstModule, + struct_decl: ppAstStruct, + constant_def: ppAstConstant, + constant_decl: ppAstConstDecl, + function_def: ppAstFunctionDef, + contract: ppAstContract, + trait: ppAstTrait, + primitive_type_decl: ppAstPrimitiveTypeDecl, + message_decl: ppAstMessage, + native_function_decl: ppAstNativeFunction, + field_decl: ppAstFieldDecl, + function_decl: ppAstFunctionDecl, + receiver: ppAstReceiver, + contract_init: ppAstInitFunction, + statement_let: ppAstStatementLet, + statement_return: ppAstStatementReturn, + statement_expression: ppAstStatementExpression, + statement_assign: ppAstStatementAssign, + statement_augmentedassign: ppAstStatementAugmentedAssign, + statement_condition: ppAstCondition, + statement_while: ppAstStatementWhile, + statement_until: ppAstStatementUntil, + statement_repeat: ppAstStatementRepeat, + statement_try: ppAstStatementTry, + statement_try_catch: ppAstStatementTryCatch, + statement_foreach: ppAstStatementForEach, + import: ppAstImport, + func_id: exprNode(ppAstFuncId), + statement_destruct: ppAstStatementDestruct, + function_attribute: exprNode(ppAstFunctionAttribute), + asm_function_def: ppAstAsmFunctionDef, +}); /** * Pretty-prints an AST node into a string representation. * @param node The AST node to format. * @returns A string that represents the formatted AST node. */ -export function prettyPrint(node: AstNode): string { - const pp = new PrettyPrinter(); - switch (node.kind) { - case "module": - return pp.ppAstModule(node); - case "op_binary": - case "op_unary": - case "field_access": - case "method_call": - case "static_call": - case "struct_instance": - case "init_of": - case "conditional": - case "number": - case "id": - case "boolean": - case "string": - case "null": - return pp.ppAstExpression(node); - case "struct_decl": - return pp.ppAstStruct(node); - case "constant_def": - return pp.ppAstConstant(node); - case "constant_decl": - return pp.ppAstConstDecl(node); - case "function_def": - return pp.ppAstFunctionDef(node); - case "contract": - return pp.ppAstContract(node); - case "trait": - return pp.ppAstTrait(node); - case "type_id": - case "optional_type": - case "map_type": - case "bounced_message_type": - return pp.ppAstType(node); - case "primitive_type_decl": - return pp.ppAstPrimitiveTypeDecl(node); - case "message_decl": - return pp.ppAstMessage(node); - case "native_function_decl": - return pp.ppAstNativeFunction(node); - case "field_decl": - return pp.ppAstFieldDecl(node); - case "function_decl": - return pp.ppAstFunctionDecl(node); - case "receiver": - return pp.ppAstReceiver(node); - case "contract_init": - return pp.ppAstInitFunction(node); - case "statement_let": - return pp.ppAstStatementLet(node); - case "statement_return": - return pp.ppAstStatementReturn(node); - case "statement_expression": - return pp.ppAstStatementExpression(node); - case "statement_assign": - return pp.ppAstStatementAssign(node); - case "statement_augmentedassign": - return pp.ppAstStatementAugmentedAssign(node); - case "statement_condition": - return pp.ppAstCondition(node); - case "statement_while": - return pp.ppAstStatementWhile(node); - case "statement_until": - return pp.ppAstStatementUntil(node); - case "statement_repeat": - return pp.ppAstStatementRepeat(node); - case "statement_try": - return pp.ppAstStatementTry(node); - case "statement_try_catch": - return pp.ppAstStatementTryCatch(node); - case "statement_foreach": - return pp.ppAstStatementForEach(node); - case "struct_field_initializer": - return pp.ppAstStructFieldInit(node); - case "import": - return pp.ppAstImport(node); - default: - throwInternalCompilerError( - `Unsupported AST type: ${JSONbig.stringify(node, null, 2)}`, - ); - } -} - -export function prettyPrintAsmShuffle(shuffle: AstAsmShuffle): string { - const ppArgShuffle = shuffle.args.map((id) => idText(id)).join(" "); - const ppRetShuffle = - shuffle.ret.length === 0 - ? "" - : ` -> ${shuffle.ret.map((num) => num.value.toString()).join(" ")}`; - return shuffle.args.length === 0 && shuffle.ret.length === 0 - ? "" - : `(${ppArgShuffle}${ppRetShuffle})`; -} +export const prettyPrint = (node: A.AstNode): string => + ppAstNode(node)(createContext(4)) + // Initial level of indentation is 0 + .map((f) => f(0)) + // Lines are terminated with \n + .join("\n"); diff --git a/src/test/compilation-failed/abi-global-errors.spec.ts b/src/test/compilation-failed/abi-global-errors.spec.ts new file mode 100644 index 000000000..3f9808dfc --- /dev/null +++ b/src/test/compilation-failed/abi-global-errors.spec.ts @@ -0,0 +1,13 @@ +import { __DANGER_resetNodeId } from "../../grammar/ast"; +import { itShouldNotCompile } from "./util"; + +describe("abi/global.ts errors", () => { + beforeEach(() => { + __DANGER_resetNodeId(); + }); + + itShouldNotCompile({ + testName: "sha256-expects-string-or-slice", + errorMessage: "sha256 expects string or slice argument", + }); +}); diff --git a/src/test/compilation-failed/contracts/sha256-expects-string-or-slice.tact b/src/test/compilation-failed/contracts/sha256-expects-string-or-slice.tact new file mode 100644 index 000000000..fc29d475a --- /dev/null +++ b/src/test/compilation-failed/contracts/sha256-expects-string-or-slice.tact @@ -0,0 +1,6 @@ +contract Sha256 { + val: Int = 0; + receive() { + sha256(self.val); + } +} diff --git a/src/test/compilation-failed/tact.config.json b/src/test/compilation-failed/tact.config.json index d63b7dc1a..29a952d88 100644 --- a/src/test/compilation-failed/tact.config.json +++ b/src/test/compilation-failed/tact.config.json @@ -226,6 +226,11 @@ "name": "scope-const-shadows-stdlib-ident", "path": "./contracts/scope-const-shadows-stdlib-ident.tact", "output": "./contracts/output" + }, + { + "name": "sha256-expects-string-or-slice", + "path": "./contracts/sha256-expects-string-or-slice.tact", + "output": "./contracts/output" } ] } diff --git a/src/test/contracts/case-destructuring.tact b/src/test/contracts/case-destructuring.tact index 8a4c89170..e1ace4b5b 100644 --- a/src/test/contracts/case-destructuring.tact +++ b/src/test/contracts/case-destructuring.tact @@ -12,12 +12,12 @@ message M { fun testFunc(): Int { let s = S{a: 1, b: 2, c: 3}; let S {a, b, c} = s; - let S {a: a1} = s; - let S {b: b1} = s; - let S {c: c1} = s; - let S {a: a2, b: b2} = s; - let S {a: a3, c: c3} = s; - let S {b: b4, c: c4} = s; + let S {a: a1, ..} = s; + let S {b: b1, ..} = s; + let S {c: c1, ..} = s; + let S {a: a2, b: b2, ..} = s; + let S {a: a3, c: c3, ..} = s; + let S {b: b4, c: c4, ..} = s; let m = M{a: 1, b: 2}; let M {a: a_m, b: b_m} = m; return a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m; diff --git a/src/test/contracts/case-priority.tact b/src/test/contracts/case-priority.tact new file mode 100644 index 000000000..0c290c55c --- /dev/null +++ b/src/test/contracts/case-priority.tact @@ -0,0 +1,17 @@ +contract Priority { + x: Bool; + y: Int; + + init() { + self.x = true || true && true == 5 < 6 << 9 + 7 * 8; + self.x = true || true && true != 5 > 6 >> 9 - 7 / 8; + self.x = true || true && true != 5 <= 6 >> 9 - 7 % 8; + self.x = true || true && true != 5 >= 6 >> 9 - 7 % 8; + self.y = 1 | 2 ^ 3 & 6 >> 9 - 7 % 8; + self.x = (true ? true : false) ? 1 : 2; + self.x = true ? (true ? 1 : 2) : 3; + self.x = false ? 1 : false ? 2 : 3; + self.x = +self.x!!; + self.x = (+self.x)!!; + } +} diff --git a/src/test/contracts/renamer-expected/case-destructuring.tact b/src/test/contracts/renamer-expected/case-destructuring.tact index c11334837..16b94fc9b 100644 --- a/src/test/contracts/renamer-expected/case-destructuring.tact +++ b/src/test/contracts/renamer-expected/case-destructuring.tact @@ -12,12 +12,12 @@ message message_decl_1 { fun function_def_2(): Int { let s = S{a: 1, b: 2, c: 3}; let S {a, b, c} = s; - let S {a: a1} = s; - let S {b: b1} = s; - let S {c: c1} = s; - let S {a: a2, b: b2} = s; - let S {a: a3, c: c3} = s; - let S {b: b4, c: c4} = s; + let S {a: a1, ..} = s; + let S {b: b1, ..} = s; + let S {c: c1, ..} = s; + let S {a: a2, b: b2, ..} = s; + let S {a: a3, c: c3, ..} = s; + let S {b: b4, c: c4, ..} = s; let m = M{a: 1, b: 2}; let M {a: a_m, b: b_m} = m; return a + b + c + a1 + b1 + c1 + a2 + b2 + a3 + c3 + b4 + c4 + a_m + b_m; diff --git a/src/test/contracts/renamer-expected/case-priority.tact b/src/test/contracts/renamer-expected/case-priority.tact new file mode 100644 index 000000000..d113b924f --- /dev/null +++ b/src/test/contracts/renamer-expected/case-priority.tact @@ -0,0 +1,17 @@ +contract contract_0 { + x: Bool; + y: Int; + + init() { + self.x = true || true && true == 5 < 6 << 9 + 7 * 8; + self.x = true || true && true != 5 > 6 >> 9 - 7 / 8; + self.x = true || true && true != 5 <= 6 >> 9 - 7 % 8; + self.x = true || true && true != 5 >= 6 >> 9 - 7 % 8; + self.y = 1 | 2 ^ 3 & 6 >> 9 - 7 % 8; + self.x = (true ? true : false) ? 1 : 2; + self.x = true ? (true ? 1 : 2) : 3; + self.x = false ? 1 : false ? 2 : 3; + self.x = +self.x!!; + self.x = (+self.x)!!; + } +} diff --git a/src/test/e2e-emulated/contracts/intrinsics.tact b/src/test/e2e-emulated/contracts/intrinsics.tact index d3ef60d6f..5f45cd563 100644 --- a/src/test/e2e-emulated/contracts/intrinsics.tact +++ b/src/test/e2e-emulated/contracts/intrinsics.tact @@ -92,6 +92,14 @@ contract IntrinsicsTester { return sha256(src); } + get fun getHashLongComptime(): Int { + return sha256("------------------------------------------------------------------------------------------------------------------------------129"); + } + + get fun getHashLongRuntime(src: String): Int { + return sha256(src); + } + receive("emit_1") { emit("Hello world".asComment()); } @@ -231,4 +239,4 @@ contract IntrinsicsTester { get fun getRawSlice24(): Slice { return self.v; } -} \ No newline at end of file +} diff --git a/src/test/e2e-emulated/contracts/maps.tact b/src/test/e2e-emulated/contracts/maps.tact index 64ddf2f8e..2fb55bddc 100644 --- a/src/test/e2e-emulated/contracts/maps.tact +++ b/src/test/e2e-emulated/contracts/maps.tact @@ -20,6 +20,7 @@ struct GetAllMapsResult { int_uint8: Int?; int_uint42: Int?; int_uint256: Int?; + int_coins: Int?; int_bool: Bool?; int_cell: Cell?; int_address: Address?; @@ -32,6 +33,7 @@ struct GetAllMapsResult { int8_uint8: Int?; int8_uint42: Int?; int8_uint256: Int?; + int8_coins: Int?; int8_bool: Bool?; int8_cell: Cell?; int8_address: Address?; @@ -44,6 +46,7 @@ struct GetAllMapsResult { int42_uint8: Int?; int42_uint42: Int?; int42_uint256: Int?; + int42_coins: Int?; int42_bool: Bool?; int42_cell: Cell?; int42_address: Address?; @@ -56,6 +59,7 @@ struct GetAllMapsResult { int256_uint8: Int?; int256_uint42: Int?; int256_uint256: Int?; + int256_coins: Int?; int256_bool: Bool?; int256_cell: Cell?; int256_address: Address?; @@ -68,6 +72,7 @@ struct GetAllMapsResult { uint8_uint8: Int?; uint8_uint42: Int?; uint8_uint256: Int?; + uint8_coins: Int?; uint8_bool: Bool?; uint8_cell: Cell?; uint8_address: Address?; @@ -80,6 +85,7 @@ struct GetAllMapsResult { uint42_uint8: Int?; uint42_uint42: Int?; uint42_uint256: Int?; + uint42_coins: Int?; uint42_bool: Bool?; uint42_cell: Cell?; uint42_address: Address?; @@ -92,6 +98,7 @@ struct GetAllMapsResult { uint256_uint8: Int?; uint256_uint42: Int?; uint256_uint256: Int?; + uint256_coins: Int?; uint256_bool: Bool?; uint256_cell: Cell?; uint256_address: Address?; @@ -105,6 +112,7 @@ struct GetAllMapsResult { address_uint8: Int?; address_uint42: Int?; address_uint256: Int?; + address_coins: Int?; address_bool: Bool?; address_cell: Cell?; address_address: Address?; @@ -120,6 +128,7 @@ struct ReplaceAllMapsResult { int_uint8: Bool; int_uint42: Bool; int_uint256: Bool; + int_coins: Bool; int_bool: Bool; int_cell: Bool; int_address: Bool; @@ -132,6 +141,7 @@ struct ReplaceAllMapsResult { int8_uint8: Bool; int8_uint42: Bool; int8_uint256: Bool; + int8_coins: Bool; int8_bool: Bool; int8_cell: Bool; int8_address: Bool; @@ -144,6 +154,7 @@ struct ReplaceAllMapsResult { int42_uint8: Bool; int42_uint42: Bool; int42_uint256: Bool; + int42_coins: Bool; int42_bool: Bool; int42_cell: Bool; int42_address: Bool; @@ -156,6 +167,7 @@ struct ReplaceAllMapsResult { int256_uint8: Bool; int256_uint42: Bool; int256_uint256: Bool; + int256_coins: Bool; int256_bool: Bool; int256_cell: Bool; int256_address: Bool; @@ -168,6 +180,7 @@ struct ReplaceAllMapsResult { uint8_uint8: Bool; uint8_uint42: Bool; uint8_uint256: Bool; + uint8_coins: Bool; uint8_bool: Bool; uint8_cell: Bool; uint8_address: Bool; @@ -180,6 +193,7 @@ struct ReplaceAllMapsResult { uint42_uint8: Bool; uint42_uint42: Bool; uint42_uint256: Bool; + uint42_coins: Bool; uint42_bool: Bool; uint42_cell: Bool; uint42_address: Bool; @@ -192,6 +206,7 @@ struct ReplaceAllMapsResult { uint256_uint8: Bool; uint256_uint42: Bool; uint256_uint256: Bool; + uint256_coins: Bool; uint256_bool: Bool; uint256_cell: Bool; uint256_address: Bool; @@ -205,6 +220,7 @@ struct ReplaceAllMapsResult { address_uint8: Bool; address_uint42: Bool; address_uint256: Bool; + address_coins: Bool; address_bool: Bool; address_cell: Bool; address_address: Bool; @@ -220,6 +236,7 @@ struct ReplaceGetAllMapsResult { int_uint8: Int?; int_uint42: Int?; int_uint256: Int?; + int_coins: Int?; int_bool: Bool?; int_cell: Cell?; int_address: Address?; @@ -232,6 +249,7 @@ struct ReplaceGetAllMapsResult { int8_uint8: Int?; int8_uint42: Int?; int8_uint256: Int?; + int8_coins: Int?; int8_bool: Bool?; int8_cell: Cell?; int8_address: Address?; @@ -244,6 +262,7 @@ struct ReplaceGetAllMapsResult { int42_uint8: Int?; int42_uint42: Int?; int42_uint256: Int?; + int42_coins: Int?; int42_bool: Bool?; int42_cell: Cell?; int42_address: Address?; @@ -256,6 +275,7 @@ struct ReplaceGetAllMapsResult { int256_uint8: Int?; int256_uint42: Int?; int256_uint256: Int?; + int256_coins: Int?; int256_bool: Bool?; int256_cell: Cell?; int256_address: Address?; @@ -268,6 +288,7 @@ struct ReplaceGetAllMapsResult { uint8_uint8: Int?; uint8_uint42: Int?; uint8_uint256: Int?; + uint8_coins: Int?; uint8_bool: Bool?; uint8_cell: Cell?; uint8_address: Address?; @@ -280,6 +301,7 @@ struct ReplaceGetAllMapsResult { uint42_uint8: Int?; uint42_uint42: Int?; uint42_uint256: Int?; + uint42_coins: Int?; uint42_bool: Bool?; uint42_cell: Cell?; uint42_address: Address?; @@ -292,6 +314,7 @@ struct ReplaceGetAllMapsResult { uint256_uint8: Int?; uint256_uint42: Int?; uint256_uint256: Int?; + uint256_coins: Int?; uint256_bool: Bool?; uint256_cell: Cell?; uint256_address: Address?; @@ -305,6 +328,7 @@ struct ReplaceGetAllMapsResult { address_uint8: Int?; address_uint42: Int?; address_uint256: Int?; + address_coins: Int?; address_bool: Bool?; address_cell: Cell?; address_address: Address?; @@ -320,6 +344,7 @@ struct ExistsAllMapsResult { int_uint8: Bool; int_uint42: Bool; int_uint256: Bool; + int_coins: Bool; int_bool: Bool; int_cell: Bool; int_address: Bool; @@ -332,6 +357,7 @@ struct ExistsAllMapsResult { int8_uint8: Bool; int8_uint42: Bool; int8_uint256: Bool; + int8_coins: Bool; int8_bool: Bool; int8_cell: Bool; int8_address: Bool; @@ -344,6 +370,7 @@ struct ExistsAllMapsResult { int42_uint8: Bool; int42_uint42: Bool; int42_uint256: Bool; + int42_coins: Bool; int42_bool: Bool; int42_cell: Bool; int42_address: Bool; @@ -356,6 +383,7 @@ struct ExistsAllMapsResult { int256_uint8: Bool; int256_uint42: Bool; int256_uint256: Bool; + int256_coins: Bool; int256_bool: Bool; int256_cell: Bool; int256_address: Bool; @@ -368,6 +396,7 @@ struct ExistsAllMapsResult { uint8_uint8: Bool; uint8_uint42: Bool; uint8_uint256: Bool; + uint8_coins: Bool; uint8_bool: Bool; uint8_cell: Bool; uint8_address: Bool; @@ -380,6 +409,7 @@ struct ExistsAllMapsResult { uint42_uint8: Bool; uint42_uint42: Bool; uint42_uint256: Bool; + uint42_coins: Bool; uint42_bool: Bool; uint42_cell: Bool; uint42_address: Bool; @@ -392,6 +422,7 @@ struct ExistsAllMapsResult { uint256_uint8: Bool; uint256_uint42: Bool; uint256_uint256: Bool; + uint256_coins: Bool; uint256_bool: Bool; uint256_cell: Bool; uint256_address: Bool; @@ -405,6 +436,7 @@ struct ExistsAllMapsResult { address_uint8: Bool; address_uint42: Bool; address_uint256: Bool; + address_coins: Bool; address_bool: Bool; address_cell: Bool; address_address: Bool; @@ -420,6 +452,7 @@ struct IsEmptyAllMapsResult { int_uint8: Bool; int_uint42: Bool; int_uint256: Bool; + int_coins: Bool; int_bool: Bool; int_cell: Bool; int_address: Bool; @@ -432,6 +465,7 @@ struct IsEmptyAllMapsResult { int8_uint8: Bool; int8_uint42: Bool; int8_uint256: Bool; + int8_coins: Bool; int8_bool: Bool; int8_cell: Bool; int8_address: Bool; @@ -444,6 +478,7 @@ struct IsEmptyAllMapsResult { int42_uint8: Bool; int42_uint42: Bool; int42_uint256: Bool; + int42_coins: Bool; int42_bool: Bool; int42_cell: Bool; int42_address: Bool; @@ -456,6 +491,7 @@ struct IsEmptyAllMapsResult { int256_uint8: Bool; int256_uint42: Bool; int256_uint256: Bool; + int256_coins: Bool; int256_bool: Bool; int256_cell: Bool; int256_address: Bool; @@ -468,6 +504,7 @@ struct IsEmptyAllMapsResult { uint8_uint8: Bool; uint8_uint42: Bool; uint8_uint256: Bool; + uint8_coins: Bool; uint8_bool: Bool; uint8_cell: Bool; uint8_address: Bool; @@ -480,6 +517,7 @@ struct IsEmptyAllMapsResult { uint42_uint8: Bool; uint42_uint42: Bool; uint42_uint256: Bool; + uint42_coins: Bool; uint42_bool: Bool; uint42_cell: Bool; uint42_address: Bool; @@ -492,6 +530,7 @@ struct IsEmptyAllMapsResult { uint256_uint8: Bool; uint256_uint42: Bool; uint256_uint256: Bool; + uint256_coins: Bool; uint256_bool: Bool; uint256_cell: Bool; uint256_address: Bool; @@ -505,6 +544,7 @@ struct IsEmptyAllMapsResult { address_uint8: Bool; address_uint42: Bool; address_uint256: Bool; + address_coins: Bool; address_bool: Bool; address_cell: Bool; address_address: Bool; @@ -520,6 +560,7 @@ struct AsCellAllMapsResult { int_uint8: Cell?; int_uint42: Cell?; int_uint256: Cell?; + int_coins: Cell?; int_bool: Cell?; int_cell: Cell?; int_address: Cell?; @@ -532,6 +573,7 @@ struct AsCellAllMapsResult { int8_uint8: Cell?; int8_uint42: Cell?; int8_uint256: Cell?; + int8_coins: Cell?; int8_bool: Cell?; int8_cell: Cell?; int8_address: Cell?; @@ -544,6 +586,7 @@ struct AsCellAllMapsResult { int42_uint8: Cell?; int42_uint42: Cell?; int42_uint256: Cell?; + int42_coins: Cell?; int42_bool: Cell?; int42_cell: Cell?; int42_address: Cell?; @@ -556,6 +599,7 @@ struct AsCellAllMapsResult { int256_uint8: Cell?; int256_uint42: Cell?; int256_uint256: Cell?; + int256_coins: Cell?; int256_bool: Cell?; int256_cell: Cell?; int256_address: Cell?; @@ -568,6 +612,7 @@ struct AsCellAllMapsResult { uint8_uint8: Cell?; uint8_uint42: Cell?; uint8_uint256: Cell?; + uint8_coins: Cell?; uint8_bool: Cell?; uint8_cell: Cell?; uint8_address: Cell?; @@ -580,6 +625,7 @@ struct AsCellAllMapsResult { uint42_uint8: Cell?; uint42_uint42: Cell?; uint42_uint256: Cell?; + uint42_coins: Cell?; uint42_bool: Cell?; uint42_cell: Cell?; uint42_address: Cell?; @@ -592,6 +638,7 @@ struct AsCellAllMapsResult { uint256_uint8: Cell?; uint256_uint42: Cell?; uint256_uint256: Cell?; + uint256_coins: Cell?; uint256_bool: Cell?; uint256_cell: Cell?; uint256_address: Cell?; @@ -605,6 +652,7 @@ struct AsCellAllMapsResult { address_uint8: Cell?; address_uint42: Cell?; address_uint256: Cell?; + address_coins: Cell?; address_bool: Cell?; address_cell: Cell?; address_address: Cell?; @@ -634,6 +682,7 @@ message SetAllMaps { valueUint8: Int?; valueUint42: Int?; valueUint256: Int?; + valueCoins: Int?; valueBool: Bool?; valueCell: Cell?; valueAddress: Address?; @@ -671,6 +720,7 @@ message ReplaceAllMaps { valueUint8: Int?; valueUint42: Int?; valueUint256: Int?; + valueCoins: Int?; valueBool: Bool?; valueCell: Cell?; valueAddress: Address?; @@ -696,6 +746,7 @@ message ReplaceGetAllMaps { valueUint8: Int?; valueUint42: Int?; valueUint256: Int?; + valueCoins: Int?; valueBool: Bool?; valueCell: Cell?; valueAddress: Address?; @@ -724,6 +775,7 @@ contract MapTestContract { int_uint8: map; int_uint42: map; int_uint256: map; + int_coins: map; int_bool: map; int_cell: map; int_address: map; @@ -740,6 +792,7 @@ contract MapTestContract { int8_uint8: map; int8_uint42: map; int8_uint256: map; + int8_coins: map; int8_bool: map; int8_cell: map; int8_address: map; @@ -756,6 +809,7 @@ contract MapTestContract { int42_uint8: map; int42_uint42: map; int42_uint256: map; + int42_coins: map; int42_bool: map; int42_cell: map; int42_address: map; @@ -772,6 +826,7 @@ contract MapTestContract { int256_uint8: map; int256_uint42: map; int256_uint256: map; + int256_coins: map; int256_bool: map; int256_cell: map; int256_address: map; @@ -788,6 +843,7 @@ contract MapTestContract { uint8_uint8: map; uint8_uint42: map; uint8_uint256: map; + uint8_coins: map; uint8_bool: map; uint8_cell: map; uint8_address: map; @@ -804,6 +860,7 @@ contract MapTestContract { uint42_uint8: map; uint42_uint42: map; uint42_uint256: map; + uint42_coins: map; uint42_bool: map; uint42_cell: map; uint42_address: map; @@ -820,6 +877,7 @@ contract MapTestContract { uint256_uint8: map; uint256_uint42: map; uint256_uint256: map; + uint256_coins: map; uint256_bool: map; uint256_cell: map; uint256_address: map; @@ -836,6 +894,7 @@ contract MapTestContract { address_uint8: map; address_uint42: map; address_uint256: map; + address_coins: map; address_bool: map; address_cell: map; address_address: map; @@ -854,6 +913,7 @@ contract MapTestContract { self.int_uint8.set(msg.keyInt, msg.valueUint8); self.int_uint42.set(msg.keyInt, msg.valueUint42); self.int_uint256.set(msg.keyInt, msg.valueUint256); + self.int_coins.set(msg.keyInt, msg.valueCoins); self.int_bool.set(msg.keyInt, msg.valueBool); self.int_cell.set(msg.keyInt, msg.valueCell); self.int_address.set(msg.keyInt, msg.valueAddress); @@ -866,6 +926,7 @@ contract MapTestContract { self.int8_uint8.set(msg.keyInt8, msg.valueUint8); self.int8_uint42.set(msg.keyInt8, msg.valueUint42); self.int8_uint256.set(msg.keyInt8, msg.valueUint256); + self.int8_coins.set(msg.keyInt8, msg.valueCoins); self.int8_bool.set(msg.keyInt8, msg.valueBool); self.int8_cell.set(msg.keyInt8, msg.valueCell); self.int8_address.set(msg.keyInt8, msg.valueAddress); @@ -878,6 +939,7 @@ contract MapTestContract { self.int42_uint8.set(msg.keyInt42, msg.valueUint8); self.int42_uint42.set(msg.keyInt42, msg.valueUint42); self.int42_uint256.set(msg.keyInt42, msg.valueUint256); + self.int42_coins.set(msg.keyInt42, msg.valueCoins); self.int42_bool.set(msg.keyInt42, msg.valueBool); self.int42_cell.set(msg.keyInt42, msg.valueCell); self.int42_address.set(msg.keyInt42, msg.valueAddress); @@ -890,6 +952,7 @@ contract MapTestContract { self.int256_uint8.set(msg.keyInt256, msg.valueUint8); self.int256_uint42.set(msg.keyInt256, msg.valueUint42); self.int256_uint256.set(msg.keyInt256, msg.valueUint256); + self.int256_coins.set(msg.keyInt256, msg.valueCoins); self.int256_bool.set(msg.keyInt256, msg.valueBool); self.int256_cell.set(msg.keyInt256, msg.valueCell); self.int256_address.set(msg.keyInt256, msg.valueAddress); @@ -902,6 +965,7 @@ contract MapTestContract { self.uint8_uint8.set(msg.keyUint8, msg.valueUint8); self.uint8_uint42.set(msg.keyUint8, msg.valueUint42); self.uint8_uint256.set(msg.keyUint8, msg.valueUint256); + self.uint8_coins.set(msg.keyUint8, msg.valueCoins); self.uint8_bool.set(msg.keyUint8, msg.valueBool); self.uint8_cell.set(msg.keyUint8, msg.valueCell); self.uint8_address.set(msg.keyUint8, msg.valueAddress); @@ -914,6 +978,7 @@ contract MapTestContract { self.uint42_uint8.set(msg.keyUint42, msg.valueUint8); self.uint42_uint42.set(msg.keyUint42, msg.valueUint42); self.uint42_uint256.set(msg.keyUint42, msg.valueUint256); + self.uint42_coins.set(msg.keyUint42, msg.valueCoins); self.uint42_bool.set(msg.keyUint42, msg.valueBool); self.uint42_cell.set(msg.keyUint42, msg.valueCell); self.uint42_address.set(msg.keyUint42, msg.valueAddress); @@ -926,6 +991,7 @@ contract MapTestContract { self.uint256_uint8.set(msg.keyUint256, msg.valueUint8); self.uint256_uint42.set(msg.keyUint256, msg.valueUint42); self.uint256_uint256.set(msg.keyUint256, msg.valueUint256); + self.uint256_coins.set(msg.keyUint256, msg.valueCoins); self.uint256_bool.set(msg.keyUint256, msg.valueBool); self.uint256_cell.set(msg.keyUint256, msg.valueCell); self.uint256_address.set(msg.keyUint256, msg.valueAddress); @@ -939,6 +1005,7 @@ contract MapTestContract { self.address_uint8.set(msg.keyAddress, msg.valueUint8); self.address_uint42.set(msg.keyAddress, msg.valueUint42); self.address_uint256.set(msg.keyAddress, msg.valueUint256); + self.address_coins.set(msg.keyAddress, msg.valueCoins); self.address_bool.set(msg.keyAddress, msg.valueBool); self.address_cell.set(msg.keyAddress, msg.valueCell); self.address_address.set(msg.keyAddress, msg.valueAddress); @@ -954,6 +1021,7 @@ contract MapTestContract { self.int_uint8.del(msg.keyInt); self.int_uint42.del(msg.keyInt); self.int_uint256.del(msg.keyInt); + self.int_coins.del(msg.keyInt); self.int_bool.del(msg.keyInt); self.int_cell.del(msg.keyInt); self.int_address.del(msg.keyInt); @@ -966,6 +1034,7 @@ contract MapTestContract { self.int8_uint8.del(msg.keyInt8); self.int8_uint42.del(msg.keyInt8); self.int8_uint256.del(msg.keyInt8); + self.int8_coins.del(msg.keyInt8); self.int8_bool.del(msg.keyInt8); self.int8_cell.del(msg.keyInt8); self.int8_address.del(msg.keyInt8); @@ -978,6 +1047,7 @@ contract MapTestContract { self.int42_uint8.del(msg.keyInt42); self.int42_uint42.del(msg.keyInt42); self.int42_uint256.del(msg.keyInt42); + self.int42_coins.del(msg.keyInt42); self.int42_bool.del(msg.keyInt42); self.int42_cell.del(msg.keyInt42); self.int42_address.del(msg.keyInt42); @@ -990,6 +1060,7 @@ contract MapTestContract { self.int256_uint8.del(msg.keyInt256); self.int256_uint42.del(msg.keyInt256); self.int256_uint256.del(msg.keyInt256); + self.int256_coins.del(msg.keyInt256); self.int256_bool.del(msg.keyInt256); self.int256_cell.del(msg.keyInt256); self.int256_address.del(msg.keyInt256); @@ -1002,6 +1073,7 @@ contract MapTestContract { self.uint8_uint8.del(msg.keyUint8); self.uint8_uint42.del(msg.keyUint8); self.uint8_uint256.del(msg.keyUint8); + self.uint8_coins.del(msg.keyUint8); self.uint8_bool.del(msg.keyUint8); self.uint8_cell.del(msg.keyUint8); self.uint8_address.del(msg.keyUint8); @@ -1014,6 +1086,7 @@ contract MapTestContract { self.uint42_uint8.del(msg.keyUint42); self.uint42_uint42.del(msg.keyUint42); self.uint42_uint256.del(msg.keyUint42); + self.uint42_coins.del(msg.keyUint42); self.uint42_bool.del(msg.keyUint42); self.uint42_cell.del(msg.keyUint42); self.uint42_address.del(msg.keyUint42); @@ -1026,6 +1099,7 @@ contract MapTestContract { self.uint256_uint8.del(msg.keyUint256); self.uint256_uint42.del(msg.keyUint256); self.uint256_uint256.del(msg.keyUint256); + self.uint256_coins.del(msg.keyUint256); self.uint256_bool.del(msg.keyUint256); self.uint256_cell.del(msg.keyUint256); self.uint256_address.del(msg.keyUint256); @@ -1039,6 +1113,7 @@ contract MapTestContract { self.address_uint8.del(msg.keyAddress); self.address_uint42.del(msg.keyAddress); self.address_uint256.del(msg.keyAddress); + self.address_coins.del(msg.keyAddress); self.address_bool.del(msg.keyAddress); self.address_cell.del(msg.keyAddress); self.address_address.del(msg.keyAddress); @@ -1054,6 +1129,7 @@ contract MapTestContract { self.int_uint8.replace(msg.keyInt, msg.valueUint8); self.int_uint42.replace(msg.keyInt, msg.valueUint42); self.int_uint256.replace(msg.keyInt, msg.valueUint256); + self.int_coins.replace(msg.keyInt, msg.valueCoins); self.int_bool.replace(msg.keyInt, msg.valueBool); self.int_cell.replace(msg.keyInt, msg.valueCell); self.int_address.replace(msg.keyInt, msg.valueAddress); @@ -1066,6 +1142,7 @@ contract MapTestContract { self.int8_uint8.replace(msg.keyInt8, msg.valueUint8); self.int8_uint42.replace(msg.keyInt8, msg.valueUint42); self.int8_uint256.replace(msg.keyInt8, msg.valueUint256); + self.int8_coins.replace(msg.keyInt8, msg.valueCoins); self.int8_bool.replace(msg.keyInt8, msg.valueBool); self.int8_cell.replace(msg.keyInt8, msg.valueCell); self.int8_address.replace(msg.keyInt8, msg.valueAddress); @@ -1078,6 +1155,7 @@ contract MapTestContract { self.int42_uint8.replace(msg.keyInt42, msg.valueUint8); self.int42_uint42.replace(msg.keyInt42, msg.valueUint42); self.int42_uint256.replace(msg.keyInt42, msg.valueUint256); + self.int42_coins.replace(msg.keyInt42, msg.valueCoins); self.int42_bool.replace(msg.keyInt42, msg.valueBool); self.int42_cell.replace(msg.keyInt42, msg.valueCell); self.int42_address.replace(msg.keyInt42, msg.valueAddress); @@ -1090,6 +1168,7 @@ contract MapTestContract { self.int256_uint8.replace(msg.keyInt256, msg.valueUint8); self.int256_uint42.replace(msg.keyInt256, msg.valueUint42); self.int256_uint256.replace(msg.keyInt256, msg.valueUint256); + self.int256_coins.replace(msg.keyInt256, msg.valueCoins); self.int256_bool.replace(msg.keyInt256, msg.valueBool); self.int256_cell.replace(msg.keyInt256, msg.valueCell); self.int256_address.replace(msg.keyInt256, msg.valueAddress); @@ -1102,6 +1181,7 @@ contract MapTestContract { self.uint8_uint8.replace(msg.keyUint8, msg.valueUint8); self.uint8_uint42.replace(msg.keyUint8, msg.valueUint42); self.uint8_uint256.replace(msg.keyUint8, msg.valueUint256); + self.uint8_coins.replace(msg.keyUint8, msg.valueCoins); self.uint8_bool.replace(msg.keyUint8, msg.valueBool); self.uint8_cell.replace(msg.keyUint8, msg.valueCell); self.uint8_address.replace(msg.keyUint8, msg.valueAddress); @@ -1114,6 +1194,7 @@ contract MapTestContract { self.uint42_uint8.replace(msg.keyUint42, msg.valueUint8); self.uint42_uint42.replace(msg.keyUint42, msg.valueUint42); self.uint42_uint256.replace(msg.keyUint42, msg.valueUint256); + self.uint42_coins.replace(msg.keyUint42, msg.valueCoins); self.uint42_bool.replace(msg.keyUint42, msg.valueBool); self.uint42_cell.replace(msg.keyUint42, msg.valueCell); self.uint42_address.replace(msg.keyUint42, msg.valueAddress); @@ -1126,6 +1207,7 @@ contract MapTestContract { self.uint256_uint8.replace(msg.keyUint256, msg.valueUint8); self.uint256_uint42.replace(msg.keyUint256, msg.valueUint42); self.uint256_uint256.replace(msg.keyUint256, msg.valueUint256); + self.uint256_coins.replace(msg.keyUint256, msg.valueCoins); self.uint256_bool.replace(msg.keyUint256, msg.valueBool); self.uint256_cell.replace(msg.keyUint256, msg.valueCell); self.uint256_address.replace(msg.keyUint256, msg.valueAddress); @@ -1139,6 +1221,7 @@ contract MapTestContract { self.address_uint8.replace(msg.keyAddress, msg.valueUint8); self.address_uint42.replace(msg.keyAddress, msg.valueUint42); self.address_uint256.replace(msg.keyAddress, msg.valueUint256); + self.address_coins.replace(msg.keyAddress, msg.valueCoins); self.address_bool.replace(msg.keyAddress, msg.valueBool); self.address_cell.replace(msg.keyAddress, msg.valueCell); self.address_address.replace(msg.keyAddress, msg.valueAddress); @@ -1154,6 +1237,7 @@ contract MapTestContract { self.int_uint8.replaceGet(msg.keyInt, msg.valueUint8); self.int_uint42.replaceGet(msg.keyInt, msg.valueUint42); self.int_uint256.replaceGet(msg.keyInt, msg.valueUint256); + self.int_coins.replaceGet(msg.keyInt, msg.valueCoins); self.int_bool.replaceGet(msg.keyInt, msg.valueBool); self.int_cell.replaceGet(msg.keyInt, msg.valueCell); self.int_address.replaceGet(msg.keyInt, msg.valueAddress); @@ -1166,6 +1250,7 @@ contract MapTestContract { self.int8_uint8.replaceGet(msg.keyInt8, msg.valueUint8); self.int8_uint42.replaceGet(msg.keyInt8, msg.valueUint42); self.int8_uint256.replaceGet(msg.keyInt8, msg.valueUint256); + self.int8_coins.replaceGet(msg.keyInt8, msg.valueCoins); self.int8_bool.replaceGet(msg.keyInt8, msg.valueBool); self.int8_cell.replaceGet(msg.keyInt8, msg.valueCell); self.int8_address.replaceGet(msg.keyInt8, msg.valueAddress); @@ -1178,6 +1263,7 @@ contract MapTestContract { self.int42_uint8.replaceGet(msg.keyInt42, msg.valueUint8); self.int42_uint42.replaceGet(msg.keyInt42, msg.valueUint42); self.int42_uint256.replaceGet(msg.keyInt42, msg.valueUint256); + self.int42_coins.replaceGet(msg.keyInt42, msg.valueCoins); self.int42_bool.replaceGet(msg.keyInt42, msg.valueBool); self.int42_cell.replaceGet(msg.keyInt42, msg.valueCell); self.int42_address.replaceGet(msg.keyInt42, msg.valueAddress); @@ -1190,6 +1276,7 @@ contract MapTestContract { self.int256_uint8.replaceGet(msg.keyInt256, msg.valueUint8); self.int256_uint42.replaceGet(msg.keyInt256, msg.valueUint42); self.int256_uint256.replaceGet(msg.keyInt256, msg.valueUint256); + self.int256_coins.replaceGet(msg.keyInt256, msg.valueCoins); self.int256_bool.replaceGet(msg.keyInt256, msg.valueBool); self.int256_cell.replaceGet(msg.keyInt256, msg.valueCell); self.int256_address.replaceGet(msg.keyInt256, msg.valueAddress); @@ -1202,6 +1289,7 @@ contract MapTestContract { self.uint8_uint8.replaceGet(msg.keyUint8, msg.valueUint8); self.uint8_uint42.replaceGet(msg.keyUint8, msg.valueUint42); self.uint8_uint256.replaceGet(msg.keyUint8, msg.valueUint256); + self.uint8_coins.replaceGet(msg.keyUint8, msg.valueCoins); self.uint8_bool.replaceGet(msg.keyUint8, msg.valueBool); self.uint8_cell.replaceGet(msg.keyUint8, msg.valueCell); self.uint8_address.replaceGet(msg.keyUint8, msg.valueAddress); @@ -1214,6 +1302,7 @@ contract MapTestContract { self.uint42_uint8.replaceGet(msg.keyUint42, msg.valueUint8); self.uint42_uint42.replaceGet(msg.keyUint42, msg.valueUint42); self.uint42_uint256.replaceGet(msg.keyUint42, msg.valueUint256); + self.uint42_coins.replaceGet(msg.keyUint42, msg.valueCoins); self.uint42_bool.replaceGet(msg.keyUint42, msg.valueBool); self.uint42_cell.replaceGet(msg.keyUint42, msg.valueCell); self.uint42_address.replaceGet(msg.keyUint42, msg.valueAddress); @@ -1226,6 +1315,7 @@ contract MapTestContract { self.uint256_uint8.replaceGet(msg.keyUint256, msg.valueUint8); self.uint256_uint42.replaceGet(msg.keyUint256, msg.valueUint42); self.uint256_uint256.replaceGet(msg.keyUint256, msg.valueUint256); + self.uint256_coins.replaceGet(msg.keyUint256, msg.valueCoins); self.uint256_bool.replaceGet(msg.keyUint256, msg.valueBool); self.uint256_cell.replaceGet(msg.keyUint256, msg.valueCell); self.uint256_address.replaceGet(msg.keyUint256, msg.valueAddress); @@ -1239,6 +1329,7 @@ contract MapTestContract { self.address_uint8.replaceGet(msg.keyAddress, msg.valueUint8); self.address_uint42.replaceGet(msg.keyAddress, msg.valueUint42); self.address_uint256.replaceGet(msg.keyAddress, msg.valueUint256); + self.address_coins.replaceGet(msg.keyAddress, msg.valueCoins); self.address_bool.replaceGet(msg.keyAddress, msg.valueBool); self.address_cell.replaceGet(msg.keyAddress, msg.valueCell); self.address_address.replaceGet(msg.keyAddress, msg.valueAddress); @@ -1272,6 +1363,7 @@ contract MapTestContract { int_uint8: self.int_uint8.get(keyInt), int_uint42: self.int_uint42.get(keyInt), int_uint256: self.int_uint256.get(keyInt), + int_coins: self.int_coins.get(keyInt), int_bool: self.int_bool.get(keyInt), int_cell: self.int_cell.get(keyInt), int_address: self.int_address.get(keyInt), @@ -1284,6 +1376,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.get(keyInt8), int8_uint42: self.int8_uint42.get(keyInt8), int8_uint256: self.int8_uint256.get(keyInt8), + int8_coins: self.int8_coins.get(keyInt8), int8_bool: self.int8_bool.get(keyInt8), int8_cell: self.int8_cell.get(keyInt8), int8_address: self.int8_address.get(keyInt8), @@ -1296,6 +1389,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.get(keyInt42), int42_uint42: self.int42_uint42.get(keyInt42), int42_uint256: self.int42_uint256.get(keyInt42), + int42_coins: self.int42_coins.get(keyInt42), int42_bool: self.int42_bool.get(keyInt42), int42_cell: self.int42_cell.get(keyInt42), int42_address: self.int42_address.get(keyInt42), @@ -1308,6 +1402,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.get(keyInt256), int256_uint42: self.int256_uint42.get(keyInt256), int256_uint256: self.int256_uint256.get(keyInt256), + int256_coins: self.int256_coins.get(keyInt256), int256_bool: self.int256_bool.get(keyInt256), int256_cell: self.int256_cell.get(keyInt256), int256_address: self.int256_address.get(keyInt256), @@ -1320,6 +1415,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.get(keyUint8), uint8_uint42: self.uint8_uint42.get(keyUint8), uint8_uint256: self.uint8_uint256.get(keyUint8), + uint8_coins: self.uint8_coins.get(keyUint8), uint8_bool: self.uint8_bool.get(keyUint8), uint8_cell: self.uint8_cell.get(keyUint8), uint8_address: self.uint8_address.get(keyUint8), @@ -1332,6 +1428,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.get(keyUint42), uint42_uint42: self.uint42_uint42.get(keyUint42), uint42_uint256: self.uint42_uint256.get(keyUint42), + uint42_coins: self.uint42_coins.get(keyUint42), uint42_bool: self.uint42_bool.get(keyUint42), uint42_cell: self.uint42_cell.get(keyUint42), uint42_address: self.uint42_address.get(keyUint42), @@ -1344,6 +1441,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.get(keyUint256), uint256_uint42: self.uint256_uint42.get(keyUint256), uint256_uint256: self.uint256_uint256.get(keyUint256), + uint256_coins: self.uint256_coins.get(keyUint256), uint256_bool: self.uint256_bool.get(keyUint256), uint256_cell: self.uint256_cell.get(keyUint256), uint256_address: self.uint256_address.get(keyUint256), @@ -1357,6 +1455,7 @@ contract MapTestContract { address_uint8: self.address_uint8.get(keyAddress), address_uint42: self.address_uint42.get(keyAddress), address_uint256: self.address_uint256.get(keyAddress), + address_coins: self.address_coins.get(keyAddress), address_bool: self.address_bool.get(keyAddress), address_cell: self.address_cell.get(keyAddress), address_address: self.address_address.get(keyAddress), @@ -1380,6 +1479,7 @@ contract MapTestContract { valueUint8: Int, valueUint42: Int, valueUint256: Int, + valueCoins: Int, valueBool: Bool, valueCell: Cell, valueAddress: Address, @@ -1394,6 +1494,7 @@ contract MapTestContract { int_uint8: self.int_uint8.replace(keyInt, valueUint8), int_uint42: self.int_uint42.replace(keyInt, valueUint42), int_uint256: self.int_uint256.replace(keyInt, valueUint256), + int_coins: self.int_coins.replace(keyInt, valueCoins), int_bool: self.int_bool.replace(keyInt, valueBool), int_cell: self.int_cell.replace(keyInt, valueCell), int_address: self.int_address.replace(keyInt, valueAddress), @@ -1406,6 +1507,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.replace(keyInt8, valueUint8), int8_uint42: self.int8_uint42.replace(keyInt8, valueUint42), int8_uint256: self.int8_uint256.replace(keyInt8, valueUint256), + int8_coins: self.int8_coins.replace(keyInt8, valueCoins), int8_bool: self.int8_bool.replace(keyInt8, valueBool), int8_cell: self.int8_cell.replace(keyInt8, valueCell), int8_address: self.int8_address.replace(keyInt8, valueAddress), @@ -1418,6 +1520,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.replace(keyInt42, valueUint8), int42_uint42: self.int42_uint42.replace(keyInt42, valueUint42), int42_uint256: self.int42_uint256.replace(keyInt42, valueUint256), + int42_coins: self.int42_coins.replace(keyInt42, valueCoins), int42_bool: self.int42_bool.replace(keyInt42, valueBool), int42_cell: self.int42_cell.replace(keyInt42, valueCell), int42_address: self.int42_address.replace(keyInt42, valueAddress), @@ -1430,6 +1533,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.replace(keyInt256, valueUint8), int256_uint42: self.int256_uint42.replace(keyInt256, valueUint42), int256_uint256: self.int256_uint256.replace(keyInt256, valueUint256), + int256_coins: self.int256_coins.replace(keyInt256, valueCoins), int256_bool: self.int256_bool.replace(keyInt256, valueBool), int256_cell: self.int256_cell.replace(keyInt256, valueCell), int256_address: self.int256_address.replace(keyInt256, valueAddress), @@ -1442,6 +1546,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.replace(keyUint8, valueUint8), uint8_uint42: self.uint8_uint42.replace(keyUint8, valueUint42), uint8_uint256: self.uint8_uint256.replace(keyUint8, valueUint256), + uint8_coins: self.uint8_coins.replace(keyUint8, valueCoins), uint8_bool: self.uint8_bool.replace(keyUint8, valueBool), uint8_cell: self.uint8_cell.replace(keyUint8, valueCell), uint8_address: self.uint8_address.replace(keyUint8, valueAddress), @@ -1454,6 +1559,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.replace(keyUint42, valueUint8), uint42_uint42: self.uint42_uint42.replace(keyUint42, valueUint42), uint42_uint256: self.uint42_uint256.replace(keyUint42, valueUint256), + uint42_coins: self.uint42_coins.replace(keyUint42, valueCoins), uint42_bool: self.uint42_bool.replace(keyUint42, valueBool), uint42_cell: self.uint42_cell.replace(keyUint42, valueCell), uint42_address: self.uint42_address.replace(keyUint42, valueAddress), @@ -1466,6 +1572,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.replace(keyUint256, valueUint8), uint256_uint42: self.uint256_uint42.replace(keyUint256, valueUint42), uint256_uint256: self.uint256_uint256.replace(keyUint256, valueUint256), + uint256_coins: self.uint256_coins.replace(keyUint256, valueCoins), uint256_bool: self.uint256_bool.replace(keyUint256, valueBool), uint256_cell: self.uint256_cell.replace(keyUint256, valueCell), uint256_address: self.uint256_address.replace(keyUint256, valueAddress), @@ -1479,6 +1586,7 @@ contract MapTestContract { address_uint8: self.address_uint8.replace(keyAddress, valueUint8), address_uint42: self.address_uint42.replace(keyAddress, valueUint42), address_uint256: self.address_uint256.replace(keyAddress, valueUint256), + address_coins: self.address_coins.replace(keyAddress, valueCoins), address_bool: self.address_bool.replace(keyAddress, valueBool), address_cell: self.address_cell.replace(keyAddress, valueCell), address_address: self.address_address.replace(keyAddress, valueAddress), @@ -1502,6 +1610,7 @@ contract MapTestContract { valueUint8: Int, valueUint42: Int, valueUint256: Int, + valueCoins: Int, valueBool: Bool, valueCell: Cell, valueAddress: Address, @@ -1516,6 +1625,7 @@ contract MapTestContract { int_uint8: self.int_uint8.replaceGet(keyInt, valueUint8), int_uint42: self.int_uint42.replaceGet(keyInt, valueUint42), int_uint256: self.int_uint256.replaceGet(keyInt, valueUint256), + int_coins: self.int_coins.replaceGet(keyInt, valueCoins), int_bool: self.int_bool.replaceGet(keyInt, valueBool), int_cell: self.int_cell.replaceGet(keyInt, valueCell), int_address: self.int_address.replaceGet(keyInt, valueAddress), @@ -1528,6 +1638,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.replaceGet(keyInt8, valueUint8), int8_uint42: self.int8_uint42.replaceGet(keyInt8, valueUint42), int8_uint256: self.int8_uint256.replaceGet(keyInt8, valueUint256), + int8_coins: self.int8_coins.replaceGet(keyInt8, valueCoins), int8_bool: self.int8_bool.replaceGet(keyInt8, valueBool), int8_cell: self.int8_cell.replaceGet(keyInt8, valueCell), int8_address: self.int8_address.replaceGet(keyInt8, valueAddress), @@ -1540,6 +1651,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.replaceGet(keyInt42, valueUint8), int42_uint42: self.int42_uint42.replaceGet(keyInt42, valueUint42), int42_uint256: self.int42_uint256.replaceGet(keyInt42, valueUint256), + int42_coins: self.int42_coins.replaceGet(keyInt42, valueCoins), int42_bool: self.int42_bool.replaceGet(keyInt42, valueBool), int42_cell: self.int42_cell.replaceGet(keyInt42, valueCell), int42_address: self.int42_address.replaceGet(keyInt42, valueAddress), @@ -1552,6 +1664,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.replaceGet(keyInt256, valueUint8), int256_uint42: self.int256_uint42.replaceGet(keyInt256, valueUint42), int256_uint256: self.int256_uint256.replaceGet(keyInt256, valueUint256), + int256_coins: self.int256_coins.replaceGet(keyInt256, valueCoins), int256_bool: self.int256_bool.replaceGet(keyInt256, valueBool), int256_cell: self.int256_cell.replaceGet(keyInt256, valueCell), int256_address: self.int256_address.replaceGet(keyInt256, valueAddress), @@ -1564,6 +1677,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.replaceGet(keyUint8, valueUint8), uint8_uint42: self.uint8_uint42.replaceGet(keyUint8, valueUint42), uint8_uint256: self.uint8_uint256.replaceGet(keyUint8, valueUint256), + uint8_coins: self.uint8_coins.replaceGet(keyUint8, valueCoins), uint8_bool: self.uint8_bool.replaceGet(keyUint8, valueBool), uint8_cell: self.uint8_cell.replaceGet(keyUint8, valueCell), uint8_address: self.uint8_address.replaceGet(keyUint8, valueAddress), @@ -1576,6 +1690,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.replaceGet(keyUint42, valueUint8), uint42_uint42: self.uint42_uint42.replaceGet(keyUint42, valueUint42), uint42_uint256: self.uint42_uint256.replaceGet(keyUint42, valueUint256), + uint42_coins: self.uint42_coins.replaceGet(keyUint42, valueCoins), uint42_bool: self.uint42_bool.replaceGet(keyUint42, valueBool), uint42_cell: self.uint42_cell.replaceGet(keyUint42, valueCell), uint42_address: self.uint42_address.replaceGet(keyUint42, valueAddress), @@ -1588,6 +1703,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.replaceGet(keyUint256, valueUint8), uint256_uint42: self.uint256_uint42.replaceGet(keyUint256, valueUint42), uint256_uint256: self.uint256_uint256.replaceGet(keyUint256, valueUint256), + uint256_coins: self.uint256_coins.replaceGet(keyUint256, valueCoins), uint256_bool: self.uint256_bool.replaceGet(keyUint256, valueBool), uint256_cell: self.uint256_cell.replaceGet(keyUint256, valueCell), uint256_address: self.uint256_address.replaceGet(keyUint256, valueAddress), @@ -1601,6 +1717,7 @@ contract MapTestContract { address_uint8: self.address_uint8.replaceGet(keyAddress, valueUint8), address_uint42: self.address_uint42.replaceGet(keyAddress, valueUint42), address_uint256: self.address_uint256.replaceGet(keyAddress, valueUint256), + address_coins: self.address_coins.replaceGet(keyAddress, valueCoins), address_bool: self.address_bool.replaceGet(keyAddress, valueBool), address_cell: self.address_cell.replaceGet(keyAddress, valueCell), address_address: self.address_address.replaceGet(keyAddress, valueAddress), @@ -1627,6 +1744,7 @@ contract MapTestContract { int_uint8: self.int_uint8.exists(keyInt), int_uint42: self.int_uint42.exists(keyInt), int_uint256: self.int_uint256.exists(keyInt), + int_coins: self.int_coins.exists(keyInt), int_bool: self.int_bool.exists(keyInt), int_cell: self.int_cell.exists(keyInt), int_address: self.int_address.exists(keyInt), @@ -1639,6 +1757,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.exists(keyInt8), int8_uint42: self.int8_uint42.exists(keyInt8), int8_uint256: self.int8_uint256.exists(keyInt8), + int8_coins: self.int8_coins.exists(keyInt8), int8_bool: self.int8_bool.exists(keyInt8), int8_cell: self.int8_cell.exists(keyInt8), int8_address: self.int8_address.exists(keyInt8), @@ -1651,6 +1770,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.exists(keyInt42), int42_uint42: self.int42_uint42.exists(keyInt42), int42_uint256: self.int42_uint256.exists(keyInt42), + int42_coins: self.int42_coins.exists(keyInt42), int42_bool: self.int42_bool.exists(keyInt42), int42_cell: self.int42_cell.exists(keyInt42), int42_address: self.int42_address.exists(keyInt42), @@ -1663,6 +1783,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.exists(keyInt256), int256_uint42: self.int256_uint42.exists(keyInt256), int256_uint256: self.int256_uint256.exists(keyInt256), + int256_coins: self.int256_coins.exists(keyInt256), int256_bool: self.int256_bool.exists(keyInt256), int256_cell: self.int256_cell.exists(keyInt256), int256_address: self.int256_address.exists(keyInt256), @@ -1675,6 +1796,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.exists(keyUint8), uint8_uint42: self.uint8_uint42.exists(keyUint8), uint8_uint256: self.uint8_uint256.exists(keyUint8), + uint8_coins: self.uint8_coins.exists(keyUint8), uint8_bool: self.uint8_bool.exists(keyUint8), uint8_cell: self.uint8_cell.exists(keyUint8), uint8_address: self.uint8_address.exists(keyUint8), @@ -1687,6 +1809,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.exists(keyUint42), uint42_uint42: self.uint42_uint42.exists(keyUint42), uint42_uint256: self.uint42_uint256.exists(keyUint42), + uint42_coins: self.uint42_coins.exists(keyUint42), uint42_bool: self.uint42_bool.exists(keyUint42), uint42_cell: self.uint42_cell.exists(keyUint42), uint42_address: self.uint42_address.exists(keyUint42), @@ -1699,6 +1822,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.exists(keyUint256), uint256_uint42: self.uint256_uint42.exists(keyUint256), uint256_uint256: self.uint256_uint256.exists(keyUint256), + uint256_coins: self.uint256_coins.exists(keyUint256), uint256_bool: self.uint256_bool.exists(keyUint256), uint256_cell: self.uint256_cell.exists(keyUint256), uint256_address: self.uint256_address.exists(keyUint256), @@ -1712,6 +1836,7 @@ contract MapTestContract { address_uint8: self.address_uint8.exists(keyAddress), address_uint42: self.address_uint42.exists(keyAddress), address_uint256: self.address_uint256.exists(keyAddress), + address_coins: self.address_coins.exists(keyAddress), address_bool: self.address_bool.exists(keyAddress), address_cell: self.address_cell.exists(keyAddress), address_address: self.address_address.exists(keyAddress), @@ -1729,6 +1854,7 @@ contract MapTestContract { int_uint8: self.int_uint8.isEmpty(), int_uint42: self.int_uint42.isEmpty(), int_uint256: self.int_uint256.isEmpty(), + int_coins: self.int_coins.isEmpty(), int_bool: self.int_bool.isEmpty(), int_cell: self.int_cell.isEmpty(), int_address: self.int_address.isEmpty(), @@ -1741,6 +1867,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.isEmpty(), int8_uint42: self.int8_uint42.isEmpty(), int8_uint256: self.int8_uint256.isEmpty(), + int8_coins: self.int8_coins.isEmpty(), int8_bool: self.int8_bool.isEmpty(), int8_cell: self.int8_cell.isEmpty(), int8_address: self.int8_address.isEmpty(), @@ -1753,6 +1880,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.isEmpty(), int42_uint42: self.int42_uint42.isEmpty(), int42_uint256: self.int42_uint256.isEmpty(), + int42_coins: self.int42_coins.isEmpty(), int42_bool: self.int42_bool.isEmpty(), int42_cell: self.int42_cell.isEmpty(), int42_address: self.int42_address.isEmpty(), @@ -1765,6 +1893,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.isEmpty(), int256_uint42: self.int256_uint42.isEmpty(), int256_uint256: self.int256_uint256.isEmpty(), + int256_coins: self.int256_coins.isEmpty(), int256_bool: self.int256_bool.isEmpty(), int256_cell: self.int256_cell.isEmpty(), int256_address: self.int256_address.isEmpty(), @@ -1777,6 +1906,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.isEmpty(), uint8_uint42: self.uint8_uint42.isEmpty(), uint8_uint256: self.uint8_uint256.isEmpty(), + uint8_coins: self.uint8_coins.isEmpty(), uint8_bool: self.uint8_bool.isEmpty(), uint8_cell: self.uint8_cell.isEmpty(), uint8_address: self.uint8_address.isEmpty(), @@ -1789,6 +1919,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.isEmpty(), uint42_uint42: self.uint42_uint42.isEmpty(), uint42_uint256: self.uint42_uint256.isEmpty(), + uint42_coins: self.uint42_coins.isEmpty(), uint42_bool: self.uint42_bool.isEmpty(), uint42_cell: self.uint42_cell.isEmpty(), uint42_address: self.uint42_address.isEmpty(), @@ -1801,6 +1932,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.isEmpty(), uint256_uint42: self.uint256_uint42.isEmpty(), uint256_uint256: self.uint256_uint256.isEmpty(), + uint256_coins: self.uint256_coins.isEmpty(), uint256_bool: self.uint256_bool.isEmpty(), uint256_cell: self.uint256_cell.isEmpty(), uint256_address: self.uint256_address.isEmpty(), @@ -1814,6 +1946,7 @@ contract MapTestContract { address_uint8: self.address_uint8.isEmpty(), address_uint42: self.address_uint42.isEmpty(), address_uint256: self.address_uint256.isEmpty(), + address_coins: self.address_coins.isEmpty(), address_bool: self.address_bool.isEmpty(), address_cell: self.address_cell.isEmpty(), address_address: self.address_address.isEmpty(), @@ -1831,6 +1964,7 @@ contract MapTestContract { int_uint8: self.int_uint8.asCell(), int_uint42: self.int_uint42.asCell(), int_uint256: self.int_uint256.asCell(), + int_coins: self.int_coins.asCell(), int_bool: self.int_bool.asCell(), int_cell: self.int_cell.asCell(), int_address: self.int_address.asCell(), @@ -1843,6 +1977,7 @@ contract MapTestContract { int8_uint8: self.int8_uint8.asCell(), int8_uint42: self.int8_uint42.asCell(), int8_uint256: self.int8_uint256.asCell(), + int8_coins: self.int8_coins.asCell(), int8_bool: self.int8_bool.asCell(), int8_cell: self.int8_cell.asCell(), int8_address: self.int8_address.asCell(), @@ -1855,6 +1990,7 @@ contract MapTestContract { int42_uint8: self.int42_uint8.asCell(), int42_uint42: self.int42_uint42.asCell(), int42_uint256: self.int42_uint256.asCell(), + int42_coins: self.int42_coins.asCell(), int42_bool: self.int42_bool.asCell(), int42_cell: self.int42_cell.asCell(), int42_address: self.int42_address.asCell(), @@ -1867,6 +2003,7 @@ contract MapTestContract { int256_uint8: self.int256_uint8.asCell(), int256_uint42: self.int256_uint42.asCell(), int256_uint256: self.int256_uint256.asCell(), + int256_coins: self.int256_coins.asCell(), int256_bool: self.int256_bool.asCell(), int256_cell: self.int256_cell.asCell(), int256_address: self.int256_address.asCell(), @@ -1879,6 +2016,7 @@ contract MapTestContract { uint8_uint8: self.uint8_uint8.asCell(), uint8_uint42: self.uint8_uint42.asCell(), uint8_uint256: self.uint8_uint256.asCell(), + uint8_coins: self.uint8_coins.asCell(), uint8_bool: self.uint8_bool.asCell(), uint8_cell: self.uint8_cell.asCell(), uint8_address: self.uint8_address.asCell(), @@ -1891,6 +2029,7 @@ contract MapTestContract { uint42_uint8: self.uint42_uint8.asCell(), uint42_uint42: self.uint42_uint42.asCell(), uint42_uint256: self.uint42_uint256.asCell(), + uint42_coins: self.uint42_coins.asCell(), uint42_bool: self.uint42_bool.asCell(), uint42_cell: self.uint42_cell.asCell(), uint42_address: self.uint42_address.asCell(), @@ -1903,6 +2042,7 @@ contract MapTestContract { uint256_uint8: self.uint256_uint8.asCell(), uint256_uint42: self.uint256_uint42.asCell(), uint256_uint256: self.uint256_uint256.asCell(), + uint256_coins: self.uint256_coins.asCell(), uint256_bool: self.uint256_bool.asCell(), uint256_cell: self.uint256_cell.asCell(), uint256_address: self.uint256_address.asCell(), @@ -1916,6 +2056,7 @@ contract MapTestContract { address_uint8: self.address_uint8.asCell(), address_uint42: self.address_uint42.asCell(), address_uint256: self.address_uint256.asCell(), + address_coins: self.address_coins.asCell(), address_bool: self.address_bool.asCell(), address_cell: self.address_cell.asCell(), address_address: self.address_address.asCell(), diff --git a/src/test/e2e-emulated/contracts/structs.tact b/src/test/e2e-emulated/contracts/structs.tact index 4ab416e3a..cea1d5f8e 100644 --- a/src/test/e2e-emulated/contracts/structs.tact +++ b/src/test/e2e-emulated/contracts/structs.tact @@ -318,6 +318,17 @@ fun destructuringTest7(): S1 { return S1 {a: e, b: b, c: a}; } +fun destructuringTest8(): Int { + let s = S { + a: true, + b: 42 + }; + + let S {b, ..} = s; + + return b; +} + contract StructsTester { s1: S = S {a: false, b: 21 + 21}; s2: S; @@ -1101,4 +1112,19 @@ contract StructsTester { get fun destructuringTest7Const(): S1 { return destructuringTest7(); } + + get fun destructuringTest8(): Int { + let s = S { + a: true, + b: 42 + }; + + let S {b, ..} = s; + + return b; + } + + get fun destructuringTest8Const(): Int { + return destructuringTest8(); + } } diff --git a/src/test/e2e-emulated/intrinsics.spec.ts b/src/test/e2e-emulated/intrinsics.spec.ts index eebb4a877..95a77a0be 100644 --- a/src/test/e2e-emulated/intrinsics.spec.ts +++ b/src/test/e2e-emulated/intrinsics.spec.ts @@ -106,6 +106,16 @@ describe("intrinsics", () => { ), ).toBe(sha256("sometest")); expect(await contract.getGetHash4("wallet")).toBe(sha256("wallet")); + const longString = + "------------------------------------------------------------------------------------------------------------------------------129"; + expect(await contract.getGetHashLongComptime()).toBe( + sha256(longString), + ); + // NOTE: The discrepancy here is expected, since SHA256U operates only on the first 127 bytes + expect( + (await contract.getGetHashLongRuntime(longString)) !== + sha256(longString), + ).toBe(true); // Check `slice` expect( diff --git a/src/test/e2e-emulated/map.spec.ts b/src/test/e2e-emulated/map.spec.ts index def08f733..245196f1e 100644 --- a/src/test/e2e-emulated/map.spec.ts +++ b/src/test/e2e-emulated/map.spec.ts @@ -65,6 +65,7 @@ type TestValues = { valueUint8: bigint; valueUint42: bigint; valueUint256: bigint; + valueCoins: bigint; valueBool: boolean; valueCell: Cell; valueAddress: Address; @@ -105,6 +106,7 @@ const testCases: TestCase[] = [ valueUint8: 255n, valueUint42: 123_456_789n, valueUint256: 999_999_999_999n, + valueCoins: 100_000_000n, valueBool: true, valueCell: beginCell().storeUint(42, 32).endCell(), valueAddress: randomAddress(0, "address"), @@ -137,6 +139,7 @@ const testCases: TestCase[] = [ valueUint8: 0n, // Min unsigned int valueUint42: 0n, // Min unsigned int valueUint256: 0n, // Min unsigned int + valueCoins: 0n, valueBool: false, valueCell: beginCell() .storeUint(2n ** 32n - 1n, 32) @@ -171,6 +174,7 @@ const testCases: TestCase[] = [ valueUint8: 1n, valueUint42: 1n, valueUint256: 1n, + valueCoins: 1n, valueBool: false, valueCell: beginCell().storeUint(0, 32).endCell(), valueAddress: randomAddress(0, "address"), @@ -203,6 +207,7 @@ const testCases: TestCase[] = [ valueUint8: 128n, // Middle value valueUint42: 2n ** 41n, // Large power of 2 valueUint256: 2n ** 255n, // Large power of 2 + valueCoins: 2n ** 120n - 1n, valueBool: true, valueCell: beginCell() .storeUint(2n ** 31n, 32) @@ -591,6 +596,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -680,6 +686,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -767,6 +774,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -862,6 +870,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1504,6 +1513,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1578,6 +1588,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1672,6 +1683,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1764,6 +1776,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -1830,6 +1843,7 @@ describe("MapTestContract", () => { values.valueUint8, values.valueUint42, values.valueUint256, + values.valueCoins, values.valueBool, values.valueCell, values.valueAddress, @@ -1873,6 +1887,7 @@ describe("MapTestContract", () => { values.valueUint8, values.valueUint42, values.valueUint256, + values.valueCoins, values.valueBool, values.valueCell, values.valueAddress, @@ -1966,6 +1981,7 @@ describe("MapTestContract", () => { valueUint8: null, valueUint42: null, valueUint256: null, + valueCoins: null, valueBool: null, valueCell: null, valueAddress: null, @@ -2032,6 +2048,7 @@ describe("MapTestContract", () => { values.valueUint8, values.valueUint42, values.valueUint256, + values.valueCoins, values.valueBool, values.valueCell, values.valueAddress, @@ -2076,6 +2093,7 @@ describe("MapTestContract", () => { values.valueUint8, values.valueUint42, values.valueUint256, + values.valueCoins, values.valueBool, values.valueCell, values.valueAddress, diff --git a/src/test/e2e-emulated/structs.spec.ts b/src/test/e2e-emulated/structs.spec.ts index f7f20f5db..2ffd4fdc0 100644 --- a/src/test/e2e-emulated/structs.spec.ts +++ b/src/test/e2e-emulated/structs.spec.ts @@ -439,5 +439,7 @@ describe("structs", () => { b: 2n, c: 1n, }); + expect(await contract.getDestructuringTest8()).toBe(42n); + expect(await contract.getDestructuringTest8Const()).toBe(42n); }); }); diff --git a/src/types/__snapshots__/resolveDescriptors.spec.ts.snap b/src/types/__snapshots__/resolveDescriptors.spec.ts.snap index edb29b44e..e4173d1ff 100644 --- a/src/types/__snapshots__/resolveDescriptors.spec.ts.snap +++ b/src/types/__snapshots__/resolveDescriptors.spec.ts.snap @@ -10698,3 +10698,882 @@ exports[`resolveDescriptors should resolve descriptors for trait-base 1`] = ` `; exports[`resolveDescriptors should resolve descriptors for trait-base 2`] = `[]`; + +exports[`resolveDescriptors should resolve descriptors for trait-foreach 1`] = ` +[ + { + "ast": { + "id": 2, + "kind": "primitive_type_decl", + "loc": primitive Int;, + "name": { + "id": 1, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "primitive_type_decl", + "name": "Int", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 38154, + }, + { + "ast": { + "attributes": [], + "declarations": [], + "id": 4, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 3, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + { + "ast": { + "attributes": [], + "declarations": [ + { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 6, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 9, + "keyStorageType": null, + "keyType": { + "id": 7, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 8, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + { + "attributes": [], + "id": 18, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 17, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 14, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 16, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + ], + "id": 19, + "kind": "trait", + "loc": trait TraitWithForeach { + m: map; + + fun test() { foreach(_, _ in self.m) { } } +}, + "name": { + "id": 5, + "kind": "id", + "loc": TraitWithForeach, + "text": "TraitWithForeach", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [ + { + "abi": { + "name": "m", + "type": { + "key": "int", + "keyFormat": undefined, + "kind": "dict", + "value": "int", + "valueFormat": undefined, + }, + }, + "as": null, + "ast": { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 6, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 9, + "keyStorageType": null, + "keyType": { + "id": 7, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 8, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + "default": undefined, + "index": 0, + "loc": m: map, + "name": "m", + "type": { + "key": "Int", + "keyAs": null, + "kind": "map", + "value": "Int", + "valueAs": null, + }, + }, + ], + "functions": Map { + "test" => { + "ast": { + "attributes": [], + "id": 18, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 17, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 14, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 16, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + "isAbstract": false, + "isGetter": false, + "isInline": false, + "isMutating": true, + "isOverride": false, + "isVirtual": false, + "methodId": null, + "name": "test", + "origin": "user", + "params": [], + "returns": { + "kind": "void", + }, + "self": { + "kind": "ref", + "name": "TraitWithForeach", + "optional": false, + }, + }, + }, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "TraitWithForeach", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [ + { + "ast": { + "attributes": [], + "declarations": [], + "id": 4, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 3, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + ], + "uid": 20892, + }, + { + "ast": { + "attributes": [], + "declarations": [ + { + "as": null, + "id": 25, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 21, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 24, + "keyStorageType": null, + "keyType": { + "id": 22, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 23, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + ], + "id": 27, + "kind": "contract", + "loc": contract Test with TraitWithForeach { + m: map; +}, + "name": { + "id": 20, + "kind": "id", + "loc": Test, + "text": "Test", + }, + "traits": [ + { + "id": 26, + "kind": "id", + "loc": TraitWithForeach, + "text": "TraitWithForeach", + }, + ], + }, + "constants": [], + "dependsOn": [], + "fields": [ + { + "abi": { + "name": "m", + "type": { + "key": "int", + "keyFormat": undefined, + "kind": "dict", + "value": "int", + "valueFormat": undefined, + }, + }, + "as": null, + "ast": { + "as": null, + "id": 25, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 21, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 24, + "keyStorageType": null, + "keyType": { + "id": 22, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 23, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + "default": undefined, + "index": 0, + "loc": m: map, + "name": "m", + "type": { + "key": "Int", + "keyAs": null, + "kind": "map", + "value": "Int", + "valueAs": null, + }, + }, + ], + "functions": Map { + "test" => { + "ast": { + "attributes": [], + "id": 33, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 32, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 30, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 31, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + "isAbstract": false, + "isGetter": false, + "isInline": false, + "isMutating": true, + "isOverride": false, + "isVirtual": false, + "methodId": null, + "name": "test", + "origin": "user", + "params": [], + "returns": { + "kind": "void", + }, + "self": { + "kind": "ref", + "name": "Test", + "optional": false, + }, + }, + }, + "header": null, + "init": { + "ast": { + "id": 29, + "kind": "contract_init", + "loc": contract Test with TraitWithForeach { + m: map; +}, + "params": [], + "statements": [], + }, + "params": [], + }, + "interfaces": [], + "kind": "contract", + "name": "Test", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [ + { + "ast": { + "attributes": [], + "declarations": [], + "id": 4, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 3, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + { + "ast": { + "attributes": [], + "declarations": [ + { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 6, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 9, + "keyStorageType": null, + "keyType": { + "id": 7, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 8, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + { + "attributes": [], + "id": 18, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 17, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 14, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 16, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + ], + "id": 19, + "kind": "trait", + "loc": trait TraitWithForeach { + m: map; + + fun test() { foreach(_, _ in self.m) { } } +}, + "name": { + "id": 5, + "kind": "id", + "loc": TraitWithForeach, + "text": "TraitWithForeach", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [ + { + "abi": { + "name": "m", + "type": { + "key": "int", + "keyFormat": undefined, + "kind": "dict", + "value": "int", + "valueFormat": undefined, + }, + }, + "as": null, + "ast": { + "as": null, + "id": 10, + "initializer": null, + "kind": "field_decl", + "loc": m: map, + "name": { + "id": 6, + "kind": "id", + "loc": m, + "text": "m", + }, + "type": { + "id": 9, + "keyStorageType": null, + "keyType": { + "id": 7, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + "kind": "map_type", + "loc": map, + "valueStorageType": null, + "valueType": { + "id": 8, + "kind": "type_id", + "loc": Int, + "text": "Int", + }, + }, + }, + "default": undefined, + "index": 0, + "loc": m: map, + "name": "m", + "type": { + "key": "Int", + "keyAs": null, + "kind": "map", + "value": "Int", + "valueAs": null, + }, + }, + ], + "functions": Map { + "test" => { + "ast": { + "attributes": [], + "id": 18, + "kind": "function_def", + "loc": fun test() { foreach(_, _ in self.m) { } }, + "name": { + "id": 11, + "kind": "id", + "loc": test, + "text": "test", + }, + "params": [], + "return": null, + "statements": [ + { + "id": 17, + "keyName": { + "id": 12, + "kind": "id", + "loc": _, + "text": "_", + }, + "kind": "statement_foreach", + "loc": foreach(_, _ in self.m) { }, + "map": { + "aggregate": { + "id": 14, + "kind": "id", + "loc": self, + "text": "self", + }, + "field": { + "id": 15, + "kind": "id", + "loc": m, + "text": "m", + }, + "id": 16, + "kind": "field_access", + "loc": self.m, + }, + "statements": [], + "valueName": { + "id": 13, + "kind": "id", + "loc": _, + "text": "_", + }, + }, + ], + }, + "isAbstract": false, + "isGetter": false, + "isInline": false, + "isMutating": true, + "isOverride": false, + "isVirtual": false, + "methodId": null, + "name": "test", + "origin": "user", + "params": [], + "returns": { + "kind": "void", + }, + "self": { + "kind": "ref", + "name": "TraitWithForeach", + "optional": false, + }, + }, + }, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "TraitWithForeach", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [ + { + "ast": { + "attributes": [], + "declarations": [], + "id": 4, + "kind": "trait", + "loc": trait BaseTrait { }, + "name": { + "id": 3, + "kind": "id", + "loc": BaseTrait, + "text": "BaseTrait", + }, + "traits": [], + }, + "constants": [], + "dependsOn": [], + "fields": [], + "functions": Map {}, + "header": null, + "init": null, + "interfaces": [], + "kind": "trait", + "name": "BaseTrait", + "origin": "user", + "partialFieldCount": 0, + "receivers": [], + "signature": null, + "tlb": null, + "traits": [], + "uid": 1020, + }, + ], + "uid": 20892, + }, + ], + "uid": 44104, + }, +] +`; + +exports[`resolveDescriptors should resolve descriptors for trait-foreach 2`] = `[]`; diff --git a/src/types/__snapshots__/resolveStatements.spec.ts.snap b/src/types/__snapshots__/resolveStatements.spec.ts.snap index b3bc08044..7f6b8009a 100644 --- a/src/types/__snapshots__/resolveStatements.spec.ts.snap +++ b/src/types/__snapshots__/resolveStatements.spec.ts.snap @@ -577,6 +577,66 @@ Line 8, col 13: " `; +exports[`resolveStatements should fail statements for getter-collision-with-reserved_opt1 1`] = ` +":10:9: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 9: + 9 | +> 10 | get(115390) fun _lazy_deployment_completed(): String{ + ^~~~~~ + 11 | return "not ok"; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved_opt2 1`] = ` +":10:9: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 9: + 9 | +> 10 | get(121275) fun _get_abi_ipfs(): Int{ + ^~~~~~ + 11 | return 123; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved_opt3 1`] = ` +":10:9: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 9: + 9 | +> 10 | get(113617) fun _supported_interfaces(): String{ + ^~~~~~ + 11 | return "ok"; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved1 1`] = ` +":10:5: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 5: + 9 | +> 10 | get fun lazy_deployment_completed(): String{ + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | return "not ok"; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved2 1`] = ` +":10:5: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 5: + 9 | +> 10 | get fun get_abi_ipfs(): Int{ + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | return 123; +" +`; + +exports[`resolveStatements should fail statements for getter-collision-with-reserved3 1`] = ` +":10:5: method ids cannot overlap with Tact reserved method ids: 113617, 115390, 121275 +Line 10, col 5: + 9 | +> 10 | get fun supported_interfaces(): String{ + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | return "ok"; +" +`; + exports[`resolveStatements should fail statements for getter-collision-with-trait 1`] = ` ":4:13: Method ID collision: getter 'pko' has the same method ID 103057 as getter 'getter1' Pick a different getter name or explicit method ID to avoid collisions @@ -939,12 +999,12 @@ Line 16, col 29: `; exports[`resolveStatements should fail statements for stmt-destructuring-fields-non-existing 1`] = ` -":15:22: Field '"d"' not found in type 'S' -Line 15, col 22: +":15:19: Field '"d"' not found in type 'S' +Line 15, col 19: 14 | let s = S{ a: 1, b: 2, c: 3 }; -> 15 | let S { a, b, c, d: e } = s; - ^ - 16 | return a + b + c + e; +> 15 | let S { a, b, d: e } = s; + ^ + 16 | return a + b + e; " `; @@ -959,12 +1019,12 @@ Line 15, col 16: `; exports[`resolveStatements should fail statements for stmt-destructuring-fields-non-existing-punned2 1`] = ` -":15:22: Field '"d"' not found in type 'S' -Line 15, col 22: +":15:19: Field '"d"' not found in type 'S' +Line 15, col 19: 14 | let s = S{ a: 1, b: 2, c: 3 }; -> 15 | let S { a, b, c, d } = s; - ^ - 16 | return a + b + c + d; +> 15 | let S { a, b, d } = s; + ^ + 16 | return a + b + d; " `; @@ -972,9 +1032,9 @@ exports[`resolveStatements should fail statements for stmt-destructuring-fields- ":15:12: Field '"_"' not found in type 'S' Line 15, col 12: 14 | let s = S{ a: 1, b: 2, c: 3 }; -> 15 | let S {_, b} = s; +> 15 | let S {_, b, c} = s; ^ - 16 | return b; + 16 | return b + c; " `; @@ -998,6 +1058,26 @@ Line 16, col 16: " `; +exports[`resolveStatements should fail statements for stmt-destructuring-fields-wrong-count 1`] = ` +":15:5: Expected 3 fields, but got 2 +Line 15, col 5: + 14 | let s = S{ a: 1, b: 2, c: 3 }; +> 15 | let S { a, b } = s; + ^~~~~~~~~~~~~~~~~~~ + 16 | return a + b; +" +`; + +exports[`resolveStatements should fail statements for stmt-destructuring-fields-wrong-count2 1`] = ` +":15:5: Expected 3 fields, but got 4 +Line 15, col 5: + 14 | let s = S{ a: 1, b: 2, c: 3 }; +> 15 | let S { a, b, c, d } = s; + ^~~~~~~~~~~~~~~~~~~~~~~~~ + 16 | return a + b + c + d; +" +`; + exports[`resolveStatements should fail statements for stmt-destructuring-fields-wrong-type 1`] = ` ":18:21: Type mismatch: "S2" is not assignable to "S1" Line 18, col 21: diff --git a/src/types/resolveDescriptors.ts b/src/types/resolveDescriptors.ts index f6eaafa3e..557c83fb5 100644 --- a/src/types/resolveDescriptors.ts +++ b/src/types/resolveDescriptors.ts @@ -43,6 +43,7 @@ import { import { getRawAST } from "../grammar/store"; import { cloneNode } from "../grammar/clone"; import { crc16 } from "../utils/crc16"; +import { isSubsetOf } from "../utils/isSubsetOf"; import { evalConstantExpression } from "../constEval"; import { resolveABIType, intMapFormats } from "./resolveABITypeRef"; import { enabledExternals } from "../config/features"; @@ -909,13 +910,13 @@ export function resolveDescriptors(ctx: CompilerContext) { const paramSet = new Set( a.params.map((typedId) => idText(typedId.name)), ); - if (!paramSet.isSubsetOf(shuffleArgSet)) { + if (!isSubsetOf(paramSet, shuffleArgSet)) { throwCompilationError( "asm argument rearrangement must mention all function parameters", a.loc, ); } - if (!shuffleArgSet.isSubsetOf(paramSet)) { + if (!isSubsetOf(shuffleArgSet, paramSet)) { throwCompilationError( "asm argument rearrangement must mention only function parameters", a.loc, @@ -973,13 +974,13 @@ export function resolveDescriptors(ctx: CompilerContext) { // mutating functions also return `self` arg (implicitly in Tact, but explicitly in FunC) retTupleSize += isMutating ? 1 : 0; const returnValueSet = new Set([...Array(retTupleSize).keys()]); - if (!returnValueSet.isSubsetOf(shuffleRetSet)) { + if (!isSubsetOf(returnValueSet, shuffleRetSet)) { throwCompilationError( `asm return rearrangement must mention all return position numbers: [0..${retTupleSize - 1}]`, a.loc, ); } - if (!shuffleRetSet.isSubsetOf(returnValueSet)) { + if (!isSubsetOf(shuffleRetSet, returnValueSet)) { throwCompilationError( `asm return rearrangement must mention only valid return position numbers: [0..${retTupleSize - 1}]`, a.loc, diff --git a/src/types/resolveExpression.ts b/src/types/resolveExpression.ts index 702523f0f..2500cffca 100644 --- a/src/types/resolveExpression.ts +++ b/src/types/resolveExpression.ts @@ -38,6 +38,7 @@ import { GlobalFunctions } from "../abi/global"; import { isAssignable, moreGeneralType } from "./subtyping"; import { throwInternalCompilerError } from "../errors"; import { StructFunctions } from "../abi/struct"; +import { prettyPrint } from "../prettyPrinter"; const store = createContextStore<{ ast: AstExpression; @@ -62,7 +63,10 @@ function registerExpType( if (typeRefEquals(ex.description, description)) { return ctx; } - throwInternalCompilerError(`Expression ${exp.id} already has a type`); + throwInternalCompilerError( + `Expression ${prettyPrint(exp)} with exp.id = ${exp.id} already has registered type "${printTypeRef(ex.description)}" but the typechecker is trying to re-register it as "${printTypeRef(description)}"`, + exp.loc, + ); } return store.set(ctx, exp.id, { ast: exp, description }); } diff --git a/src/types/resolveStatements.ts b/src/types/resolveStatements.ts index 9ea42ceea..760c4b950 100644 --- a/src/types/resolveStatements.ts +++ b/src/types/resolveStatements.ts @@ -717,6 +717,17 @@ function processStatements( ); } + // Check variables count + if ( + !s.ignoreUnspecifiedFields && + s.identifiers.size !== ty.fields.length + ) { + throwCompilationError( + `Expected ${ty.fields.length} fields, but got ${s.identifiers.size}`, + s.loc, + ); + } + // Compare type with the specified one const typeRef = resolveTypeRef(ctx, s.type); if (typeRef.kind !== "ref") { @@ -967,6 +978,40 @@ export function resolveStatements(ctx: CompilerContext) { return ctx; } +function checkMethodId(methodId: bigint, loc: SrcInfo) { + // method ids are 19-bit signed integers + if (methodId < -(2n ** 18n) || methodId >= 2n ** 18n) { + throwConstEvalError( + "method ids must fit 19-bit signed integer range", + true, + loc, + ); + } + // method ids -4, -3, -2, -1, 0 ... 2^14 - 1 (inclusive) are kind of reserved by TVM + // for the upper bound see F12_n (CALL) TVM instruction + // and many small ids will be taken by internal procedures + // + // also, some ids are taken by the getters generated by Tact: + // supported_interfaces -> 113617 + // lazy_deployment_completed -> 115390 + // get_abi_ipfs -> 121275 + if (-4n <= methodId && methodId < 2n ** 14n) { + throwConstEvalError( + "method ids cannot overlap with the TVM reserved ids: -4, -3, -2, -1, 0 ... 2^14 - 1", + true, + loc, + ); + } + const tactGeneratedGetterMethodIds = [113617n, 115390n, 121275n]; + if (tactGeneratedGetterMethodIds.includes(methodId)) { + throwConstEvalError( + `method ids cannot overlap with Tact reserved method ids: ${tactGeneratedGetterMethodIds.map((n) => n.toString()).join(", ")}`, + true, + loc, + ); + } +} + function getMethodId( funcDescr: FunctionDescription, ctx: CompilerContext, @@ -990,39 +1035,11 @@ function getMethodId( evalConstantExpression(optMethodId, ctx), optMethodId.loc, ); - // method ids are 19-bit signed integers - if (methodId < -(2n ** 18n) || methodId >= 2n ** 18n) { - throwConstEvalError( - "method ids must fit 19-bit signed integer range", - true, - optMethodId!.loc, - ); - } - // method ids -4, -3, -2, -1, 0 ... 2^14 - 1 (inclusive) are kind of reserved by TVM - // for the upper bound see F12_n (CALL) TVM instruction - // and many small ids will be taken by internal procedures - // - // also, some ids are taken by the getters generated by Tact: - // supported_interfaces -> 113617 - // lazy_deployment_completed -> 115390 - // get_abi_ipfs -> 121275 - if (-4n <= methodId && methodId < 2n ** 14n) { - throwConstEvalError( - "method ids cannot overlap with the TVM reserved ids: -4, -3, -2, -1, 0 ... 2^14 - 1", - true, - optMethodId!.loc, - ); - } - const tactGeneratedGetterMethodIds = [113617n, 115390n, 121275n]; - if (tactGeneratedGetterMethodIds.includes(methodId)) { - throwConstEvalError( - `method ids cannot overlap with Tact reserved method ids: ${tactGeneratedGetterMethodIds.map((n) => n.toString()).join(", ")}`, - true, - optMethodId!.loc, - ); - } + checkMethodId(methodId, optMethodId.loc); return Number(methodId); } else { - return (crc16(funcDescr.name) & 0xffff) | 0x10000; + const methodId = (crc16(funcDescr.name) & 0xffff) | 0x10000; + checkMethodId(BigInt(methodId), funcDescr.ast.loc); + return methodId; } } diff --git a/src/types/stmts-failed/getter-collision-with-reserved1.tact b/src/types/stmts-failed/getter-collision-with-reserved1.tact new file mode 100644 index 000000000..acc248c2d --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved1.tact @@ -0,0 +1,13 @@ +primitive String; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get fun lazy_deployment_completed(): String{ + return "not ok"; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved2.tact b/src/types/stmts-failed/getter-collision-with-reserved2.tact new file mode 100644 index 000000000..dc4b6f2e4 --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved2.tact @@ -0,0 +1,13 @@ +primitive Int; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get fun get_abi_ipfs(): Int{ + return 123; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved3.tact b/src/types/stmts-failed/getter-collision-with-reserved3.tact new file mode 100644 index 000000000..5500943b7 --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved3.tact @@ -0,0 +1,13 @@ +primitive String; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get fun supported_interfaces(): String{ + return "ok"; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved_opt1.tact b/src/types/stmts-failed/getter-collision-with-reserved_opt1.tact new file mode 100644 index 000000000..79062573a --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved_opt1.tact @@ -0,0 +1,13 @@ +primitive String; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get(115390) fun _lazy_deployment_completed(): String{ + return "not ok"; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved_opt2.tact b/src/types/stmts-failed/getter-collision-with-reserved_opt2.tact new file mode 100644 index 000000000..a972009f1 --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved_opt2.tact @@ -0,0 +1,13 @@ +primitive Int; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get(121275) fun _get_abi_ipfs(): Int{ + return 123; + } +} diff --git a/src/types/stmts-failed/getter-collision-with-reserved_opt3.tact b/src/types/stmts-failed/getter-collision-with-reserved_opt3.tact new file mode 100644 index 000000000..3898ab008 --- /dev/null +++ b/src/types/stmts-failed/getter-collision-with-reserved_opt3.tact @@ -0,0 +1,13 @@ +primitive String; + +trait BaseTrait { } + +contract TestContract { + get fun getter1() { + return; + } + + get(113617) fun _supported_interfaces(): String{ + return "ok"; + } +} diff --git a/src/types/stmts-failed/stmt-destructuring-fields-non-existing-punned2.tact b/src/types/stmts-failed/stmt-destructuring-fields-non-existing-punned2.tact index cf02bcad2..09952ac7d 100644 --- a/src/types/stmts-failed/stmt-destructuring-fields-non-existing-punned2.tact +++ b/src/types/stmts-failed/stmt-destructuring-fields-non-existing-punned2.tact @@ -12,6 +12,6 @@ struct S { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; - let S { a, b, c, d } = s; - return a + b + c + d; + let S { a, b, d } = s; + return a + b + d; } \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields-non-existing-underscore.tact b/src/types/stmts-failed/stmt-destructuring-fields-non-existing-underscore.tact index 9bf8c31a5..783bcad18 100644 --- a/src/types/stmts-failed/stmt-destructuring-fields-non-existing-underscore.tact +++ b/src/types/stmts-failed/stmt-destructuring-fields-non-existing-underscore.tact @@ -12,6 +12,6 @@ struct S { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; - let S {_, b} = s; - return b; + let S {_, b, c} = s; + return b + c; } \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields-non-existing.tact b/src/types/stmts-failed/stmt-destructuring-fields-non-existing.tact index 8dcf1283e..1ed48343a 100644 --- a/src/types/stmts-failed/stmt-destructuring-fields-non-existing.tact +++ b/src/types/stmts-failed/stmt-destructuring-fields-non-existing.tact @@ -12,6 +12,6 @@ struct S { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; - let S { a, b, c, d: e } = s; - return a + b + c + e; + let S { a, b, d: e } = s; + return a + b + e; } \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields-wrong-count.tact b/src/types/stmts-failed/stmt-destructuring-fields-wrong-count.tact new file mode 100644 index 000000000..d711b2cf8 --- /dev/null +++ b/src/types/stmts-failed/stmt-destructuring-fields-wrong-count.tact @@ -0,0 +1,17 @@ +primitive Int; + +trait BaseTrait { + +} + +struct S { + a: Int; + b: Int; + c: Int; +} + +fun testFunc(): Int { + let s = S{ a: 1, b: 2, c: 3 }; + let S { a, b } = s; + return a + b; +} \ No newline at end of file diff --git a/src/types/stmts-failed/stmt-destructuring-fields-wrong-count2.tact b/src/types/stmts-failed/stmt-destructuring-fields-wrong-count2.tact new file mode 100644 index 000000000..cf02bcad2 --- /dev/null +++ b/src/types/stmts-failed/stmt-destructuring-fields-wrong-count2.tact @@ -0,0 +1,17 @@ +primitive Int; + +trait BaseTrait { + +} + +struct S { + a: Int; + b: Int; + c: Int; +} + +fun testFunc(): Int { + let s = S{ a: 1, b: 2, c: 3 }; + let S { a, b, c, d } = s; + return a + b + c + d; +} \ No newline at end of file diff --git a/src/types/stmts/stmt-destructuring.tact b/src/types/stmts/stmt-destructuring.tact index c559d99c1..e0a4ae779 100644 --- a/src/types/stmts/stmt-destructuring.tact +++ b/src/types/stmts/stmt-destructuring.tact @@ -18,12 +18,12 @@ message M { fun testFunc(): Int { let s = S{ a: 1, b: 2, c: 3 }; let S { a, b, c } = s; - let S { a: a1 } = s; - let S { b: b1 } = s; - let S { c: c1 } = s; - let S { a: a2, b: b2 } = s; - let S { a: a3, c: c3 } = s; - let S { b: b4, c: c4 } = s; + let S { a: a1, .. } = s; + let S { b: b1, .. } = s; + let S { c: c1, .. } = s; + let S { a: a2, b: b2, .. } = s; + let S { a: a3, c: c3, .. } = s; + let S { b: b4, c: c4, .. } = s; let m = M{ a: 1, b: 2 }; let M { a: a_m, b: b_m } = m; diff --git a/src/types/test/trait-foreach.tact b/src/types/test/trait-foreach.tact new file mode 100644 index 000000000..34abda3a9 --- /dev/null +++ b/src/types/test/trait-foreach.tact @@ -0,0 +1,13 @@ +primitive Int; +trait BaseTrait { } + +trait TraitWithForeach { + m: map; + + fun test() { foreach(_, _ in self.m) { } } +} + + +contract Test with TraitWithForeach { + m: map; +} diff --git a/src/utils/array.ts b/src/utils/array.ts new file mode 100644 index 000000000..ab1c33bf0 --- /dev/null +++ b/src/utils/array.ts @@ -0,0 +1,42 @@ +export const isUndefined = (t: T | undefined): t is undefined => + typeof t === "undefined"; + +export const groupBy = ( + items: readonly T[], + f: (t: T) => U, +): readonly (readonly T[])[] => { + const result: T[][] = []; + const [head, ...tail] = items; + if (isUndefined(head)) { + return result; + } + let group: T[] = [head]; + result.push(group); + let tag: U = f(head); + for (const item of tail) { + const nextTag = f(item); + if (tag === nextTag) { + group.push(item); + } else { + group = [item]; + result.push(group); + tag = nextTag; + } + } + return result; +}; + +export const intercalate = ( + items: readonly (readonly T[])[], + value: T, +): readonly T[] => { + const [head, ...tail] = items; + if (isUndefined(head)) { + return []; + } + const result: T[] = [...head]; + for (const item of tail) { + result.push(value, ...item); + } + return result; +}; diff --git a/src/utils/isSubsetOf.spec.ts b/src/utils/isSubsetOf.spec.ts new file mode 100644 index 000000000..cde7c2925 --- /dev/null +++ b/src/utils/isSubsetOf.spec.ts @@ -0,0 +1,91 @@ +import { ReadonlySetLike, isSubsetOf } from "./isSubsetOf"; + +// Tests are adapted from: +// https://github.com/zloirock/core-js/blob/227a758ef96fa585a66cc1e89741e7d0bb696f48/tests/unit-global/es.set.is-subset-of.js + +describe("isSubsetOf", () => { + /* eslint-disable @typescript-eslint/no-explicit-any */ + let s1: Set; + let s2: ReadonlySetLike; + + it("should implement isSubsetOf correctly", () => { + s1 = new Set([1]); + s2 = new Set([1, 2, 3]); + expect(isSubsetOf(s1, s2)).toBe(true); + + s1 = new Set([1]); + s2 = new Set([2, 3, 4]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1, 2, 3]); + s2 = new Set([5, 4, 3, 2, 1]); + expect(isSubsetOf(s1, s2)).toBe(true); + + s1 = new Set([1, 2, 3]); + s2 = new Set([5, 4, 3, 2]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1]); + s2 = createSetLike([1, 2, 3]); + expect(isSubsetOf(s1, s2)).toBe(true); + + s1 = new Set([1]); + s2 = createSetLike([2, 3, 4]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1, 2, 3]); + s2 = createSetLike([5, 4, 3, 2, 1]); + expect(isSubsetOf(s1, s2)).toBe(true); + + s1 = new Set([1, 2, 3]); + s2 = createSetLike([5, 4, 3, 2]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1, 2, 3]); + s2 = new Set([1]); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set([1, 2, 3]); + s2 = new Set(); + expect(isSubsetOf(s1, s2)).toBe(false); + + s1 = new Set(); + s2 = new Set([1, 2, 3]); + expect(isSubsetOf(s1, s2)).toBe(true); + }); +}); + +// Helper functions are adapted from: +// https://github.com/zloirock/core-js/blob/227a758ef96fa585a66cc1e89741e7d0bb696f48/tests/helpers/helpers.js + +function createSetLike(elements: T[]): ReadonlySetLike { + return { + size: elements.length, + has(value: T): boolean { + return includes(elements, value); + }, + keys(): Iterator { + return createIterator(elements); + }, + }; +} + +function includes(target: T[], wanted: T) { + return target.some((element) => element === wanted); +} + +function createIterator(elements: T[]): Iterator { + let index = 0; + const iterator = { + called: false, + /* eslint-disable @typescript-eslint/no-explicit-any */ + next(): IteratorResult { + iterator.called = true; + return { + value: elements[index++], + done: index > elements.length, + }; + }, + }; + return iterator; +} diff --git a/src/utils/isSubsetOf.ts b/src/utils/isSubsetOf.ts new file mode 100644 index 000000000..c333bb58c --- /dev/null +++ b/src/utils/isSubsetOf.ts @@ -0,0 +1,38 @@ +/** Taken from TypeScript collection lib to perfectly match the .isSubsetOf signature */ +export interface ReadonlySetLike { + /** + * Despite its name, returns an iterator of the values in the set-like. + */ + keys(): Iterator; + /** + * @returns a boolean indicating whether an element with the specified value exists in the set-like or not. + */ + has(value: T): boolean; + /** + * @returns the number of (unique) elements in the set-like. + */ + readonly size: number; +} + +/** + * @returns a boolean indicating whether all the elements in Set `one` are also in the `other`. + */ +export function isSubsetOf( + one: Set, + other: ReadonlySetLike, +): boolean { + // If the builtin method exists, just call it + if ("isSubsetOf" in Set.prototype) { + return one.isSubsetOf(other); + } + // If not, provide the implementation + if (one.size > other.size) { + return false; + } + for (const element of one) { + if (!other.has(element)) { + return false; + } + } + return true; +} diff --git a/src/utils/tricks.ts b/src/utils/tricks.ts new file mode 100644 index 000000000..7afbfdff2 --- /dev/null +++ b/src/utils/tricks.ts @@ -0,0 +1,136 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +type Extend = H extends infer A ? [...T, A] : never; +type Flat = TS extends [ + infer H, + ...infer T, +] + ? Flat> + : R; + +declare const NoSuchCase: unique symbol; +interface NoSuchCaseBug extends Array { + [NoSuchCase]: L; +} +type On = { + on: ( + ...key: I extends Flat ? DI : NoSuchCaseBug + ) => ( + handler: (...args: Extract>) => DO, + ) => MV>, O | DO>; +}; + +declare const CasesAreNotExhaustive: unique symbol; +interface NonExhaustiveBug { + [CasesAreNotExhaustive]: L; +} +type End = [I] extends [never] + ? EndInternal + : { + otherwise: (handle: (...input: I) => DO) => O | DO; + end: NonExhaustiveBug; + }; +type MV = End & On; + +type OnInternal = { + on: ( + ...key: DI + ) => ( + handler: (...args: Extract>) => DO, + ) => MVInternal>, O | DO>; +}; +type EndInternal = { + otherwise: (handle: (...input: I) => DO) => O | DO; + end: () => O; +}; +type MVInternal = EndInternal & OnInternal; + +const deepMatch = (a: unknown, b: unknown): boolean => { + if ( + a === b && + ["number", "string", "boolean", "bigint"].includes(typeof a) && + typeof a === typeof b + ) { + return true; + } + if (a === null || b === null) { + return a === b; + } + if (typeof a === "object" && typeof b === "object") { + if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) { + return a.every((a, i) => deepMatch(a, b[i])); + } else { + return Object.entries(b).every(([k, b]) => + deepMatch(k in a ? (a as any)[k] : undefined, b), + ); + } + } + return false; +}; + +export const match = ( + ...args: I +): MV, never> => { + const rec = (end: () => O): MVInternal => ({ + end, + otherwise: (handler) => handler(...(args as unknown as I)), + on: + (...match: DI) => + (handler: (...args: Extract>) => DO) => + rec>, O | DO>(() => + deepMatch(args, match) + ? handler( + ...(args as unknown as Extract>), + ) + : end(), + ), + }); + return rec, never>(() => { + throw new Error("Not exhaustive"); + }) as MV, never>; +}; + +import { throwInternalCompilerError } from "../errors"; + +/** + * Convert union to intersection. See https://stackoverflow.com/q/50374908 + */ +type Intersect = (T extends unknown ? (x: T) => 0 : never) extends ( + x: infer R, +) => 0 + ? R + : never; + +/** + * Makes types more readable + * Example: Unwrap<{ a: 1 } & { b: 2 }> = { a: 1, b: 2 } + */ +type Unwrap = T extends infer R ? { [K in keyof R]: R[K] } : never; + +type Inputs = I extends { kind: infer K } + ? K extends string + ? Record unknown> + : never + : never; +type Outputs = { [K in keyof O]: (input: never) => O[K] }; +type Handlers = Unwrap>> & Outputs; + +/** + * Make visitor for disjoint union (tagged union, discriminated union) + */ +export const makeVisitor = + () => + (handlers: Handlers) => + (input: Extract): O[keyof O] => { + const handler = (handlers as Record O[keyof O]>)[ + input.kind + ]; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (handler) { + return handler(input); + } else { + throwInternalCompilerError( + `Reached impossible case: ${input.kind}`, + ); + } + }; diff --git a/stdlib/std/cells.tact b/stdlib/std/cells.tact index eb9b1c561..2bb474d1a 100644 --- a/stdlib/std/cells.tact +++ b/stdlib/std/cells.tact @@ -74,6 +74,31 @@ asm extends fun beginParse(self: Cell): Slice { CTOS } asm(-> 1 0) extends mutates fun loadRef(self: Slice): Cell { LDREF } +/// Extension function for the `Slice`. Available since Tact 1.5.0. +/// +/// Preloads the next reference from the `Slice` as a `Cell`. Doesn't modify the original `Slice`. +/// +/// Attempts to preload such reference `Cell` when `Slice` doesn't contain it throw an exception with exit code 8: `Cell overflow`. +/// +/// Attempts to preload more data than `Slice` contains throw an exception with exit code 9: `Cell underflow`. +/// +/// ```tact +/// let s1: Slice = beginCell().storeRef(emptyCell()).asSlice(); +/// let fizz: Cell = s1.preloadRef(); // didn't modify s1 +/// +/// let s2: Slice = beginCell() +/// .storeRef(emptyCell()) +/// .storeRef(s1.asCell()) +/// .asSlice(); +/// let ref1: Cell = s2.preloadRef(); +/// let ref2: Cell = s2.preloadRef(); +/// ref1 == ref2; // true +/// ``` +/// +/// See: +/// * https://docs.tact-lang.org/ref/core-cells#slicepreloadref +/// * https://docs.tact-lang.org/book/exit-codes +/// asm extends fun preloadRef(self: Slice): Cell { PLDREF } // special treatment in Func compiler, so not replaced with asm "LDSLICEX" diff --git a/yarn.lock b/yarn.lock index aa0621530..2432349c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -308,16 +308,17 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cspell/cspell-bundled-dicts@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.15.4.tgz#dcba969630b8ce132e649721027a2e67ff483174" - integrity sha512-t5b2JwGeUmzmjl319mCuaeKGxTvmzLLRmrpdHr+ZZGRO4nf7L48Lbe9A6uwNUvsZe0cXohiNXsrrsuzRVXswVA== +"@cspell/cspell-bundled-dicts@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.16.0.tgz#f82749921e3e1181e295670f17ca28453cddc238" + integrity sha512-R0Eqq5kTZnmZ0elih5uY3TWjMqqAeMl7ciU7maUs+m1FNjCEdJXtJ9wrQxNgjmXi0tX8cvahZRO3O558tEz/KA== dependencies: "@cspell/dict-ada" "^4.0.5" + "@cspell/dict-al" "^1.0.3" "@cspell/dict-aws" "^4.0.7" "@cspell/dict-bash" "^4.1.8" "@cspell/dict-companies" "^3.1.7" - "@cspell/dict-cpp" "^5.1.22" + "@cspell/dict-cpp" "^6.0.1" "@cspell/dict-cryptocurrencies" "^5.0.3" "@cspell/dict-csharp" "^4.0.5" "@cspell/dict-css" "^4.0.16" @@ -329,7 +330,7 @@ "@cspell/dict-en-common-misspellings" "^2.0.7" "@cspell/dict-en-gb" "1.1.33" "@cspell/dict-en_us" "^4.3.26" - "@cspell/dict-filetypes" "^3.0.7" + "@cspell/dict-filetypes" "^3.0.8" "@cspell/dict-flutter" "^1.0.3" "@cspell/dict-fonts" "^4.0.3" "@cspell/dict-fsharp" "^1.0.4" @@ -339,7 +340,7 @@ "@cspell/dict-golang" "^6.0.16" "@cspell/dict-google" "^1.0.4" "@cspell/dict-haskell" "^4.0.4" - "@cspell/dict-html" "^4.0.9" + "@cspell/dict-html" "^4.0.10" "@cspell/dict-html-symbol-entities" "^4.0.3" "@cspell/dict-java" "^5.0.10" "@cspell/dict-julia" "^1.0.4" @@ -348,9 +349,10 @@ "@cspell/dict-lorem-ipsum" "^4.0.3" "@cspell/dict-lua" "^4.0.6" "@cspell/dict-makefile" "^1.0.3" + "@cspell/dict-markdown" "^2.0.7" "@cspell/dict-monkeyc" "^1.0.9" - "@cspell/dict-node" "^5.0.4" - "@cspell/dict-npm" "^5.1.8" + "@cspell/dict-node" "^5.0.5" + "@cspell/dict-npm" "^5.1.11" "@cspell/dict-php" "^4.0.13" "@cspell/dict-powershell" "^5.0.13" "@cspell/dict-public-licenses" "^2.0.11" @@ -359,48 +361,53 @@ "@cspell/dict-ruby" "^5.0.7" "@cspell/dict-rust" "^4.0.9" "@cspell/dict-scala" "^5.0.6" - "@cspell/dict-software-terms" "^4.1.11" + "@cspell/dict-software-terms" "^4.1.13" "@cspell/dict-sql" "^2.1.8" "@cspell/dict-svelte" "^1.0.5" "@cspell/dict-swift" "^2.0.4" - "@cspell/dict-terraform" "^1.0.5" - "@cspell/dict-typescript" "^3.1.10" + "@cspell/dict-terraform" "^1.0.6" + "@cspell/dict-typescript" "^3.1.11" "@cspell/dict-vue" "^3.0.3" -"@cspell/cspell-json-reporter@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.15.4.tgz#f8902234bd000040d40ae3574860d221c991aff9" - integrity sha512-solraYhZG4l++NeVCOtpc8DMvwHc46TmJt8DQbgvKtk6wOjTEcFrwKfA6Ei9YKbvyebJlpWMenO3goOll0loYg== +"@cspell/cspell-json-reporter@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.16.0.tgz#d83f09a8ba4467db22c0cda4ca5ba20a3e20cf00" + integrity sha512-KLjPK94gA3JNuWy70LeenJ6EL3SFk2ejERKYJ6SVV/cVOKIvVd2qe42yX3/A/DkF2xzuZ2LD4z0sfoqQL1BaqA== dependencies: - "@cspell/cspell-types" "8.15.4" + "@cspell/cspell-types" "8.16.0" -"@cspell/cspell-pipe@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.15.4.tgz#c3c958d3b900ccdcc4f2a8610fe464882e3f1759" - integrity sha512-WfCmZVFC6mX6vYlf02hWwelcSBTbDQgc5YqY+1miuMk+OHSUAHSACjZId6/a4IAID94xScvFfj7jgrdejUVvIQ== +"@cspell/cspell-pipe@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.16.0.tgz#368dc5de4ea4807ec109962045de1f7ad9c0dbc3" + integrity sha512-WoCgrv/mrtwCY4lhc6vEcqN3AQ7lT6K0NW5ShoSo116U2tRaW0unApIYH4Va8u7T9g3wyspFEceQRR1xD9qb9w== -"@cspell/cspell-resolver@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.15.4.tgz#83ade61a1e11b88be756a805138ec8b31191e0d0" - integrity sha512-Zr428o+uUTqywrdKyjluJVnDPVAJEqZ1chQLKIrHggUah1cgs5aQ7rZ+0Rv5euYMlC2idZnP7IL6TDaIib80oA== +"@cspell/cspell-resolver@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.16.0.tgz#0360270e7beb17dcb7dfbff6af7ed310e1aacdfc" + integrity sha512-b+99bph43ptkXlQHgPXSkN/jK6LQHy2zL1Fm9up7+x6Yr64bxAzWzoeqJAPtnrPvFuOrFN0jZasZzKBw8CvrrQ== dependencies: global-directory "^4.0.1" -"@cspell/cspell-service-bus@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.15.4.tgz#76a0c380e0102a4521ce96a64ade580331ca8404" - integrity sha512-pXYofnV/V9Y3LZdfFGbmhdxPX/ABjiD3wFjGHt5YhIU9hjVVuvjFlgY7pH2AvRjs4F8xKXv1ReWl44BJOL9gLA== +"@cspell/cspell-service-bus@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.16.0.tgz#f55dec069b2ed9f2017c6b687fe6288ed473097c" + integrity sha512-+fn763JKA4EYCOv+1VShFq015UMEBAFRDr+rlCnesgLE0fv9TSFVLsjOfh9/g6GuGQLCRLUqKztwwuueeErstQ== -"@cspell/cspell-types@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.15.4.tgz#307db2055ff669d99487c1772edb393243bb5098" - integrity sha512-1hDtgYDQVW11zgtrr12EmGW45Deoi7IjZOhzPFLb+3WkhZ46ggWdbrRalWwBolQPDDo6+B2Q6WXz5hdND+Tpwg== +"@cspell/cspell-types@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.16.0.tgz#22b641512eb6b33b79b2940d1273df0bb5478ba8" + integrity sha512-bGrIK7p4NVsK+QX/CYWmjax+FkzfSIZaIaoiBESGV5gmwgXDVRMJ3IP6tQVAmTtckOYHCmtT5CZgI8zXWr8dHQ== "@cspell/dict-ada@^4.0.5": version "4.0.5" resolved "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.5.tgz#c14aae2faaecbad2d99f0d701e4700a48c68ef60" integrity sha512-6/RtZ/a+lhFVmrx/B7bfP7rzC4yjEYe8o74EybXcvu4Oue6J4Ey2WSYj96iuodloj1LWrkNCQyX5h4Pmcj0Iag== +"@cspell/dict-al@^1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.0.3.tgz#09e288b5ab56b126dce895d3301faf7c0dd732d6" + integrity sha512-V1HClwlfU/qwSq2Kt+MkqRAsonNu3mxjSCDyGRecdLGIHmh7yeEeaxqRiO/VZ4KP+eVSiSIlbwrb5YNFfxYZbw== + "@cspell/dict-aws@^4.0.7": version "4.0.7" resolved "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.7.tgz#f96f3b70cd52a25b895eb08e297de5a5cc3fc5b6" @@ -416,10 +423,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.1.7.tgz#c9abd6f5293f103062f54dde01f2bee939189f79" integrity sha512-ncVs/efuAkP1/tLDhWbXukBjgZ5xOUfe03neHMWsE8zvXXc5+Lw6TX5jaJXZLOoES/f4j4AhRE20jsPCF5pm+A== -"@cspell/dict-cpp@^5.1.22": - version "5.1.22" - resolved "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.1.22.tgz#ee14d2b193c0a25dc58c609979f1500cd2f6e870" - integrity sha512-g1/8P5/Q+xnIc8Js4UtBg3XOhcFrFlFbG3UWVtyEx49YTf0r9eyDtDt1qMMDBZT91pyCwLcAEbwS+4i5PIfNZw== +"@cspell/dict-cpp@^6.0.1": + version "6.0.2" + resolved "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.2.tgz#e4549ee1bdf4b6402c0b978eb9dd3deac0eb05df" + integrity sha512-yw5eejWvY4bAnc6LUA44m4WsFwlmgPt2uMSnO7QViGMBDuoeopMma4z9XYvs4lSjTi8fIJs/A1YDfM9AVzb8eg== "@cspell/dict-cryptocurrencies@^5.0.3": version "5.0.3" @@ -481,10 +488,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.26.tgz#f0d2c9492715e85b60a78f62f03918525639aa48" integrity sha512-hDbHYJsi3UgU1J++B0WLiYhWQdsmve3CH53FIaMRAdhrWOHcuw7h1dYkQXHFEP5lOjaq53KUHp/oh5su6VkIZg== -"@cspell/dict-filetypes@^3.0.7": - version "3.0.7" - resolved "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.7.tgz#bd79078e35f01ee6d86bb9c5ebe811cb7ee1c0d9" - integrity sha512-/DN0Ujp9/EXvpTcgih9JmBaE8n+G0wtsspyNdvHT5luRfpfol1xm/CIQb6xloCXCiLkWX+EMPeLSiVIZq+24dA== +"@cspell/dict-filetypes@^3.0.8": + version "3.0.8" + resolved "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.8.tgz#016d523ca2c34dea972ea0ca931255868348d81a" + integrity sha512-D3N8sm/iptzfVwsib/jvpX+K/++rM8SRpLDFUaM4jxm8EyGmSIYRbKZvdIv5BkAWmMlTWoRqlLn7Yb1b11jKJg== "@cspell/dict-flutter@^1.0.3": version "1.0.3" @@ -536,10 +543,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.3.tgz#bf2887020ca4774413d8b1f27c9b6824ba89e9ef" integrity sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A== -"@cspell/dict-html@^4.0.9": - version "4.0.9" - resolved "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.9.tgz#b400a777e347e9437105f088d48f44eb268a5a9f" - integrity sha512-BNp7w3m910K4qIVyOBOZxHuFNbVojUY6ES8Y8r7YjYgJkm2lCuQoVwwhPjurnomJ7BPmZTb+3LLJ58XIkgF7JQ== +"@cspell/dict-html@^4.0.10": + version "4.0.10" + resolved "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.10.tgz#7b536b2adca4b58ed92752c9d3c7ffc724dd5991" + integrity sha512-I9uRAcdtHbh0wEtYZlgF0TTcgH0xaw1B54G2CW+tx4vHUwlde/+JBOfIzird4+WcMv4smZOfw+qHf7puFUbI5g== "@cspell/dict-java@^5.0.10": version "5.0.10" @@ -576,20 +583,25 @@ resolved "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.3.tgz#08d3349bf7cbd8f5dacf8641f3d35092ca0b8b38" integrity sha512-R3U0DSpvTs6qdqfyBATnePj9Q/pypkje0Nj26mQJ8TOBQutCRAJbr2ZFAeDjgRx5EAJU/+8txiyVF97fbVRViw== +"@cspell/dict-markdown@^2.0.7": + version "2.0.7" + resolved "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.7.tgz#15d6f9eed6bd1b33921b4332426ff387961163f1" + integrity sha512-F9SGsSOokFn976DV4u/1eL4FtKQDSgJHSZ3+haPRU5ki6OEqojxKa8hhj4AUrtNFpmBaJx/WJ4YaEzWqG7hgqg== + "@cspell/dict-monkeyc@^1.0.9": version "1.0.9" resolved "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.9.tgz#58b5f6f15fc7c11ce0eeffd0742fba4b39fc0b8b" integrity sha512-Jvf6g5xlB4+za3ThvenYKREXTEgzx5gMUSzrAxIiPleVG4hmRb/GBSoSjtkGaibN3XxGx5x809gSTYCA/IHCpA== -"@cspell/dict-node@^5.0.4": - version "5.0.4" - resolved "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.4.tgz#dfe1f159a1ffb1c4f389ec43b15f705123113658" - integrity sha512-Hz5hiuOvZTd7Cp1IBqUZ7/ChwJeQpD5BJuwCaDn4mPNq4iMcQ1iWBYMThvNVqCEDgKv63X52nT8RAWacss98qg== +"@cspell/dict-node@^5.0.5": + version "5.0.5" + resolved "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.5.tgz#11653612ebdd833208432e8b3cbe61bd6dd35dc3" + integrity sha512-7NbCS2E8ZZRZwlLrh2sA0vAk9n1kcTUiRp/Nia8YvKaItGXLfxYqD2rMQ3HpB1kEutal6hQLVic3N2Yi1X7AaA== -"@cspell/dict-npm@^5.1.8": - version "5.1.8" - resolved "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.8.tgz#d179e555d351aecd4c7e5a87756f3b1192701ee8" - integrity sha512-AJELYXeB4fQdIoNfmuaQxB1Hli3cX6XPsQCjfBxlu0QYXhrjB/IrCLLQAjWIywDqJiWyGUFTz4DqaANm8C/r9Q== +"@cspell/dict-npm@^5.1.11": + version "5.1.11" + resolved "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.11.tgz#fbaef533d7c25ad3a01f8cd823624a74f6cc778c" + integrity sha512-5ricJyVMw5TmqR0NfsZS8jEJu1+DLzyUXyjpVFnffPuEtz9jF2XswLK0swZqc9uwWrz0M7IhGVCnmq90srVZCA== "@cspell/dict-php@^4.0.13": version "4.0.13" @@ -633,10 +645,10 @@ resolved "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.6.tgz#5e925def2fe6dc27ee2ad1c452941c3d6790fb6d" integrity sha512-tl0YWAfjUVb4LyyE4JIMVE8DlLzb1ecHRmIWc4eT6nkyDqQgHKzdHsnusxFEFMVLIQomgSg0Zz6hJ5S1E4W4ww== -"@cspell/dict-software-terms@^4.1.11": - version "4.1.11" - resolved "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.11.tgz#54e1cfcda53f308135215f7163797d7ed8f69ee4" - integrity sha512-77CTHxWFTVw6tVoMN8WBMrlNW2F2FbgATwD/6vcOuiyrJUmh8klN5ZK3m+yyK3ZzsnaW2Bduoc0fw2Ckcm/riQ== +"@cspell/dict-software-terms@^4.1.13": + version "4.1.14" + resolved "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.14.tgz#19e159ae854e5dbabedc0ec823d05153b77b8940" + integrity sha512-p3oZQSQTgdu3UjZ5aaEeU5aKRD00j/oZzt51ohbhhJ94UYECi8te8SfcA45UbGkylSSGcAtJWkuwjCLMiKAgyQ== "@cspell/dict-sql@^2.1.8": version "2.1.8" @@ -653,42 +665,42 @@ resolved "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.4.tgz#bc19522418ed68cf914736b612c4e4febbf07e8d" integrity sha512-CsFF0IFAbRtYNg0yZcdaYbADF5F3DsM8C4wHnZefQy8YcHP/qjAF/GdGfBFBLx+XSthYuBlo2b2XQVdz3cJZBw== -"@cspell/dict-terraform@^1.0.5": - version "1.0.5" - resolved "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.0.5.tgz#14c427ae47d2f78b4acd6fe9ed12bcae53aec441" - integrity sha512-qH3epPB2d6d5w1l4hR2OsnN8qDQ4P0z6oDB7+YiNH+BoECXv4Z38MIV1H8cxIzD2wkzkt2JTcFYaVW72MDZAlg== +"@cspell/dict-terraform@^1.0.6": + version "1.0.6" + resolved "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.0.6.tgz#f67b7363d0cf08c820818980bbe8c927332ad0b8" + integrity sha512-Sqm5vGbXuI9hCFcr4w6xWf4Y25J9SdleE/IqfM6RySPnk8lISEmVdax4k6+Kinv9qaxyvnIbUUN4WFLWcBPQAg== -"@cspell/dict-typescript@^3.1.10": - version "3.1.10" - resolved "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.1.10.tgz#aa297c3f910cde8e644eaf9f711598a85763aaf8" - integrity sha512-7Zek3w4Rh3ZYyhihJ34FdnUBwP3OmRldnEq3hZ+FgQ0PyYZjXv5ztEViRBBxXjiFx1nHozr6pLi74TxToD8xsg== +"@cspell/dict-typescript@^3.1.11": + version "3.1.11" + resolved "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.1.11.tgz#40586f13b0337bd9cba958e0661b35888580b249" + integrity sha512-FwvK5sKbwrVpdw0e9+1lVTl8FPoHYvfHRuQRQz2Ql5XkC0gwPPkpoyD1zYImjIyZRoYXk3yp9j8ss4iz7A7zoQ== "@cspell/dict-vue@^3.0.3": version "3.0.3" resolved "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.3.tgz#295c288f6fd363879898223202ec3be048663b98" integrity sha512-akmYbrgAGumqk1xXALtDJcEcOMYBYMnkjpmGzH13Ozhq1mkPF4VgllFQlm1xYde+BUKNnzMgPEzxrL2qZllgYA== -"@cspell/dynamic-import@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.15.4.tgz#6aee2c81a3a45d3ec6c3f225fbe24557d46e0f85" - integrity sha512-tr0F6EYN6qtniNvt1Uib+PgYQHeo4dQHXE2Optap+hYTOoQ2VoQ+SwBVjZ+Q2bmSAB0fmOyf0AvgsUtnWIpavw== +"@cspell/dynamic-import@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.16.0.tgz#bcb785335fe6f22a5cf66780a5ade61483da7f32" + integrity sha512-FH+B5y71qfunagXiLSJhXP9h/Vwb1Z8Cc/hLmliGekw/Y8BuYknL86tMg9grXBYNmM0kifIv6ZesQl8Km/p/rA== dependencies: import-meta-resolve "^4.1.0" -"@cspell/filetypes@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.15.4.tgz#cdc64f21f5b1b824490aa003d53b9a07703c5818" - integrity sha512-sNl6jr3ym/4151EY76qlI/00HHsiLZBqW7Vb1tqCzsgSg3EpL30ddjr74So6Sg2PN26Yf09hvxGTJzXn1R4aYw== +"@cspell/filetypes@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.16.0.tgz#7b1163946cd115a17f382d6332a6afed9bc23734" + integrity sha512-u2Ub0uSwXFPJFvXhAO/0FZBj3sMr4CeYCiQwTUsdFRkRMFpbTc7Vf+a+aC2vIj6WcaWrYXrJy3NZF/yjqF6SGw== -"@cspell/strong-weak-map@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.15.4.tgz#0e5419862e0a8634e2db20a9e660b827321a6436" - integrity sha512-m5DeQksbhJFqcSYF8Q0Af/WXmXCMAJocCUShkzOXK+uZNXnvhBZN7VyQ9hL+GRzX8JTPEPdVcz2lFyVE5p+LzQ== +"@cspell/strong-weak-map@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.16.0.tgz#608ec5036d2c4e6086823eed7581bbaf764781f4" + integrity sha512-R6N12wEIQpBk2uyni/FU1SFSIjP0uql7ynXVcF1ob8/JJeRoikssydi9Xq5J6ghMw+X50u35mFvg9BgWKz0d+g== -"@cspell/url@8.15.4": - version "8.15.4" - resolved "https://registry.npmjs.org/@cspell/url/-/url-8.15.4.tgz#759a9832e9fd6f9fd022c4eb3031c1b28c272faa" - integrity sha512-K2oZu/oLQPs5suRpLS8uu04O3YMUioSlEU1D66fRoOxzI5NzLt7i7yMg3HQHjChGa09N5bzqmrVdhmQrRZXwGg== +"@cspell/url@8.16.0": + version "8.16.0" + resolved "https://registry.npmjs.org/@cspell/url/-/url-8.16.0.tgz#1c426dd19b52bc9b5f700ff28a0221575256340a" + integrity sha512-zW+6hAieD/FjysfjY4mVv7iHWWasBP3ldj6L+xy2p4Kuax1nug7uuJqMHlAVude/OywNwENG0rYaP/P9Pg4O+w== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -1442,9 +1454,9 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*", "@types/node@>=13.7.0", "@types/node@^22.5.0": - version "22.8.2" - resolved "https://registry.npmjs.org/@types/node/-/node-22.8.2.tgz#8e82bb8201c0caf751dcdc61b0a262d2002d438b" - integrity sha512-NzaRNFV+FZkvK/KLCsNdTvID0SThyrs5SHB6tsD/lajr22FGC73N2QeDPM2wHtVde8mgcXuSsHQkH5cX1pbPLw== + version "22.9.0" + resolved "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" + integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== dependencies: undici-types "~6.19.8" @@ -2242,88 +2254,88 @@ cross-env@^7.0.3: cross-spawn "^7.0.1" cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + version "7.0.5" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz#910aac880ff5243da96b728bc6521a5f6c2f2f82" + integrity sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" -cspell-config-lib@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.15.4.tgz#c9530304e072cbc8d9d4cd76fdb3abcf45dab8af" - integrity sha512-vUgikQTRkRMTdkZqSs7F2cTdPpX61cTjr/9L/VCkXkbW38ObCr4650ioiF1Wq3zDF3Gy2bc4ECTpD2PZUXX5SA== +cspell-config-lib@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.16.0.tgz#3da31b827786f83dd584e55df9e7ef40f32ccf25" + integrity sha512-PGT6ohLtIYXYLIm+R5hTcTrF0dzj8e7WAUJSJe5WlV/7lrwVdwgWaliLcXtSSPmfxgczr6sndX9TMJ2IEmPrmg== dependencies: - "@cspell/cspell-types" "8.15.4" + "@cspell/cspell-types" "8.16.0" comment-json "^4.2.5" yaml "^2.6.0" -cspell-dictionary@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.15.4.tgz#be701af741a0be1a8680674bcbd3dadd7bc5c1fe" - integrity sha512-8+p/l9Saac7qyCbqtELneDoT7CwHu9gYmnI8uXMu34/lPGjhVhy10ZeI0+t1djaO2YyASK400YFKq5uP/5KulA== +cspell-dictionary@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.16.0.tgz#11bdf41702432ef8bcdb57c7181a85f3cc069355" + integrity sha512-Y3sN6ttLBKbu0dOLcduY641n5QP1srUvZkW4bOTnG455DbIZfilrP1El/2Hl0RS6hC8LN9PM4bsIm/2xgdbApA== dependencies: - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-types" "8.15.4" - cspell-trie-lib "8.15.4" + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-types" "8.16.0" + cspell-trie-lib "8.16.0" fast-equals "^5.0.1" -cspell-gitignore@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.15.4.tgz#79bcfcc343466cc483c1805baf0c53f6bdef1753" - integrity sha512-9n5PpQ8gEf8YcvEtoZGZ2Ma6wnqSFkD2GrmyjISy39DfIX/jNLN7GX2wJm6OD2P4FjXer95ypmIb/JWTlfmbTw== +cspell-gitignore@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.16.0.tgz#fd64d18a3101705b9cf9b4fd5b35522c1a75e071" + integrity sha512-ODKe0ooyzYSBJkwgIVZSRIvzoZfT4tEbFt4fFDT88wPyyfX7xp7MAQhXy5KD1ocXH0WvYbdv37qzn2UbckrahA== dependencies: - "@cspell/url" "8.15.4" - cspell-glob "8.15.4" - cspell-io "8.15.4" + "@cspell/url" "8.16.0" + cspell-glob "8.16.0" + cspell-io "8.16.0" find-up-simple "^1.0.0" -cspell-glob@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.15.4.tgz#24934982f3ecf664a32618f0fdf3965de63f05da" - integrity sha512-TTfRRHRAN+PN9drIz4MAEgKKYnPThBOlPMdFddyuisvU33Do1sPAnqkkOjTEFdi3jAA5KwnSva68SVH6IzzMBQ== +cspell-glob@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.16.0.tgz#4f08edf54fcdaa71dc8352416960aad5f884a42f" + integrity sha512-xJSXRHwfENCNFmjpVSEucXY8E3BrpSCA+TukmOYtLyaMKtn6EAwoCpEU7Oj2tZOjdivprPmQ74k4Dqb1RHjIVQ== dependencies: - "@cspell/url" "8.15.4" + "@cspell/url" "8.16.0" micromatch "^4.0.8" -cspell-grammar@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.15.4.tgz#28619d564f4c128618fcecb1d71e31e30b69d213" - integrity sha512-MKiKyYi05mRtXOxPoTv3Ksi0GwYLiK84Uq0C+5PaMrnIjXeed0bsddSFXCT+7ywFJc7PdjhTtz0M/9WWK3UgbA== - dependencies: - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-types" "8.15.4" - -cspell-io@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-io/-/cspell-io-8.15.4.tgz#5d55471cbcff9f2d061a6f3504cd525152da754e" - integrity sha512-rXIEREPTFV9dwwg4EKfvzqlCNOvT6910AYED5YrSt8Y68usRJ9lbqdx0BrDndVCd33bp1o+9JBfHuRiFIQC81g== - dependencies: - "@cspell/cspell-service-bus" "8.15.4" - "@cspell/url" "8.15.4" - -cspell-lib@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.15.4.tgz#ebab39a5e9f74d2a50c588cd8397f0a2f5f7c4d0" - integrity sha512-iLp/625fvCyFFxSyZYLMgqHIKcrhN4hT7Hw5+ySa38Bp/OfA81ANqWHpsDQ0bGsALTRn/DHBpQYj4xCW/aN9tw== - dependencies: - "@cspell/cspell-bundled-dicts" "8.15.4" - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-resolver" "8.15.4" - "@cspell/cspell-types" "8.15.4" - "@cspell/dynamic-import" "8.15.4" - "@cspell/filetypes" "8.15.4" - "@cspell/strong-weak-map" "8.15.4" - "@cspell/url" "8.15.4" +cspell-grammar@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.16.0.tgz#8be03e617f559627fd39e82dd7812636a054efd7" + integrity sha512-vvbJEkBqXocGH/H975RtkfMzVpNxNGMd0JCDd+NjbpeRyZceuChFw5Tie7kHteFY29SwZovub+Am3F4H1kmf9A== + dependencies: + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-types" "8.16.0" + +cspell-io@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-io/-/cspell-io-8.16.0.tgz#f32c60a2d272a88883a7c883be1e3d8b7be8fe64" + integrity sha512-WIK5uhPMjGsTAzm2/fGRbIdr7zWsMVG1fn8wNJYUiYELuyvzvLelfI1VG6szaFCGYqd6Uvgb/fS0uNbwGqCLAQ== + dependencies: + "@cspell/cspell-service-bus" "8.16.0" + "@cspell/url" "8.16.0" + +cspell-lib@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.16.0.tgz#26824f7c096fba1cd1672b50696053f03da342eb" + integrity sha512-fU8CfECyuhT12COIi4ViQu2bTkdqaa+05YSd2ZV8k8NA7lapPaMFnlooxdfcwwgZJfHeMhRVMzvQF1OhWmwGfA== + dependencies: + "@cspell/cspell-bundled-dicts" "8.16.0" + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-resolver" "8.16.0" + "@cspell/cspell-types" "8.16.0" + "@cspell/dynamic-import" "8.16.0" + "@cspell/filetypes" "8.16.0" + "@cspell/strong-weak-map" "8.16.0" + "@cspell/url" "8.16.0" clear-module "^4.1.2" comment-json "^4.2.5" - cspell-config-lib "8.15.4" - cspell-dictionary "8.15.4" - cspell-glob "8.15.4" - cspell-grammar "8.15.4" - cspell-io "8.15.4" - cspell-trie-lib "8.15.4" + cspell-config-lib "8.16.0" + cspell-dictionary "8.16.0" + cspell-glob "8.16.0" + cspell-grammar "8.16.0" + cspell-io "8.16.0" + cspell-trie-lib "8.16.0" env-paths "^3.0.0" fast-equals "^5.0.1" gensequence "^7.0.0" @@ -2333,38 +2345,38 @@ cspell-lib@8.15.4: vscode-uri "^3.0.8" xdg-basedir "^5.1.0" -cspell-trie-lib@8.15.4: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.15.4.tgz#48027e9bddc311f4b578b47286fc5d530c6da14a" - integrity sha512-sg9klsNHyrfos0Boiio+qy5d6fI9cCNjBqFYrNxvpKpwZ4gEzDzjgEKdZY1C76RD2KoBQ8I1NF5YcGc0+hhhCw== +cspell-trie-lib@8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.16.0.tgz#47c1a9c521c3c3cabcdc8567cbd6018d29728bf1" + integrity sha512-Io1qqI0r4U9ewAWBLClFBBlxLeAoIi15PUGJi4Za1xrlgQJwRE8PMNIJNHKmPEIp78Iute3o/JyC2OfWlxl4Sw== dependencies: - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-types" "8.15.4" + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-types" "8.16.0" gensequence "^7.0.0" cspell@^8.8.3: - version "8.15.4" - resolved "https://registry.npmjs.org/cspell/-/cspell-8.15.4.tgz#8292a6ece4ef05f0602c4ba6fb9ef8e962cb0433" - integrity sha512-hUOxcwmNWuHzVeGHyN5v/T9MkyCE5gi0mvatxsM794B2wOuR1ZORgjZH62P2HY1uBkXe/x5C6ITWrSyh0WgAcg== - dependencies: - "@cspell/cspell-json-reporter" "8.15.4" - "@cspell/cspell-pipe" "8.15.4" - "@cspell/cspell-types" "8.15.4" - "@cspell/dynamic-import" "8.15.4" - "@cspell/url" "8.15.4" + version "8.16.0" + resolved "https://registry.npmjs.org/cspell/-/cspell-8.16.0.tgz#1897d123f8854304bc84ac332590c730e93c5123" + integrity sha512-U6Up/4nODE+Ca+zqwZXTgBioGuF2JQHLEUIuoRJkJzAZkIBYDqrMXM+zdSL9E39+xb9jAtr9kPAYJf1Eybgi9g== + dependencies: + "@cspell/cspell-json-reporter" "8.16.0" + "@cspell/cspell-pipe" "8.16.0" + "@cspell/cspell-types" "8.16.0" + "@cspell/dynamic-import" "8.16.0" + "@cspell/url" "8.16.0" chalk "^5.3.0" chalk-template "^1.1.0" commander "^12.1.0" - cspell-dictionary "8.15.4" - cspell-gitignore "8.15.4" - cspell-glob "8.15.4" - cspell-io "8.15.4" - cspell-lib "8.15.4" + cspell-dictionary "8.16.0" + cspell-gitignore "8.16.0" + cspell-glob "8.16.0" + cspell-io "8.16.0" + cspell-lib "8.16.0" fast-json-stable-stringify "^2.1.0" file-entry-cache "^9.1.0" get-stdin "^9.0.0" semver "^7.6.3" - tinyglobby "^0.2.9" + tinyglobby "^0.2.10" data-uri-to-buffer@^6.0.2: version "6.0.2" @@ -2795,10 +2807,10 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fdir@^6.4.0: - version "6.4.0" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.4.0.tgz#8e80ab4b18a2ac24beebf9d20d71e1bc2627dbae" - integrity sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ== +fdir@^6.4.2: + version "6.4.2" + resolved "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689" + integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ== file-entry-cache@^6.0.1: version "6.0.1" @@ -3135,9 +3147,9 @@ human-signals@^5.0.0: integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== husky@^9.1.5: - version "9.1.6" - resolved "https://registry.npmjs.org/husky/-/husky-9.1.6.tgz#e23aa996b6203ab33534bdc82306b0cf2cb07d6c" - integrity sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A== + version "9.1.7" + resolved "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d" + integrity sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA== iconv-lite@^0.4.24: version "0.4.24" @@ -3890,10 +3902,10 @@ jest@^29.3.1: import-local "^3.0.2" jest-cli "^29.7.0" -jiti@^2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/jiti/-/jiti-2.3.3.tgz#39c66fc77476b92a694e65dfe04b294070e2e096" - integrity sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ== +jiti@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/jiti/-/jiti-2.4.0.tgz#393d595fb6031a11d11171b5e4fc0b989ba3e053" + integrity sha512-H5UpaUI+aHOqZXlYOaFP/8AzKsg+guWu+Pr3Y8i7+Y3zr1aXAvCvTAQ1RxSc6oVD8R8c7brgNtTVP91E7upH/g== js-tokens@^4.0.0: version "4.0.0" @@ -3996,19 +4008,19 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== knip@^5.24.1: - version "5.34.2" - resolved "https://registry.npmjs.org/knip/-/knip-5.34.2.tgz#7e3cd9b0cb36dc5a41d53be74ef549708aeac042" - integrity sha512-quSohta9fKJUWTXIqHenA4NYW24rmoP3g/Naw5Lq/Xb/LjAQYSUK4aIyU1wizDLC4EnMicGA5g/H33vJ3ovz5Q== + version "5.37.1" + resolved "https://registry.npmjs.org/knip/-/knip-5.37.1.tgz#3c3e91c425dfb35be68b4d12cc0b9ee3cde794e8" + integrity sha512-69gjKj5lLsLXcIPXlHyFfX5AOHgRdh/iXH8gUqvmsHtjqoWhOATeXZDjvvemmgw7KxbWbUzxBNbpjhtJWzgqGA== dependencies: "@nodelib/fs.walk" "1.2.8" "@snyk/github-codeowners" "1.1.0" easy-table "1.2.0" enhanced-resolve "^5.17.1" fast-glob "^3.3.2" - jiti "^2.3.3" + jiti "^2.4.0" js-yaml "^4.1.0" minimist "^1.2.8" - picocolors "^1.0.0" + picocolors "^1.1.0" picomatch "^4.0.1" pretty-ms "^9.0.0" smol-toml "^1.3.0" @@ -4646,10 +4658,10 @@ path-type@^5.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" @@ -5281,12 +5293,12 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -tinyglobby@^0.2.9: - version "0.2.9" - resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.9.tgz#6baddd1b0fe416403efb0dd40442c7d7c03c1c66" - integrity sha512-8or1+BGEdk1Zkkw2ii16qSS7uVrQJPre5A9o/XkWPATkk23FZh/15BKFxPnlTy6vkljZxLqYCzzBMj30ZrSvjw== +tinyglobby@^0.2.10: + version "0.2.10" + resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz#e712cf2dc9b95a1f5c5bbd159720e15833977a0f" + integrity sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew== dependencies: - fdir "^6.4.0" + fdir "^6.4.2" picomatch "^4.0.2" tmp@^0.0.33: