From b1c47db32bb1ed18be66009cbb0f812be0e03e38 Mon Sep 17 00:00:00 2001 From: Anton Trunov Date: Tue, 2 Apr 2024 23:01:59 +0300 Subject: [PATCH] chore: format codebase with Prettier Introduce a CI check that the codebase is properly formatted --- .editorconfig | 7 + .eslintrc.cjs | 27 +- .github/pull_request_template.md | 10 +- .github/workflows/tact.yml | 108 +- .prettierignore | 8 + CHANGELOG.md | 121 +- README.md | 35 +- ROADMAP.md | 38 +- bin/tact | 39 +- examples/increment.spec.ts | 21 +- examples/multisig-3.spec.ts | 15 +- examples/wallet.spec.ts | 44 +- jest.config.js | 16 +- jest.setup.js | 6 +- jest.teardown.js | 30 +- package.json | 4 +- scripts/grammar.ts | 131 +- scripts/pack.ts | 44 +- scripts/prepare.ts | 96 +- src/abi/AbiFunction.ts | 9 +- src/abi/errors.ts | 26 +- src/abi/global.ts | 531 +++-- src/abi/map.ts | 575 ++--- src/abi/struct.ts | 60 +- src/benchmarks/benchmarks.spec.ts | 62 +- src/bindings/typescript/serializers.ts | 530 +++-- src/bindings/typescript/writeStruct.ts | 77 +- src/bindings/writeTypescript.ts | 295 ++- src/browser.ts | 20 +- src/check.ts | 85 +- src/config/features.ts | 12 +- src/config/parseConfig.ts | 45 +- src/context.ts | 9 +- src/errors.ts | 2 +- src/func/funcCompile.spec.ts | 38 +- src/func/funcCompile.ts | 179 +- src/generator/Writer.ts | 73 +- src/generator/createABI.ts | 146 +- src/generator/emitter/createPadded.ts | 7 +- src/generator/emitter/emit.ts | 38 +- .../intrinsics/tryExpressionIntrinsics.ts | 29 +- src/generator/writeProgram.ts | 211 +- src/generator/writeReport.ts | 8 +- src/generator/writers/cast.ts | 15 +- src/generator/writers/id.ts | 8 +- src/generator/writers/ops.ts | 84 +- src/generator/writers/resolveFuncFlatPack.ts | 45 +- src/generator/writers/resolveFuncFlatTypes.ts | 49 +- src/generator/writers/resolveFuncPrimitive.ts | 43 +- .../writers/resolveFuncTupledType.ts | 64 +- src/generator/writers/resolveFuncType.spec.ts | 148 +- src/generator/writers/resolveFuncType.ts | 115 +- .../writers/resolveFuncTypeFromAbi.ts | 53 +- .../writers/resolveFuncTypeFromAbiUnpack.ts | 42 +- .../writers/resolveFuncTypeUnpack.ts | 91 +- src/generator/writers/writeAccessors.ts | 192 +- src/generator/writers/writeConstant.ts | 52 +- src/generator/writers/writeContract.ts | 159 +- src/generator/writers/writeExpression.spec.ts | 63 +- src/generator/writers/writeExpression.ts | 464 ++-- src/generator/writers/writeFunction.ts | 180 +- src/generator/writers/writeInterfaces.ts | 9 +- src/generator/writers/writeRouter.ts | 282 ++- .../writers/writeSerialization.spec.ts | 45 +- src/generator/writers/writeSerialization.ts | 406 ++-- src/generator/writers/writeStdlib.ts | 616 +++--- src/grammar/ast.ts | 1120 +++++----- src/grammar/checkConstAttributes.ts | 36 +- src/grammar/checkFunctionAttributes.ts | 36 +- src/grammar/checkVariableName.ts | 14 +- src/grammar/clone.ts | 291 +-- src/grammar/grammar.spec.ts | 35 +- src/grammar/grammar.ts | 1894 ++++++++++------- src/grammar/store.ts | 96 +- src/imports/parseImportPath.spec.ts | 26 +- src/imports/parseImportPath.ts | 6 +- src/imports/resolveImports.spec.ts | 81 +- src/imports/resolveImports.ts | 57 +- src/imports/resolveLibrary.spec.ts | 94 +- src/imports/resolveLibrary.ts | 56 +- src/logger.ts | 4 +- src/main.ts | 30 +- src/node.ts | 45 +- src/packaging/fileFormat.ts | 70 +- src/packaging/packageCode.ts | 2 +- src/pipeline/build.ts | 240 ++- src/pipeline/compile.ts | 8 +- src/pipeline/precompile.ts | 18 +- src/pipeline/version.ts | 6 +- src/storage/StorageAllocation.ts | 6 +- src/storage/allocator.ts | 305 ++- src/storage/operation.ts | 101 +- src/storage/resolveAllocation.spec.ts | 37 +- src/storage/resolveAllocation.ts | 77 +- src/test/bugs.spec.ts | 48 +- src/test/feature-bounced-routing.spec.ts | 57 +- src/test/feature-debug.spec.ts | 26 +- src/test/feature-deep.spec.ts | 23 +- src/test/feature-dns.spec.ts | 214 +- src/test/feature-implicit-init.spec.ts | 44 +- src/test/feature-instrinsics.spec.ts | 83 +- src/test/feature-integer-literals.spec.ts | 16 +- src/test/feature-map.spec.ts | 525 +++-- src/test/feature-masterchain.spec.ts | 220 +- src/test/feature-math.spec.ts | 36 +- src/test/feature-optionals.spec.ts | 113 +- src/test/feature-ordering.spec.ts | 23 +- src/test/feature-random.spec.ts | 27 +- src/test/feature-send.spec.ts | 41 +- src/test/feature-serialization.spec.ts | 93 +- src/test/feature-strings.spec.ts | 103 +- src/test/feature-ternary.spec.ts | 19 +- src/test/integration.spec.ts | 39 +- src/test/stdlib.spec.ts | 21 +- src/types/createTLBType.ts | 142 +- src/types/getSupportedInterfaces.ts | 17 +- src/types/isAssignable.ts | 25 +- src/types/isRuntimeType.ts | 6 +- src/types/resolveABITypeRef.ts | 379 ++-- src/types/resolveConstantValue.ts | 125 +- src/types/resolveDescriptors.spec.ts | 34 +- src/types/resolveDescriptors.ts | 1048 ++++++--- src/types/resolveErrors.ts | 28 +- src/types/resolveExpression.ts | 536 +++-- src/types/resolveSignatures.ts | 163 +- src/types/resolveStatements.spec.ts | 20 +- src/types/resolveStatements.ts | 318 ++- src/types/types.ts | 275 +-- src/utils/Writer.ts | 10 +- src/utils/calculateIPFSlink.ts | 13 +- src/utils/crc16.ts | 75 +- src/utils/crc32.ts | 6 +- src/utils/errorToString.ts | 4 +- src/utils/filePath.ts | 9 +- src/utils/idToHex.ts | 9 +- src/utils/loadCases.ts | 13 +- src/utils/newMessageId.ts | 8 +- src/utils/text.spec.ts | 18 +- src/utils/text.ts | 12 +- src/utils/utils.ts | 6 +- src/verify.ts | 73 +- src/vfs/VirtualFileSystem.ts | 2 +- src/vfs/createNodeFileSystem.spec.ts | 47 +- src/vfs/createNodeFileSystem.ts | 27 +- src/vfs/createVirtualFileSystem.spec.ts | 44 +- src/vfs/createVirtualFileSystem.ts | 43 +- tact.config.json | 14 +- tsconfig.eslint.json | 32 +- tsconfig.json | 30 +- yarn.lock | 5 + 150 files changed, 10740 insertions(+), 6530 deletions(-) create mode 100644 .editorconfig create mode 100644 .prettierignore diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..576ee0daf --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +[*.{js,ts}] +indent_style = space +indent_size = 4 + +[{package.json, *.yml, .eslintrc.cjs}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2f93eac59..08dcaf501 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,32 +1,33 @@ /* eslint-env node */ module.exports = { - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], - parser: '@typescript-eslint/parser', + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + parser: "@typescript-eslint/parser", parserOptions: { sourceType: "module", ecmaVersion: 2020, project: "./tsconfig.eslint.json", }, ignorePatterns: ["*.cjs"], - plugins: ['@typescript-eslint'], + plugins: ["@typescript-eslint"], root: true, env: { node: true, es2021: true, }, rules: { - '@typescript-eslint/no-unused-vars': [ - 'error', + "@typescript-eslint/no-unused-vars": [ + "error", { - "argsIgnorePattern": "^_", - "varsIgnorePattern": "^_", - "caughtErrorsIgnorePattern": "^_" - } + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + caughtErrorsIgnorePattern: "^_", + }, ], - "@typescript-eslint/no-var-requires": ['error', - { - allow: ['/package\\.json$'] - } + "@typescript-eslint/no-var-requires": [ + "error", + { + allow: ["/package\\.json$"], + }, ], "@typescript-eslint/no-duplicate-type-constituents": "error", }, diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8da74524c..e33126c21 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -5,8 +5,8 @@ IMPORTANT: If your PR doesn't close a particular issue, please, create the issue first and describe the whole context: what you're adding/changing and why you're doing so. And only then open the Pull Request, which would close that issue! --> -* [ ] I have updated CHANGELOG.md -* [ ] I have added unit tests to demonstrate the contribution is correctly implemented -* [ ] I have run all the tests locally and no test failure was reported -* [ ] I did not introduce unrelated formatting/whitespace changes in the code base -* [ ] I did not do unrelated and/or undiscussed refactorings +- [ ] I have updated CHANGELOG.md +- [ ] I have added unit tests to demonstrate the contribution is correctly implemented +- [ ] I have run all the tests locally and no test failure was reported +- [ ] I did not introduce unrelated formatting/whitespace changes in the code base +- [ ] I did not do unrelated and/or undiscussed refactorings diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 0a8b247c1..f32c8b78d 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -2,9 +2,9 @@ name: Tact CI on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] workflow_dispatch: jobs: @@ -14,63 +14,67 @@ jobs: matrix: node-version: [18.x] os: [ubuntu-latest, windows-latest, macos-latest] - + runs-on: ${{ matrix.os }} steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: "yarn" + + - name: Install dependencies + run: yarn install + + - name: Build and Test Tact compiler + run: | + yarn clean + yarn gen + yarn build + yarn coverage - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: 'yarn' + - name: Check there are no errors reported by ESLint + run: | + yarn lint - - name: Install dependencies - run: yarn install + - name: Check formatting with Prettier + run: | + yarn fmt:check - - name: Build and Test Tact compiler - run: | - yarn clean - yarn gen - yarn build - yarn coverage - - - name: Check there are no errors reported by ESLint - run: | - yarn lint + - name: Check that tact.config.json adheres to the JSON schema + run: | + yarn lint:schema - - name: Check that tact.config.json adheres to the JSON schema - run: | - yarn lint:schema + - name: Compare Tact version from CLI flag `--version` against package.json + if: runner.os != 'Windows' + run: | + if [ "$(./bin/tact --version)" != "$(jq -r '.version' < package.json)" ]; + then false + fi - - name: Compare Tact version from CLI flag `--version` against package.json - if: runner.os != 'Windows' - run: | - if [ "$(./bin/tact --version)" != "$(jq -r '.version' < package.json)" ]; - then false - fi + - name: Link Tact compiler + run: | + yarn link - - name: Link Tact compiler - run: | - yarn link + - name: Test compatibility with tact-template + run: | + git clone https://github.com/tact-lang/tact-template.git + cd tact-template + yarn install + yarn link @tact-lang/compiler + yarn build + yarn test + yarn tact --version - - name: Test compatibility with tact-template - run: | - git clone https://github.com/tact-lang/tact-template.git - cd tact-template - yarn install - yarn link @tact-lang/compiler - yarn build - yarn test - yarn tact --version - - - name: Test compatibility with Blueprint - run: | - yarn create ton -- test-project --type tact-counter --contractName Counter - cd test-project - yarn link @tact-lang/compiler - yarn build - yarn test - yarn tact --version + - name: Test compatibility with Blueprint + run: | + yarn create ton -- test-project --type tact-counter --contractName Counter + cd test-project + yarn link @tact-lang/compiler + yarn build + yarn test + yarn tact --version diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..7569fde55 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +dist + +src/func/funcfiftlib.js +src/func/funcfiftlib.wasm.js +src/grammar/grammar.ohm-bundle.d.ts +src/grammar/grammar.ohm-bundle.js +src/imports/stdlib.ts +grammar \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fd64de40..8c1968d25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + - `log2` and `log` math functions in `@stdlib/math`: PR [#166](https://github.com/tact-lang/tact/pull/166) - Reserve mode constants in `@stdlib/reserve`, namely `ReserveExact`, `ReserveAllExcept`, `ReserveAtMost`, `ReserveAddOriginalBalance`, `ReserveInvertSign`, `ReserveBounceIfActionFail`: PR [#173](https://github.com/tact-lang/tact/pull/173) - JSON Schema for `tact.config.json`: PR [#194](https://github.com/tact-lang/tact/pull/194) @@ -16,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `toString` extension function for `Address` type: PR [#224](https://github.com/tact-lang/tact/pull/224) ### Changed + - Update the `dump` function to handle addresses: PR [#175](https://github.com/tact-lang/tact/pull/175) - Support trailing commas for struct fields and function arguments: PR [#179](https://github.com/tact-lang/tact/pull/179) - The implicit empty `init` function is now present by default in the contract if not declared: PR [#167](https://github.com/tact-lang/tact/pull/167) @@ -23,12 +25,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support escape sequences for strings (`\\`, `\"`, `\n`, `\r`, `\t`, `\v`, `\b`, `\f`, `\u{0}` through `\u{FFFFFF}`, `\u0000` through `\uFFFF`, `\x00` through `\xFF`): PR [#192](https://github.com/tact-lang/tact/pull/192) ### Fixed + - Incorrect "already exists" errors when using names such as `toString` or `valueOf`: PR [#208](https://github.com/tact-lang/tact/pull/208) - Escape backticks in error messages for generated TypeScript code: PR [#192](https://github.com/tact-lang/tact/pull/192) ## [1.2.0] - 2024-02-29 ### Added + - Augmented assignment operators (`+=`, `-=`, `*=`, `/=` and `%=`): PR [#87](https://github.com/tact-lang/tact/pull/87) - Binary and octal literals with underscores as numerical separators: PR [#99](https://github.com/tact-lang/tact/pull/99) - Ternary conditional operator (`condition ? then : else`): PR [#97](https://github.com/tact-lang/tact/pull/97) @@ -36,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `SendBounceIfActionFail` send mode constant to the standard library: PR [#122](https://github.com/tact-lang/tact/pull/122) ### Changed + - Decimal and hexadecimal literals now allow underscores as numerical separators: PR [#99](https://github.com/tact-lang/tact/pull/99) - The equality and non-equality operators (`==` and `!=`) now support slices and strings by comparing the hashes of the left-hand and right-hand sides : PR [#105](https://github.com/tact-lang/tact/pull/105) - Continuous integration now tests the dev [tact-template](https://github.com/tact-lang/tact-template)'s version with the dev version of Tact: PR [#111](https://github.com/tact-lang/tact/pull/111) @@ -43,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Continuous integration now checks there are no ESLint warnings: PR [#157](https://github.com/tact-lang/tact/pull/157) ### Fixed + - Relative imports from parent directories: PR [#125](https://github.com/tact-lang/tact/pull/125) - The typechecker failed to identify different types when using the `==` and `!=` operators: PR [#127](https://github.com/tact-lang/tact/pull/127) - ESLint warnings for the whole Tact codebase: PR [#157](https://github.com/tact-lang/tact/pull/157) @@ -51,12 +57,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.1.5] - 2023-12-01 ### Added + - Continuous integration to run Tact tests on Linux, macOS and Windows: PR [#96](https://github.com/tact-lang/tact/pull/96) ### Changed + - Migration to `@ton` NPM packages: PR [#89](https://github.com/tact-lang/tact/pull/89) ### Fixed + - Struct and message identifiers need to be capitalized: PRs [#81](https://github.com/tact-lang/tact/pull/81) and [#83](https://github.com/tact-lang/tact/pull/83) - Fixed the signature of the `checkDataSignature` function in `stdlib/std/crypto.tact`: PR [#50](https://github.com/tact-lang/tact/pull/50) - Show location info for the internal compiler error 'Invalid types for binary operation': PR [#63](https://github.com/tact-lang/tact/pull/63) @@ -64,28 +73,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.1.4] - 2023-09-27 ### Changed + - Hacked paths to support builds on Windows ## [1.1.3] - 2023-06-27 ### Added + - bitwise and and or operations - statically compile expressions with bitwise operations if possible ## [1.1.2] - 2023-04-27 ### Added + - Add full ABI in bindings ## [1.1.1] - 2023-04-20 ### Fixed + - Fix typescript bindings generation for custom key and value serialization formats - Fix missing external messages in bindings ## [1.1.0] - 2023-04-19 ### ⚡️ Breaking changes + - `reply` is now a method of `Contract` instead of global context and changed it's behavior if storage reserve is non-zero in contract. - Logical expressions are now calculated differently: `&&` now does not execute right expression if left is `false` and `||` does not execute right expression if left is `true`. Before it was executed in any case. This change is made in attempt to reduce unexpected behavior. - `OwnableTransferable` is now sends response to the sender. @@ -93,6 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Deployable` trait now sends non-bounceable notifications instead of bounceable ones. ### Features + - `Address` to `Address` maps - Ability to define key and value serializations for maps - `sha256` hashing @@ -110,183 +125,221 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Some string-related operations are now computed at compile time if possible ### Fixed + - Signature of `preloadBits` function - Fixed `readForwardFee` function ## [1.1.0-beta.28] - 2023-04-19 ### Fixed + - Fix `func` invocation ## [1.1.0-beta.27] - 2023-04-14 ### Fixed + - Remove tact-bindings binary reference ## [1.1.0-beta.26] - 2023-04-14 ### Added + - Ability to define empty messages (but not structs) ## [1.1.0-beta.25] - 2023-04-14 ### Added + - Support for bounced receivers for message structs ## [1.1.0-beta.24] - 2023-04-13 ### Changed + - Bounced messages now skipped first 32 bits before passing it to receivers ### Fixed + - Passing optional structs as arguments ## [1.1.0-beta.23] - 2023-04-13 ### Changed + - deploy trait now sends non-bounceable notifications - changed `forward` and added bounceable and init arguments ### Added + - `Contract.notify()` non-bounceable alternative to reply ## [1.1.0-beta.22] - 2023-04-13 ### Added + - `commit` function to commit state changes ## [1.1.0-beta.21] - 2023-04-13 ### Fixed + - Work-around func `0.4.3` bug with pragma processing - Fix external messages with arguments type checking ## [1.1.0-beta.20] - 2023-04-11 ### Changed + - Upgrade `func` to `0.4.3` ## [1.1.0-beta.19] - 2023-04-10 ### Fixed + - Fix bouncing unknown messages ## [1.1.0-beta.18] - 2023-04-10 ### Added + - `FactoryDeployable` trait for deploying from factory contract ## [1.1.0-beta.17] - 2023-04-10 ### Added + - Abstract functions - Abstract and virtual constants in traits ### Changed + - Rename `overrides` to `override` - Updated ownership transferring methods ### Removed + - Unused `public` modifier ## [1.1.0-beta.16] - 2023-04-09 ### Changed + - `reply` now in contract instead of global context ## [1.1.0-beta.15] - 2023-04-09 ### Added + - `asCell` to maps ## [1.1.0-beta.14] - 2023-04-09 ### Fixed + - Fix `dnsResolveWallet` compilation error ## [1.1.0-beta.13] - 2023-04-09 ### Added + - `dns` library - map key and value serialization formats ## [1.1.0-beta.12] - 2023-04-08 ### Fixed + - Upgrade decompiler to a `@tact-lang/opcodes@0.0.13` ## [1.1.0-beta.11] - 2023-04-08 ### Fixed + - Signature of `preloadBits` function ## [1.1.0-beta.10] - 2023-04-08 ### Added + - `sha256` function to compute sha256 hash of a text or byte string ## [1.1.0-beta.9] - 2023-04-02 ### Added + - Opt-in external messages support ## [1.1.0-beta.8] - 2023-04-02 ### Fixed -- Missing implementation of `Address` to `Address` maps + +- Missing implementation of `Address` to `Address` maps ## [1.1.0-beta.7] - 2023-03-28 ### Added + - `inline` modifier for functions to inline them into the caller ### Fixed + - Fix missing `method_id` in `get_abi_ipfs` and `lazy_deployment_completed` ## [1.1.0-beta.6] - 2023-03-27 ### Changed + - Optimization of gas usage of low level primitives ## [1.1.0-beta.5] - 2023-03-25 ### Changed + - Optimization of `String.asComment()` that tries to compute it compile time if possible ## [1.1.0-beta.4] - 2023-03-23 ### Added + - Ability to compare cells ### Fixed + - Fixed contract crash when equality check involving nullable variables ### Changed + - Change logic of `&&` and `||`. Now second argument is not calculated when first argument is `false` or `true` respectively. ## [1.1.0-beta.3] - 2023-03-22 ### Added + - `emit` function to emit events ### Fixed + - Fixed possible inconsistent behavior when calling mutating get methods from inside of the contract - Fixed regression of order of functions in generated files ## [1.1.0-beta.2] - 2023-03-22 ### Changed + - Tact now emits func in multiple files, optimized not only for blockchain, but also for human ## [1.1.0-beta.1] - 2023-03-20 ### Fixed + - Some functions for deep structures with optionals not emitted - Crash in bindings generator on boolean value in dictionary ## [1.1.0-beta.0] - 2023-03-14 ### Fixed + - `overwrites` -> `override` - Invalid `check` function error generation - Error message for `address(0)` @@ -294,41 +347,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.0] - 2023-03-08 ### Added + - `sender()` function to get message sender address ## [1.0.0-rc.13] - 2023-03-08 ### Changed + - Upgrade `func` to `0.4.2` ### Fixed + - Windows paths support ## [1.0.0-rc.12] - 2023-03-03 ### Fixed + - `pow` is now compile-only function ### Changed + - Use new FunC wasm bundle ## [1.0.0-rc.11] - 2023-03-02 ### Added + - exported `check` function for language server support ## [1.0.0-rc.10] - 2023-03-02 ### Changed + - Contracts now can be deployed only to the basic workchain unless `masterchain` set `true` - Checking field initialization in init function ## [1.0.0-rc.9] - 2023-03-01 ### Changed + - Contracts now work only with basic workchain. To enable masterchain support set `masterchain: true` in `tact.conf.json` ### Added + - `pow` function for exponentiation - `address()` compile-time function for creating addresses from strings - `cell()` compile-time function for creating cells from base64 strings @@ -338,65 +400,78 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.0-rc.8] - 2023-02-27 ### Added + - `logger` interface to programmatic API ## [1.0.0-rc.7] - 2023-02-27 ### Added + - `verify` function to verify compiled package ## [1.0.0-rc.6] - 2023-02-26 ### Fixed + - Fixing npm exports ## [1.0.0-rc.5] - 2023-02-26 ### Fixed + - Fixing npm exports for typescript ## [1.0.0-rc.4] - 2023-02-26 ### Fixed + - Fixing npm exports for typescript ## [1.0.0-rc.3] - 2023-02-26 ### Fixed + - Fixed browser/node typings and exports - Fixed browser environment execution ## [1.0.0-rc.2] - 2023-02-26 ### Fixed + - Fixed missing `mkdirp` dependency ## [1.0.0-rc.1] - 2023-02-26 ### Fixed + - Fixed cli command ## [1.0.0-rc.0] - 2023-02-26 ### Added + - `@ton-lang/compiler/node` to invoke compiler from node similar how cli works - `@ton-lang/compiler/browser` to invoke compiler from browser ### Removed + - Removed jetton library from stdlib. It would be re-introduced after 1.0 version with more thought put into it. ## [0.10.1] - 2023-02-23 ### Added + - Display line and column numbers in error messages to be able to navigate to the error in the editor ### Fixed + - Execution order of struct and message fields - `initOf` argument type checks ## [0.10.0] - 2023-02-23 ### Changed + - Tact contracts are now [Argument-addressable](https://docs.tact-lang.org/evolution/OTP-005) meaning that they depend on init arguments and code hash only. Init function is now called when first valid message is received. - Refactoring of allocator - Moving contract's load function to the beginning of the execution @@ -408,37 +483,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `map` syntax now uses `<>` instead of `[]` for future compatibility with generics ### Added + - Allow `Builder` type as a field type similar to `Cell` and `Slice` - Allow `String` type as a field type ## [0.9.3] - 2023-02-19 ### Added + - Error codes in reports - Client-friendly typescript bindings ### Changed + - Change repository locations ## [0.9.2] - 2023-02-05 ### Added + - `emptyMap()` for creating empty maps - Allowing assigning `null` value to a map variable (same as calling `emptyMap()`) ## [0.9.1] - 2023-02-03 ### Changed + - Update `dump` function to handle booleans and strings, better type checking or arguments - Report `org.ton.debug.v0` interface if debug mode is enabled -- Update bindings generator to support `ton-emulator >= v2.1.0` +- Update bindings generator to support `ton-emulator >= v2.1.0` ## [0.9.0] - 2023-02-02 ### Added + - Importing `func` files ### Changed + - Upgrade `func` to `0.4.1` - Enforce `func` version in generated files - Enable critical pragmas by default @@ -447,29 +529,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.8.11] - 2023-01-28 ### Fixed + - Optional `Address` fields in typescript bindings ### Added + - `Address.asSlice` for manual address parsing - `@stdlib/content` library with `createOffchainContent` functions ### [0.8.10] - 2023-01-27 ## Fixed + - `>>` and `<<` operations - Type checking of struct constructors ## [0.8.9] - 2023-01-25 ### Fixed + - Fix missing func compiler in distributive ## [0.8.8] - 2023-01-25 ### Added + - TextMate Grammar for syntax highlighting ### Changed + - Embed `func` compiler to package - Better builder types - Moved docs to `ton-docs` repository @@ -477,36 +565,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.8.7] - 2023-01-13 ### Added + - `beginTailString` and `beginStringFromBuilder` for starting a `StringBuilder` - `Slice.asString` for converting slice to a `String` (without checks of contents) ## [0.8.6] - 2023-01-10 ### Fixed + - Fixing passing non-nullable type as second argument to map's `set` operation ### Changed + - New `2022.v12` func compiler ## [0.8.5] - 2023-01-09 ### Changed + - Improve gas usage in `storeBool` ## [0.8.4] - 2023-01-09 ### Added --`newAddress` function to create a new address from chain and hash --`getConfigParam` to get system configuration + +-`newAddress` function to create a new address from chain and hash -`getConfigParam` to get system configuration ## [0.8.3] - 2023-01-09 ### Fixed + - Deep contract dependencies ## [0.8.2] - 2023-01-08 ### Added + - `loadAddress` in `Slice` ## [0.8.1] - 2023-01-07 @@ -516,9 +610,11 @@ Fixing missing NPM release ## [0.8.0] - 2023-01-07 ### Changed + - Changed message id algorithm to the one based on type signatures instead of tlb ### Added + - Dictionaries in typescript bindings - Introduced packaging compilation step that packages a contract to a single package that can be deployed in predictable way. - `tact-bindings` to build bindings to non-tact contracts @@ -526,32 +622,39 @@ Fixing missing NPM release ## [0.7.1] - 2023-01-04 ### Fixed + - Assignability type checks ## [0.7.0] - 2023-01-04 ### Added + - `toCell` to all structs and messages - restored disassembler as part of a compilation flow - `typescript` bindings parser of structs and messages ### Removed + - `abi.pack_cell` and `abi.pack_slice` ### Changed + - Updated codegen to prefix function names with a `$` to avoid clashing with system functions -- `random` and `randomInt` that are correctly initialized on first use unlike native one +- `random` and `randomInt` that are correctly initialized on first use unlike native one - Changed the way get and init methods expect their arguments and return values to match func-like primitives ### Fixed + - non-nullable value could break the nullable variable memory representation ## [0.6.0] - 2023-01-03 ### Changed + - Large bindings generator refactoring to match new `ton-core` and `ton-emulator` packages ### Added + - `Deployable` trait in `@stdlib/deploy` ## [0.5.0] - 2022-12-23 @@ -572,14 +675,17 @@ Fixing missing NPM release ## [0.4.0] - 2022-12-22 ### Changed + - Renamed Map's `get2` to `get` and removing `get` from keywords list. ### Fixed + - Fixed missing call arguments verification ## [0.3.0] - 2022-12-22 -### Added +### Added + - `String` literals and variables - `Int.toString()` and `Int.toFloatString()` - `StringBuilder` for gas-efficient string building @@ -596,12 +702,14 @@ Fixing missing NPM release - `myBalance` that returns current balance of a contract before execution phase ### Changed + - `contractAddress` now accepts single argument of type `StateInit` and always produces address for workchain. Old method is renamed to `contractAddressExt`. - `hashCell` and `hashSlice` are now extension function `hash` on `Slice` and `Cell` - Removed some keywords such as `message`, `contract`, `init` to allow use this names as variable names - Renamed `receiveBounced` to `bounced` ### Fixed + - Fixing importing tact with providing extension, now `import "./lib";` and `import "./lib.tact";` are equivalent. - Fixing extension function generation - Fixing clashing of variable names with func primitives and global functions @@ -612,5 +720,6 @@ Fixing missing NPM release ## [0.2.0] ### Added + - `supported_interfaces` TEP support. TACT now automatically builds a list of supported interfaces of a contract - `IPFS`-based ABI reporting. TACT now automatically calculates and embeds ABI hash into smart contract and prepares a file to upload to IPFS. diff --git a/README.md b/README.md index 4ab9a6ce3..8b48477dd 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,19 @@ A next-gen smart contract language for TON focused on efficiency and simplicity. -* [Changelog](/CHANGELOG.md) -* [Roadmap](/ROADMAP.md) -* [Examples](/examples/) +- [Changelog](/CHANGELOG.md) +- [Roadmap](/ROADMAP.md) +- [Examples](/examples/) ## Key Resources -* [Tact By Example](https://tact-by-example.org/00-hello-world) -* [Tact Documentation](https://docs.tact-lang.org) -* [Awesome Tact](https://github.com/tact-lang/awesome-tact) +- [Tact By Example](https://tact-by-example.org/00-hello-world) +- [Tact Documentation](https://docs.tact-lang.org) +- [Awesome Tact](https://github.com/tact-lang/awesome-tact) ## Community -* [Tact Discussion Group](https://t.me/tactlang) +- [Tact Discussion Group](https://t.me/tactlang) ## Getting started @@ -38,40 +38,39 @@ TACT doesn't have development environment dependencies and has everything built For Visual Studio Code syntax support, please download the [tact extension](https://marketplace.visualstudio.com/items?itemName=ton-community.tact-vscode). - ## 10 Commandments of Tact We have formed a large-scale vision for the philosophy of Tact to make sure that community has something to refer to. 1. Familiar syntax -Tact features modern post-C syntax familiar to developers who know TypeScript, Swift, Kotlin and Rust. + Tact features modern post-C syntax familiar to developers who know TypeScript, Swift, Kotlin and Rust. 2. First-class data structures -Tact makes it easy to declare, decode and encode data structures according to their TL-B schemas. + Tact makes it easy to declare, decode and encode data structures according to their TL-B schemas. 3. Safe contract interfaces and ABI -Tact offers strong compile-time checks for contract interfaces, typed addresses and lets describe messages natively in a subset of TL-B. + Tact offers strong compile-time checks for contract interfaces, typed addresses and lets describe messages natively in a subset of TL-B. 4. Message dispatch -Tact offers a convenient yet flexible way to declare, receive and send messages between contracts. + Tact offers a convenient yet flexible way to declare, receive and send messages between contracts. 5. Plaintext commands -Tact offers an innovative way for securely sending commands to the contracts by the users using plaintext commands that are parsed on-chain. + Tact offers an innovative way for securely sending commands to the contracts by the users using plaintext commands that are parsed on-chain. 6. Composition of contracts -Tact offers traits to extract commonly used behaviors into reusable and verified components. + Tact offers traits to extract commonly used behaviors into reusable and verified components. 7. Statically bounded iterators -Tact offers convenient iterators and arrays are bounded and do not hurt scalability of the contracts. + Tact offers convenient iterators and arrays are bounded and do not hurt scalability of the contracts. 8. Batteries-included standard library -Tact comes with a rich standard library that offers data handling functions and standardized behaviors. + Tact comes with a rich standard library that offers data handling functions and standardized behaviors. 9. Interactive -Tact comes with a live playground, explorer and easy to use deployment tools. + Tact comes with a live playground, explorer and easy to use deployment tools. 10. Verifiable -Tact produces deterministic builds. Compiler helps analyze gas usage and storage costs. + Tact produces deterministic builds. Compiler helps analyze gas usage and storage costs. ## License diff --git a/ROADMAP.md b/ROADMAP.md index 2c6b45ea8..adf588b07 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -8,28 +8,30 @@ ## ✍️ Version v1 This version will be the first stable release of TACT. It will be released when the following features are implemented: -* All basic API for Cells, Slices, Builders -* Routing of internal messages -* Random number generation, including a way to seed it -* Ability to deploy contracts -* Ability to send messages to other contracts -* Ability to receive messages from other contracts -* Limited Strings and Maps support -* Importing `func` files -* [Arguments-addressable contracts](https://docs.tact-lang.org/evolution/OTP-005) -* Typescript bindings for Node, Browser and React Native + +- All basic API for Cells, Slices, Builders +- Routing of internal messages +- Random number generation, including a way to seed it +- Ability to deploy contracts +- Ability to send messages to other contracts +- Ability to receive messages from other contracts +- Limited Strings and Maps support +- Importing `func` files +- [Arguments-addressable contracts](https://docs.tact-lang.org/evolution/OTP-005) +- Typescript bindings for Node, Browser and React Native ## ✍️ Version v2 This version would include the following features: -* Full String support -* Full Maps support, including the ability to iterate over items -* Typed tuples -* `let` type inference when possible -* Limited external messages support -* Contract automatic optimisations -* Auto-unpacking nullable variables to non-null one + +- Full String support +- Full Maps support, including the ability to iterate over items +- Typed tuples +- `let` type inference when possible +- Limited external messages support +- Contract automatic optimisations +- Auto-unpacking nullable variables to non-null one ## ✍️ Version v3 -* Upgradable contracts and contract families +- Upgradable contracts and contract families diff --git a/bin/tact b/bin/tact index 717a89d50..f5d2cb0ca 100755 --- a/bin/tact +++ b/bin/tact @@ -1,30 +1,35 @@ #!/usr/bin/env node -const main = require('../dist/node'); -const arg = require('arg'); +const main = require("../dist/node"); +const arg = require("arg"); // Resolve arguments const args = arg({ - '--config': String, - '--project': String, - '--version': Boolean + "--config": String, + "--project": String, + "--version": Boolean, }); -if (args['--version']) { - console.log('1.2.0'); - return; +if (args["--version"]) { + console.log("1.2.0"); + return; } -if (!args['--config']) { - console.log('USAGE: tact --config [--project [--project { - try { - await main.run({ configPath: args['--config'], projectNames: args['--project'] ? args['--project'] : [] }); - } catch (e) { - console.warn('Internal compiler error. Please, report it to https://github.com/tact-lang/tact/issues.'); - console.log(e); - } + try { + await main.run({ + configPath: args["--config"], + projectNames: args["--project"] ? args["--project"] : [], + }); + } catch (e) { + console.warn( + "Internal compiler error. Please, report it to https://github.com/tact-lang/tact/issues.", + ); + console.log(e); + } })(); diff --git a/examples/increment.spec.ts b/examples/increment.spec.ts index 466f92dd4..dd7d87bb1 100644 --- a/examples/increment.spec.ts +++ b/examples/increment.spec.ts @@ -2,21 +2,28 @@ import { IncrementContract } from "./output/increment_IncrementContract"; import { ContractSystem } from "@tact-lang/emulator"; import { toNano } from "@ton/core"; -describe('increment', () => { - it('should deploy', async () => { - +describe("increment", () => { + it("should deploy", async () => { // Create wallet const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await IncrementContract.fromInit()); const tracker = system.track(contract.address); - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Deploy', queryId: 0n }); + await contract.send( + treasure, + { value: toNano("10") }, + { $$type: "Deploy", queryId: 0n }, + ); await system.run(); expect(tracker.collect()).toMatchSnapshot(); // Send internal message - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Increment', key: 0n, value: -1232n }); + await contract.send( + treasure, + { value: toNano("10") }, + { $$type: "Increment", key: 0n, value: -1232n }, + ); await system.run(); expect(tracker.collect()).toMatchSnapshot(); }); -}); \ No newline at end of file +}); diff --git a/examples/multisig-3.spec.ts b/examples/multisig-3.spec.ts index 91b0dbfec..1a8834984 100644 --- a/examples/multisig-3.spec.ts +++ b/examples/multisig-3.spec.ts @@ -2,18 +2,19 @@ import { toNano } from "@ton/core"; import { ContractSystem } from "@tact-lang/emulator"; import { MultisigContract } from "./output/multisig-3_MultisigContract"; -describe('muiltisig-3', () => { - it('should deploy', async () => { - +describe("muiltisig-3", () => { + it("should deploy", async () => { // Init contract const key1 = 1n; const key2 = 1n; const key3 = 1n; const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); - const contract = system.open(await MultisigContract.fromInit(key1, key2, key3)); + const treasure = system.treasure("treasure"); + const contract = system.open( + await MultisigContract.fromInit(key1, key2, key3), + ); const tracker = system.track(contract.address); - await contract.send(treasure, { value: toNano('10') }, 'Deploy'); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); expect(tracker.collect()).toMatchSnapshot(); @@ -22,4 +23,4 @@ describe('muiltisig-3', () => { expect(await contract.getKey2()).toBe(key2); expect(await contract.getKey3()).toBe(key3); }); -}); \ No newline at end of file +}); diff --git a/examples/wallet.spec.ts b/examples/wallet.spec.ts index f6a3c5e40..c5bb2ce50 100644 --- a/examples/wallet.spec.ts +++ b/examples/wallet.spec.ts @@ -3,17 +3,20 @@ import { ContractSystem, testKey } from "@tact-lang/emulator"; import { beginCell, toNano } from "@ton/core"; import { sign } from "@ton/crypto"; -describe('wallet', () => { - it('should deploy', async () => { - +describe("wallet", () => { + it("should deploy", async () => { // Create wallet - const key = testKey('wallet-key'); - const publicKey = beginCell().storeBuffer(key.publicKey).endCell().beginParse().loadUintBig(256); + const key = testKey("wallet-key"); + const publicKey = beginCell() + .storeBuffer(key.publicKey) + .endCell() + .beginParse() + .loadUintBig(256); const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await Wallet.fromInit(publicKey, 0n)); const tracker = system.track(contract.address); - await contract.send(treasure, { value: toNano('10') }, 'Deploy'); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); // Create executor @@ -23,25 +26,32 @@ describe('wallet', () => { // Send transfer and check seqno const transfer: Transfer = { - $$type: 'Transfer', + $$type: "Transfer", seqno: 0n, mode: 1n, amount: toNano(10), to: treasure.address, - body: null + body: null, }; - const signature = sign(beginCell().store(storeTransfer(transfer)).endCell().hash(), key.secretKey); - await contract.send(treasure, { value: toNano(1) }, { - $$type: 'TransferMessage', - transfer, - signature: beginCell().storeBuffer(signature).endCell() - }); + const signature = sign( + beginCell().store(storeTransfer(transfer)).endCell().hash(), + key.secretKey, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { + $$type: "TransferMessage", + transfer, + signature: beginCell().storeBuffer(signature).endCell(), + }, + ); await system.run(); expect(tracker.collect()).toMatchSnapshot(); expect(await contract.getSeqno()).toBe(1n); // Send empty message - await contract.send(treasure, { value: toNano(1) }, 'notify'); + await contract.send(treasure, { value: toNano(1) }, "notify"); await system.run(); expect(tracker.collect()).toMatchSnapshot(); expect(await contract.getSeqno()).toBe(2n); @@ -52,4 +62,4 @@ describe('wallet', () => { expect(tracker.collect()).toMatchSnapshot(); expect(await contract.getSeqno()).toBe(3n); }); -}); \ No newline at end of file +}); diff --git a/jest.config.js b/jest.config.js index 70e1d015f..2e13cfea9 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,9 +1,9 @@ module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testPathIgnorePatterns: ["/node_modules/","/dist/"], - maxWorkers: 1, - globalSetup: './jest.setup.js', - globalTeardown: './jest.teardown.js', - snapshotSerializers: ["@tact-lang/ton-jest/serializers"], -}; \ No newline at end of file + preset: "ts-jest", + testEnvironment: "node", + testPathIgnorePatterns: ["/node_modules/", "/dist/"], + maxWorkers: 1, + globalSetup: "./jest.setup.js", + globalTeardown: "./jest.teardown.js", + snapshotSerializers: ["@tact-lang/ton-jest/serializers"], +}; diff --git a/jest.setup.js b/jest.setup.js index f9f704d67..454fcbf08 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1,8 +1,8 @@ // eslint-disable-next-line @typescript-eslint/no-var-requires -const coverage = require('@tact-lang/coverage'); +const coverage = require("@tact-lang/coverage"); module.exports = async () => { - if (process.env.COVERAGE === 'true') { + if (process.env.COVERAGE === "true") { coverage.beginCoverage(); } -}; \ No newline at end of file +}; diff --git a/jest.teardown.js b/jest.teardown.js index 549b9a14f..fa59b7190 100644 --- a/jest.teardown.js +++ b/jest.teardown.js @@ -1,13 +1,27 @@ -const coverage = require('@tact-lang/coverage'); -const path = require('path'); +const coverage = require("@tact-lang/coverage"); +const path = require("path"); module.exports = async () => { - if (process.env.COVERAGE === 'true') { + if (process.env.COVERAGE === "true") { coverage.completeCoverage([ - path.resolve(__dirname, 'examples', 'output', '*.boc'), - path.resolve(__dirname, 'src', 'test', 'features', 'output', '*.boc'), - path.resolve(__dirname, 'src', 'test', 'bugs', 'output', '*.boc'), - path.resolve(__dirname, 'src', 'benchmarks', 'contracts', 'output', '*.boc') + path.resolve(__dirname, "examples", "output", "*.boc"), + path.resolve( + __dirname, + "src", + "test", + "features", + "output", + "*.boc", + ), + path.resolve(__dirname, "src", "test", "bugs", "output", "*.boc"), + path.resolve( + __dirname, + "src", + "benchmarks", + "contracts", + "output", + "*.boc", + ), ]); } -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 84bbd51a7..0cd3ebbaa 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "coverage": "cross-env COVERAGE=true jest", "release": "yarn clean && yarn build && yarn coverage && yarn release-it --npm.yarn1", "lint": "yarn eslint .", - "lint:schema": "ajv validate -s grammar/configSchema.json -d tact.config.json" + "lint:schema": "ajv validate -s grammar/configSchema.json -d tact.config.json", + "fmt:check": "yarn prettier --check ." }, "files": [ "dist/**/*", @@ -66,6 +67,7 @@ "glob": "^8.1.0", "jest": "^29.3.1", "js-yaml": "^4.1.0", + "prettier": "^3.2.5", "release-it": "^15.5.1", "rimraf": "^4.1.2", "rollup": "^3.17.2", diff --git a/scripts/grammar.ts b/scripts/grammar.ts index a167ffa54..199f05e3f 100644 --- a/scripts/grammar.ts +++ b/scripts/grammar.ts @@ -1,22 +1,41 @@ -import { getHighlighter, BUNDLED_LANGUAGES, BUNDLED_THEMES, ILanguageRegistration } from 'shiki'; -import fs from 'fs'; -import yaml from 'js-yaml'; -import path from 'path'; +import { + getHighlighter, + BUNDLED_LANGUAGES, + BUNDLED_THEMES, + ILanguageRegistration, +} from "shiki"; +import fs from "fs"; +import yaml from "js-yaml"; +import path from "path"; (async () => { - // Patch grammar bundler - const grammarBundlePath = path.resolve(__dirname, '..', 'src', 'grammar', 'grammar.ohm-bundle.js'); - let src = fs.readFileSync(grammarBundlePath, 'utf-8'); - src = src.replace(`require('ohm-js')`, `(require('ohm-js').default || require('ohm-js'))`); - fs.writeFileSync(grammarBundlePath, src, 'utf-8'); + const grammarBundlePath = path.resolve( + __dirname, + "..", + "src", + "grammar", + "grammar.ohm-bundle.js", + ); + let src = fs.readFileSync(grammarBundlePath, "utf-8"); + src = src.replace( + `require('ohm-js')`, + `(require('ohm-js').default || require('ohm-js'))`, + ); + fs.writeFileSync(grammarBundlePath, src, "utf-8"); // Load textmate grammar - const sourceGrammar = fs.readFileSync(require.resolve('../grammar/tact.tmLanguage.yaml'), 'utf-8'); + const sourceGrammar = fs.readFileSync( + require.resolve("../grammar/tact.tmLanguage.yaml"), + "utf-8", + ); let loadedGrammar = yaml.load(sourceGrammar); // Process grammar - function replacePatternVariables(pattern: string, variableReplacers: VariableReplacer[]) { + function replacePatternVariables( + pattern: string, + variableReplacers: VariableReplacer[], + ) { let result = pattern; for (const [variableName, value] of variableReplacers) { result = result.replace(variableName, value); @@ -24,26 +43,38 @@ import path from 'path'; return result; } // eslint-disable-next-line @typescript-eslint/no-explicit-any - function transformGrammarRule(rule: any, propertyNames: string[], transformProperty: (ruleProperty: string) => string) { + function transformGrammarRule( + rule: any, + propertyNames: string[], + transformProperty: (ruleProperty: string) => string, + ) { for (const propertyName of propertyNames) { const value = rule[propertyName]; - if (typeof value === 'string') { + if (typeof value === "string") { rule[propertyName] = transformProperty(value); } } for (const propertyName in rule) { const value = rule[propertyName]; - if (typeof value === 'object') { + if (typeof value === "object") { transformGrammarRule(value, propertyNames, transformProperty); } } } // eslint-disable-next-line @typescript-eslint/no-explicit-any - function transformGrammarRepository(grammar: any, propertyNames: string[], transformProperty: (ruleProperty: string) => string) { + function transformGrammarRepository( + grammar: any, + propertyNames: string[], + transformProperty: (ruleProperty: string) => string, + ) { const repository = grammar.repository; for (const key in repository) { - transformGrammarRule(repository[key], propertyNames, transformProperty); + transformGrammarRule( + repository[key], + propertyNames, + transformProperty, + ); } } type VariableReplacer = [RegExp, string]; @@ -54,48 +85,78 @@ import path from 'path'; const variableReplacers: VariableReplacer[] = []; for (const variableName in variables) { // Replace the pattern with earlier variables - const pattern = replacePatternVariables(variables[variableName], variableReplacers); - variableReplacers.push([new RegExp(`{{${variableName}}}`, "gim"), pattern]); + const pattern = replacePatternVariables( + variables[variableName], + variableReplacers, + ); + variableReplacers.push([ + new RegExp(`{{${variableName}}}`, "gim"), + pattern, + ]); } transformGrammarRepository( src, ["begin", "end", "match", "name"], - pattern => replacePatternVariables(pattern, variableReplacers) + (pattern) => replacePatternVariables(pattern, variableReplacers), ); return src; } loadedGrammar = processGrammar(loadedGrammar); - fs.writeFileSync(require.resolve('../grammar/tact.tmLanguage.json'), JSON.stringify(loadedGrammar, null, 2)); + fs.writeFileSync( + require.resolve("../grammar/tact.tmLanguage.json"), + JSON.stringify(loadedGrammar, null, 2), + ); // Generate sample highlight - const grammarTact = JSON.parse(fs.readFileSync(require.resolve('../grammar/tact.tmLanguage.json'), 'utf-8')); + const grammarTact = JSON.parse( + fs.readFileSync( + require.resolve("../grammar/tact.tmLanguage.json"), + "utf-8", + ), + ); - for (const f of fs.readdirSync(path.resolve(__dirname, '..', 'grammar'))) { - if (f.endsWith('.tact')) { - const name = f.substring(0, f.length - '.tact'.length); - const grammarSample = fs.readFileSync(path.resolve(__dirname, '..', 'grammar', name + '.tact'), 'utf-8'); + for (const f of fs.readdirSync(path.resolve(__dirname, "..", "grammar"))) { + if (f.endsWith(".tact")) { + const name = f.substring(0, f.length - ".tact".length); + const grammarSample = fs.readFileSync( + path.resolve(__dirname, "..", "grammar", name + ".tact"), + "utf-8", + ); const highlighter = await getHighlighter({ themes: BUNDLED_THEMES, langs: [ ...BUNDLED_LANGUAGES, { - id: 'tact', - scopeName: 'source.tact', + id: "tact", + scopeName: "source.tact", grammar: grammarTact, - aliases: ['tact'], - } + aliases: ["tact"], + }, ] as ILanguageRegistration[], }); - const theme = 'dark-plus'; // Most features + const theme = "dark-plus"; // Most features - let res = highlighter.codeToHtml(grammarSample, { lang: 'tact', theme }); + let res = highlighter.codeToHtml(grammarSample, { + lang: "tact", + theme, + }); res = `${res}`; - fs.writeFileSync(path.resolve(__dirname, '..', 'grammar', name + '.html'), res); + fs.writeFileSync( + path.resolve(__dirname, "..", "grammar", name + ".html"), + res, + ); - const tokens = highlighter.codeToThemedTokens(grammarSample, 'tact', theme); - fs.writeFileSync(path.resolve(__dirname, '..', 'grammar', name + '.json'), JSON.stringify(tokens, null, 2)); + const tokens = highlighter.codeToThemedTokens( + grammarSample, + "tact", + theme, + ); + fs.writeFileSync( + path.resolve(__dirname, "..", "grammar", name + ".json"), + JSON.stringify(tokens, null, 2), + ); } } -})(); \ No newline at end of file +})(); diff --git a/scripts/pack.ts b/scripts/pack.ts index 496aeac72..a0cc8df80 100644 --- a/scripts/pack.ts +++ b/scripts/pack.ts @@ -1,33 +1,47 @@ -import fs from 'fs'; -import path from 'path'; -import glob from 'glob'; -import { posixNormalize } from '../src/utils/filePath'; +import fs from "fs"; +import path from "path"; +import glob from "glob"; +import { posixNormalize } from "../src/utils/filePath"; // Pack func -const wasmBase64 = fs.readFileSync(path.resolve(__dirname, '..', 'src', 'func', 'funcfiftlib.wasm')).toString('base64'); +const wasmBase64 = fs + .readFileSync( + path.resolve(__dirname, "..", "src", "func", "funcfiftlib.wasm"), + ) + .toString("base64"); const wasmBase64js = `module.exports = { FuncFiftLibWasm: '${wasmBase64}' };`; -fs.writeFileSync(path.resolve(__dirname, '..', 'src', 'func', 'funcfiftlib.wasm.js'), wasmBase64js); +fs.writeFileSync( + path.resolve(__dirname, "..", "src", "func", "funcfiftlib.wasm.js"), + wasmBase64js, +); // Pack stdlib -const stdlibFiles = glob.sync(path.resolve(__dirname, '..', 'stdlib', '**', '*.@(tact|fc)'), {windowsPathsNoEscape: true}); -const dirPrefixToRemove = posixNormalize(path.resolve(__dirname, '..', 'stdlib')) + '/'; // Remove also the leading slash -let output: string = ''; -output = 'const files: { [key: string]: string } = {};\n'; +const stdlibFiles = glob.sync( + path.resolve(__dirname, "..", "stdlib", "**", "*.@(tact|fc)"), + { windowsPathsNoEscape: true }, +); +const dirPrefixToRemove = + posixNormalize(path.resolve(__dirname, "..", "stdlib")) + "/"; // Remove also the leading slash +let output: string = ""; +output = "const files: { [key: string]: string } = {};\n"; for (const f of stdlibFiles) { - let code = fs.readFileSync(f).toString('base64'); - const name = f.replace(dirPrefixToRemove, ''); + let code = fs.readFileSync(f).toString("base64"); + const name = f.replace(dirPrefixToRemove, ""); output += `files['${name}'] =\n`; let first = true; while (code.length > 0) { if (first) { first = false; } else { - output += ' +\n'; + output += " +\n"; } output += ` '${code.slice(0, 128)}'`; code = code.slice(128); } output += `;\n`; } -output += 'export default files;'; -fs.writeFileSync(path.resolve(__dirname, '..', 'src', 'imports', 'stdlib.ts'), output); \ No newline at end of file +output += "export default files;"; +fs.writeFileSync( + path.resolve(__dirname, "..", "src", "imports", "stdlib.ts"), + output, +); diff --git a/scripts/prepare.ts b/scripts/prepare.ts index f7b8999ff..185330857 100644 --- a/scripts/prepare.ts +++ b/scripts/prepare.ts @@ -1,56 +1,61 @@ -import fs from 'fs'; -import { decompileAll } from '@tact-lang/opcode'; -import { run } from '../src/node'; -import { build } from '../src/pipeline/build'; -import { FuncCompilationResult, funcCompile } from '../src/func/funcCompile'; -import path from 'path'; -import { ConfigProject } from '../src/config/parseConfig'; -import { createNodeFileSystem } from '../src/vfs/createNodeFileSystem'; -import { glob } from 'glob'; -import { verify } from '../src/verify'; -import { consoleLogger } from '../src/logger'; -import { __DANGER__disableVersionNumber } from '../src/pipeline/version'; +import fs from "fs"; +import { decompileAll } from "@tact-lang/opcode"; +import { run } from "../src/node"; +import { build } from "../src/pipeline/build"; +import { FuncCompilationResult, funcCompile } from "../src/func/funcCompile"; +import path from "path"; +import { ConfigProject } from "../src/config/parseConfig"; +import { createNodeFileSystem } from "../src/vfs/createNodeFileSystem"; +import { glob } from "glob"; +import { verify } from "../src/verify"; +import { consoleLogger } from "../src/logger"; +import { __DANGER__disableVersionNumber } from "../src/pipeline/version"; // Read cases (async () => { - // Disable version number in packages __DANGER__disableVersionNumber(); // Compile projects - if (!await run({ configPath: __dirname + '/../tact.config.json' })) { - console.error('Tact projects compilation failed'); + if (!(await run({ configPath: __dirname + "/../tact.config.json" }))) { + console.error("Tact projects compilation failed"); process.exit(1); } // Verify projects - for (const pkgPath of glob.sync(path.normalize(path.resolve(__dirname, '..', 'examples', 'output', '*.pkg')))) { - const res = await verify({ pkg: fs.readFileSync(pkgPath, 'utf-8') }); + for (const pkgPath of glob.sync( + path.normalize( + path.resolve(__dirname, "..", "examples", "output", "*.pkg"), + ), + )) { + const res = await verify({ pkg: fs.readFileSync(pkgPath, "utf-8") }); if (!res.ok) { - console.error('Failed to verify ' + pkgPath + ': ' + res.error); + console.error("Failed to verify " + pkgPath + ": " + res.error); process.exit(1); } } // Compile test contracts - for (const p of [{ path: path.resolve(__dirname, '..', 'src', 'test', 'contracts') }]) { + for (const p of [ + { path: path.resolve(__dirname, "..", "src", "test", "contracts") }, + ]) { const recs = fs.readdirSync(p.path); for (const r of recs) { - if (!r.endsWith('.tact')) { + if (!r.endsWith(".tact")) { continue; } const config: ConfigProject = { - name: r.slice(0, r.length - '.tact'.length), - path: './' + r, - output: './output/', + name: r.slice(0, r.length - ".tact".length), + path: "./" + r, + output: "./output/", }; - const stdlib = '@stdlib'; + const stdlib = "@stdlib"; const project = createNodeFileSystem(p.path, false); await build({ config, stdlib, - project + project, }); } } @@ -59,30 +64,35 @@ import { __DANGER__disableVersionNumber } from '../src/pipeline/version'; for (const p of [{ path: __dirname + "/../func/" }]) { const recs = fs.readdirSync(p.path); for (const r of recs) { - if (!r.endsWith('.fc')) { + if (!r.endsWith(".fc")) { continue; } // Precompile - console.log('Processing ' + p.path + r); + console.log("Processing " + p.path + r); let c: FuncCompilationResult; try { - const stdlibPath = path.resolve(__dirname, '..', 'stdlib', 'stdlib.fc'); - const stdlib = fs.readFileSync(stdlibPath, 'utf-8'); - const code = fs.readFileSync(p.path + r, 'utf-8'); + const stdlibPath = path.resolve( + __dirname, + "..", + "stdlib", + "stdlib.fc", + ); + const stdlib = fs.readFileSync(stdlibPath, "utf-8"); + const code = fs.readFileSync(p.path + r, "utf-8"); c = await funcCompile({ - entries: [ - stdlibPath, - p.path + r + entries: [stdlibPath, p.path + r], + sources: [ + { + path: stdlibPath, + content: stdlib, + }, + { + path: p.path + r, + content: code, + }, ], - sources: [{ - path: stdlibPath, - content: stdlib - }, { - path: p.path + r, - content: code - }], - logger: consoleLogger + logger: consoleLogger, }); if (!c.ok) { console.error(c.log); @@ -90,7 +100,7 @@ import { __DANGER__disableVersionNumber } from '../src/pipeline/version'; } } catch (e) { console.error(e); - console.error('Failed'); + console.error("Failed"); process.exit(1); } fs.writeFileSync(p.path + r + ".fift", c.fift!); @@ -101,4 +111,4 @@ import { __DANGER__disableVersionNumber } from '../src/pipeline/version'; fs.writeFileSync(p.path + r + ".rev.fift", source); } } -})(); \ No newline at end of file +})(); diff --git a/src/abi/AbiFunction.ts b/src/abi/AbiFunction.ts index 11f3507f4..f0293b4df 100644 --- a/src/abi/AbiFunction.ts +++ b/src/abi/AbiFunction.ts @@ -6,5 +6,10 @@ import { TypeRef } from "../types/types"; export type AbiFunction = { name: string; resolve: (ctx: CompilerContext, args: TypeRef[], ref: ASTRef) => TypeRef; - generate: (ctx: WriterContext, args: TypeRef[], resolved: ASTExpression[], ref: ASTRef) => string; -} \ No newline at end of file + generate: ( + ctx: WriterContext, + args: TypeRef[], + resolved: ASTExpression[], + ref: ASTRef, + ) => string; +}; diff --git a/src/abi/errors.ts b/src/abi/errors.ts index 6e34cd729..ee04f2fc8 100644 --- a/src/abi/errors.ts +++ b/src/abi/errors.ts @@ -1,15 +1,17 @@ - // Errors 0-127 are reserved for VM errors // Errors 128-255 are reserved for contract errors export const contractErrors = { - null: { id: 128, message: 'Null reference exception' }, - invalidPrefix: { id: 129, message: 'Invalid serialization prefix' }, - invalidMessage: { id: 130, message: 'Invalid incoming message' }, - constraintsError: { id: 131, message: 'Constraints error' }, - accessDenied: { id: 132, message: 'Access denied' }, - contractStopped: { id: 133, message: 'Contract stopped' }, - invalidArgument: { id: 134, message: 'Invalid argument' }, - codeNotFound: { id: 135, message: 'Code of a contract was not found' }, - invalidAddress: { id: 136, message: 'Invalid address' }, - masterchainNotEnabled: { id: 137, message: 'Masterchain support is not enabled for this contract' }, -} \ No newline at end of file + null: { id: 128, message: "Null reference exception" }, + invalidPrefix: { id: 129, message: "Invalid serialization prefix" }, + invalidMessage: { id: 130, message: "Invalid incoming message" }, + constraintsError: { id: 131, message: "Constraints error" }, + accessDenied: { id: 132, message: "Access denied" }, + contractStopped: { id: 133, message: "Contract stopped" }, + invalidArgument: { id: 134, message: "Invalid argument" }, + codeNotFound: { id: 135, message: "Code of a contract was not found" }, + invalidAddress: { id: 136, message: "Invalid address" }, + masterchainNotEnabled: { + id: 137, + message: "Masterchain support is not enabled for this contract", + }, +}; diff --git a/src/abi/global.ts b/src/abi/global.ts index 6f756f8cb..e24569eaf 100644 --- a/src/abi/global.ts +++ b/src/abi/global.ts @@ -9,250 +9,319 @@ import { AbiFunction } from "./AbiFunction"; import { sha256_sync } from "@ton/crypto"; export const GlobalFunctions: Map = new Map([ - ['ton', { - name: 'ton', - resolve: (ctx, args, ref) => { - if (args.length !== 1) { - throwError('ton() expects single string argument', ref); - } - if (args[0].kind !== 'ref') { - throwError('ton() expects single string argument', ref); - } - if (args[0].name !== 'String') { - throwError('ton() expects single string argument', ref); - } - return { kind: 'ref', name: 'Int', optional: false }; - }, - generate: (ctx, args, resolved, ref) => { - if (resolved.length !== 1) { - throwError('ton() expects single string argument', ref); - } - const str = resolveConstantValue({ kind: 'ref', name: 'String', optional: false }, resolved[0], ctx.ctx) as string; - return toNano(str).toString(10); - } - }], - ['pow', { - name: 'pow', - resolve: (ctx, args, ref) => { - if (args.length !== 2) { - throwError('pow() expects two integer arguments', ref); - } - if (args[0].kind !== 'ref') { - throwError('pow() expects two integer arguments', ref); - } - if (args[0].name !== 'Int') { - throwError('pow() expects two integer arguments', ref); - } - if (args[1].kind !== 'ref') { - throwError('pow() expects two integer arguments', ref); - } - if (args[1].name !== 'Int') { - throwError('pow() expects two integer arguments', ref); - } - return { kind: 'ref', name: 'Int', optional: false }; + [ + "ton", + { + name: "ton", + resolve: (ctx, args, ref) => { + if (args.length !== 1) { + throwError("ton() expects single string argument", ref); + } + if (args[0].kind !== "ref") { + throwError("ton() expects single string argument", ref); + } + if (args[0].name !== "String") { + throwError("ton() expects single string argument", ref); + } + return { kind: "ref", name: "Int", optional: false }; + }, + generate: (ctx, args, resolved, ref) => { + if (resolved.length !== 1) { + throwError("ton() expects single string argument", ref); + } + const str = resolveConstantValue( + { kind: "ref", name: "String", optional: false }, + resolved[0], + ctx.ctx, + ) as string; + return toNano(str).toString(10); + }, }, - generate: (ctx, args, resolved, ref) => { - if (resolved.length !== 2) { - throwError('pow() expects two integer arguments', ref); - } - const a = resolveConstantValue({ kind: 'ref', name: 'Int', optional: false }, resolved[0], ctx.ctx) as bigint; - const b = resolveConstantValue({ kind: 'ref', name: 'Int', optional: false }, resolved[1], ctx.ctx) as bigint; - return (a ** b).toString(10); - } - }], - ['require', { - name: 'require', - resolve: (ctx, args, ref) => { - if (args.length !== 2) { - throwError('require() expects two arguments', ref); - } - if (args[0].kind !== 'ref') { - throwError('require() expects first Bool argument', ref); - } - if (args[0].name !== 'Bool') { - throwError('require() expects first Bool argument', ref); - } - if (args[1].kind !== 'ref') { - throwError('require() expects second string argument', ref); - } - if (args[1].name !== 'String') { - throwError('require() expects second string argument', ref); - } - return { kind: 'void' }; + ], + [ + "pow", + { + name: "pow", + resolve: (ctx, args, ref) => { + if (args.length !== 2) { + throwError("pow() expects two integer arguments", ref); + } + if (args[0].kind !== "ref") { + throwError("pow() expects two integer arguments", ref); + } + if (args[0].name !== "Int") { + throwError("pow() expects two integer arguments", ref); + } + if (args[1].kind !== "ref") { + throwError("pow() expects two integer arguments", ref); + } + if (args[1].name !== "Int") { + throwError("pow() expects two integer arguments", ref); + } + return { kind: "ref", name: "Int", optional: false }; + }, + generate: (ctx, args, resolved, ref) => { + if (resolved.length !== 2) { + throwError("pow() expects two integer arguments", ref); + } + const a = resolveConstantValue( + { kind: "ref", name: "Int", optional: false }, + resolved[0], + ctx.ctx, + ) as bigint; + const b = resolveConstantValue( + { kind: "ref", name: "Int", optional: false }, + resolved[1], + ctx.ctx, + ) as bigint; + return (a ** b).toString(10); + }, }, - generate: (ctx, args, resolved, ref) => { - if (resolved.length !== 2) { - throwError('require() expects two arguments', ref); - } - const str = resolveConstantValue({ kind: 'ref', name: 'String', optional: false }, resolved[1], ctx.ctx) as string; - return `throw_unless(${getErrorId(str, ctx.ctx)}, ${writeExpression(resolved[0], ctx)})`; - } - }], - ['address', { - name: 'address', - resolve: (ctx, args, ref) => { - if (args.length !== 1) { - throwError('address() expects one argument', ref); - } - if (args[0].kind !== 'ref') { - throwError('address() expects string argument', ref); - } - if (args[0].name !== 'String') { - throwError('address() expects string argument', ref); - } - return { kind: 'ref', name: 'Address', optional: false }; + ], + [ + "require", + { + name: "require", + resolve: (ctx, args, ref) => { + if (args.length !== 2) { + throwError("require() expects two arguments", ref); + } + if (args[0].kind !== "ref") { + throwError("require() expects first Bool argument", ref); + } + if (args[0].name !== "Bool") { + throwError("require() expects first Bool argument", ref); + } + if (args[1].kind !== "ref") { + throwError("require() expects second string argument", ref); + } + if (args[1].name !== "String") { + throwError("require() expects second string argument", ref); + } + return { kind: "void" }; + }, + generate: (ctx, args, resolved, ref) => { + if (resolved.length !== 2) { + throwError("require() expects two arguments", ref); + } + const str = resolveConstantValue( + { kind: "ref", name: "String", optional: false }, + resolved[1], + ctx.ctx, + ) as string; + return `throw_unless(${getErrorId(str, ctx.ctx)}, ${writeExpression(resolved[0], ctx)})`; + }, }, - generate: (ctx, args, resolved, ref) => { - if (resolved.length !== 1) { - throwError('address() expects one argument', ref); - } - const str = resolveConstantValue({ kind: 'ref', name: 'String', optional: false }, resolved[0], ctx.ctx) as string; - const address = Address.parse(str); - if (address.workChain !== 0 && address.workChain !== -1) { - throwError(`Address ${str} invalid address`, ref); - } - if (!enabledMasterchain(ctx.ctx)) { - if (address.workChain !== 0) { - throwError(`Address ${str} from masterchain are not enabled for this contract`, ref); - } - } + ], + [ + "address", + { + name: "address", + resolve: (ctx, args, ref) => { + if (args.length !== 1) { + throwError("address() expects one argument", ref); + } + if (args[0].kind !== "ref") { + throwError("address() expects string argument", ref); + } + if (args[0].name !== "String") { + throwError("address() expects string argument", ref); + } + return { kind: "ref", name: "Address", optional: false }; + }, + generate: (ctx, args, resolved, ref) => { + if (resolved.length !== 1) { + throwError("address() expects one argument", ref); + } + const str = resolveConstantValue( + { kind: "ref", name: "String", optional: false }, + resolved[0], + ctx.ctx, + ) as string; + const address = Address.parse(str); + if (address.workChain !== 0 && address.workChain !== -1) { + throwError(`Address ${str} invalid address`, ref); + } + if (!enabledMasterchain(ctx.ctx)) { + if (address.workChain !== 0) { + throwError( + `Address ${str} from masterchain are not enabled for this contract`, + ref, + ); + } + } - // Generate address - const res = writeAddress(address, ctx); - ctx.used(res); - return res + '()'; - } - }], - ['cell', { - name: 'cell', - resolve: (ctx, args, ref) => { - if (args.length !== 1) { - throwError('cell() expects one argument', ref); - } - if (args[0].kind !== 'ref') { - throwError('cell() expects string argument', ref); - } - if (args[0].name !== 'String') { - throwError('cell() expects string argument', ref); - } - return { kind: 'ref', name: 'Cell', optional: false }; + // Generate address + const res = writeAddress(address, ctx); + ctx.used(res); + return res + "()"; + }, }, - generate: (ctx, args, resolved, ref) => { - if (resolved.length !== 1) { - throwError('cell() expects one argument', ref); - } + ], + [ + "cell", + { + name: "cell", + resolve: (ctx, args, ref) => { + if (args.length !== 1) { + throwError("cell() expects one argument", ref); + } + if (args[0].kind !== "ref") { + throwError("cell() expects string argument", ref); + } + if (args[0].name !== "String") { + throwError("cell() expects string argument", ref); + } + return { kind: "ref", name: "Cell", optional: false }; + }, + generate: (ctx, args, resolved, ref) => { + if (resolved.length !== 1) { + throwError("cell() expects one argument", ref); + } - // Load cell data - const str = resolveConstantValue({ kind: 'ref', name: 'String', optional: false }, resolved[0], ctx.ctx) as string; - let c: Cell; - try { - c = Cell.fromBase64(str); - } catch (e) { - throwError(`Invalid cell ${str}`, ref); - } + // Load cell data + const str = resolveConstantValue( + { kind: "ref", name: "String", optional: false }, + resolved[0], + ctx.ctx, + ) as string; + let c: Cell; + try { + c = Cell.fromBase64(str); + } catch (e) { + throwError(`Invalid cell ${str}`, ref); + } - // Generate address - const res = writeCell(c, ctx); - ctx.used(res); - return `${res}()`; - } - }], - ['dump', { - name: 'dump', - resolve: (ctx, args, ref) => { - if (args.length !== 1) { - throwError('dump expects 1 argument', ref); - } - return { kind: 'void' }; + // Generate address + const res = writeCell(c, ctx); + ctx.used(res); + return `${res}()`; + }, }, - generate: (ctx, args, resolved, ref) => { - if (!enabledDebug(ctx.ctx)) { - return `${ctx.used('__tact_nop')}()`; - } - const arg = args[0]; - if (arg.kind === 'map') { - const exp = writeExpression(resolved[0], ctx); - return `${ctx.used(`__tact_debug`)}(${exp})`; - } else if (arg.kind === 'null') { - return `${ctx.used(`__tact_debug_str`)}("null")`; - } else if (arg.kind === 'void') { - return `${ctx.used(`__tact_debug_str`)}("void")`; - } else if (arg.kind === 'ref') { - if (arg.name === 'Int' || arg.name === 'Builder' || arg.name === 'Slice' || arg.name === 'Cell' || arg.name === 'StringBuilder') { - const exp = writeExpression(resolved[0], ctx); - return `${ctx.used(`__tact_debug_str`)}(${ctx.used(`__tact_int_to_string`)}(${exp}))`; - } else if (arg.name === 'Bool') { - const exp = writeExpression(resolved[0], ctx); - return `${ctx.used(`__tact_debug_bool`)}(${exp})`; - } else if (arg.name === 'String') { - const exp = writeExpression(resolved[0], ctx); - return `${ctx.used(`__tact_debug_str`)}(${exp})`; - } else if (arg.name === 'Address') { + ], + [ + "dump", + { + name: "dump", + resolve: (ctx, args, ref) => { + if (args.length !== 1) { + throwError("dump expects 1 argument", ref); + } + return { kind: "void" }; + }, + generate: (ctx, args, resolved, ref) => { + if (!enabledDebug(ctx.ctx)) { + return `${ctx.used("__tact_nop")}()`; + } + const arg = args[0]; + if (arg.kind === "map") { const exp = writeExpression(resolved[0], ctx); - return `${ctx.used(`__tact_debug_address`)}(${exp})`; - } - throwError('dump() not supported for type: ' + arg.name, ref); - } else { - throwError('dump() not supported for argument', ref); - } - } - }], - ['emptyMap', { - name: 'emptyMap', - resolve: (ctx, args, ref) => { - if (args.length !== 0) { - throwError('emptyMap expects no arguments', ref); - } - return { kind: 'null' }; + return `${ctx.used(`__tact_debug`)}(${exp})`; + } else if (arg.kind === "null") { + return `${ctx.used(`__tact_debug_str`)}("null")`; + } else if (arg.kind === "void") { + return `${ctx.used(`__tact_debug_str`)}("void")`; + } else if (arg.kind === "ref") { + if ( + arg.name === "Int" || + arg.name === "Builder" || + arg.name === "Slice" || + arg.name === "Cell" || + arg.name === "StringBuilder" + ) { + const exp = writeExpression(resolved[0], ctx); + return `${ctx.used(`__tact_debug_str`)}(${ctx.used(`__tact_int_to_string`)}(${exp}))`; + } else if (arg.name === "Bool") { + const exp = writeExpression(resolved[0], ctx); + return `${ctx.used(`__tact_debug_bool`)}(${exp})`; + } else if (arg.name === "String") { + const exp = writeExpression(resolved[0], ctx); + return `${ctx.used(`__tact_debug_str`)}(${exp})`; + } else if (arg.name === "Address") { + const exp = writeExpression(resolved[0], ctx); + return `${ctx.used(`__tact_debug_address`)}(${exp})`; + } + throwError( + "dump() not supported for type: " + arg.name, + ref, + ); + } else { + throwError("dump() not supported for argument", ref); + } + }, }, - generate: (_ctx, _args, _resolved, _ref) => { - return 'null()'; - } - }], - ['sha256', { - name: 'sha256', - resolve: (ctx, args, ref) => { - if (args.length !== 1) { - throwError('sha256 expects 1 argument', ref); - } - if (args[0].kind !== 'ref') { - throwError('sha256 expects string argument', ref); - } - if (args[0].name !== 'String' && args[0].name !== 'Slice') { - throwError('sha256 expects string or slice argument', ref); - } - return { kind: 'ref', name: 'Int', optional: false }; + ], + [ + "emptyMap", + { + name: "emptyMap", + resolve: (ctx, args, ref) => { + if (args.length !== 0) { + throwError("emptyMap expects no arguments", ref); + } + return { kind: "null" }; + }, + generate: (_ctx, _args, _resolved, _ref) => { + return "null()"; + }, }, - generate: (ctx, args, resolved, ref) => { - if (args.length !== 1) { - throwError('sha256 expects 1 argument', ref); - } - if (args[0].kind !== 'ref') { - throwError('sha256 expects string argument', ref); - } + ], + [ + "sha256", + { + name: "sha256", + resolve: (ctx, args, ref) => { + if (args.length !== 1) { + throwError("sha256 expects 1 argument", ref); + } + if (args[0].kind !== "ref") { + throwError("sha256 expects string argument", ref); + } + if (args[0].name !== "String" && args[0].name !== "Slice") { + throwError("sha256 expects string or slice argument", ref); + } + return { kind: "ref", name: "Int", optional: false }; + }, + generate: (ctx, args, resolved, ref) => { + if (args.length !== 1) { + throwError("sha256 expects 1 argument", ref); + } + if (args[0].kind !== "ref") { + throwError("sha256 expects string argument", ref); + } - // String case - if (args[0].name === 'String') { - try { - const str = resolveConstantValue({ kind: 'ref', name: 'String', optional: false }, resolved[0], ctx.ctx) as string; - if (Buffer.from(str).length > 128) { - throwError('sha256 expects string argument with byte length <= 128', ref); + // String case + if (args[0].name === "String") { + try { + const str = resolveConstantValue( + { kind: "ref", name: "String", optional: false }, + resolved[0], + ctx.ctx, + ) as string; + if (Buffer.from(str).length > 128) { + throwError( + "sha256 expects string argument with byte length <= 128", + ref, + ); + } + return BigInt( + "0x" + sha256_sync(str).toString("hex"), + ).toString(10); + } catch (e) { + // Not a constant } - return BigInt('0x' + sha256_sync(str).toString('hex')).toString(10); - } catch (e) { - // Not a constant + const exp = writeExpression(resolved[0], ctx); + return `string_hash(${exp})`; } - const exp = writeExpression(resolved[0], ctx); - return `string_hash(${exp})`; - } - // Slice case - if (args[0].name === 'Slice') { - const exp = writeExpression(resolved[0], ctx); - return `string_hash(${exp})`; - } + // Slice case + if (args[0].name === "Slice") { + const exp = writeExpression(resolved[0], ctx); + return `string_hash(${exp})`; + } - throwError('sha256 expects string or slice argument', ref); - } - }] -]) + throwError("sha256 expects string or slice argument", ref); + }, + }, + ], +]); diff --git a/src/abi/map.ts b/src/abi/map.ts index 881e7f36f..1605201f9 100644 --- a/src/abi/map.ts +++ b/src/abi/map.ts @@ -5,304 +5,353 @@ import { getType } from "../types/resolveDescriptors"; import { AbiFunction } from "./AbiFunction"; export const MapFunctions: Map = new Map([ - ['set', { - name: 'set', - resolve(ctx, args, ref) { - - // Check arguments - if (args.length !== 3) { - throwError('set expects two arguments', ref); // Should not happen - } - const self = args[0]; - if (!self || self.kind !== 'map') { - throwError('set expects a map as self argument', ref); // Should not happen - } - - // Resolve map types - if (self.key !== 'Int' && self.key !== 'Address') { - throwError('set expects a map with Int or Address keys', ref); - } + [ + "set", + { + name: "set", + resolve(ctx, args, ref) { + // Check arguments + if (args.length !== 3) { + throwError("set expects two arguments", ref); // Should not happen + } + const self = args[0]; + if (!self || self.kind !== "map") { + throwError("set expects a map as self argument", ref); // Should not happen + } - // Check key type - if (args[1].kind !== 'ref' || args[1].optional) { - throwError('set expects a direct type as first argument', ref); - } - if (args[1].name !== self.key) { - throwError(`set expects a ${self.key} as first argument`, ref); - } + // Resolve map types + if (self.key !== "Int" && self.key !== "Address") { + throwError( + "set expects a map with Int or Address keys", + ref, + ); + } - // Check value type - if (args[2].kind !== 'null' && args[2].kind !== 'ref') { - throwError('set expects a direct type as second argument', ref); - } - if (args[2].kind !== 'null' && args[2].name !== self.value) { - throwError(`set expects a ${self.value} as second argument`, ref); - } + // Check key type + if (args[1].kind !== "ref" || args[1].optional) { + throwError( + "set expects a direct type as first argument", + ref, + ); + } + if (args[1].name !== self.key) { + throwError( + `set expects a ${self.key} as first argument`, + ref, + ); + } - // Returns nothing - return { kind: 'void' }; - }, - generate: (ctx, args, exprs, ref) => { + // Check value type + if (args[2].kind !== "null" && args[2].kind !== "ref") { + throwError( + "set expects a direct type as second argument", + ref, + ); + } + if (args[2].kind !== "null" && args[2].name !== self.value) { + throwError( + `set expects a ${self.value} as second argument`, + ref, + ); + } - // Check arguments - if (args.length !== 3) { - throwError('set expects two arguments', ref); // Ignore self argument - } - const self = args[0]; - if (!self || self.kind !== 'map') { - throwError('set expects a map as self argument', ref); // Should not happen - } + // Returns nothing + return { kind: "void" }; + }, + generate: (ctx, args, exprs, ref) => { + // Check arguments + if (args.length !== 3) { + throwError("set expects two arguments", ref); // Ignore self argument + } + const self = args[0]; + if (!self || self.kind !== "map") { + throwError("set expects a map as self argument", ref); // Should not happen + } - // Render expressions - const resolved = exprs.map((v) => writeExpression(v, ctx)); + // Render expressions + const resolved = exprs.map((v) => writeExpression(v, ctx)); - // Handle Int key - if (self.key === 'Int') { - let bits = 257; - let kind = 'int'; - if (self.keyAs && self.keyAs.startsWith('int')) { - bits = parseInt(self.keyAs.slice(3), 10); - } else if (self.keyAs && self.keyAs.startsWith('uint')) { - bits = parseInt(self.keyAs.slice(4), 10); - kind = 'uint'; - } - if (self.value === 'Int') { - let vbits = 257; - let vkind = 'int'; - if (self.valueAs && self.valueAs.startsWith('int')) { - vbits = parseInt(self.valueAs.slice(3), 10); - } else if (self.valueAs && self.valueAs.startsWith('uint')) { - vbits = parseInt(self.valueAs.slice(4), 10); - vkind = 'uint'; - } - ctx.used(`__tact_dict_set_${kind}_${vkind}`); - return `${resolved[0]}~__tact_dict_set_${kind}_${vkind}(${bits}, ${resolved[1]}, ${resolved[2]}, ${vbits})`; - } else if (self.value === 'Bool') { - ctx.used(`__tact_dict_set_${kind}_int`); - return `${resolved[0]}~__tact_dict_set_${kind}_int(${bits}, ${resolved[1]}, ${resolved[2]}, 1)`; - } else if (self.value === 'Cell') { - ctx.used(`__tact_dict_set_${kind}_cell`); - return `${resolved[0]}~__tact_dict_set_${kind}_cell(${bits}, ${resolved[1]}, ${resolved[2]})`; - } else if (self.value === 'Address') { - ctx.used(`__tact_dict_set_${kind}_slice`); - return `${resolved[0]}~__tact_dict_set_${kind}_slice(${bits}, ${resolved[1]}, ${resolved[2]})`; - } else { - const t = getType(ctx.ctx, self.value); - if (t.kind === 'contract') { - throwError(`Contract can't be value of a map`, ref); - } - if (t.kind === 'trait') { - throwError(`Trait can't be value of a map`, ref); + // Handle Int key + if (self.key === "Int") { + let bits = 257; + let kind = "int"; + if (self.keyAs && self.keyAs.startsWith("int")) { + bits = parseInt(self.keyAs.slice(3), 10); + } else if (self.keyAs && self.keyAs.startsWith("uint")) { + bits = parseInt(self.keyAs.slice(4), 10); + kind = "uint"; } - if (t.kind === 'struct') { + if (self.value === "Int") { + let vbits = 257; + let vkind = "int"; + if (self.valueAs && self.valueAs.startsWith("int")) { + vbits = parseInt(self.valueAs.slice(3), 10); + } else if ( + self.valueAs && + self.valueAs.startsWith("uint") + ) { + vbits = parseInt(self.valueAs.slice(4), 10); + vkind = "uint"; + } + ctx.used(`__tact_dict_set_${kind}_${vkind}`); + return `${resolved[0]}~__tact_dict_set_${kind}_${vkind}(${bits}, ${resolved[1]}, ${resolved[2]}, ${vbits})`; + } else if (self.value === "Bool") { + ctx.used(`__tact_dict_set_${kind}_int`); + return `${resolved[0]}~__tact_dict_set_${kind}_int(${bits}, ${resolved[1]}, ${resolved[2]}, 1)`; + } else if (self.value === "Cell") { ctx.used(`__tact_dict_set_${kind}_cell`); - if (args[2].kind === 'ref' && !args[2].optional) { - return `${resolved[0]}~__tact_dict_set_${kind}_cell(${bits}, ${resolved[1]}, ${ops.writerCell(t.name, ctx)}(${resolved[2]}))`; + return `${resolved[0]}~__tact_dict_set_${kind}_cell(${bits}, ${resolved[1]}, ${resolved[2]})`; + } else if (self.value === "Address") { + ctx.used(`__tact_dict_set_${kind}_slice`); + return `${resolved[0]}~__tact_dict_set_${kind}_slice(${bits}, ${resolved[1]}, ${resolved[2]})`; + } else { + const t = getType(ctx.ctx, self.value); + if (t.kind === "contract") { + throwError(`Contract can't be value of a map`, ref); + } + if (t.kind === "trait") { + throwError(`Trait can't be value of a map`, ref); + } + if (t.kind === "struct") { + ctx.used(`__tact_dict_set_${kind}_cell`); + if (args[2].kind === "ref" && !args[2].optional) { + return `${resolved[0]}~__tact_dict_set_${kind}_cell(${bits}, ${resolved[1]}, ${ops.writerCell(t.name, ctx)}(${resolved[2]}))`; + } else { + return `${resolved[0]}~__tact_dict_set_${kind}_cell(${bits}, ${resolved[1]}, ${ops.writerCellOpt(t.name, ctx)}(${resolved[2]}))`; + } } else { - return `${resolved[0]}~__tact_dict_set_${kind}_cell(${bits}, ${resolved[1]}, ${ops.writerCellOpt(t.name, ctx)}(${resolved[2]}))`; + throwError( + `${t.name} can't be value of a map`, + ref, + ); } - } else { - throwError(`${t.name} can't be value of a map`, ref); } } - } - // Handle address key - if (self.key === 'Address') { - if (self.value === 'Int') { - let vbits = 257; - let vkind = 'int'; - if (self.valueAs && self.valueAs.startsWith('int')) { - vbits = parseInt(self.valueAs.slice(3), 10); - } else if (self.valueAs && self.valueAs.startsWith('uint')) { - vbits = parseInt(self.valueAs.slice(4), 10); - vkind = 'uint'; - } - ctx.used(`__tact_dict_set_slice_${vkind}`); - return `${resolved[0]}~__tact_dict_set_slice_${vkind}(267, ${resolved[1]}, ${resolved[2]}, ${vbits})`; - } else if (self.value === 'Bool') { - ctx.used(`__tact_dict_set_slice_int`); - return `${resolved[0]}~__tact_dict_set_slice_int(267, ${resolved[1]}, ${resolved[2]}, 1)`; - } else if (self.value === 'Cell') { - ctx.used(`__tact_dict_set_slice_cell`); - return `${resolved[0]}~__tact_dict_set_slice_cell(267, ${resolved[1]}, ${resolved[2]})`; - } else if (self.value === 'Address') { - ctx.used(`__tact_dict_set_slice_slice`); - return `${resolved[0]}~__tact_dict_set_slice_slice(267, ${resolved[1]}, ${resolved[2]})`; - } else { - const t = getType(ctx.ctx, self.value); - if (t.kind === 'contract') { - throwError(`Contract can't be value of a map`, ref); - } - if (t.kind === 'trait') { - throwError(`Trait can't be value of a map`, ref); - } - if (t.kind === 'struct') { + // Handle address key + if (self.key === "Address") { + if (self.value === "Int") { + let vbits = 257; + let vkind = "int"; + if (self.valueAs && self.valueAs.startsWith("int")) { + vbits = parseInt(self.valueAs.slice(3), 10); + } else if ( + self.valueAs && + self.valueAs.startsWith("uint") + ) { + vbits = parseInt(self.valueAs.slice(4), 10); + vkind = "uint"; + } + ctx.used(`__tact_dict_set_slice_${vkind}`); + return `${resolved[0]}~__tact_dict_set_slice_${vkind}(267, ${resolved[1]}, ${resolved[2]}, ${vbits})`; + } else if (self.value === "Bool") { + ctx.used(`__tact_dict_set_slice_int`); + return `${resolved[0]}~__tact_dict_set_slice_int(267, ${resolved[1]}, ${resolved[2]}, 1)`; + } else if (self.value === "Cell") { ctx.used(`__tact_dict_set_slice_cell`); - if (args[2].kind === 'ref' && !args[2].optional) { - return `${resolved[0]}~__tact_dict_set_slice_cell(267, ${resolved[1]}, ${ops.writerCell(t.name, ctx)}(${resolved[2]}))`; + return `${resolved[0]}~__tact_dict_set_slice_cell(267, ${resolved[1]}, ${resolved[2]})`; + } else if (self.value === "Address") { + ctx.used(`__tact_dict_set_slice_slice`); + return `${resolved[0]}~__tact_dict_set_slice_slice(267, ${resolved[1]}, ${resolved[2]})`; + } else { + const t = getType(ctx.ctx, self.value); + if (t.kind === "contract") { + throwError(`Contract can't be value of a map`, ref); + } + if (t.kind === "trait") { + throwError(`Trait can't be value of a map`, ref); + } + if (t.kind === "struct") { + ctx.used(`__tact_dict_set_slice_cell`); + if (args[2].kind === "ref" && !args[2].optional) { + return `${resolved[0]}~__tact_dict_set_slice_cell(267, ${resolved[1]}, ${ops.writerCell(t.name, ctx)}(${resolved[2]}))`; + } else { + return `${resolved[0]}~__tact_dict_set_slice_cell(267, ${resolved[1]}, ${ops.writerCellOpt(t.name, ctx)}(${resolved[2]}))`; + } } else { - return `${resolved[0]}~__tact_dict_set_slice_cell(267, ${resolved[1]}, ${ops.writerCellOpt(t.name, ctx)}(${resolved[2]}))`; + throwError( + `${t.name} can't be value of a map`, + ref, + ); } - } else { - throwError(`${t.name} can't be value of a map`, ref); } } - } - - throwError(`set expects a map with Int keys`, ref); - } - }], - ['get', { - name: 'get', - resolve(ctx, args, ref) { - - // Check arguments - if (args.length !== 2) { - throwError('set expects one argument', ref); // Ignore self argument - } - const self = args[0]; - if (!self || self.kind !== 'map') { - throwError('set expects a map as self argument', ref); // Should not happen - } - // Check key type - if (args[1].kind !== 'ref' || args[1].optional) { - throwError('set expects a direct type as first argument', ref); - } - if (args[1].name !== self.key) { - throwError(`set expects a ${self.key} as first argument`, ref); - } - - return { kind: 'ref', name: self.value, optional: true }; + throwError(`set expects a map with Int keys`, ref); + }, }, - generate: (ctx, args, exprs, ref) => { - - if (args.length !== 2) { - throwError('set expects one argument', ref); // Ignore self argument - } - const self = args[0]; - if (!self || self.kind !== 'map') { - throwError('set expects a map as self argument', ref); // Should not happen - } + ], + [ + "get", + { + name: "get", + resolve(ctx, args, ref) { + // Check arguments + if (args.length !== 2) { + throwError("set expects one argument", ref); // Ignore self argument + } + const self = args[0]; + if (!self || self.kind !== "map") { + throwError("set expects a map as self argument", ref); // Should not happen + } - // Render expressions - const resolved = exprs.map((v) => writeExpression(v, ctx)); + // Check key type + if (args[1].kind !== "ref" || args[1].optional) { + throwError( + "set expects a direct type as first argument", + ref, + ); + } + if (args[1].name !== self.key) { + throwError( + `set expects a ${self.key} as first argument`, + ref, + ); + } - // Handle Int key - if (self.key === 'Int') { - let bits = 257; - let kind = 'int'; - if (self.keyAs && self.keyAs.startsWith('int')) { - bits = parseInt(self.keyAs.slice(3), 10); - } else if (self.keyAs && self.keyAs.startsWith('uint')) { - bits = parseInt(self.keyAs.slice(4), 10); - kind = 'uint'; + return { kind: "ref", name: self.value, optional: true }; + }, + generate: (ctx, args, exprs, ref) => { + if (args.length !== 2) { + throwError("set expects one argument", ref); // Ignore self argument } - if (self.value === 'Int') { - let vbits = 257; - let vkind = 'int'; - if (self.valueAs && self.valueAs.startsWith('int')) { - vbits = parseInt(self.valueAs.slice(3), 10); - } else if (self.valueAs && self.valueAs.startsWith('uint')) { - vbits = parseInt(self.valueAs.slice(4), 10); - vkind = 'uint'; - } - ctx.used(`__tact_dict_get_${kind}_${vkind}`); - return `__tact_dict_get_${kind}_${vkind}(${resolved[0]}, ${bits}, ${resolved[1]}, ${vbits})`; - } else if (self.value === 'Bool') { - ctx.used(`__tact_dict_get_${kind}_int`); - return `__tact_dict_get_int_int(${resolved[0]}, ${bits}, ${resolved[1]}, 1)`; - } else if (self.value === 'Cell') { - ctx.used(`__tact_dict_get_${kind}_cell`); - return `__tact_dict_get_${kind}_cell(${resolved[0]}, ${bits}, ${resolved[1]})`; - } else if (self.value === 'Address') { - ctx.used(`__tact_dict_get_${kind}_slice`); - return `__tact_dict_get_${kind}_slice(${resolved[0]}, ${bits}, ${resolved[1]})`; - } else { - const t = getType(ctx.ctx, self.value); - if (t.kind === 'contract') { - throwError(`Contract can't be value of a map`, ref); - } - if (t.kind === 'trait') { - throwError(`Trait can't be value of a map`, ref); + const self = args[0]; + if (!self || self.kind !== "map") { + throwError("set expects a map as self argument", ref); // Should not happen + } + + // Render expressions + const resolved = exprs.map((v) => writeExpression(v, ctx)); + + // Handle Int key + if (self.key === "Int") { + let bits = 257; + let kind = "int"; + if (self.keyAs && self.keyAs.startsWith("int")) { + bits = parseInt(self.keyAs.slice(3), 10); + } else if (self.keyAs && self.keyAs.startsWith("uint")) { + bits = parseInt(self.keyAs.slice(4), 10); + kind = "uint"; } - if (t.kind === 'struct') { + if (self.value === "Int") { + let vbits = 257; + let vkind = "int"; + if (self.valueAs && self.valueAs.startsWith("int")) { + vbits = parseInt(self.valueAs.slice(3), 10); + } else if ( + self.valueAs && + self.valueAs.startsWith("uint") + ) { + vbits = parseInt(self.valueAs.slice(4), 10); + vkind = "uint"; + } + ctx.used(`__tact_dict_get_${kind}_${vkind}`); + return `__tact_dict_get_${kind}_${vkind}(${resolved[0]}, ${bits}, ${resolved[1]}, ${vbits})`; + } else if (self.value === "Bool") { + ctx.used(`__tact_dict_get_${kind}_int`); + return `__tact_dict_get_int_int(${resolved[0]}, ${bits}, ${resolved[1]}, 1)`; + } else if (self.value === "Cell") { ctx.used(`__tact_dict_get_${kind}_cell`); - return `${ops.readerOpt(t.name, ctx)}(__tact_dict_get_${kind}_cell(${resolved[0]}, ${bits}, ${resolved[1]}))`; + return `__tact_dict_get_${kind}_cell(${resolved[0]}, ${bits}, ${resolved[1]})`; + } else if (self.value === "Address") { + ctx.used(`__tact_dict_get_${kind}_slice`); + return `__tact_dict_get_${kind}_slice(${resolved[0]}, ${bits}, ${resolved[1]})`; } else { - throwError(`${t.name} can't be value of a map`, ref); + const t = getType(ctx.ctx, self.value); + if (t.kind === "contract") { + throwError(`Contract can't be value of a map`, ref); + } + if (t.kind === "trait") { + throwError(`Trait can't be value of a map`, ref); + } + if (t.kind === "struct") { + ctx.used(`__tact_dict_get_${kind}_cell`); + return `${ops.readerOpt(t.name, ctx)}(__tact_dict_get_${kind}_cell(${resolved[0]}, ${bits}, ${resolved[1]}))`; + } else { + throwError( + `${t.name} can't be value of a map`, + ref, + ); + } } } - } - // Handle Address key - if (self.key === 'Address') { - if (self.value === 'Int') { - let vbits = 257; - let vkind = 'int'; - if (self.valueAs && self.valueAs.startsWith('int')) { - vbits = parseInt(self.valueAs.slice(3), 10); - } else if (self.valueAs && self.valueAs.startsWith('uint')) { - vbits = parseInt(self.valueAs.slice(4), 10); - vkind = 'uint'; - } - ctx.used(`__tact_dict_get_slice_${vkind}`); - return `__tact_dict_get_slice_${vkind}(${resolved[0]}, 267, ${resolved[1]}, ${vbits})`; - } else if (self.value === 'Bool') { - ctx.used(`__tact_dict_get_slice_int`); - return `__tact_dict_get_slice_int(${resolved[0]}, 267, ${resolved[1]}, 1)`; - } else if (self.value === 'Cell') { - ctx.used(`__tact_dict_get_slice_cell`); - return `__tact_dict_get_slice_cell(${resolved[0]}, 267, ${resolved[1]})`; - } else if (self.value === 'Address') { - ctx.used(`__tact_dict_get_slice_slice`); - return `__tact_dict_get_slice_slice(${resolved[0]}, 267, ${resolved[1]})`; - } else { - const t = getType(ctx.ctx, self.value); - if (t.kind === 'contract') { - throwError(`Contract can't be value of a map`, ref); - } - if (t.kind === 'trait') { - throwError(`Trait can't be value of a map`, ref); - } - if (t.kind === 'struct') { + // Handle Address key + if (self.key === "Address") { + if (self.value === "Int") { + let vbits = 257; + let vkind = "int"; + if (self.valueAs && self.valueAs.startsWith("int")) { + vbits = parseInt(self.valueAs.slice(3), 10); + } else if ( + self.valueAs && + self.valueAs.startsWith("uint") + ) { + vbits = parseInt(self.valueAs.slice(4), 10); + vkind = "uint"; + } + ctx.used(`__tact_dict_get_slice_${vkind}`); + return `__tact_dict_get_slice_${vkind}(${resolved[0]}, 267, ${resolved[1]}, ${vbits})`; + } else if (self.value === "Bool") { + ctx.used(`__tact_dict_get_slice_int`); + return `__tact_dict_get_slice_int(${resolved[0]}, 267, ${resolved[1]}, 1)`; + } else if (self.value === "Cell") { ctx.used(`__tact_dict_get_slice_cell`); - return `${ops.readerOpt(t.name, ctx)}(__tact_dict_get_slice_cell(${resolved[0]}, 267, ${resolved[1]}))`; + return `__tact_dict_get_slice_cell(${resolved[0]}, 267, ${resolved[1]})`; + } else if (self.value === "Address") { + ctx.used(`__tact_dict_get_slice_slice`); + return `__tact_dict_get_slice_slice(${resolved[0]}, 267, ${resolved[1]})`; } else { - throwError(`${t.name} can't be value of a map`, ref); + const t = getType(ctx.ctx, self.value); + if (t.kind === "contract") { + throwError(`Contract can't be value of a map`, ref); + } + if (t.kind === "trait") { + throwError(`Trait can't be value of a map`, ref); + } + if (t.kind === "struct") { + ctx.used(`__tact_dict_get_slice_cell`); + return `${ops.readerOpt(t.name, ctx)}(__tact_dict_get_slice_cell(${resolved[0]}, 267, ${resolved[1]}))`; + } else { + throwError( + `${t.name} can't be value of a map`, + ref, + ); + } } } - } - throwError(`set expects a map with Int keys`, ref); - } - }], - ['asCell', { - name: 'asCell', - resolve(ctx, args, ref) { + throwError(`set expects a map with Int keys`, ref); + }, + }, + ], + [ + "asCell", + { + name: "asCell", + resolve(ctx, args, ref) { + // Check arguments + if (args.length !== 1) { + throwError("asCell expects one argument", ref); // Ignore self argument + } + const self = args[0]; + if (!self || self.kind !== "map") { + throwError("asCell expects a map as self argument", ref); // Should not happen + } - // Check arguments - if (args.length !== 1) { - throwError('asCell expects one argument', ref); // Ignore self argument - } - const self = args[0]; - if (!self || self.kind !== 'map') { - throwError('asCell expects a map as self argument', ref); // Should not happen - } + return { kind: "ref", name: "Cell", optional: true }; + }, + generate: (ctx, args, exprs, ref) => { + if (args.length !== 1) { + throwError("asCell expects one argument", ref); // Ignore self argument + } + const self = args[0]; + if (!self || self.kind !== "map") { + throwError("asCell expects a map as self argument", ref); // Should not happen + } - return { kind: 'ref', name: 'Cell', optional: true }; + return writeExpression(exprs[0], ctx); + }, }, - generate: (ctx, args, exprs, ref) => { - if (args.length !== 1) { - throwError('asCell expects one argument', ref); // Ignore self argument - } - const self = args[0]; - if (!self || self.kind !== 'map') { - throwError('asCell expects a map as self argument', ref); // Should not happen - } - - return writeExpression(exprs[0], ctx); - } - }] + ], ]); diff --git a/src/abi/struct.ts b/src/abi/struct.ts index e8656b35d..7ab28624d 100644 --- a/src/abi/struct.ts +++ b/src/abi/struct.ts @@ -5,29 +5,41 @@ import { getType } from "../types/resolveDescriptors"; import { AbiFunction } from "./AbiFunction"; export const StructFunctions: Map = new Map([ - ['toCell', { - name: 'toCell', - resolve: (ctx, args, ref) => { - if (args.length !== 1) { - throwError('toCell() expects no arguments', ref); - } - if (args[0].kind !== 'ref') { - throwError('toCell() is implemented only a struct type', ref); - } - const tp = getType(ctx, args[0].name); - if (tp.kind !== 'struct') { - throwError('toCell() is implemented only a struct type', ref); - } - return { kind: 'ref', name: 'Cell', optional: false }; + [ + "toCell", + { + name: "toCell", + resolve: (ctx, args, ref) => { + if (args.length !== 1) { + throwError("toCell() expects no arguments", ref); + } + if (args[0].kind !== "ref") { + throwError( + "toCell() is implemented only a struct type", + ref, + ); + } + const tp = getType(ctx, args[0].name); + if (tp.kind !== "struct") { + throwError( + "toCell() is implemented only a struct type", + ref, + ); + } + return { kind: "ref", name: "Cell", optional: false }; + }, + generate: (ctx, args, resolved, ref) => { + if (resolved.length !== 1) { + throwError("toCell() expects no arguments", ref); + } + if (args[0].kind !== "ref") { + throwError( + "toCell() is implemented only a struct type", + ref, + ); + } + return `${ops.writerCell(args[0].name, ctx)}(${resolved.map((v) => writeExpression(v, ctx)).join(", ")})`; + }, }, - generate: (ctx, args, resolved, ref) => { - if (resolved.length !== 1) { - throwError('toCell() expects no arguments', ref); - } - if (args[0].kind !== 'ref') { - throwError('toCell() is implemented only a struct type', ref); - } - return `${ops.writerCell(args[0].name, ctx)}(${resolved.map((v) => writeExpression(v, ctx)).join(', ')})`; - } - }] + ], ]); diff --git a/src/benchmarks/benchmarks.spec.ts b/src/benchmarks/benchmarks.spec.ts index 8ed7533e1..97ad42aae 100644 --- a/src/benchmarks/benchmarks.spec.ts +++ b/src/benchmarks/benchmarks.spec.ts @@ -1,37 +1,67 @@ import { toNano } from "@ton/core"; import { ContractSystem } from "@tact-lang/emulator"; -import { Functions } from './contracts/output/benchmark_functions_Functions'; -import { Functions as FunctionsInline } from './contracts/output/benchmark_functions_inline_Functions'; - -describe('benchmarks', () => { - it('benchmark functions', async () => { +import { Functions } from "./contracts/output/benchmark_functions_Functions"; +import { Functions as FunctionsInline } from "./contracts/output/benchmark_functions_inline_Functions"; +describe("benchmarks", () => { + it("benchmark functions", async () => { // Launch emulator const system = await ContractSystem.create(); - const treasure = system.treasure('benchmarks'); + const treasure = system.treasure("benchmarks"); const functions = system.open(await Functions.fromInit()); const tracker = system.track(functions.address); - await functions.send(treasure, { value: toNano(1) }, { $$type: 'Add', value: 10n }); + await functions.send( + treasure, + { value: toNano(1) }, + { $$type: "Add", value: 10n }, + ); await system.run(); // Find gas used - const gasUsed = tracker.collect().reduce((a, v) => a + v.events.reduce((c, d) => d.$type === 'processed' ? c + d.gasUsed : c, 0n), 0n); + const gasUsed = tracker + .collect() + .reduce( + (a, v) => + a + + v.events.reduce( + (c, d) => (d.$type === "processed" ? c + d.gasUsed : c), + 0n, + ), + 0n, + ); expect(gasUsed).toMatchInlineSnapshot(`3648n`); - expect(functions.init!.code.toBoc().length).toMatchInlineSnapshot(`429`); + expect(functions.init!.code.toBoc().length).toMatchInlineSnapshot( + `429`, + ); }); - it('benchmark functions(inline)', async () => { - + it("benchmark functions(inline)", async () => { // Launch emulator const system = await ContractSystem.create(); - const treasure = system.treasure('benchmarks'); + const treasure = system.treasure("benchmarks"); const functions = system.open(await FunctionsInline.fromInit()); const tracker = system.track(functions.address); - await functions.send(treasure, { value: toNano(1) }, { $$type: 'Add', value: 10n }); + await functions.send( + treasure, + { value: toNano(1) }, + { $$type: "Add", value: 10n }, + ); await system.run(); // Find gas used - const gasUsed = tracker.collect().reduce((a, v) => a + v.events.reduce((c, d) => d.$type === 'processed' ? c + d.gasUsed : c, 0n), 0n); + const gasUsed = tracker + .collect() + .reduce( + (a, v) => + a + + v.events.reduce( + (c, d) => (d.$type === "processed" ? c + d.gasUsed : c), + 0n, + ), + 0n, + ); expect(gasUsed).toMatchInlineSnapshot(`3517n`); - expect(functions.init!.code.toBoc().length).toMatchInlineSnapshot(`422`); + expect(functions.init!.code.toBoc().length).toMatchInlineSnapshot( + `422`, + ); }); -}); \ No newline at end of file +}); diff --git a/src/bindings/typescript/serializers.ts b/src/bindings/typescript/serializers.ts index b82979701..e7c3deeac 100644 --- a/src/bindings/typescript/serializers.ts +++ b/src/bindings/typescript/serializers.ts @@ -1,32 +1,49 @@ import { ABITypeRef } from "@ton/core"; import { Writer } from "../../utils/Writer"; -const primitiveTypes = ['int', 'uint', 'address', 'bool', 'string', 'cell', 'slice', 'builder', 'fixed-bytes']; +const primitiveTypes = [ + "int", + "uint", + "address", + "bool", + "string", + "cell", + "slice", + "builder", + "fixed-bytes", +]; export type Serializer = { - // Typescript - tsType: (v: T) => string, - tsLoad: (v: T, slice: string, field: string, w: Writer) => void, - tsLoadTuple: (v: T, reader: string, field: string, w: Writer, fromGet: boolean) => void, - tsStore: (v: T, builder: string, field: string, w: Writer) => void, - tsStoreTuple: (v: T, to: string, field: string, w: Writer) => void, + tsType: (v: T) => string; + tsLoad: (v: T, slice: string, field: string, w: Writer) => void; + tsLoadTuple: ( + v: T, + reader: string, + field: string, + w: Writer, + fromGet: boolean, + ) => void; + tsStore: (v: T, builder: string, field: string, w: Writer) => void; + tsStoreTuple: (v: T, to: string, field: string, w: Writer) => void; // Matcher - abiMatcher: (src: ABITypeRef) => T | null, + abiMatcher: (src: ABITypeRef) => T | null; }; -const intSerializer: Serializer<{ bits: number, optional: boolean }> = { +const intSerializer: Serializer<{ bits: number; optional: boolean }> = { tsType(v) { if (v.optional) { - return 'bigint | null'; + return "bigint | null"; } else { - return 'bigint'; + return "bigint"; } }, tsLoad(v, slice, field, w) { if (v.optional) { - w.append(`let ${field} = ${slice}.loadBit() ? ${slice}.loadIntBig(${v.bits}) : null;`); + w.append( + `let ${field} = ${slice}.loadBit() ? ${slice}.loadIntBig(${v.bits}) : null;`, + ); } else { w.append(`let ${field} = ${slice}.loadIntBig(${v.bits});`); } @@ -43,36 +60,46 @@ const intSerializer: Serializer<{ bits: number, optional: boolean }> = { }, tsStore(v, builder, field, w) { if (v.optional) { - w.append(`if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeInt(${field}, ${v.bits}); } else { ${builder}.storeBit(false); }`); + w.append( + `if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeInt(${field}, ${v.bits}); } else { ${builder}.storeBit(false); }`, + ); } else { w.append(`${builder}.storeInt(${field}, ${v.bits});`); } }, abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'int') { - if (typeof src.format === 'number') { - return { bits: src.format, optional: src.optional ? src.optional : false }; + if (src.kind === "simple") { + if (src.type === "int") { + if (typeof src.format === "number") { + return { + bits: src.format, + optional: src.optional ? src.optional : false, + }; } else if (src.format === null || src.format === undefined) { - return { bits: 257, optional: src.optional ? src.optional : false }; + return { + bits: 257, + optional: src.optional ? src.optional : false, + }; } } } return null; - } + }, }; -const uintSerializer: Serializer<{ bits: number, optional: boolean }> = { +const uintSerializer: Serializer<{ bits: number; optional: boolean }> = { tsType(v) { if (v.optional) { - return 'bigint | null'; + return "bigint | null"; } else { - return 'bigint'; + return "bigint"; } }, tsLoad(v, slice, field, w) { if (v.optional) { - w.append(`let ${field} = ${slice}.loadBit() ? ${slice}.loadUintBig(${v.bits}) : null;`); + w.append( + `let ${field} = ${slice}.loadBit() ? ${slice}.loadUintBig(${v.bits}) : null;`, + ); } else { w.append(`let ${field} = ${slice}.loadUintBig(${v.bits});`); } @@ -86,7 +113,9 @@ const uintSerializer: Serializer<{ bits: number, optional: boolean }> = { }, tsStore(v, builder, field, w) { if (v.optional) { - w.append(`if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeUint(${field}, ${v.bits}); } else { ${builder}.storeBit(false); }`); + w.append( + `if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeUint(${field}, ${v.bits}); } else { ${builder}.storeBit(false); }`, + ); } else { w.append(`${builder}.storeUint(${field}, ${v.bits});`); } @@ -95,30 +124,38 @@ const uintSerializer: Serializer<{ bits: number, optional: boolean }> = { w.append(`${to}.writeNumber(${field});`); }, abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'uint') { - if (typeof src.format === 'number') { - return { bits: src.format, optional: src.optional ? src.optional : false }; + if (src.kind === "simple") { + if (src.type === "uint") { + if (typeof src.format === "number") { + return { + bits: src.format, + optional: src.optional ? src.optional : false, + }; } else if (src.format === null || src.format === undefined) { - return { bits: 256, optional: src.optional ? src.optional : false }; + return { + bits: 256, + optional: src.optional ? src.optional : false, + }; } } } return null; - } + }, }; const coinsSerializer: Serializer<{ optional: boolean }> = { tsType(v) { if (v.optional) { - return 'bigint | null'; + return "bigint | null"; } else { - return 'bigint'; + return "bigint"; } }, tsLoad(v, slice, field, w) { if (v.optional) { - w.append(`let ${field} = ${slice}.loadBit() ? ${slice}.loadCoins() : null;`); + w.append( + `let ${field} = ${slice}.loadBit() ? ${slice}.loadCoins() : null;`, + ); } else { w.append(`let ${field} = ${slice}.loadCoins();`); } @@ -132,7 +169,9 @@ const coinsSerializer: Serializer<{ optional: boolean }> = { }, tsStore(v, builder, field, w) { if (v.optional) { - w.append(`if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeCoins(${field}); } else { ${builder}.storeBit(false); }`); + w.append( + `if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeCoins(${field}); } else { ${builder}.storeBit(false); }`, + ); } else { w.append(`${builder}.storeCoins(${field});`); } @@ -141,28 +180,30 @@ const coinsSerializer: Serializer<{ optional: boolean }> = { w.append(`${to}.writeNumber(${field});`); }, abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'uint') { - if (src.format === 'coins') { + if (src.kind === "simple") { + if (src.type === "uint") { + if (src.format === "coins") { return { optional: src.optional ? src.optional : false }; } } } return null; - } + }, }; const boolSerializer: Serializer<{ optional: boolean }> = { tsType(v) { if (v.optional) { - return 'boolean | null'; + return "boolean | null"; } else { - return 'boolean'; + return "boolean"; } }, tsLoad(v, slice, field, w) { if (v.optional) { - w.append(`let ${field} = ${slice}.loadBit() ? ${slice}.loadBit() : null;`); + w.append( + `let ${field} = ${slice}.loadBit() ? ${slice}.loadBit() : null;`, + ); } else { w.append(`let ${field} = ${slice}.loadBit();`); } @@ -176,7 +217,9 @@ const boolSerializer: Serializer<{ optional: boolean }> = { }, tsStore(v, builder, field, w) { if (v.optional) { - w.append(`if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeBit(${field}); } else { ${builder}.storeBit(false); }`); + w.append( + `if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeBit(${field}); } else { ${builder}.storeBit(false); }`, + ); } else { w.append(`${builder}.storeBit(${field});`); } @@ -185,23 +228,23 @@ const boolSerializer: Serializer<{ optional: boolean }> = { w.append(`${to}.writeBoolean(${field});`); }, abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'bool') { + if (src.kind === "simple") { + if (src.type === "bool") { if (src.format === null || src.format === undefined) { return { optional: src.optional ? src.optional : false }; } } } return null; - } + }, }; const addressSerializer: Serializer<{ optional: boolean }> = { tsType(v) { if (v.optional) { - return 'Address | null'; + return "Address | null"; } else { - return 'Address'; + return "Address"; } }, tsLoad(v, slice, field, w) { @@ -225,28 +268,33 @@ const addressSerializer: Serializer<{ optional: boolean }> = { w.append(`${to}.writeAddress(${field});`); }, abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'address') { + if (src.kind === "simple") { + if (src.type === "address") { if (src.format === null || src.format === undefined) { return { optional: src.optional ? src.optional : false }; } } } return null; - } + }, }; -const cellSerializer: Serializer<{ kind: 'cell' | 'slice' | 'builder', optional: boolean }> = { +const cellSerializer: Serializer<{ + kind: "cell" | "slice" | "builder"; + optional: boolean; +}> = { tsType(v) { if (v.optional) { - return 'Cell | null'; + return "Cell | null"; } else { - return 'Cell'; + return "Cell"; } }, tsLoad(v, slice, field, w) { if (v.optional) { - w.append(`let ${field} = ${slice}.loadBit() ? ${slice}.loadRef() : null;`); + w.append( + `let ${field} = ${slice}.loadBit() ? ${slice}.loadRef() : null;`, + ); } else { w.append(`let ${field} = ${slice}.loadRef();`); } @@ -260,77 +308,97 @@ const cellSerializer: Serializer<{ kind: 'cell' | 'slice' | 'builder', optional: }, tsStore(v, builder, field, w) { if (v.optional) { - w.append(`if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeRef(${field}); } else { ${builder}.storeBit(false); }`); + w.append( + `if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeRef(${field}); } else { ${builder}.storeBit(false); }`, + ); } else { w.append(`${builder}.storeRef(${field});`); } }, tsStoreTuple(v, to, field, w) { - if (v.kind === 'cell') { + if (v.kind === "cell") { w.append(`${to}.writeCell(${field});`); - } else if (v.kind === 'slice') { + } else if (v.kind === "slice") { w.append(`${to}.writeSlice(${field});`); } else { w.append(`${to}.writeBuilder(${field});`); } }, abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'cell' || src.type === 'slice' || src.type === 'builder') { - if (src.format === null || src.format === undefined || src.format === 'ref') { - return { optional: src.optional ? src.optional : false, kind: src.type }; + if (src.kind === "simple") { + if ( + src.type === "cell" || + src.type === "slice" || + src.type === "builder" + ) { + if ( + src.format === null || + src.format === undefined || + src.format === "ref" + ) { + return { + optional: src.optional ? src.optional : false, + kind: src.type, + }; } } } return null; - } -} - -const remainderSerializer: Serializer<{ kind: 'cell' | 'slice' | 'builder' }> = { - tsType(_v) { - return 'Cell'; }, - tsLoad(v, slice, field, w) { - w.append(`let ${field} = ${slice}.asCell();`); - }, - tsLoadTuple(v, reader, field, w) { - w.append(`let ${field} = ${reader}.readCell();`); - }, - tsStore(v, builder, field, w) { - w.append(`${builder}.storeBuilder(${field}.asBuilder());`); - }, - tsStoreTuple(v, to, field, w) { - if (v.kind === 'cell') { - w.append(`${to}.writeCell(${field});`); - } else if (v.kind === 'slice') { - w.append(`${to}.writeSlice(${field});`); - } else { - w.append(`${to}.writeBuilder(${field});`); - } - }, - abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'cell' || src.type === 'slice' || src.type === 'builder') { - if (src.format === 'remainder') { - return { kind: src.type }; +}; + +const remainderSerializer: Serializer<{ kind: "cell" | "slice" | "builder" }> = + { + tsType(_v) { + return "Cell"; + }, + tsLoad(v, slice, field, w) { + w.append(`let ${field} = ${slice}.asCell();`); + }, + tsLoadTuple(v, reader, field, w) { + w.append(`let ${field} = ${reader}.readCell();`); + }, + tsStore(v, builder, field, w) { + w.append(`${builder}.storeBuilder(${field}.asBuilder());`); + }, + tsStoreTuple(v, to, field, w) { + if (v.kind === "cell") { + w.append(`${to}.writeCell(${field});`); + } else if (v.kind === "slice") { + w.append(`${to}.writeSlice(${field});`); + } else { + w.append(`${to}.writeBuilder(${field});`); + } + }, + abiMatcher(src) { + if (src.kind === "simple") { + if ( + src.type === "cell" || + src.type === "slice" || + src.type === "builder" + ) { + if (src.format === "remainder") { + return { kind: src.type }; + } } } - } - return null; - } -} + return null; + }, + }; -const fixedBytesSerializer: Serializer<{ bytes: number, optional: boolean }> = { +const fixedBytesSerializer: Serializer<{ bytes: number; optional: boolean }> = { tsType(v) { if (v.optional) { - return 'Buffer | null'; + return "Buffer | null"; } else { - return 'Buffer'; + return "Buffer"; } }, tsLoad(v, slice, field, w) { if (v.optional) { - w.append(`let ${field} = ${slice}.loadBit() ? ${slice}.loadBuffer(${v.bytes}) : null;`); + w.append( + `let ${field} = ${slice}.loadBit() ? ${slice}.loadBuffer(${v.bytes}) : null;`, + ); } else { w.append(`let ${field} = ${slice}.loadBuffer(${v.bytes});`); } @@ -344,7 +412,9 @@ const fixedBytesSerializer: Serializer<{ bytes: number, optional: boolean }> = { }, tsStore(v, builder, field, w) { if (v.optional) { - w.append(`if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeBuffer(${field}); } else { ${builder}.storeBit(false); }`); + w.append( + `if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeBuffer(${field}); } else { ${builder}.storeBit(false); }`, + ); } else { w.append(`${builder}.storeBuffer(${field});`); } @@ -353,28 +423,33 @@ const fixedBytesSerializer: Serializer<{ bytes: number, optional: boolean }> = { w.append(`${to}.writeBuffer(${field});`); }, abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'fixed-bytes') { - if (typeof src.format === 'number') { - return { bytes: src.format, optional: src.optional ? src.optional : false }; + if (src.kind === "simple") { + if (src.type === "fixed-bytes") { + if (typeof src.format === "number") { + return { + bytes: src.format, + optional: src.optional ? src.optional : false, + }; } } } return null; - } + }, }; const stringSerializer: Serializer<{ optional: boolean }> = { tsType(v) { if (v.optional) { - return 'string | null'; + return "string | null"; } else { - return 'string'; + return "string"; } }, tsLoad(v, slice, field, w) { if (v.optional) { - w.append(`let ${field} = ${slice}.loadBit() ? ${slice}.loadStringRefTail() : null;`); + w.append( + `let ${field} = ${slice}.loadBit() ? ${slice}.loadStringRefTail() : null;`, + ); } else { w.append(`let ${field} = ${slice}.loadStringRefTail();`); } @@ -388,7 +463,9 @@ const stringSerializer: Serializer<{ optional: boolean }> = { }, tsStore(v, builder, field, w) { if (v.optional) { - w.append(`if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeStringRefTail(${field}); } else { ${builder}.storeBit(false); }`); + w.append( + `if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true).storeStringRefTail(${field}); } else { ${builder}.storeBit(false); }`, + ); } else { w.append(`${builder}.storeStringRefTail(${field});`); } @@ -397,63 +474,70 @@ const stringSerializer: Serializer<{ optional: boolean }> = { w.append(`${to}.writeString(${field});`); }, abiMatcher(src) { - if (src.kind === 'simple') { - if (src.type === 'string') { + if (src.kind === "simple") { + if (src.type === "string") { if (src.format === null || src.format === undefined) { return { optional: src.optional ? src.optional : false }; } } } return null; - } -} + }, +}; const guard: Serializer = { abiMatcher(src) { - if (src.kind === 'simple') { + if (src.kind === "simple") { if (primitiveTypes.includes(src.type)) { - throw Error(`Unable to resolve serializer for ${src.type} with ${src.format ? src.format : null} format`); + throw Error( + `Unable to resolve serializer for ${src.type} with ${src.format ? src.format : null} format`, + ); } } return null; }, tsType(_v) { - throw Error('Unreachable'); + throw Error("Unreachable"); }, tsLoad(_v, _slice, _field, _w) { - throw Error('Unreachable'); + throw Error("Unreachable"); }, tsLoadTuple(_v, _reader, _field, _w) { - throw Error('Unreachable'); + throw Error("Unreachable"); }, tsStore(_v, _builder, _field, _w) { - throw Error('Unreachable'); + throw Error("Unreachable"); }, tsStoreTuple(_v, _to, _field, _w) { - throw Error('Unreachable'); - } -} + throw Error("Unreachable"); + }, +}; -const struct: Serializer<{ name: string, optional: boolean }> = { +const struct: Serializer<{ name: string; optional: boolean }> = { abiMatcher(src) { - if (src.kind === 'simple') { + if (src.kind === "simple") { if (src.format !== null && src.format !== undefined) { return null; } - return { name: src.type, optional: src.optional ? src.optional : false }; + return { + name: src.type, + optional: src.optional ? src.optional : false, + }; } return null; }, tsType(v) { if (v.optional) { - return v.name + ' | null'; + return v.name + " | null"; } else { return v.name; } }, tsLoad(v, slice, field, w) { if (v.optional) { - w.append(`let ${field} = ${slice}.loadBit() ? load${v.name}(${slice}) : null;`); + w.append( + `let ${field} = ${slice}.loadBit() ? load${v.name}(${slice}) : null;`, + ); } else { w.append(`let ${field} = load${v.name}(${slice});`); } @@ -461,18 +545,24 @@ const struct: Serializer<{ name: string, optional: boolean }> = { tsLoadTuple(v, reader, field, w, fromGet: boolean) { if (v.optional) { w.append(`const ${field}_p = ${reader}.readTupleOpt();`); - w.append(`const ${field} = ${field}_p ? loadTuple${v.name}(${field}_p) : null;`); + w.append( + `const ${field} = ${field}_p ? loadTuple${v.name}(${field}_p) : null;`, + ); } else { if (fromGet) { w.append(`const ${field} = loadTuple${v.name}(${reader});`); } else { - w.append(`const ${field} = loadTuple${v.name}(${reader}.readTuple());`); + w.append( + `const ${field} = loadTuple${v.name}(${reader}.readTuple());`, + ); } } }, tsStore(v, builder, field, w) { if (v.optional) { - w.append(`if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true); ${builder}.store(store${v.name}(${field})); } else { ${builder}.storeBit(false); }`); + w.append( + `if (${field} !== null && ${field} !== undefined) { ${builder}.storeBit(true); ${builder}.store(store${v.name}(${field})); } else { ${builder}.storeBit(false); }`, + ); } else { w.append(`${builder}.store(store${v.name}(${field}));`); } @@ -491,123 +581,153 @@ const struct: Serializer<{ name: string, optional: boolean }> = { } else { w.append(`${to}.writeTuple(storeTuple${v.name}(${field}));`); } - } -} + }, +}; -type MapSerializerDescrKey = { kind: 'int' | 'uint', bits: number } | { kind: 'address' }; -type MapSerializerDescrValue = { kind: 'int' | 'uint', bits: number } | { kind: 'boolean' } | { kind: 'address' } | { kind: 'cell' } | { kind: 'struct', type: string }; +type MapSerializerDescrKey = + | { kind: "int" | "uint"; bits: number } + | { kind: "address" }; +type MapSerializerDescrValue = + | { kind: "int" | "uint"; bits: number } + | { kind: "boolean" } + | { kind: "address" } + | { kind: "cell" } + | { kind: "struct"; type: string }; type MapSerializerDescr = { - key: MapSerializerDescrKey, - value: MapSerializerDescrValue -} + key: MapSerializerDescrKey; + value: MapSerializerDescrValue; +}; function getKeyParser(src: MapSerializerDescrKey) { - if (src.kind === 'int') { + if (src.kind === "int") { if (src.bits <= 32) { return `Dictionary.Keys.Int(${src.bits})`; } else { return `Dictionary.Keys.BigInt(${src.bits})`; } - } else if (src.kind === 'uint') { + } else if (src.kind === "uint") { if (src.bits <= 32) { return `Dictionary.Keys.Uint(${src.bits})`; } else { return `Dictionary.Keys.BigUint(${src.bits})`; } - } else if (src.kind === 'address') { - return 'Dictionary.Keys.Address()'; + } else if (src.kind === "address") { + return "Dictionary.Keys.Address()"; } else { - throw Error('Unreachable'); + throw Error("Unreachable"); } } function getValueParser(src: MapSerializerDescrValue) { - if (src.kind === 'int') { + if (src.kind === "int") { if (src.bits <= 32) { return `Dictionary.Values.Int(${src.bits})`; } else { return `Dictionary.Values.BigInt(${src.bits})`; } - } else if (src.kind === 'uint') { + } else if (src.kind === "uint") { if (src.bits <= 32) { return `Dictionary.Values.Uint(${src.bits})`; } else { return `Dictionary.Values.BigUint(${src.bits})`; } - } else if (src.kind === 'address') { - return 'Dictionary.Values.Address()'; - } else if (src.kind === 'cell') { - return 'Dictionary.Values.Cell()'; - } else if (src.kind === 'boolean') { - return 'Dictionary.Values.Bool()'; - } else if (src.kind === 'struct') { + } else if (src.kind === "address") { + return "Dictionary.Values.Address()"; + } else if (src.kind === "cell") { + return "Dictionary.Values.Cell()"; + } else if (src.kind === "boolean") { + return "Dictionary.Values.Bool()"; + } else if (src.kind === "struct") { return `dictValueParser${src.type}()`; } else { - throw Error('Unreachable'); + throw Error("Unreachable"); } } const map: Serializer = { abiMatcher(src) { - if (src.kind === 'dict') { + if (src.kind === "dict") { if (src.format !== null && src.format !== undefined) { return null; } // Resolve key - let key: { kind: 'int' | 'uint', bits: number } | { kind: 'address' } | null = null; - if (src.key === 'int') { - if (typeof src.keyFormat === 'number') { - key = { kind: 'int', bits: src.keyFormat }; - } else if (src.keyFormat === null || src.keyFormat === undefined) { - key = { kind: 'int', bits: 257 }; + let key: + | { kind: "int" | "uint"; bits: number } + | { kind: "address" } + | null = null; + if (src.key === "int") { + if (typeof src.keyFormat === "number") { + key = { kind: "int", bits: src.keyFormat }; + } else if ( + src.keyFormat === null || + src.keyFormat === undefined + ) { + key = { kind: "int", bits: 257 }; } } - if (src.key === 'uint') { - if (typeof src.keyFormat === 'number') { - key = { kind: 'uint', bits: src.keyFormat }; - } else if (src.keyFormat === null || src.keyFormat === undefined) { - key = { kind: 'uint', bits: 256 }; + if (src.key === "uint") { + if (typeof src.keyFormat === "number") { + key = { kind: "uint", bits: src.keyFormat }; + } else if ( + src.keyFormat === null || + src.keyFormat === undefined + ) { + key = { kind: "uint", bits: 256 }; } } - if (src.key === 'address') { + if (src.key === "address") { if (src.keyFormat === null || src.keyFormat === undefined) { - key = { kind: 'address' }; + key = { kind: "address" }; } } // Resolve value let value: MapSerializerDescrValue | null = null; - if (src.value === 'int') { - if (typeof src.valueFormat === 'number') { - value = { kind: 'int', bits: src.valueFormat }; - } else if (src.valueFormat === null || src.valueFormat === undefined) { - value = { kind: 'int', bits: 257 }; + if (src.value === "int") { + if (typeof src.valueFormat === "number") { + value = { kind: "int", bits: src.valueFormat }; + } else if ( + src.valueFormat === null || + src.valueFormat === undefined + ) { + value = { kind: "int", bits: 257 }; } } - if (src.value === 'uint') { - if (typeof src.valueFormat === 'number') { - value = { kind: 'uint', bits: src.valueFormat }; - } else if (src.valueFormat === null || src.valueFormat === undefined) { - value = { kind: 'uint', bits: 256 }; + if (src.value === "uint") { + if (typeof src.valueFormat === "number") { + value = { kind: "uint", bits: src.valueFormat }; + } else if ( + src.valueFormat === null || + src.valueFormat === undefined + ) { + value = { kind: "uint", bits: 256 }; } } - if (src.value === 'address') { + if (src.value === "address") { if (src.valueFormat === null || src.valueFormat === undefined) { - value = { kind: 'address' }; + value = { kind: "address" }; } } - if (src.value === 'cell') { - if (src.valueFormat === null || src.valueFormat === undefined || src.valueFormat === 'ref') { - value = { kind: 'cell' }; + if (src.value === "cell") { + if ( + src.valueFormat === null || + src.valueFormat === undefined || + src.valueFormat === "ref" + ) { + value = { kind: "cell" }; } } if (primitiveTypes.indexOf(src.value) === -1) { - if (src.valueFormat === null || src.valueFormat === undefined || src.valueFormat === 'ref') { - value = { kind: 'struct', type: src.value }; + if ( + src.valueFormat === null || + src.valueFormat === undefined || + src.valueFormat === "ref" + ) { + value = { kind: "struct", type: src.value }; } } - if (src.value === 'bool') { + if (src.value === "bool") { if (src.valueFormat === null || src.valueFormat === undefined) { - value = { kind: 'boolean' }; + value = { kind: "boolean" }; } } @@ -618,60 +738,66 @@ const map: Serializer = { return null; }, tsType(v) { - // Resolve key type let keyT: string; - if (v.key.kind === 'int' || v.key.kind === 'uint') { + if (v.key.kind === "int" || v.key.kind === "uint") { if (v.key.bits <= 32) { keyT = `number`; } else { keyT = `bigint`; } - } else if (v.key.kind === 'address') { + } else if (v.key.kind === "address") { keyT = `Address`; } else { - throw Error('Unexpected key type'); + throw Error("Unexpected key type"); } // Resolve value type let valueT: string; - if (v.value.kind === 'int' || v.value.kind === 'uint') { + if (v.value.kind === "int" || v.value.kind === "uint") { if (v.value.bits <= 32) { valueT = `number`; } else { valueT = `bigint`; } - } else if (v.value.kind === 'boolean') { + } else if (v.value.kind === "boolean") { valueT = `boolean`; - } else if (v.value.kind === 'address') { + } else if (v.value.kind === "address") { valueT = `Address`; - } else if (v.value.kind === 'cell') { + } else if (v.value.kind === "cell") { valueT = `Cell`; - } else if (v.value.kind === 'struct') { + } else if (v.value.kind === "struct") { valueT = v.value.type; } else { - throw Error('Unexpected key type'); + throw Error("Unexpected key type"); } return `Dictionary<${keyT}, ${valueT}>`; }, tsLoad(v, slice, field, w) { - w.append(`let ${field} = Dictionary.load(${getKeyParser(v.key)}, ${getValueParser(v.value)}, ${slice});`); + w.append( + `let ${field} = Dictionary.load(${getKeyParser(v.key)}, ${getValueParser(v.value)}, ${slice});`, + ); }, tsLoadTuple(v, reader, field, w) { - w.append(`let ${field} = Dictionary.loadDirect(${getKeyParser(v.key)}, ${getValueParser(v.value)}, ${reader}.readCellOpt());`); + w.append( + `let ${field} = Dictionary.loadDirect(${getKeyParser(v.key)}, ${getValueParser(v.value)}, ${reader}.readCellOpt());`, + ); }, tsStore(v, builder, field, w) { - w.append(`${builder}.storeDict(${field}, ${getKeyParser(v.key)}, ${getValueParser(v.value)});`); + w.append( + `${builder}.storeDict(${field}, ${getKeyParser(v.key)}, ${getValueParser(v.value)});`, + ); }, tsStoreTuple(v, to, field, w) { - w.append(`${to}.writeCell(${field}.size > 0 ? beginCell().storeDictDirect(${field}, ${getKeyParser(v.key)}, ${getValueParser(v.value)}).endCell() : null);`); + w.append( + `${to}.writeCell(${field}.size > 0 ? beginCell().storeDictDirect(${field}, ${getKeyParser(v.key)}, ${getValueParser(v.value)}).endCell() : null);`, + ); }, -} +}; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const serializers: Serializer[] = [ - // Primitive types intSerializer, uintSerializer, @@ -688,5 +814,5 @@ export const serializers: Serializer[] = [ // Structs as fallback struct, - map -]; \ No newline at end of file + map, +]; diff --git a/src/bindings/typescript/writeStruct.ts b/src/bindings/typescript/writeStruct.ts index 3b74f6d32..3599af398 100644 --- a/src/bindings/typescript/writeStruct.ts +++ b/src/bindings/typescript/writeStruct.ts @@ -3,12 +3,16 @@ import { serializers } from "./serializers"; import { AllocationCell, AllocationOperation } from "../../storage/operation"; import { Writer } from "../../utils/Writer"; -export function writeStruct(name: string, fields: { name: string, type: ABITypeRef }[], exp: boolean, w: Writer) { - w.append(`${exp ? 'export ' : ' '}type ${name} = {`); +export function writeStruct( + name: string, + fields: { name: string; type: ABITypeRef }[], + exp: boolean, + w: Writer, +) { + w.append(`${exp ? "export " : " "}type ${name} = {`); w.inIndent(() => { w.append(`$$type: '${name}';`); outer: for (const f of fields) { - for (const s of serializers) { const v = s.abiMatcher(f.type); if (v) { @@ -17,7 +21,7 @@ export function writeStruct(name: string, fields: { name: string, type: ABITypeR } } - throw Error('Unsupported type: ' + JSON.stringify(f.type)); + throw Error("Unsupported type: " + JSON.stringify(f.type)); } }); w.append(`}`); @@ -29,16 +33,25 @@ export function writeParser(s: ABIType, allocation: AllocationCell, w: Writer) { w.inIndent(() => { w.append(`let sc_0 = slice;`); if (s.header) { - w.append(`if (sc_0.loadUint(32) !== ${s.header}) { throw Error('Invalid prefix'); }`); + w.append( + `if (sc_0.loadUint(32) !== ${s.header}) { throw Error('Invalid prefix'); }`, + ); } writeParserCell(0, allocation, s, w); - w.append(`return { ${[`$$type: '${s.name}' as const`, ...s.fields.map((v) => v.name + ': _' + v.name)].join(', ')} };`); + w.append( + `return { ${[`$$type: '${s.name}' as const`, ...s.fields.map((v) => v.name + ": _" + v.name)].join(", ")} };`, + ); }); w.append(`}`); w.append(); } -function writeParserCell(gen: number, src: AllocationCell, s: ABIType, w: Writer) { +function writeParserCell( + gen: number, + src: AllocationCell, + s: ABIType, + w: Writer, +) { for (const f of src.ops) { writeParserField(gen, f, s, w); } @@ -48,8 +61,13 @@ function writeParserCell(gen: number, src: AllocationCell, s: ABIType, w: Writer } } -function writeParserField(gen: number, field: AllocationOperation, s: ABIType, w: Writer) { - const name = '_' + field.name; +function writeParserField( + gen: number, + field: AllocationOperation, + s: ABIType, + w: Writer, +) { + const name = "_" + field.name; const type = field.type; for (const s of serializers) { const v = s.abiMatcher(type); @@ -58,13 +76,17 @@ function writeParserField(gen: number, field: AllocationOperation, s: ABIType, w return; } } - throw Error('Unsupported type'); + throw Error("Unsupported type"); } -export function writeSerializer(s: ABIType, allocation: AllocationCell, w: Writer) { +export function writeSerializer( + s: ABIType, + allocation: AllocationCell, + w: Writer, +) { w.append(`export function store${s.name}(src: ${s.name}) {`); w.inIndent(() => { - w.append(`return (builder: Builder) => {`) + w.append(`return (builder: Builder) => {`); w.inIndent(() => { w.append(`let b_0 = builder;`); if (s.header) { @@ -78,10 +100,14 @@ export function writeSerializer(s: ABIType, allocation: AllocationCell, w: Write w.append(); } -export function writeInitSerializer(name: string, allocation: AllocationCell, w: Writer) { +export function writeInitSerializer( + name: string, + allocation: AllocationCell, + w: Writer, +) { w.append(`function init${name}(src: ${name}) {`); w.inIndent(() => { - w.append(`return (builder: Builder) => {`) + w.append(`return (builder: Builder) => {`); w.inIndent(() => { w.append(`let b_0 = builder;`); writeSerializerCell(0, allocation, w); @@ -104,7 +130,7 @@ function writeSerializerCell(gen: number, src: AllocationCell, w: Writer) { } function writeSerializerField(gen: number, s: AllocationOperation, w: Writer) { - const name = 'src.' + s.name; + const name = "src." + s.name; const type = s.type; for (const s of serializers) { const v = s.abiMatcher(type); @@ -113,16 +139,18 @@ function writeSerializerField(gen: number, s: AllocationOperation, w: Writer) { return; } } - throw Error('Unsupported field type: ' + JSON.stringify(type)); + throw Error("Unsupported field type: " + JSON.stringify(type)); } export function writeTupleParser(s: ABIType, w: Writer) { w.append(`function loadTuple${s.name}(source: TupleReader) {`); w.inIndent(() => { for (const f of s.fields) { - writeTupleFieldParser('_' + f.name, f.type, w); + writeTupleFieldParser("_" + f.name, f.type, w); } - w.append(`return { ${[`$$type: '${s.name}' as const`, ...s.fields.map((v) => v.name + ': _' + v.name)].join(', ')} };`); + w.append( + `return { ${[`$$type: '${s.name}' as const`, ...s.fields.map((v) => v.name + ": _" + v.name)].join(", ")} };`, + ); }); w.append(`}`); w.append(); @@ -132,7 +160,12 @@ export function writeGetParser(name: string, type: ABITypeRef, w: Writer) { writeTupleFieldParser(name, type, w, true); } -function writeTupleFieldParser(name: string, type: ABITypeRef, w: Writer, fromGet = false) { +function writeTupleFieldParser( + name: string, + type: ABITypeRef, + w: Writer, + fromGet = false, +) { for (const s of serializers) { const v = s.abiMatcher(type); if (v) { @@ -140,7 +173,7 @@ function writeTupleFieldParser(name: string, type: ABITypeRef, w: Writer, fromGe return; } } - throw Error('Unsupported field type: ' + JSON.stringify(type)); + throw Error("Unsupported field type: " + JSON.stringify(type)); } export function writeTupleSerializer(s: ABIType, w: Writer) { @@ -168,7 +201,7 @@ function writeVariableToStack(name: string, type: ABITypeRef, w: Writer) { return; } } - throw Error('Unsupported field type: ' + JSON.stringify(type)); + throw Error("Unsupported field type: " + JSON.stringify(type)); } export function writeDictParser(s: ABIType, w: Writer) { @@ -185,4 +218,4 @@ export function writeDictParser(s: ABIType, w: Writer) { } `); w.append(); -} \ No newline at end of file +} diff --git a/src/bindings/writeTypescript.ts b/src/bindings/writeTypescript.ts index 4715803e2..ac9603143 100644 --- a/src/bindings/writeTypescript.ts +++ b/src/bindings/writeTypescript.ts @@ -1,14 +1,26 @@ -import * as changeCase from 'change-case'; +import * as changeCase from "change-case"; import { Writer } from "../utils/Writer"; import { ABIArgument, ABIType, ContractABI } from "@ton/core"; -import { writeArgumentToStack, writeDictParser, writeGetParser, writeInitSerializer, writeParser, writeSerializer, writeStruct, writeTupleParser, writeTupleSerializer } from "./typescript/writeStruct"; +import { + writeArgumentToStack, + writeDictParser, + writeGetParser, + writeInitSerializer, + writeParser, + writeSerializer, + writeStruct, + writeTupleParser, + writeTupleSerializer, +} from "./typescript/writeStruct"; import { AllocationCell } from "../storage/operation"; import { topologicalSort } from "../utils/utils"; -import { allocate, getAllocationOperationFromField } from "../storage/allocator"; -import { serializers } from './typescript/serializers'; +import { + allocate, + getAllocationOperationFromField, +} from "../storage/allocator"; +import { serializers } from "./typescript/serializers"; function writeArguments(args: ABIArgument[]) { - const res: string[] = []; outer: for (const f of args) { for (const s of serializers) { @@ -18,21 +30,26 @@ function writeArguments(args: ABIArgument[]) { continue outer; } } - throw Error('Unsupported type: ' + JSON.stringify(f.type)); + throw Error("Unsupported type: " + JSON.stringify(f.type)); } return res; } -export function writeTypescript(abi: ContractABI, init?: { - code: string, - system: string, - args: ABIArgument[], - prefix?: { - value: number; - bits: number; - } | undefined -}) { +export function writeTypescript( + abi: ContractABI, + init?: { + code: string; + system: string; + args: ABIArgument[]; + prefix?: + | { + value: number; + bits: number; + } + | undefined; + }, +) { const w = new Writer(); w.write(` @@ -60,18 +77,22 @@ export function writeTypescript(abi: ContractABI, init?: { `); w.append(); - const allocations: { [key: string]: { size: { bits: number, refs: number }, root: AllocationCell } } = {}; + const allocations: { + [key: string]: { + size: { bits: number; refs: number }; + root: AllocationCell; + }; + } = {}; // Structs if (abi.types) { - // Allocations const refs = (src: ABIType) => { - const res: ABIType[] = [] + const res: ABIType[] = []; const t = new Set(); for (const f of src.fields) { const r = f.type; - if (r.kind === 'simple') { + if (r.kind === "simple") { const e = abi.types!.find((v) => v.name === r.type); if (e) { if (!t.has(r.type)) { @@ -82,17 +103,29 @@ export function writeTypescript(abi: ContractABI, init?: { } } return res; - } + }; const sortedTypes = topologicalSort(abi.types, refs); for (const f of sortedTypes) { const ops = f.fields.map((v) => ({ name: v.name, type: v.type, - op: getAllocationOperationFromField(v.type, (s) => allocations[s].size) + op: getAllocationOperationFromField( + v.type, + (s) => allocations[s].size, + ), })); const headerBits = f.header ? 32 : 0; - const allocation = allocate({ reserved: { bits: headerBits, refs: 0 }, ops }); - allocations[f.name] = { size: { bits: allocation.size.bits + headerBits, refs: allocation.size.refs }, root: allocation }; + const allocation = allocate({ + reserved: { bits: headerBits, refs: 0 }, + ops, + }); + allocations[f.name] = { + size: { + bits: allocation.size.bits + headerBits, + refs: allocation.size.refs, + }, + root: allocation, + }; } for (const s of abi.types) { @@ -107,33 +140,43 @@ export function writeTypescript(abi: ContractABI, init?: { // Init if (init) { - // Write serializer - const argTypeName = (abi.name || 'Contract') + '_init_args'; + const argTypeName = (abi.name || "Contract") + "_init_args"; const ops = init.args.map((v) => ({ name: v.name, type: v.type, - op: getAllocationOperationFromField(v.type, (s) => allocations[s].size) + op: getAllocationOperationFromField( + v.type, + (s) => allocations[s].size, + ), })); - const allocation = allocate({ reserved: { bits: init.prefix ? init.prefix.bits : 0, refs: 1 }, ops }); + const allocation = allocate({ + reserved: { bits: init.prefix ? init.prefix.bits : 0, refs: 1 }, + ops, + }); writeStruct(argTypeName, init.args, false, w); writeInitSerializer(argTypeName, allocation, w); // Write init function - w.append(`async function ${abi.name}_init(${writeArguments(init.args).join(', ')}) {`); + w.append( + `async function ${abi.name}_init(${writeArguments(init.args).join(", ")}) {`, + ); w.inIndent(() => { - // Code references w.append(`const __code = Cell.fromBase64('${init.code}');`); w.append(`const __system = Cell.fromBase64('${init.system}');`); // Stack - w.append('let builder = beginCell();'); + w.append("let builder = beginCell();"); w.append(`builder.storeRef(__system);`); if (init.prefix) { - w.append(`builder.storeUint(${init.prefix.value}, ${init.prefix.bits});`); + w.append( + `builder.storeUint(${init.prefix.value}, ${init.prefix.bits});`, + ); } - w.append(`init${argTypeName}({ ${[`$$type: '${argTypeName}'`, ...init.args.map((v) => v.name)].join(', ')} })(builder);`); + w.append( + `init${argTypeName}({ ${[`$$type: '${argTypeName}'`, ...init.args.map((v) => v.name)].join(", ")} })(builder);`, + ); w.append(`const __data = builder.endCell();`); w.append(`return { code: __code, data: __data };`); }); @@ -142,14 +185,16 @@ export function writeTypescript(abi: ContractABI, init?: { } // Errors - w.append(`const ${abi.name}_errors: { [key: number]: { message: string } } = {`); + w.append( + `const ${abi.name}_errors: { [key: number]: { message: string } } = {`, + ); w.inIndent(() => { if (abi.errors) { for (const k in abi.errors) { w.append( `${k}: { message: \`${abi.errors[ parseInt(k, 10) - ].message.replaceAll('`', '\\`')}\` },` + ].message.replaceAll("`", "\\`")}\` },`, ); } } @@ -162,10 +207,10 @@ export function writeTypescript(abi: ContractABI, init?: { w.inIndent(() => { if (abi.types) { for (const t of abi.types) { - w.append(JSON.stringify(t) + ','); + w.append(JSON.stringify(t) + ","); } } - }) + }); w.append(`]`); w.append(); @@ -174,10 +219,10 @@ export function writeTypescript(abi: ContractABI, init?: { w.inIndent(() => { if (abi.getters) { for (const t of abi.getters) { - w.append(JSON.stringify(t) + ','); + w.append(JSON.stringify(t) + ","); } } - }) + }); w.append(`]`); w.append(); @@ -186,10 +231,10 @@ export function writeTypescript(abi: ContractABI, init?: { w.inIndent(() => { if (abi.receivers) { for (const t of abi.receivers) { - w.append(JSON.stringify(t) + ','); + w.append(JSON.stringify(t) + ","); } } - }) + }); w.append(`]`); w.append(); @@ -199,16 +244,24 @@ export function writeTypescript(abi: ContractABI, init?: { w.append(); if (init) { - w.append(`static async init(${writeArguments(init.args).join(', ')}) {`); + w.append( + `static async init(${writeArguments(init.args).join(", ")}) {`, + ); w.inIndent(() => { - w.append(`return await ${abi.name}_init(${init!.args.map((v) => v.name).join(', ')});`); + w.append( + `return await ${abi.name}_init(${init!.args.map((v) => v.name).join(", ")});`, + ); }); w.append(`}`); w.append(); - w.append(`static async fromInit(${writeArguments(init.args).join(', ')}) {`); + w.append( + `static async fromInit(${writeArguments(init.args).join(", ")}) {`, + ); w.inIndent(() => { - w.append(`const init = await ${abi.name}_init(${init!.args.map((v) => v.name).join(', ')});`); + w.append( + `const init = await ${abi.name}_init(${init!.args.map((v) => v.name).join(", ")});`, + ); w.append(`const address = contractAddress(0, init);`); w.append(`return new ${abi.name}(address, init);`); }); @@ -228,171 +281,213 @@ export function writeTypescript(abi: ContractABI, init?: { w.append(`readonly abi: ContractABI = {`); w.inIndent(() => { w.append(`types: ${abi.name}_types,`); - w.append(`getters: ${abi.name}_getters,`) - w.append(`receivers: ${abi.name}_receivers,`) - w.append(`errors: ${abi.name}_errors,`) + w.append(`getters: ${abi.name}_getters,`); + w.append(`receivers: ${abi.name}_receivers,`); + w.append(`errors: ${abi.name}_errors,`); }); w.append(`};`); w.append(); - w.append(`private constructor(address: Address, init?: { code: Cell, data: Cell }) {`); + w.append( + `private constructor(address: Address, init?: { code: Cell, data: Cell }) {`, + ); w.inIndent(() => { - w.append('this.address = address;'); - w.append('this.init = init;'); + w.append("this.address = address;"); + w.append("this.init = init;"); }); - w.append('}') + w.append("}"); w.append(); // Internal receivers - if (abi.receivers && abi.receivers.filter((v) => v.receiver === 'internal').length > 0) { - + if ( + abi.receivers && + abi.receivers.filter((v) => v.receiver === "internal").length > 0 + ) { // Types const receivers: string[] = []; for (const r of abi.receivers) { - if (r.receiver !== 'internal') { + if (r.receiver !== "internal") { continue; } - if (r.message.kind === 'empty') { + if (r.message.kind === "empty") { receivers.push(`null`); - } else if (r.message.kind === 'typed') { + } else if (r.message.kind === "typed") { receivers.push(`${r.message.type}`); - } else if (r.message.kind === 'text') { - if (r.message.text !== null && r.message.text !== undefined) { + } else if (r.message.kind === "text") { + if ( + r.message.text !== null && + r.message.text !== undefined + ) { receivers.push(`'${r.message.text}'`); } else { receivers.push(`string`); } - } else if (r.message.kind === 'any') { + } else if (r.message.kind === "any") { receivers.push(`Slice`); } } // Receiver function - w.append(`async send(provider: ContractProvider, via: Sender, args: { value: bigint, bounce?: boolean| null | undefined }, message: ${receivers.join(' | ')}) {`); + w.append( + `async send(provider: ContractProvider, via: Sender, args: { value: bigint, bounce?: boolean| null | undefined }, message: ${receivers.join(" | ")}) {`, + ); w.inIndent(() => { w.append(); // Parse message w.append(`let body: Cell | null = null;`); for (const r of abi.receivers!) { - if (r.receiver !== 'internal') { + if (r.receiver !== "internal") { continue; } const msg = r.message; - if (msg.kind === 'typed') { - w.append(`if (message && typeof message === 'object' && !(message instanceof Slice) && message.$$type === '${msg.type}') {`); + if (msg.kind === "typed") { + w.append( + `if (message && typeof message === 'object' && !(message instanceof Slice) && message.$$type === '${msg.type}') {`, + ); w.inIndent(() => { - w.append(`body = beginCell().store(store${msg.type}(message)).endCell();`); + w.append( + `body = beginCell().store(store${msg.type}(message)).endCell();`, + ); }); w.append(`}`); - } else if (msg.kind === 'empty') { + } else if (msg.kind === "empty") { w.append(`if (message === null) {`); w.inIndent(() => { w.append(`body = new Cell();`); }); w.append(`}`); - } else if (msg.kind === 'text') { - if ((msg.text === null || msg.text === undefined)) { + } else if (msg.kind === "text") { + if (msg.text === null || msg.text === undefined) { w.append(`if (typeof message === 'string') {`); w.inIndent(() => { - w.append(`body = beginCell().storeUint(0, 32).storeStringTail(message).endCell();`); + w.append( + `body = beginCell().storeUint(0, 32).storeStringTail(message).endCell();`, + ); }); w.append(`}`); } else { w.append(`if (message === '${msg.text}') {`); w.inIndent(() => { - w.append(`body = beginCell().storeUint(0, 32).storeStringTail(message).endCell();`); + w.append( + `body = beginCell().storeUint(0, 32).storeStringTail(message).endCell();`, + ); }); w.append(`}`); } - } else if (msg.kind === 'any') { - w.append(`if (message && typeof message === 'object' && message instanceof Slice) {`); + } else if (msg.kind === "any") { + w.append( + `if (message && typeof message === 'object' && message instanceof Slice) {`, + ); w.inIndent(() => { w.append(`body = message.asCell();`); }); w.append(`}`); } } - w.append(`if (body === null) { throw new Error('Invalid message type'); }`); + w.append( + `if (body === null) { throw new Error('Invalid message type'); }`, + ); w.append(); // Send message - w.append(`await provider.internal(via, { ...args, body: body });`); + w.append( + `await provider.internal(via, { ...args, body: body });`, + ); w.append(); }); w.append(`}`); w.append(); } - if (abi.receivers && abi.receivers.filter((v) => v.receiver === 'external').length > 0) { - + if ( + abi.receivers && + abi.receivers.filter((v) => v.receiver === "external").length > 0 + ) { // Types const receivers: string[] = []; for (const r of abi.receivers) { - if (r.receiver !== 'external') { + if (r.receiver !== "external") { continue; } - if (r.message.kind === 'empty') { + if (r.message.kind === "empty") { receivers.push(`null`); - } else if (r.message.kind === 'typed') { + } else if (r.message.kind === "typed") { receivers.push(`${r.message.type}`); - } else if (r.message.kind === 'text') { - if (r.message.text !== null && r.message.text !== undefined) { + } else if (r.message.kind === "text") { + if ( + r.message.text !== null && + r.message.text !== undefined + ) { receivers.push(`'${r.message.text}'`); } else { receivers.push(`string`); } - } else if (r.message.kind === 'any') { + } else if (r.message.kind === "any") { receivers.push(`Slice`); } } // Receiver function - w.append(`async sendExternal(provider: ContractProvider, message: ${receivers.join(' | ')}) {`); + w.append( + `async sendExternal(provider: ContractProvider, message: ${receivers.join(" | ")}) {`, + ); w.inIndent(() => { w.append(); // Parse message w.append(`let body: Cell | null = null;`); for (const r of abi.receivers!) { - if (r.receiver !== 'external') { + if (r.receiver !== "external") { continue; } const msg = r.message; - if (msg.kind === 'typed') { - w.append(`if (message && typeof message === 'object' && !(message instanceof Slice) && message.$$type === '${msg.type}') {`); + if (msg.kind === "typed") { + w.append( + `if (message && typeof message === 'object' && !(message instanceof Slice) && message.$$type === '${msg.type}') {`, + ); w.inIndent(() => { - w.append(`body = beginCell().store(store${msg.type}(message)).endCell();`); + w.append( + `body = beginCell().store(store${msg.type}(message)).endCell();`, + ); }); w.append(`}`); - } else if (msg.kind === 'empty') { + } else if (msg.kind === "empty") { w.append(`if (message === null) {`); w.inIndent(() => { w.append(`body = new Cell();`); }); w.append(`}`); - } else if (msg.kind === 'text') { - if ((msg.text === null || msg.text === undefined)) { + } else if (msg.kind === "text") { + if (msg.text === null || msg.text === undefined) { w.append(`if (typeof message === 'string') {`); w.inIndent(() => { - w.append(`body = beginCell().storeUint(0, 32).storeStringTail(message).endCell();`); + w.append( + `body = beginCell().storeUint(0, 32).storeStringTail(message).endCell();`, + ); }); w.append(`}`); } else { w.append(`if (message === '${msg.text}') {`); w.inIndent(() => { - w.append(`body = beginCell().storeUint(0, 32).storeStringTail(message).endCell();`); + w.append( + `body = beginCell().storeUint(0, 32).storeStringTail(message).endCell();`, + ); }); w.append(`}`); } - } else if (msg.kind === 'any') { - w.append(`if (message && typeof message === 'object' && message instanceof Slice) {`); + } else if (msg.kind === "any") { + w.append( + `if (message && typeof message === 'object' && message instanceof Slice) {`, + ); w.inIndent(() => { w.append(`body = message.asCell();`); }); w.append(`}`); } } - w.append(`if (body === null) { throw new Error('Invalid message type'); }`); + w.append( + `if (body === null) { throw new Error('Invalid message type'); }`, + ); w.append(); // Send message @@ -406,7 +501,9 @@ export function writeTypescript(abi: ContractABI, init?: { // Getters if (abi.getters) { for (const g of abi.getters) { - w.append(`async get${changeCase.pascalCase(g.name)}(${['provider: ContractProvider', ...writeArguments(g.arguments ? g.arguments : [])].join(', ')}) {`); + w.append( + `async get${changeCase.pascalCase(g.name)}(${["provider: ContractProvider", ...writeArguments(g.arguments ? g.arguments : [])].join(", ")}) {`, + ); w.inIndent(() => { w.append(`let builder = new TupleBuilder();`); if (g.arguments) { @@ -414,9 +511,11 @@ export function writeTypescript(abi: ContractABI, init?: { writeArgumentToStack(a.name, a.type, w); } } - w.append(`let source = (await provider.get('${g.name}', builder.build())).stack;`); + w.append( + `let source = (await provider.get('${g.name}', builder.build())).stack;`, + ); if (g.returnType) { - writeGetParser('result', g.returnType, w); + writeGetParser("result", g.returnType, w); w.append(`return result;`); } }); @@ -428,4 +527,4 @@ export function writeTypescript(abi: ContractABI, init?: { w.append(`}`); return w.end(); -} \ No newline at end of file +} diff --git a/src/browser.ts b/src/browser.ts index cc8dbb294..5dd218640 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -4,25 +4,29 @@ import { build } from "./pipeline/build"; import { createVirtualFileSystem } from "./vfs/createVirtualFileSystem"; export async function run(args: { - config: Config, - files: { [key: string]: string }, - logger?: TactLogger | null | undefined + config: Config; + files: { [key: string]: string }; + logger?: TactLogger | null | undefined; }) { - // Verify config const config = verifyConfig(args.config); // Create project's writable fs - const project = createVirtualFileSystem('/', args.files, false); + const project = createVirtualFileSystem("/", args.files, false); // Create stdlib path - const stdlib = '@stdlib'; + const stdlib = "@stdlib"; // Compile let success = true; for (const p of config.projects) { - const built = await build({ config: p, project, stdlib, logger: args.logger }); + const built = await build({ + config: p, + project, + stdlib, + logger: args.logger, + }); success = success && built; } return success; -} \ No newline at end of file +} diff --git a/src/check.ts b/src/check.ts index 7c15c196c..5de8cd5a0 100644 --- a/src/check.ts +++ b/src/check.ts @@ -1,11 +1,15 @@ import { featureEnable } from "./config/features"; import { CompilerContext } from "./context"; import files from "./imports/stdlib"; -import { createVirtualFileSystem, TactSourceError, VirtualFileSystem } from "./main"; +import { + createVirtualFileSystem, + TactSourceError, + VirtualFileSystem, +} from "./main"; import { precompile } from "./pipeline/precompile"; export type CheckResultItem = { - type: 'error' | 'warning'; + type: "error" | "warning"; message: string; location: { file: string; @@ -13,23 +17,27 @@ export type CheckResultItem = { column: number; length: number; }; -} - -export type CheckResult = { - ok: true; -} | { - ok: false; - messages: CheckResultItem[]; -} +}; -export function check(args: { project: VirtualFileSystem, entrypoint: string }): CheckResult { +export type CheckResult = + | { + ok: true; + } + | { + ok: false; + messages: CheckResultItem[]; + }; +export function check(args: { + project: VirtualFileSystem; + entrypoint: string; +}): CheckResult { // Create context - const stdlib = createVirtualFileSystem('@stdlib/', files); + const stdlib = createVirtualFileSystem("@stdlib/", files); let ctx: CompilerContext = new CompilerContext({ shared: {} }); - ctx = featureEnable(ctx, 'debug'); // Enable debug flag (does not affect type checking in practice) - ctx = featureEnable(ctx, 'masterchain'); // Enable masterchain flag to avoid masterchain-specific errors - ctx = featureEnable(ctx, 'external'); // Enable external messages flag to avoid external-specific errors + ctx = featureEnable(ctx, "debug"); // Enable debug flag (does not affect type checking in practice) + ctx = featureEnable(ctx, "masterchain"); // Enable masterchain flag to avoid masterchain-specific errors + ctx = featureEnable(ctx, "external"); // Enable external messages flag to avoid external-specific errors // Execute check const items: CheckResultItem[] = []; @@ -38,44 +46,47 @@ export function check(args: { project: VirtualFileSystem, entrypoint: string }): } catch (e) { if (e instanceof TactSourceError) { items.push({ - type: 'error', + type: "error", message: e.message, - location: e.ref.file ? { - file: e.ref.file, - line: e.ref.interval.getLineAndColumn().lineNum, - column: e.ref.interval.getLineAndColumn().colNum, - length: e.ref.interval.endIdx - e.ref.interval.startIdx - } : { - file: args.entrypoint, - line: 0, - column: 0, - length: 0 - } + location: e.ref.file + ? { + file: e.ref.file, + line: e.ref.interval.getLineAndColumn().lineNum, + column: e.ref.interval.getLineAndColumn().colNum, + length: + e.ref.interval.endIdx - e.ref.interval.startIdx, + } + : { + file: args.entrypoint, + line: 0, + column: 0, + length: 0, + }, }); } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any const msg = (e as any).message; - if (typeof msg === 'string') { + if (typeof msg === "string") { items.push({ - type: 'error', + type: "error", message: msg, location: { file: args.entrypoint, line: 0, column: 0, - length: 0 - } + length: 0, + }, }); } else { items.push({ - type: 'error', - message: 'Unknown internal message', + type: "error", + message: "Unknown internal message", location: { file: args.entrypoint, line: 0, column: 0, - length: 0 - } + length: 0, + }, }); } } @@ -84,10 +95,10 @@ export function check(args: { project: VirtualFileSystem, entrypoint: string }): if (items.length > 0) { return { ok: false, - messages: items + messages: items, }; } return { - ok: true + ok: true, }; } diff --git a/src/config/features.ts b/src/config/features.ts index 210201b1e..8b615a173 100644 --- a/src/config/features.ts +++ b/src/config/features.ts @@ -3,19 +3,19 @@ import { CompilerContext, createContextStore } from "../context"; const featureStore = createContextStore(); export function enabledInline(ctx: CompilerContext) { - return featureEnabled(ctx, 'inline'); + return featureEnabled(ctx, "inline"); } export function enabledDebug(ctx: CompilerContext) { - return featureEnabled(ctx, 'debug'); + return featureEnabled(ctx, "debug"); } export function enabledMasterchain(ctx: CompilerContext) { - return featureEnabled(ctx, 'masterchain'); + return featureEnabled(ctx, "masterchain"); } export function enabledExternals(ctx: CompilerContext) { - return featureEnabled(ctx, 'external'); + return featureEnabled(ctx, "external"); } export function featureEnabled(ctx: CompilerContext, key: string) { @@ -23,5 +23,5 @@ export function featureEnabled(ctx: CompilerContext, key: string) { } export function featureEnable(ctx: CompilerContext, key: string) { - return featureStore.set(ctx, key, true) -} \ No newline at end of file + return featureStore.set(ctx, key, true); +} diff --git a/src/config/parseConfig.ts b/src/config/parseConfig.ts index 31ea905b9..0d48645f7 100644 --- a/src/config/parseConfig.ts +++ b/src/config/parseConfig.ts @@ -1,25 +1,34 @@ import { z } from "zod"; -const optionsSchema = z.object({ - debug: z.boolean().optional(), - masterchain: z.boolean().optional(), - external: z.boolean().optional(), - experimental: z.object({ - inline: z.boolean().optional() - }).strict().optional() -}).strict(); +const optionsSchema = z + .object({ + debug: z.boolean().optional(), + masterchain: z.boolean().optional(), + external: z.boolean().optional(), + experimental: z + .object({ + inline: z.boolean().optional(), + }) + .strict() + .optional(), + }) + .strict(); -const projectSchema = z.object({ - name: z.string(), - path: z.string(), - output: z.string(), - options: optionsSchema.optional(), -}).strict(); +const projectSchema = z + .object({ + name: z.string(), + path: z.string(), + output: z.string(), + options: optionsSchema.optional(), + }) + .strict(); -const configSchema = z.object({ - $schema: z.string().optional(), - projects: z.array(projectSchema) -}).strict(); +const configSchema = z + .object({ + $schema: z.string().optional(), + projects: z.array(projectSchema), + }) + .strict(); export type Config = z.infer; export type ConfigProject = z.infer; diff --git a/src/context.ts b/src/context.ts index b0fd6c0e3..736729104 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,5 +1,4 @@ export class CompilerContext { - readonly shared: { [key: symbol]: object } = {}; constructor(args: { shared: { [key: symbol]: object } } = { shared: {} }) { @@ -15,7 +14,7 @@ export class CompilerContext { } sh[key] = value; return new CompilerContext({ shared: { ...this.shared, [store]: sh } }); - } + }; } export function createContextStore() { @@ -41,6 +40,6 @@ export function createContextStore() { }, set(ctx: CompilerContext, key: string | number, v: T) { return ctx.addShared(symbol, key, v); - } - } -} \ No newline at end of file + }, + }; +} diff --git a/src/errors.ts b/src/errors.ts index e6bdc9b9e..2d3db11be 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -12,4 +12,4 @@ export class TactSyntaxError extends TactSourceError { constructor(message: string, ref: ASTRef) { super(message, ref); } -} \ No newline at end of file +} diff --git a/src/func/funcCompile.spec.ts b/src/func/funcCompile.spec.ts index 0e5a44492..e8661f8af 100644 --- a/src/func/funcCompile.spec.ts +++ b/src/func/funcCompile.spec.ts @@ -1,19 +1,29 @@ -import fs from 'fs'; -import path from 'path'; -import { consoleLogger } from '../logger'; -import { funcCompile } from './funcCompile'; -import files from '../imports/stdlib'; +import fs from "fs"; +import path from "path"; +import { consoleLogger } from "../logger"; +import { funcCompile } from "./funcCompile"; +import files from "../imports/stdlib"; -describe('funcCompile', () => { - it('should compile small contract', async () => { - const source = fs.readFileSync(path.resolve(__dirname, '__testdata__', 'small.fc'), 'utf8'); +describe("funcCompile", () => { + it("should compile small contract", async () => { + const source = fs.readFileSync( + path.resolve(__dirname, "__testdata__", "small.fc"), + "utf8", + ); const res = await funcCompile({ - entries: ['/stdlib.fc', '/small.fc'], - sources: [{ - path: '/stdlib.fc', - content: Buffer.from(files['stdlib.fc']!, 'base64').toString() - }, { path: '/small.fc', content: source }], logger: consoleLogger + entries: ["/stdlib.fc", "/small.fc"], + sources: [ + { + path: "/stdlib.fc", + content: Buffer.from( + files["stdlib.fc"]!, + "base64", + ).toString(), + }, + { path: "/small.fc", content: source }, + ], + logger: consoleLogger, }); expect(res.ok).toBe(true); }); -}); \ No newline at end of file +}); diff --git a/src/func/funcCompile.ts b/src/func/funcCompile.ts index cdbe17c5b..857bb6730 100644 --- a/src/func/funcCompile.ts +++ b/src/func/funcCompile.ts @@ -3,10 +3,10 @@ import { errorToString } from "../utils/errorToString"; // Wasm Imports // eslint-disable-next-line @typescript-eslint/no-var-requires -const CompilerModule = require('./funcfiftlib.js'); +const CompilerModule = require("./funcfiftlib.js"); // eslint-disable-next-line @typescript-eslint/no-var-requires -const FuncFiftLibWasm = require('./funcfiftlib.wasm.js').FuncFiftLibWasm; -const WasmBinary = Buffer.from(FuncFiftLibWasm, 'base64'); +const FuncFiftLibWasm = require("./funcfiftlib.wasm.js").FuncFiftLibWasm; +const WasmBinary = Buffer.from(FuncFiftLibWasm, "base64"); type Pointer = unknown; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -19,46 +19,54 @@ const writeToCString = (mod: any, data: string): Pointer => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const writeToCStringPtr = (mod: any, str: string, ptr: any) => { const allocated = writeToCString(mod, str); - mod.setValue(ptr, allocated, '*'); + mod.setValue(ptr, allocated, "*"); return allocated; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any -const readFromCString = (mod: any, pointer: Pointer): string => mod.UTF8ToString(pointer); +const readFromCString = (mod: any, pointer: Pointer): string => + mod.UTF8ToString(pointer); export function cutFirstLine(src: string) { - return src.slice(src.indexOf('\n') + 1); + return src.slice(src.indexOf("\n") + 1); } -export type FuncCompilationResult = { - ok: false, - log: string, - fift: string | null, - output: Buffer | null -} | { - ok: true, - log: string, - fift: string, - output: Buffer -} - -type CompileResult = { - status: "error", - message: string -} | { - status: "ok", - codeBoc: string, - fiftCode: string, - warnings: string -}; - -export async function funcCompile(args: { entries: string[], sources: { path: string, content: string }[], logger: TactLogger }): Promise { - +export type FuncCompilationResult = + | { + ok: false; + log: string; + fift: string | null; + output: Buffer | null; + } + | { + ok: true; + log: string; + fift: string; + output: Buffer; + }; + +type CompileResult = + | { + status: "error"; + message: string; + } + | { + status: "ok"; + codeBoc: string; + fiftCode: string; + warnings: string; + }; + +export async function funcCompile(args: { + entries: string[]; + sources: { path: string; content: string }[]; + logger: TactLogger; +}): Promise { // Parameters const files: string[] = args.entries; const configStr = JSON.stringify({ sources: files, - optLevel: 2 // compileConfig.optLevel || 2 + optLevel: 2, // compileConfig.optLevel || 2 }); // Pointer tracking @@ -74,67 +82,106 @@ export async function funcCompile(args: { entries: string[], sources: { path: st }; // Create module - const logs: string[] = [] + const logs: string[] = []; // eslint-disable-next-line @typescript-eslint/no-explicit-any - const mod = await CompilerModule({ wasmBinary: WasmBinary, printErr: (e: any) => { logs.push(e); } }); + const mod = await CompilerModule({ + wasmBinary: WasmBinary, + printErr: (e: any) => { + logs.push(e); + }, + }); // Execute try { - // Write config const configPointer = trackPointer(writeToCString(mod, configStr)); // FS emulation callback // eslint-disable-next-line @typescript-eslint/no-explicit-any - const callbackPtr = trackFunctionPointer(mod.addFunction((_kind: any, _data: any, contents: any, error: any) => { - const kind: string = readFromCString(mod, _kind); - const data: string = readFromCString(mod, _data); - if (kind === 'realpath') { - allocatedPointers.push(writeToCStringPtr(mod, data, contents)); - } else if (kind === 'source') { - try { - const fl = args.sources.find((v) => v.path === data); - if (!fl) { - throw Error('File not found: ' + data) + const callbackPtr = trackFunctionPointer( + mod.addFunction( + (_kind: any, _data: any, contents: any, error: any) => { + const kind: string = readFromCString(mod, _kind); + const data: string = readFromCString(mod, _data); + if (kind === "realpath") { + allocatedPointers.push( + writeToCStringPtr(mod, data, contents), + ); + } else if (kind === "source") { + try { + const fl = args.sources.find( + (v) => v.path === data, + ); + if (!fl) { + throw Error("File not found: " + data); + } + allocatedPointers.push( + writeToCStringPtr(mod, fl.content, contents), + ); + } catch (err) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const e = err as any; + allocatedPointers.push( + writeToCStringPtr( + mod, + "message" in e ? e.message : e.toString(), + error, + ), + ); + } + } else { + allocatedPointers.push( + writeToCStringPtr( + mod, + "Unknown callback kind " + kind, + error, + ), + ); } - allocatedPointers.push(writeToCStringPtr(mod, fl.content, contents)); - } catch (err) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const e = err as any; - allocatedPointers.push(writeToCStringPtr(mod, 'message' in e ? e.message : e.toString(), error)); - } - } else { - allocatedPointers.push(writeToCStringPtr(mod, 'Unknown callback kind ' + kind, error)); - } - }, 'viiii')); + }, + "viiii", + ), + ); // Execute - const resultPointer = trackPointer(mod._func_compile(configPointer, callbackPtr)); + const resultPointer = trackPointer( + mod._func_compile(configPointer, callbackPtr), + ); const retJson = readFromCString(mod, resultPointer); const result = JSON.parse(retJson) as CompileResult; - const msg = logs.join('\n'); + const msg = logs.join("\n"); - if (result.status === 'error') { + if (result.status === "error") { return { ok: false, - log: logs.length > 0 ? msg : (result.message ? result.message : 'Unknown error'), + log: + logs.length > 0 + ? msg + : result.message + ? result.message + : "Unknown error", fift: null, - output: null + output: null, }; - } else if (result.status === 'ok') { + } else if (result.status === "ok") { return { ok: true, - log: logs.length > 0 ? msg : (result.warnings ? result.warnings : ''), - fift: cutFirstLine(result.fiftCode.replaceAll('\\n', '\n')), - output: Buffer.from(result.codeBoc, 'base64') + log: + logs.length > 0 + ? msg + : result.warnings + ? result.warnings + : "", + fift: cutFirstLine(result.fiftCode.replaceAll("\\n", "\n")), + output: Buffer.from(result.codeBoc, "base64"), }; } else { - throw Error('Unexpected compiler response'); + throw Error("Unexpected compiler response"); } } catch (e) { args.logger.error(errorToString(e)); - throw Error('Unexpected compiler response'); + throw Error("Unexpected compiler response"); } finally { for (const i of allocatedFunctions) { mod.removeFunction(i); @@ -143,4 +190,4 @@ export async function funcCompile(args: { entries: string[], sources: { path: st mod._free(i); } } -} \ No newline at end of file +} diff --git a/src/generator/Writer.ts b/src/generator/Writer.ts index fa5559955..07a0b9138 100644 --- a/src/generator/Writer.ts +++ b/src/generator/Writer.ts @@ -3,17 +3,20 @@ import { trimIndent } from "../utils/text"; import { topologicalSort } from "../utils/utils"; import { Writer } from "../utils/Writer"; -type Flag = 'inline' | 'impure' | 'inline_ref'; - -type Body = { - kind: 'generic', - code: string -} | { - kind: 'asm', - code: string -} | { - kind: 'skip' -} +type Flag = "inline" | "impure" | "inline_ref"; + +type Body = + | { + kind: "generic"; + code: string; + } + | { + kind: "asm"; + code: string; + } + | { + kind: "skip"; + }; export type WrittenFunction = { name: string; @@ -26,7 +29,6 @@ export type WrittenFunction = { }; export class WriterContext { - readonly ctx: CompilerContext; #name: string; #functions: Map = new Map(); @@ -66,7 +68,6 @@ export class WriterContext { // extract(debug: boolean = false) { - // Check dependencies const missing = new Map(); for (const f of this.#functions.values()) { @@ -77,12 +78,15 @@ export class WriterContext { } else { missing.set(d, [...missing.get(d)!, f.name]); } - } } } if (missing.size > 0) { - throw new Error(`Functions ${Array.from(missing.keys()).map((v) => `"${v}"`).join(', ')} wasn't rendered`); + throw new Error( + `Functions ${Array.from(missing.keys()) + .map((v) => `"${v}"`) + .join(", ")} wasn't rendered`, + ); } // All functions @@ -97,13 +101,15 @@ export class WriterContext { for (const d of f.depends) { visit(d); } - } - visit('$main'); + }; + visit("$main"); all = all.filter((v) => used.has(v.name)); } // Sort functions - const sorted = topologicalSort(all, (f) => Array.from(f.depends).map((v) => this.#functions.get(v)!)); + const sorted = topologicalSort(all, (f) => + Array.from(f.depends).map((v) => this.#functions.get(v)!), + ); return sorted; } @@ -114,14 +120,13 @@ export class WriterContext { skip(name: string) { this.fun(name, () => { - this.signature(''); - this.context('stdlib'); - this.#pendingCode = { kind: 'skip' }; + this.signature(""); + this.context("stdlib"); + this.#pendingCode = { kind: "skip" }; }); } fun(name: string, handler: () => void) { - // // Duplicates check // @@ -183,7 +188,7 @@ export class WriterContext { const code = this.#pendingCode; const comment = this.#pendingComment; const context = this.#pendingContext; - if (!signature && name !== '$main') { + if (!signature && name !== "$main") { throw new Error(`Function ${name} signature not set`); } if (!code) { @@ -202,16 +207,16 @@ export class WriterContext { signature, flags, comment, - context + context, }); } asm(code: string) { if (this.#pendingName) { this.#pendingCode = { - kind: 'asm', - code - } + kind: "asm", + code, + }; } else { throw new Error(`ASM can be set only inside function`); } @@ -224,13 +229,13 @@ export class WriterContext { this.#pendingWriter = new Writer(); handler(); this.#pendingCode = { - kind: 'generic', - code: this.#pendingWriter!.end() - } + kind: "generic", + code: this.#pendingWriter!.end(), + }; } main(handler: () => void) { - this.fun('$main', () => { + this.fun("$main", () => { this.body(() => { handler(); }); @@ -288,11 +293,11 @@ export class WriterContext { this.#pendingWriter!.inIndent(handler); }; - append(src: string = '') { + append(src: string = "") { this.#pendingWriter!.append(src); } - write(src: string = '') { + write(src: string = "") { this.#pendingWriter!.write(src); } @@ -318,4 +323,4 @@ export class WriterContext { } this.#rendered.add(key); } -} \ No newline at end of file +} diff --git a/src/generator/createABI.ts b/src/generator/createABI.ts index 1dace15cc..ff48304d2 100644 --- a/src/generator/createABI.ts +++ b/src/generator/createABI.ts @@ -7,7 +7,6 @@ import { getAllTypes } from "../types/resolveDescriptors"; import { getAllErrors } from "../types/resolveErrors"; export function createABI(ctx: CompilerContext, name: string): ContractABI { - const allTypes = Object.values(getAllTypes(ctx)); // Contract @@ -15,18 +14,18 @@ export function createABI(ctx: CompilerContext, name: string): ContractABI { if (!contract) { throw Error(`Contract ${name} not found`); } - if (contract.kind !== 'contract') { - throw Error('Not a contract'); + if (contract.kind !== "contract") { + throw Error("Not a contract"); } // Structs const types: ABIType[] = []; for (const t of allTypes) { - if (t.kind === 'struct') { + if (t.kind === "struct") { types.push({ name: t.name, header: t.header, - fields: t.fields.map((v) => v.abi) + fields: t.fields.map((v) => v.abi), }); } } @@ -34,79 +33,79 @@ export function createABI(ctx: CompilerContext, name: string): ContractABI { // // Receivers const receivers: ABIReceiver[] = []; for (const r of Object.values(contract.receivers)) { - if (r.selector.kind === 'internal-binary') { + if (r.selector.kind === "internal-binary") { receivers.push({ - receiver: 'internal', + receiver: "internal", message: { - kind: 'typed', - type: r.selector.type - } + kind: "typed", + type: r.selector.type, + }, }); - } else if (r.selector.kind === 'external-binary') { + } else if (r.selector.kind === "external-binary") { receivers.push({ - receiver: 'external', + receiver: "external", message: { - kind: 'typed', - type: r.selector.type - } + kind: "typed", + type: r.selector.type, + }, }); - } else if (r.selector.kind === 'internal-empty') { + } else if (r.selector.kind === "internal-empty") { receivers.push({ - receiver: 'internal', + receiver: "internal", message: { - kind: 'empty' - } + kind: "empty", + }, }); - } else if (r.selector.kind === 'external-empty') { + } else if (r.selector.kind === "external-empty") { receivers.push({ - receiver: 'external', + receiver: "external", message: { - kind: 'empty' - } + kind: "empty", + }, }); - } else if (r.selector.kind === 'internal-comment') { + } else if (r.selector.kind === "internal-comment") { receivers.push({ - receiver: 'internal', + receiver: "internal", message: { - kind: 'text', - text: r.selector.comment - } + kind: "text", + text: r.selector.comment, + }, }); - } else if (r.selector.kind === 'external-comment') { + } else if (r.selector.kind === "external-comment") { receivers.push({ - receiver: 'external', + receiver: "external", message: { - kind: 'text', - text: r.selector.comment - } + kind: "text", + text: r.selector.comment, + }, }); - } else if (r.selector.kind === 'internal-comment-fallback') { + } else if (r.selector.kind === "internal-comment-fallback") { receivers.push({ - receiver: 'internal', + receiver: "internal", message: { - kind: 'text' - } + kind: "text", + }, }); - } else if (r.selector.kind === 'external-comment-fallback') { + } else if (r.selector.kind === "external-comment-fallback") { receivers.push({ - receiver: 'external', + receiver: "external", message: { - kind: 'text' - } + kind: "text", + }, }); - } else if (r.selector.kind === 'internal-fallback') { + } else if (r.selector.kind === "internal-fallback") { receivers.push({ - receiver: 'internal', + receiver: "internal", message: { - kind: 'any' - } + kind: "any", + }, }); - } else if (r.selector.kind === 'external-fallback') { + } else if (r.selector.kind === "external-fallback") { receivers.push({ - receiver: 'external', + receiver: "external", message: { - kind: 'any' - } + kind: "any", + }, }); } } @@ -117,38 +116,47 @@ export function createABI(ctx: CompilerContext, name: string): ContractABI { if (f.isGetter) { getters.push({ name: f.name, - arguments: f.args.map((v) => ({ name: v.name, type: createABITypeRefFromTypeRef(v.type, v.ref) })), - returnType: f.returns.kind !== 'void' ? createABITypeRefFromTypeRef(f.returns, f.ast.ref) : null + arguments: f.args.map((v) => ({ + name: v.name, + type: createABITypeRefFromTypeRef(v.type, v.ref), + })), + returnType: + f.returns.kind !== "void" + ? createABITypeRefFromTypeRef(f.returns, f.ast.ref) + : null, }); } } // Errors const errors: { [key: string]: { message: string } } = {}; - errors['2'] = { message: 'Stack undeflow' }; - errors['3'] = { message: 'Stack overflow' }; - errors['4'] = { message: 'Integer overflow' }; - errors['5'] = { message: 'Integer out of expected range' }; - errors['6'] = { message: 'Invalid opcode' }; - errors['7'] = { message: 'Type check error' }; - errors['8'] = { message: 'Cell overflow' }; - errors['9'] = { message: 'Cell underflow' }; - errors['10'] = { message: 'Dictionary error' }; - errors['13'] = { message: 'Out of gas error' }; - errors['32'] = { message: 'Method ID not found' }; - errors['34'] = { message: 'Action is invalid or not supported' }; - errors['37'] = { message: 'Not enough TON' }; - errors['38'] = { message: 'Not enough extra-currencies' }; + errors["2"] = { message: "Stack undeflow" }; + errors["3"] = { message: "Stack overflow" }; + errors["4"] = { message: "Integer overflow" }; + errors["5"] = { message: "Integer out of expected range" }; + errors["6"] = { message: "Invalid opcode" }; + errors["7"] = { message: "Type check error" }; + errors["8"] = { message: "Cell overflow" }; + errors["9"] = { message: "Cell underflow" }; + errors["10"] = { message: "Dictionary error" }; + errors["13"] = { message: "Out of gas error" }; + errors["32"] = { message: "Method ID not found" }; + errors["34"] = { message: "Action is invalid or not supported" }; + errors["37"] = { message: "Not enough TON" }; + errors["38"] = { message: "Not enough extra-currencies" }; for (const e of Object.values(contractErrors)) { errors[e.id] = { message: e.message }; } const codeErrors = getAllErrors(ctx); for (const c of codeErrors) { - errors[c.id + ''] = { message: c.value }; + errors[c.id + ""] = { message: c.value }; } // Interfaces - const interfaces = ['org.ton.introspection.v0', ...getSupportedInterfaces(contract, ctx)]; + const interfaces = [ + "org.ton.introspection.v0", + ...getSupportedInterfaces(contract, ctx), + ]; return { name: contract.name, @@ -156,6 +164,6 @@ export function createABI(ctx: CompilerContext, name: string): ContractABI { receivers, getters, errors, - interfaces + interfaces, } as object; -} \ No newline at end of file +} diff --git a/src/generator/emitter/createPadded.ts b/src/generator/emitter/createPadded.ts index 4614839e5..eff2f620e 100644 --- a/src/generator/emitter/createPadded.ts +++ b/src/generator/emitter/createPadded.ts @@ -1,5 +1,8 @@ import { trimIndent } from "../../utils/text"; export function createPadded(src: string) { - return trimIndent(src).split('\n').map((v) => ' '.repeat(4) + v).join('\n'); -} \ No newline at end of file + return trimIndent(src) + .split("\n") + .map((v) => " ".repeat(4) + v) + .join("\n"); +} diff --git a/src/generator/emitter/emit.ts b/src/generator/emitter/emit.ts index 4ac0e08de..65d4db510 100644 --- a/src/generator/emitter/emit.ts +++ b/src/generator/emitter/emit.ts @@ -1,15 +1,14 @@ import { Maybe } from "@ton/core/dist/utils/maybe"; -import { trimIndent } from "../../utils/text" +import { trimIndent } from "../../utils/text"; import { WrittenFunction } from "../Writer"; import { createPadded } from "./createPadded"; export function emit(args: { - header?: Maybe, - functions?: Maybe + header?: Maybe; + functions?: Maybe; }) { - // Emut header - let res = ''; + let res = ""; if (args.header) { res = trimIndent(args.header); } @@ -17,33 +16,32 @@ export function emit(args: { // Emit functions if (args.functions) { for (const f of args.functions) { - if (f.name === '$main') { + if (f.name === "$main") { continue; } else { - if (res !== '') { - res += '\n\n'; + if (res !== "") { + res += "\n\n"; } if (f.comment) { - for (const s of f.comment.split('\n')) { + for (const s of f.comment.split("\n")) { res += `;; ${s}\n`; } } - if (f.code.kind === 'generic') { - + if (f.code.kind === "generic") { let sig = f.signature; - if (f.flags.has('impure')) { + if (f.flags.has("impure")) { sig = `${sig} impure`; } - if (f.flags.has('inline')) { + if (f.flags.has("inline")) { sig = `${sig} inline`; } else { sig = `${sig} inline_ref`; } res += `${sig} {\n${createPadded(f.code.code)}\n}`; - } else if (f.code.kind === 'asm') { + } else if (f.code.kind === "asm") { let sig = f.signature; - if (f.flags.has('impure')) { + if (f.flags.has("impure")) { sig = `${sig} impure`; } res += `${sig} ${f.code.code};`; @@ -54,17 +52,17 @@ export function emit(args: { } // Emit main - const m = args.functions.find((v) => v.name === '$main'); + const m = args.functions.find((v) => v.name === "$main"); if (m) { - if (m.code.kind !== 'generic') { + if (m.code.kind !== "generic") { throw new Error(`Main function should have generic body`); } - if (res !== '') { - res += '\n\n'; + if (res !== "") { + res += "\n\n"; } res += m.code.code; } } return res; -} \ No newline at end of file +} diff --git a/src/generator/intrinsics/tryExpressionIntrinsics.ts b/src/generator/intrinsics/tryExpressionIntrinsics.ts index 1714d3701..61c1bd6e7 100644 --- a/src/generator/intrinsics/tryExpressionIntrinsics.ts +++ b/src/generator/intrinsics/tryExpressionIntrinsics.ts @@ -4,25 +4,34 @@ import { getExpType } from "../../types/resolveExpression"; import { WriterContext } from "../Writer"; import { writeComment } from "../writers/writeConstant"; -export function tryExpressionIntrinsics(exp: ASTExpression, ctx: WriterContext): string | null { - +export function tryExpressionIntrinsics( + exp: ASTExpression, + ctx: WriterContext, +): string | null { // Calls instrinsics - if (exp.kind === 'op_call') { + if (exp.kind === "op_call") { const sourceType = getExpType(ctx.ctx, exp.src); - if (sourceType.kind === 'ref' && sourceType.name === 'String' && !sourceType.optional) { - + if ( + sourceType.kind === "ref" && + sourceType.name === "String" && + !sourceType.optional + ) { // // Handle String.asComment() // - if (exp.args.length === 0 && exp.name === 'asComment') { + if (exp.args.length === 0 && exp.name === "asComment") { let constString: string | null = null; // Try to resolve constant value try { - const res = resolveConstantValue(sourceType, exp.src, ctx.ctx); - if (typeof res !== 'string') { - throw new Error('Expected string'); + const res = resolveConstantValue( + sourceType, + exp.src, + ctx.ctx, + ); + if (typeof res !== "string") { + throw new Error("Expected string"); } constString = res; } catch (e) { @@ -40,4 +49,4 @@ export function tryExpressionIntrinsics(exp: ASTExpression, ctx: WriterContext): } return null; -} \ No newline at end of file +} diff --git a/src/generator/writeProgram.ts b/src/generator/writeProgram.ts index b41836ae8..b6f0f29a3 100644 --- a/src/generator/writeProgram.ts +++ b/src/generator/writeProgram.ts @@ -1,8 +1,18 @@ import { CompilerContext } from "../context"; import { getAllocation, getSortedTypes } from "../storage/resolveAllocation"; -import { getAllStaticFunctions, getAllTypes, toBounced } from "../types/resolveDescriptors"; +import { + getAllStaticFunctions, + getAllTypes, + toBounced, +} from "../types/resolveDescriptors"; import { WriterContext, WrittenFunction } from "./Writer"; -import { writeBouncedParser, writeOptionalParser, writeOptionalSerializer, writeParser, writeSerializer } from "./writers/writeSerialization"; +import { + writeBouncedParser, + writeOptionalParser, + writeOptionalSerializer, + writeParser, + writeSerializer, +} from "./writers/writeSerialization"; import { writeStdlib } from "./writers/writeStdlib"; import { writeAccessors } from "./writers/writeAccessors"; import { ContractABI } from "@ton/core"; @@ -10,13 +20,21 @@ import { writeFunction } from "./writers/writeFunction"; import { calculateIPFSlink } from "../utils/calculateIPFSlink"; import { getRawAST } from "../grammar/store"; import { emit } from "./emitter/emit"; -import { writeInit, writeMainContract, writeStorageOps } from "./writers/writeContract"; +import { + writeInit, + writeMainContract, + writeStorageOps, +} from "./writers/writeContract"; import { initId } from "./writers/id"; import { idToHex } from "../utils/idToHex"; import { trimIndent } from "../utils/text"; -export async function writeProgram(ctx: CompilerContext, abiSrc: ContractABI, basename: string, debug: boolean = false) { - +export async function writeProgram( + ctx: CompilerContext, + abiSrc: ContractABI, + basename: string, + debug: boolean = false, +) { // // Load ABI (required for generator) // @@ -36,7 +54,7 @@ export async function writeProgram(ctx: CompilerContext, abiSrc: ContractABI, ba // Emit files // - const files: { name: string, code: string }[] = []; + const files: { name: string; code: string }[] = []; const imported: string[] = []; // @@ -51,24 +69,24 @@ export async function writeProgram(ctx: CompilerContext, abiSrc: ContractABI, ba headers.push(``); // const sortedHeaders = [...functions].sort((a, b) => a.name.localeCompare(b.name)); for (const f of functions) { - if (f.code.kind === 'generic' && f.signature) { + if (f.code.kind === "generic" && f.signature) { headers.push(`;; ${f.name}`); let sig = f.signature; - if (f.flags.has('impure')) { - sig = sig + ' impure'; + if (f.flags.has("impure")) { + sig = sig + " impure"; } - if (f.flags.has('inline')) { - sig = sig + ' inline'; + if (f.flags.has("inline")) { + sig = sig + " inline"; } else { - sig = sig + ' inline_ref'; + sig = sig + " inline_ref"; } - headers.push(sig + ';'); - headers.push(''); + headers.push(sig + ";"); + headers.push(""); } } files.push({ - name: basename + '.headers.fc', - code: headers.join('\n') + name: basename + ".headers.fc", + code: headers.join("\n"), }); // @@ -82,19 +100,19 @@ export async function writeProgram(ctx: CompilerContext, abiSrc: ContractABI, ba global int __tact_randomized; `); - const stdlibFunctions = tryExtractModule(functions, 'stdlib', []); + const stdlibFunctions = tryExtractModule(functions, "stdlib", []); if (stdlibFunctions) { - imported.push('stdlib'); + imported.push("stdlib"); } const stdlib = emit({ header: stdlibHeader, - functions: stdlibFunctions + functions: stdlibFunctions, }); files.push({ - name: basename + '.stdlib.fc', - code: stdlib + name: basename + ".stdlib.fc", + code: stdlib, }); // @@ -103,53 +121,65 @@ export async function writeProgram(ctx: CompilerContext, abiSrc: ContractABI, ba const nativeSources = getRawAST(ctx).funcSources; if (nativeSources.length > 0) { - imported.push('native'); + imported.push("native"); files.push({ - name: basename + '.native.fc', - code: emit({ header: [...nativeSources.map((v) => v.code)].join('\n\n') }) + name: basename + ".native.fc", + code: emit({ + header: [...nativeSources.map((v) => v.code)].join("\n\n"), + }), }); } - // + // // constants // - const constantsFunctions = tryExtractModule(functions, 'constants', imported); + const constantsFunctions = tryExtractModule( + functions, + "constants", + imported, + ); if (constantsFunctions) { - imported.push('constants'); + imported.push("constants"); files.push({ - name: basename + '.constants.fc', - code: emit({ functions: constantsFunctions }) + name: basename + ".constants.fc", + code: emit({ functions: constantsFunctions }), }); } - // + // // storage // const emitedTypes: string[] = []; const types = getSortedTypes(ctx); for (const t of types) { - const ffs: WrittenFunction[] = []; - if (t.kind === 'struct' || t.kind === 'contract' || t.kind == 'trait') { - const typeFunctions = tryExtractModule(functions, 'type:' + t.name, imported); + if (t.kind === "struct" || t.kind === "contract" || t.kind == "trait") { + const typeFunctions = tryExtractModule( + functions, + "type:" + t.name, + imported, + ); if (typeFunctions) { - imported.push('type:' + t.name); + imported.push("type:" + t.name); ffs.push(...typeFunctions); } } - if (t.kind === 'contract') { - const typeFunctions = tryExtractModule(functions, 'type:' + t.name + '$init', imported); + if (t.kind === "contract") { + const typeFunctions = tryExtractModule( + functions, + "type:" + t.name + "$init", + imported, + ); if (typeFunctions) { - imported.push('type:' + t.name + '$init'); + imported.push("type:" + t.name + "$init"); ffs.push(...typeFunctions); } } if (ffs.length > 0) { - const header: string[] = []; - header.push(';;'); + header.push(";;"); header.push(`;; Type: ${t.name}`); if (t.header !== null) { header.push(`;; Header: 0x${idToHex(t.header)}`); @@ -157,18 +187,20 @@ export async function writeProgram(ctx: CompilerContext, abiSrc: ContractABI, ba if (t.tlb) { header.push(`;; TLB: ${t.tlb}`); } - header.push(';;'); - - emitedTypes.push(emit({ - functions: ffs, - header: header.join('\n') - })); + header.push(";;"); + + emitedTypes.push( + emit({ + functions: ffs, + header: header.join("\n"), + }), + ); } } if (emitedTypes.length > 0) { files.push({ - name: basename + '.storage.fc', - code: [...emitedTypes].join('\n\n') + name: basename + ".storage.fc", + code: [...emitedTypes].join("\n\n"), }); } @@ -183,40 +215,43 @@ export async function writeProgram(ctx: CompilerContext, abiSrc: ContractABI, ba // // Remaining - // + // const remainingFunctions = tryExtractModule(functions, null, imported); const header: string[] = []; - header.push('#pragma version =0.4.3;'); - header.push('#pragma allow-post-modification;'); - header.push('#pragma compute-asm-ltr;'); - header.push(''); + header.push("#pragma version =0.4.3;"); + header.push("#pragma allow-post-modification;"); + header.push("#pragma compute-asm-ltr;"); + header.push(""); for (const i of files.map((v) => `#include "${v.name}";`)) { header.push(i); } - header.push(''); - header.push(';;'); + header.push(""); + header.push(";;"); header.push(`;; Contract ${abiSrc.name} functions`); - header.push(';;'); - header.push(''); + header.push(";;"); + header.push(""); const code = emit({ - header: header.join('\n'), - functions: remainingFunctions + header: header.join("\n"), + functions: remainingFunctions, }); files.push({ - name: basename + '.code.fc', - code + name: basename + ".code.fc", + code, }); return { - entrypoint: basename + '.code.fc', + entrypoint: basename + ".code.fc", files, - abi + abi, }; } -function tryExtractModule(functions: WrittenFunction[], context: string | null, imported: string[]): WrittenFunction[] | null { - +function tryExtractModule( + functions: WrittenFunction[], + context: string | null, + imported: string[], +): WrittenFunction[] | null { // Put to map const maps = new Map(); for (const f of functions) { @@ -225,7 +260,7 @@ function tryExtractModule(functions: WrittenFunction[], context: string | null, // Extract functions of a context const ctxFunctions: WrittenFunction[] = functions - .filter((v) => v.code.kind !== 'skip') + .filter((v) => v.code.kind !== "skip") .filter((v) => { if (context) { return v.context === context; @@ -257,11 +292,15 @@ function tryExtractModule(functions: WrittenFunction[], context: string | null, return ctxFunctions; } -function writeAll(ctx: CompilerContext, wctx: WriterContext, name: string, abiLink: string) { - +function writeAll( + ctx: CompilerContext, + wctx: WriterContext, + name: string, + abiLink: string, +) { // Load all types const allTypes = Object.values(getAllTypes(ctx)); - const contracts = allTypes.filter((v) => v.kind === 'contract'); + const contracts = allTypes.filter((v) => v.kind === "contract"); const c = contracts.find((v) => v.name === name); if (!c) { throw Error(`Contract ${name} not found`); @@ -273,27 +312,45 @@ function writeAll(ctx: CompilerContext, wctx: WriterContext, name: string, abiLi // Serializators const sortedTypes = getSortedTypes(ctx); for (const t of sortedTypes) { - if (t.kind === 'contract' || t.kind === 'struct') { + if (t.kind === "contract" || t.kind === "struct") { const allocation = getAllocation(ctx, t.name); const allocationBounced = getAllocation(ctx, toBounced(t.name)); - writeSerializer(t.name, t.kind === 'contract', allocation, t.origin, wctx); + writeSerializer( + t.name, + t.kind === "contract", + allocation, + t.origin, + wctx, + ); writeOptionalSerializer(t.name, t.origin, wctx); - writeParser(t.name, t.kind === 'contract', allocation, t.origin, wctx); + writeParser( + t.name, + t.kind === "contract", + allocation, + t.origin, + wctx, + ); writeOptionalParser(t.name, t.origin, wctx); - writeBouncedParser(t.name, t.kind === 'contract', allocationBounced, t.origin, wctx); + writeBouncedParser( + t.name, + t.kind === "contract", + allocationBounced, + t.origin, + wctx, + ); } } // Accessors for (const t of allTypes) { - if (t.kind === 'contract' || t.kind === 'struct') { + if (t.kind === "contract" || t.kind === "struct") { writeAccessors(t, t.origin, wctx); } } // Init serializers for (const t of sortedTypes) { - if (t.kind === 'contract' && t.init) { + if (t.kind === "contract" && t.init) { const allocation = getAllocation(ctx, initId(t.name)); writeSerializer(initId(t.name), true, allocation, t.origin, wctx); writeParser(initId(t.name), false, allocation, t.origin, wctx); @@ -302,7 +359,7 @@ function writeAll(ctx: CompilerContext, wctx: WriterContext, name: string, abiLi // Storage Functions for (const t of sortedTypes) { - if (t.kind === 'contract') { + if (t.kind === "contract") { writeStorageOps(t, t.origin, wctx); } } @@ -316,7 +373,8 @@ function writeAll(ctx: CompilerContext, wctx: WriterContext, name: string, abiLi // Extensions for (const c of allTypes) { - if (c.kind !== 'contract' && c.kind !== 'trait') { // We are rendering contract functions separately + if (c.kind !== "contract" && c.kind !== "trait") { + // We are rendering contract functions separately for (const f of c.functions.values()) { writeFunction(f, wctx); } @@ -325,7 +383,6 @@ function writeAll(ctx: CompilerContext, wctx: WriterContext, name: string, abiLi // Contract functions for (const c of contracts) { - // Init if (c.init) { writeInit(c, c.init, wctx); diff --git a/src/generator/writeReport.ts b/src/generator/writeReport.ts index 6d549322c..6b1307392 100644 --- a/src/generator/writeReport.ts +++ b/src/generator/writeReport.ts @@ -10,13 +10,13 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) { w.write(` # TACT Compilation Report Contract: ${pkg.name} - BOC Size: ${Buffer.from(pkg.code, 'base64').length} bytes + BOC Size: ${Buffer.from(pkg.code, "base64").length} bytes `); w.append(); // Types w.write(`# Types`); - w.write('Total Types: ' + abi.types!.length); + w.write("Total Types: " + abi.types!.length); w.append(); for (const t of abi.types!) { const tt = getType(ctx, t.name); @@ -28,7 +28,7 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) { // Get methods w.write(`# Get Methods`); - w.write('Total Get Methods: ' + abi.getters!.length); + w.write("Total Get Methods: " + abi.getters!.length); w.append(); for (const t of abi.getters!) { w.write(`## ${t.name}`); @@ -45,4 +45,4 @@ export function writeReport(ctx: CompilerContext, pkg: PackageFileFormat) { } return w.end(); -} \ No newline at end of file +} diff --git a/src/generator/writers/cast.ts b/src/generator/writers/cast.ts index b742ce3b5..4c098e455 100644 --- a/src/generator/writers/cast.ts +++ b/src/generator/writers/cast.ts @@ -3,17 +3,22 @@ import { TypeRef } from "../../types/types"; import { WriterContext } from "../Writer"; import { ops } from "./ops"; -export function cast(from: TypeRef, to: TypeRef, expression: string, ctx: WriterContext) { - if (from.kind === 'ref' && to.kind === 'ref') { +export function cast( + from: TypeRef, + to: TypeRef, + expression: string, + ctx: WriterContext, +) { + if (from.kind === "ref" && to.kind === "ref") { if (from.name !== to.name) { - throw Error('Impossible'); + throw Error("Impossible"); } if (!from.optional && to.optional) { const type = getType(ctx.ctx, from.name); - if (type.kind === 'struct') { + if (type.kind === "struct") { return `${ops.typeAsOptional(type.name, ctx)}(${expression})`; } } } return expression; -} \ No newline at end of file +} diff --git a/src/generator/writers/id.ts b/src/generator/writers/id.ts index e40ed8482..db26098bc 100644 --- a/src/generator/writers/id.ts +++ b/src/generator/writers/id.ts @@ -1,11 +1,11 @@ export function id(src: string) { - return '$' + src; + return "$" + src; } export function fn(src: string) { - return '$' + src; + return "$" + src; } export function initId(src: string) { - return src + '$init'; -} \ No newline at end of file + return src + "$init"; +} diff --git a/src/generator/writers/ops.ts b/src/generator/writers/ops.ts index 0fd2b4132..8370f4e4d 100644 --- a/src/generator/writers/ops.ts +++ b/src/generator/writers/ops.ts @@ -1,6 +1,5 @@ import { WriterContext } from "../Writer"; - function used(name: string, ctx: WriterContext) { const c = ctx.currentContext(); if (c) { @@ -10,40 +9,65 @@ function used(name: string, ctx: WriterContext) { } export const ops = { - // Type operations writer: (type: string, ctx: WriterContext) => used(`$${type}$_store`, ctx), - writerCell: (type: string, ctx: WriterContext) => used(`$${type}$_store_cell`, ctx), - writerCellOpt: (type: string, ctx: WriterContext) => used(`$${type}$_store_opt`, ctx), + writerCell: (type: string, ctx: WriterContext) => + used(`$${type}$_store_cell`, ctx), + writerCellOpt: (type: string, ctx: WriterContext) => + used(`$${type}$_store_opt`, ctx), reader: (type: string, ctx: WriterContext) => used(`$${type}$_load`, ctx), - readerBounced: (type: string, ctx: WriterContext) => used(`$${type}$_load_bounced`, ctx), - readerOpt: (type: string, ctx: WriterContext) => used(`$${type}$_load_opt`, ctx), - typeField: (type: string, name: string, ctx: WriterContext) => used(`$${type}$_get_${name}`, ctx), - typeTensorCast: (type: string, ctx: WriterContext) => used(`$${type}$_tensor_cast`, ctx), - typeNotNull: (type: string, ctx: WriterContext) => used(`$${type}$_not_null`, ctx), - typeAsOptional: (type: string, ctx: WriterContext) => used(`$${type}$_as_optional`, ctx), - typeToTuple: (type: string, ctx: WriterContext) => used(`$${type}$_to_tuple`, ctx), - typeToOptTuple: (type: string, ctx: WriterContext) => used(`$${type}$_to_opt_tuple`, ctx), - typeFromTuple: (type: string, ctx: WriterContext) => used(`$${type}$_from_tuple`, ctx), - typeFromOptTuple: (type: string, ctx: WriterContext) => used(`$${type}$_from_opt_tuple`, ctx), - typeToExternal: (type: string, ctx: WriterContext) => used(`$${type}$_to_external`, ctx), - typeToOptExternal: (type: string, ctx: WriterContext) => used(`$${type}$_to_opt_external`, ctx), - typeContsturctor: (type: string, fields: string[], ctx: WriterContext) => used(`$${type}$_constructor_${fields.join('_')}`, ctx), + readerBounced: (type: string, ctx: WriterContext) => + used(`$${type}$_load_bounced`, ctx), + readerOpt: (type: string, ctx: WriterContext) => + used(`$${type}$_load_opt`, ctx), + typeField: (type: string, name: string, ctx: WriterContext) => + used(`$${type}$_get_${name}`, ctx), + typeTensorCast: (type: string, ctx: WriterContext) => + used(`$${type}$_tensor_cast`, ctx), + typeNotNull: (type: string, ctx: WriterContext) => + used(`$${type}$_not_null`, ctx), + typeAsOptional: (type: string, ctx: WriterContext) => + used(`$${type}$_as_optional`, ctx), + typeToTuple: (type: string, ctx: WriterContext) => + used(`$${type}$_to_tuple`, ctx), + typeToOptTuple: (type: string, ctx: WriterContext) => + used(`$${type}$_to_opt_tuple`, ctx), + typeFromTuple: (type: string, ctx: WriterContext) => + used(`$${type}$_from_tuple`, ctx), + typeFromOptTuple: (type: string, ctx: WriterContext) => + used(`$${type}$_from_opt_tuple`, ctx), + typeToExternal: (type: string, ctx: WriterContext) => + used(`$${type}$_to_external`, ctx), + typeToOptExternal: (type: string, ctx: WriterContext) => + used(`$${type}$_to_opt_external`, ctx), + typeContsturctor: (type: string, fields: string[], ctx: WriterContext) => + used(`$${type}$_constructor_${fields.join("_")}`, ctx), // Contract operations - contractInit: (type: string, ctx: WriterContext) => used(`$${type}$_contract_init`, ctx), - contractInitChild: (type: string, ctx: WriterContext) => used(`$${type}$_init_child`, ctx), - contractLoad: (type: string, ctx: WriterContext) => used(`$${type}$_contract_load`, ctx), - contractStore: (type: string, ctx: WriterContext) => used(`$${type}$_contract_store`, ctx), - contractRouter: (type: string, kind: 'internal' | 'external') => `$${type}$_contract_router_${kind}`, // Not rendered as dependency + contractInit: (type: string, ctx: WriterContext) => + used(`$${type}$_contract_init`, ctx), + contractInitChild: (type: string, ctx: WriterContext) => + used(`$${type}$_init_child`, ctx), + contractLoad: (type: string, ctx: WriterContext) => + used(`$${type}$_contract_load`, ctx), + contractStore: (type: string, ctx: WriterContext) => + used(`$${type}$_contract_store`, ctx), + contractRouter: (type: string, kind: "internal" | "external") => + `$${type}$_contract_router_${kind}`, // Not rendered as dependency // Router operations - receiveEmpty: (type: string, kind: 'internal' | 'external') => `%$${type}$_${kind}_empty`, - receiveType: (type: string, kind: 'internal' | 'external', msg: string) => `$${type}$_${kind}_binary_${msg}`, - receiveAnyText: (type: string, kind: 'internal' | 'external') => `$${type}$_${kind}_any_text`, - receiveText: (type: string, kind: 'internal' | 'external', hash: string) => `$${type}$_${kind}_text_${hash}`, - receiveAny: (type: string, kind: 'internal' | 'external') => `$${type}$_${kind}_any`, - receiveTypeBounce: (type: string, msg: string) => `$${type}$_receive_binary_bounce_${msg}`, + receiveEmpty: (type: string, kind: "internal" | "external") => + `%$${type}$_${kind}_empty`, + receiveType: (type: string, kind: "internal" | "external", msg: string) => + `$${type}$_${kind}_binary_${msg}`, + receiveAnyText: (type: string, kind: "internal" | "external") => + `$${type}$_${kind}_any_text`, + receiveText: (type: string, kind: "internal" | "external", hash: string) => + `$${type}$_${kind}_text_${hash}`, + receiveAny: (type: string, kind: "internal" | "external") => + `$${type}$_${kind}_any`, + receiveTypeBounce: (type: string, msg: string) => + `$${type}$_receive_binary_bounce_${msg}`, receiveBounceAny: (type: string) => `$${type}$_receive_bounce`, // Functions @@ -51,5 +75,5 @@ export const ops = { global: (name: string) => `$global_${name}`, // Constants - str: (id: string, ctx: WriterContext) => used(`__gen_str_${id}`, ctx) -}; \ No newline at end of file + str: (id: string, ctx: WriterContext) => used(`__gen_str_${id}`, ctx), +}; diff --git a/src/generator/writers/resolveFuncFlatPack.ts b/src/generator/writers/resolveFuncFlatPack.ts index fd795ae2f..b57a40eb9 100644 --- a/src/generator/writers/resolveFuncFlatPack.ts +++ b/src/generator/writers/resolveFuncFlatPack.ts @@ -2,44 +2,57 @@ import { getType } from "../../types/resolveDescriptors"; import { TypeDescription, TypeRef } from "../../types/types"; import { WriterContext } from "../Writer"; -export function resolveFuncFlatPack(descriptor: TypeRef | TypeDescription | string, name: string, ctx: WriterContext, optional: boolean = false): string[] { - +export function resolveFuncFlatPack( + descriptor: TypeRef | TypeDescription | string, + name: string, + ctx: WriterContext, + optional: boolean = false, +): string[] { // String - if (typeof descriptor === 'string') { + if (typeof descriptor === "string") { return resolveFuncFlatPack(getType(ctx.ctx, descriptor), name, ctx); } // TypeRef - if (descriptor.kind === 'ref') { - return resolveFuncFlatPack(getType(ctx.ctx, descriptor.name), name, ctx, descriptor.optional); + if (descriptor.kind === "ref") { + return resolveFuncFlatPack( + getType(ctx.ctx, descriptor.name), + name, + ctx, + descriptor.optional, + ); } - if (descriptor.kind === 'map') { + if (descriptor.kind === "map") { return [name]; } - if (descriptor.kind === 'ref_bounced') { + if (descriptor.kind === "ref_bounced") { throw Error("Unimplemented"); } - if (descriptor.kind === 'void') { - throw Error('Void type is not allowed in function arguments: ' + name); + if (descriptor.kind === "void") { + throw Error("Void type is not allowed in function arguments: " + name); } // TypeDescription - if (descriptor.kind === 'primitive') { + if (descriptor.kind === "primitive") { return [name]; - } else if (descriptor.kind === 'struct') { + } else if (descriptor.kind === "struct") { if (optional || descriptor.fields.length === 0) { return [name]; } else { - return descriptor.fields.flatMap((v) => resolveFuncFlatPack(v.type, name + `'` + v.name, ctx)); + return descriptor.fields.flatMap((v) => + resolveFuncFlatPack(v.type, name + `'` + v.name, ctx), + ); } - } else if (descriptor.kind === 'contract') { + } else if (descriptor.kind === "contract") { if (optional || descriptor.fields.length === 0) { return [name]; } else { - return descriptor.fields.flatMap((v) => resolveFuncFlatPack(v.type, name + `'` + v.name, ctx)); + return descriptor.fields.flatMap((v) => + resolveFuncFlatPack(v.type, name + `'` + v.name, ctx), + ); } } // Unreachable - throw Error('Unknown type: ' + descriptor.kind); -} \ No newline at end of file + throw Error("Unknown type: " + descriptor.kind); +} diff --git a/src/generator/writers/resolveFuncFlatTypes.ts b/src/generator/writers/resolveFuncFlatTypes.ts index 2517ac0b5..d70590c65 100644 --- a/src/generator/writers/resolveFuncFlatTypes.ts +++ b/src/generator/writers/resolveFuncFlatTypes.ts @@ -3,44 +3,55 @@ import { TypeDescription, TypeRef } from "../../types/types"; import { WriterContext } from "../Writer"; import { resolveFuncType } from "./resolveFuncType"; -export function resolveFuncFlatTypes(descriptor: TypeRef | TypeDescription | string, ctx: WriterContext, optional: boolean = false): string[] { - +export function resolveFuncFlatTypes( + descriptor: TypeRef | TypeDescription | string, + ctx: WriterContext, + optional: boolean = false, +): string[] { // String - if (typeof descriptor === 'string') { + if (typeof descriptor === "string") { return resolveFuncFlatTypes(getType(ctx.ctx, descriptor), ctx); } // TypeRef - if (descriptor.kind === 'ref') { - return resolveFuncFlatTypes(getType(ctx.ctx, descriptor.name), ctx, descriptor.optional); + if (descriptor.kind === "ref") { + return resolveFuncFlatTypes( + getType(ctx.ctx, descriptor.name), + ctx, + descriptor.optional, + ); } - if (descriptor.kind === 'map') { - return ['cell']; + if (descriptor.kind === "map") { + return ["cell"]; } - if (descriptor.kind === 'ref_bounced') { + if (descriptor.kind === "ref_bounced") { throw Error("Unimplemented"); } - if (descriptor.kind === 'void') { - throw Error('Void type is not allowed in function arguments'); + if (descriptor.kind === "void") { + throw Error("Void type is not allowed in function arguments"); } // TypeDescription - if (descriptor.kind === 'primitive') { + if (descriptor.kind === "primitive") { return [resolveFuncType(descriptor, ctx)]; - } else if (descriptor.kind === 'struct') { + } else if (descriptor.kind === "struct") { if (optional || descriptor.fields.length === 0) { - return ['tuple']; + return ["tuple"]; } else { - return descriptor.fields.flatMap((v) => resolveFuncFlatTypes(v.type, ctx)); + return descriptor.fields.flatMap((v) => + resolveFuncFlatTypes(v.type, ctx), + ); } - } else if (descriptor.kind === 'contract') { + } else if (descriptor.kind === "contract") { if (optional || descriptor.fields.length === 0) { - return ['tuple']; + return ["tuple"]; } else { - return descriptor.fields.flatMap((v) => resolveFuncFlatTypes(v.type, ctx)); + return descriptor.fields.flatMap((v) => + resolveFuncFlatTypes(v.type, ctx), + ); } } // Unreachable - throw Error('Unknown type: ' + descriptor.kind); -} \ No newline at end of file + throw Error("Unknown type: " + descriptor.kind); +} diff --git a/src/generator/writers/resolveFuncPrimitive.ts b/src/generator/writers/resolveFuncPrimitive.ts index 8d43081c5..3845462bd 100644 --- a/src/generator/writers/resolveFuncPrimitive.ts +++ b/src/generator/writers/resolveFuncPrimitive.ts @@ -2,53 +2,56 @@ import { getType } from "../../types/resolveDescriptors"; import { TypeDescription, TypeRef } from "../../types/types"; import { WriterContext } from "../Writer"; -export function resolveFuncPrimitive(descriptor: TypeRef | TypeDescription | string, ctx: WriterContext): boolean { +export function resolveFuncPrimitive( + descriptor: TypeRef | TypeDescription | string, + ctx: WriterContext, +): boolean { // String - if (typeof descriptor === 'string') { + if (typeof descriptor === "string") { return resolveFuncPrimitive(getType(ctx.ctx, descriptor), ctx); } // TypeRef - if (descriptor.kind === 'ref') { + if (descriptor.kind === "ref") { return resolveFuncPrimitive(getType(ctx.ctx, descriptor.name), ctx); } - if (descriptor.kind === 'map') { + if (descriptor.kind === "map") { return true; } - if (descriptor.kind === 'ref_bounced') { + if (descriptor.kind === "ref_bounced") { throw Error("Unimplemented"); } - if (descriptor.kind === 'void') { + if (descriptor.kind === "void") { return true; } // TypeDescription - if (descriptor.kind === 'primitive') { - if (descriptor.name === 'Int') { + if (descriptor.kind === "primitive") { + if (descriptor.name === "Int") { return true; - } else if (descriptor.name === 'Bool') { + } else if (descriptor.name === "Bool") { return true; - } else if (descriptor.name === 'Slice') { + } else if (descriptor.name === "Slice") { return true; - } else if (descriptor.name === 'Cell') { + } else if (descriptor.name === "Cell") { return true; - } else if (descriptor.name === 'Builder') { + } else if (descriptor.name === "Builder") { return true; - } else if (descriptor.name === 'Address') { + } else if (descriptor.name === "Address") { return true; - } else if (descriptor.name === 'String') { + } else if (descriptor.name === "String") { return true; - } else if (descriptor.name === 'StringBuilder') { + } else if (descriptor.name === "StringBuilder") { return true; } else { - throw Error('Unknown primitive type: ' + descriptor.name); + throw Error("Unknown primitive type: " + descriptor.name); } - } else if (descriptor.kind === 'struct') { + } else if (descriptor.kind === "struct") { return false; - } else if (descriptor.kind === 'contract') { + } else if (descriptor.kind === "contract") { return false; } // Unreachable - throw Error('Unknown type: ' + descriptor.kind); -} \ No newline at end of file + throw Error("Unknown type: " + descriptor.kind); +} diff --git a/src/generator/writers/resolveFuncTupledType.ts b/src/generator/writers/resolveFuncTupledType.ts index effd6ffec..12181f685 100644 --- a/src/generator/writers/resolveFuncTupledType.ts +++ b/src/generator/writers/resolveFuncTupledType.ts @@ -2,52 +2,54 @@ import { getType } from "../../types/resolveDescriptors"; import { TypeDescription, TypeRef } from "../../types/types"; import { WriterContext } from "../Writer"; -export function resolveFuncTupledType(descriptor: TypeRef | TypeDescription | string, ctx: WriterContext): string { - +export function resolveFuncTupledType( + descriptor: TypeRef | TypeDescription | string, + ctx: WriterContext, +): string { // String - if (typeof descriptor === 'string') { + if (typeof descriptor === "string") { return resolveFuncTupledType(getType(ctx.ctx, descriptor), ctx); } // TypeRef - if (descriptor.kind === 'ref') { + if (descriptor.kind === "ref") { return resolveFuncTupledType(getType(ctx.ctx, descriptor.name), ctx); } - if (descriptor.kind === 'map') { - return 'cell'; + if (descriptor.kind === "map") { + return "cell"; } - if (descriptor.kind === 'ref_bounced') { + if (descriptor.kind === "ref_bounced") { throw Error("Unimplemented"); } - if (descriptor.kind === 'void') { - return '()'; + if (descriptor.kind === "void") { + return "()"; } // TypeDescription - if (descriptor.kind === 'primitive') { - if (descriptor.name === 'Int') { - return 'int'; - } else if (descriptor.name === 'Bool') { - return 'int'; - } else if (descriptor.name === 'Slice') { - return 'slice'; - } else if (descriptor.name === 'Cell') { - return 'cell'; - } else if (descriptor.name === 'Builder') { - return 'builder'; - } else if (descriptor.name === 'Address') { - return 'slice'; - } else if (descriptor.name === 'String') { - return 'slice'; - } else if (descriptor.name === 'StringBuilder') { - return 'tuple'; + if (descriptor.kind === "primitive") { + if (descriptor.name === "Int") { + return "int"; + } else if (descriptor.name === "Bool") { + return "int"; + } else if (descriptor.name === "Slice") { + return "slice"; + } else if (descriptor.name === "Cell") { + return "cell"; + } else if (descriptor.name === "Builder") { + return "builder"; + } else if (descriptor.name === "Address") { + return "slice"; + } else if (descriptor.name === "String") { + return "slice"; + } else if (descriptor.name === "StringBuilder") { + return "tuple"; } else { - throw Error('Unknown primitive type: ' + descriptor.name); + throw Error("Unknown primitive type: " + descriptor.name); } - } else if (descriptor.kind === 'struct') { - return 'tuple'; + } else if (descriptor.kind === "struct") { + return "tuple"; } // Unreachable - throw Error('Unknown type: ' + descriptor.kind); -} \ No newline at end of file + throw Error("Unknown type: " + descriptor.kind); +} diff --git a/src/generator/writers/resolveFuncType.spec.ts b/src/generator/writers/resolveFuncType.spec.ts index 2fe1c4892..76aed77e0 100644 --- a/src/generator/writers/resolveFuncType.spec.ts +++ b/src/generator/writers/resolveFuncType.spec.ts @@ -44,39 +44,133 @@ contract Contract2 { } `; -describe('resolveFuncType', () => { - +describe("resolveFuncType", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should process primitive types', () => { - let ctx = openContext(new CompilerContext(), [{ code: primitiveCode, path: '', origin: 'user' }], []); + it("should process primitive types", () => { + let ctx = openContext( + new CompilerContext(), + [{ code: primitiveCode, path: "", origin: "user" }], + [], + ); ctx = resolveDescriptors(ctx); - const wctx = new WriterContext(ctx, 'Contract1'); - expect(resolveFuncType({ kind: 'ref', name: 'Int', optional: false }, wctx)).toBe('int'); - expect(resolveFuncType({ kind: 'ref', name: 'Bool', optional: false }, wctx)).toBe('int'); - expect(resolveFuncType({ kind: 'ref', name: 'Cell', optional: false }, wctx)).toBe('cell'); - expect(resolveFuncType({ kind: 'ref', name: 'Slice', optional: false }, wctx)).toBe('slice'); - expect(resolveFuncType({ kind: 'ref', name: 'Builder', optional: false }, wctx)).toBe('builder'); - expect(resolveFuncType({ kind: 'ref', name: 'Int', optional: true }, wctx)).toBe('int'); - expect(resolveFuncType({ kind: 'ref', name: 'Bool', optional: true }, wctx)).toBe('int'); - expect(resolveFuncType({ kind: 'ref', name: 'Cell', optional: true }, wctx)).toBe('cell'); - expect(resolveFuncType({ kind: 'ref', name: 'Slice', optional: true }, wctx)).toBe('slice'); - expect(resolveFuncType({ kind: 'ref', name: 'Builder', optional: true }, wctx)).toBe('builder'); + const wctx = new WriterContext(ctx, "Contract1"); + expect( + resolveFuncType( + { kind: "ref", name: "Int", optional: false }, + wctx, + ), + ).toBe("int"); + expect( + resolveFuncType( + { kind: "ref", name: "Bool", optional: false }, + wctx, + ), + ).toBe("int"); + expect( + resolveFuncType( + { kind: "ref", name: "Cell", optional: false }, + wctx, + ), + ).toBe("cell"); + expect( + resolveFuncType( + { kind: "ref", name: "Slice", optional: false }, + wctx, + ), + ).toBe("slice"); + expect( + resolveFuncType( + { kind: "ref", name: "Builder", optional: false }, + wctx, + ), + ).toBe("builder"); + expect( + resolveFuncType({ kind: "ref", name: "Int", optional: true }, wctx), + ).toBe("int"); + expect( + resolveFuncType( + { kind: "ref", name: "Bool", optional: true }, + wctx, + ), + ).toBe("int"); + expect( + resolveFuncType( + { kind: "ref", name: "Cell", optional: true }, + wctx, + ), + ).toBe("cell"); + expect( + resolveFuncType( + { kind: "ref", name: "Slice", optional: true }, + wctx, + ), + ).toBe("slice"); + expect( + resolveFuncType( + { kind: "ref", name: "Builder", optional: true }, + wctx, + ), + ).toBe("builder"); }); - it('should process contract and struct types', () => { - let ctx = openContext(new CompilerContext(), [{ code: primitiveCode, path: '', origin: 'user' }], []); + it("should process contract and struct types", () => { + let ctx = openContext( + new CompilerContext(), + [{ code: primitiveCode, path: "", origin: "user" }], + [], + ); ctx = resolveDescriptors(ctx); - const wctx = new WriterContext(ctx, 'Contract1'); - expect(resolveFuncType({ kind: 'ref', name: 'Struct1', optional: false }, wctx)).toBe('(int, int)'); - expect(resolveFuncType({ kind: 'ref', name: 'Struct2', optional: false }, wctx)).toBe('(int)'); - expect(resolveFuncType({ kind: 'ref', name: 'Contract1', optional: false }, wctx)).toBe('(int, int)'); - expect(resolveFuncType({ kind: 'ref', name: 'Contract2', optional: false }, wctx)).toBe('(int, (int, int))'); - expect(resolveFuncType({ kind: 'ref', name: 'Struct1', optional: true }, wctx)).toBe('tuple'); - expect(resolveFuncType({ kind: 'ref', name: 'Struct2', optional: true }, wctx)).toBe('tuple'); - expect(resolveFuncType({ kind: 'ref', name: 'Contract1', optional: true }, wctx)).toBe('tuple'); - expect(resolveFuncType({ kind: 'ref', name: 'Contract2', optional: true }, wctx)).toBe('tuple'); + const wctx = new WriterContext(ctx, "Contract1"); + expect( + resolveFuncType( + { kind: "ref", name: "Struct1", optional: false }, + wctx, + ), + ).toBe("(int, int)"); + expect( + resolveFuncType( + { kind: "ref", name: "Struct2", optional: false }, + wctx, + ), + ).toBe("(int)"); + expect( + resolveFuncType( + { kind: "ref", name: "Contract1", optional: false }, + wctx, + ), + ).toBe("(int, int)"); + expect( + resolveFuncType( + { kind: "ref", name: "Contract2", optional: false }, + wctx, + ), + ).toBe("(int, (int, int))"); + expect( + resolveFuncType( + { kind: "ref", name: "Struct1", optional: true }, + wctx, + ), + ).toBe("tuple"); + expect( + resolveFuncType( + { kind: "ref", name: "Struct2", optional: true }, + wctx, + ), + ).toBe("tuple"); + expect( + resolveFuncType( + { kind: "ref", name: "Contract1", optional: true }, + wctx, + ), + ).toBe("tuple"); + expect( + resolveFuncType( + { kind: "ref", name: "Contract2", optional: true }, + wctx, + ), + ).toBe("tuple"); }); -}); \ No newline at end of file +}); diff --git a/src/generator/writers/resolveFuncType.ts b/src/generator/writers/resolveFuncType.ts index 4a0ace7bd..6e113d787 100644 --- a/src/generator/writers/resolveFuncType.ts +++ b/src/generator/writers/resolveFuncType.ts @@ -2,63 +2,100 @@ import { getType } from "../../types/resolveDescriptors"; import { TypeDescription, TypeRef } from "../../types/types"; import { WriterContext } from "../Writer"; -export function resolveFuncType(descriptor: TypeRef | TypeDescription | string, ctx: WriterContext, optional: boolean = false, usePartialFields: boolean = false): string { - +export function resolveFuncType( + descriptor: TypeRef | TypeDescription | string, + ctx: WriterContext, + optional: boolean = false, + usePartialFields: boolean = false, +): string { // String - if (typeof descriptor === 'string') { - return resolveFuncType(getType(ctx.ctx, descriptor), ctx, false, usePartialFields); + if (typeof descriptor === "string") { + return resolveFuncType( + getType(ctx.ctx, descriptor), + ctx, + false, + usePartialFields, + ); } // TypeRef - if (descriptor.kind === 'ref') { - return resolveFuncType(getType(ctx.ctx, descriptor.name), ctx, descriptor.optional, usePartialFields); + if (descriptor.kind === "ref") { + return resolveFuncType( + getType(ctx.ctx, descriptor.name), + ctx, + descriptor.optional, + usePartialFields, + ); } - if (descriptor.kind === 'map') { - return 'cell'; + if (descriptor.kind === "map") { + return "cell"; } - if (descriptor.kind === 'ref_bounced') { - return resolveFuncType(getType(ctx.ctx, descriptor.name), ctx, false, true); + if (descriptor.kind === "ref_bounced") { + return resolveFuncType( + getType(ctx.ctx, descriptor.name), + ctx, + false, + true, + ); } - if (descriptor.kind === 'void') { - return '()'; + if (descriptor.kind === "void") { + return "()"; } // TypeDescription - if (descriptor.kind === 'primitive') { - if (descriptor.name === 'Int') { - return 'int'; - } else if (descriptor.name === 'Bool') { - return 'int'; - } else if (descriptor.name === 'Slice') { - return 'slice'; - } else if (descriptor.name === 'Cell') { - return 'cell'; - } else if (descriptor.name === 'Builder') { - return 'builder'; - } else if (descriptor.name === 'Address') { - return 'slice'; - } else if (descriptor.name === 'String') { - return 'slice'; - } else if (descriptor.name === 'StringBuilder') { - return 'tuple'; + if (descriptor.kind === "primitive") { + if (descriptor.name === "Int") { + return "int"; + } else if (descriptor.name === "Bool") { + return "int"; + } else if (descriptor.name === "Slice") { + return "slice"; + } else if (descriptor.name === "Cell") { + return "cell"; + } else if (descriptor.name === "Builder") { + return "builder"; + } else if (descriptor.name === "Address") { + return "slice"; + } else if (descriptor.name === "String") { + return "slice"; + } else if (descriptor.name === "StringBuilder") { + return "tuple"; } else { - throw Error('Unknown primitive type: ' + descriptor.name); + throw Error("Unknown primitive type: " + descriptor.name); } - } else if (descriptor.kind === 'struct') { - const fieldsToUse = usePartialFields ? descriptor.fields.slice(0, descriptor.partialFieldCount) : descriptor.fields; + } else if (descriptor.kind === "struct") { + const fieldsToUse = usePartialFields + ? descriptor.fields.slice(0, descriptor.partialFieldCount) + : descriptor.fields; if (optional || fieldsToUse.length === 0) { - return 'tuple'; + return "tuple"; } else { - return '(' + fieldsToUse.map((v) => resolveFuncType(v.type, ctx, false, usePartialFields)).join(', ') + ')'; + return ( + "(" + + fieldsToUse + .map((v) => + resolveFuncType(v.type, ctx, false, usePartialFields), + ) + .join(", ") + + ")" + ); } - } else if (descriptor.kind === 'contract') { + } else if (descriptor.kind === "contract") { if (optional || descriptor.fields.length === 0) { - return 'tuple'; + return "tuple"; } else { - return '(' + descriptor.fields.map((v) => resolveFuncType(v.type, ctx, false, usePartialFields)).join(', ') + ')'; + return ( + "(" + + descriptor.fields + .map((v) => + resolveFuncType(v.type, ctx, false, usePartialFields), + ) + .join(", ") + + ")" + ); } } // Unreachable - throw Error('Unknown type: ' + descriptor.kind); -} \ No newline at end of file + throw Error("Unknown type: " + descriptor.kind); +} diff --git a/src/generator/writers/resolveFuncTypeFromAbi.ts b/src/generator/writers/resolveFuncTypeFromAbi.ts index 218ebcf15..9d4dd4277 100644 --- a/src/generator/writers/resolveFuncTypeFromAbi.ts +++ b/src/generator/writers/resolveFuncTypeFromAbi.ts @@ -2,44 +2,47 @@ import { ABITypeRef } from "@ton/core"; import { getType } from "../../types/resolveDescriptors"; import { WriterContext } from "../Writer"; -export function resolveFuncTypeFromAbi(fields: ABITypeRef[], ctx: WriterContext): string { +export function resolveFuncTypeFromAbi( + fields: ABITypeRef[], + ctx: WriterContext, +): string { if (fields.length === 0) { - return 'tuple'; + return "tuple"; } const res: string[] = []; for (const f of fields) { - if (f.kind === 'dict') { - res.push('cell'); - } else if (f.kind === 'simple') { - if (f.type === 'int' || f.type === 'uint' || f.type === 'bool') { - res.push('int'); - } else if (f.type === 'cell') { - res.push('cell'); - } else if (f.type === 'slice') { - res.push('slice'); - } else if (f.type === 'builder') { - res.push('builder'); - } else if (f.type === 'address') { - res.push('slice'); - } else if (f.type === 'fixed-bytes') { - res.push('slice'); - } else if (f.type === 'string') { - res.push('slice'); + if (f.kind === "dict") { + res.push("cell"); + } else if (f.kind === "simple") { + if (f.type === "int" || f.type === "uint" || f.type === "bool") { + res.push("int"); + } else if (f.type === "cell") { + res.push("cell"); + } else if (f.type === "slice") { + res.push("slice"); + } else if (f.type === "builder") { + res.push("builder"); + } else if (f.type === "address") { + res.push("slice"); + } else if (f.type === "fixed-bytes") { + res.push("slice"); + } else if (f.type === "string") { + res.push("slice"); } else { const t = getType(ctx.ctx, f.type); - if (t.kind !== 'struct') { - throw Error('Unsupported type: ' + t.kind); + if (t.kind !== "struct") { + throw Error("Unsupported type: " + t.kind); } if (f.optional || t.fields.length === 0) { - res.push('tuple'); + res.push("tuple"); } else { const loaded = t.fields.map((v) => v.abi.type); res.push(resolveFuncTypeFromAbi(loaded, ctx)); } } } else { - throw Error('Unsupported type'); + throw Error("Unsupported type"); } } - return `(${res.join(', ')})`; -} \ No newline at end of file + return `(${res.join(", ")})`; +} diff --git a/src/generator/writers/resolveFuncTypeFromAbiUnpack.ts b/src/generator/writers/resolveFuncTypeFromAbiUnpack.ts index d945a365a..e50498fa6 100644 --- a/src/generator/writers/resolveFuncTypeFromAbiUnpack.ts +++ b/src/generator/writers/resolveFuncTypeFromAbiUnpack.ts @@ -2,28 +2,36 @@ import { ABITypeRef } from "@ton/core"; import { getType } from "../../types/resolveDescriptors"; import { WriterContext } from "../Writer"; -export function resolveFuncTypeFromAbiUnpack(name: string, fields: { name: string, type: ABITypeRef }[], ctx: WriterContext): string { +export function resolveFuncTypeFromAbiUnpack( + name: string, + fields: { name: string; type: ABITypeRef }[], + ctx: WriterContext, +): string { if (fields.length === 0) { return `${name}`; } const res: string[] = []; for (const f of fields) { - if (f.type.kind === 'dict') { + if (f.type.kind === "dict") { res.push(`${name}'${f.name}`); - } else if (f.type.kind === 'simple') { - if (f.type.type === 'int' || f.type.type === 'uint' || f.type.type === 'bool') { + } else if (f.type.kind === "simple") { + if ( + f.type.type === "int" || + f.type.type === "uint" || + f.type.type === "bool" + ) { res.push(`${name}'${f.name}`); - } else if (f.type.type === 'cell') { + } else if (f.type.type === "cell") { res.push(`${name}'${f.name}`); - } else if (f.type.type === 'slice') { + } else if (f.type.type === "slice") { res.push(`${name}'${f.name}`); - } else if (f.type.type === 'builder') { + } else if (f.type.type === "builder") { res.push(`${name}'${f.name}`); - } else if (f.type.type === 'address') { + } else if (f.type.type === "address") { res.push(`${name}'${f.name}`); - } else if (f.type.type === 'fixed-bytes') { + } else if (f.type.type === "fixed-bytes") { res.push(`${name}'${f.name}`); - } else if (f.type.type === 'string') { + } else if (f.type.type === "string") { res.push(`${name}'${f.name}`); } else { const t = getType(ctx.ctx, f.type.type); @@ -31,12 +39,18 @@ export function resolveFuncTypeFromAbiUnpack(name: string, fields: { name: strin res.push(`${name}'${f.name}`); } else { const loaded = t.fields.map((v) => v.abi); - res.push(resolveFuncTypeFromAbiUnpack(`${name}'${f.name}`, loaded, ctx)); + res.push( + resolveFuncTypeFromAbiUnpack( + `${name}'${f.name}`, + loaded, + ctx, + ), + ); } } } else { - throw Error('Unsupported type'); + throw Error("Unsupported type"); } } - return `(${res.join(', ')})`; -} \ No newline at end of file + return `(${res.join(", ")})`; +} diff --git a/src/generator/writers/resolveFuncTypeUnpack.ts b/src/generator/writers/resolveFuncTypeUnpack.ts index 5443bab1c..1396a977a 100644 --- a/src/generator/writers/resolveFuncTypeUnpack.ts +++ b/src/generator/writers/resolveFuncTypeUnpack.ts @@ -2,45 +2,98 @@ import { getType } from "../../types/resolveDescriptors"; import { TypeDescription, TypeRef } from "../../types/types"; import { WriterContext } from "../Writer"; -export function resolveFuncTypeUnpack(descriptor: TypeRef | TypeDescription | string, name: string, ctx: WriterContext, optional: boolean = false, usePartialFields: boolean = false): string { - +export function resolveFuncTypeUnpack( + descriptor: TypeRef | TypeDescription | string, + name: string, + ctx: WriterContext, + optional: boolean = false, + usePartialFields: boolean = false, +): string { // String - if (typeof descriptor === 'string') { - return resolveFuncTypeUnpack(getType(ctx.ctx, descriptor), name, ctx, false, usePartialFields); + if (typeof descriptor === "string") { + return resolveFuncTypeUnpack( + getType(ctx.ctx, descriptor), + name, + ctx, + false, + usePartialFields, + ); } // TypeRef - if (descriptor.kind === 'ref') { - return resolveFuncTypeUnpack(getType(ctx.ctx, descriptor.name), name, ctx, descriptor.optional, usePartialFields); + if (descriptor.kind === "ref") { + return resolveFuncTypeUnpack( + getType(ctx.ctx, descriptor.name), + name, + ctx, + descriptor.optional, + usePartialFields, + ); } - if (descriptor.kind === 'map') { + if (descriptor.kind === "map") { return name; } - if (descriptor.kind === 'ref_bounced') { - return resolveFuncTypeUnpack(getType(ctx.ctx, descriptor.name), name, ctx, false, true); + if (descriptor.kind === "ref_bounced") { + return resolveFuncTypeUnpack( + getType(ctx.ctx, descriptor.name), + name, + ctx, + false, + true, + ); } - if (descriptor.kind === 'void') { - throw Error('Void type is not allowed in function arguments: ' + name); + if (descriptor.kind === "void") { + throw Error("Void type is not allowed in function arguments: " + name); } // TypeDescription - if (descriptor.kind === 'primitive') { + if (descriptor.kind === "primitive") { return name; - } else if (descriptor.kind === 'struct') { - const fieldsToUse = usePartialFields ? descriptor.fields.slice(0, descriptor.partialFieldCount) : descriptor.fields; + } else if (descriptor.kind === "struct") { + const fieldsToUse = usePartialFields + ? descriptor.fields.slice(0, descriptor.partialFieldCount) + : descriptor.fields; if (optional || fieldsToUse.length === 0) { return name; } else { - return '(' + fieldsToUse.map((v) => resolveFuncTypeUnpack(v.type, name + `'` + v.name, ctx, false, usePartialFields)).join(', ') + ')'; + return ( + "(" + + fieldsToUse + .map((v) => + resolveFuncTypeUnpack( + v.type, + name + `'` + v.name, + ctx, + false, + usePartialFields, + ), + ) + .join(", ") + + ")" + ); } - } else if (descriptor.kind === 'contract') { + } else if (descriptor.kind === "contract") { if (optional || descriptor.fields.length === 0) { return name; } else { - return '(' + descriptor.fields.map((v) => resolveFuncTypeUnpack(v.type, name + `'` + v.name, ctx, false, usePartialFields)).join(', ') + ')'; + return ( + "(" + + descriptor.fields + .map((v) => + resolveFuncTypeUnpack( + v.type, + name + `'` + v.name, + ctx, + false, + usePartialFields, + ), + ) + .join(", ") + + ")" + ); } } // Unreachable - throw Error('Unknown type: ' + descriptor.kind); -} \ No newline at end of file + throw Error("Unknown type: " + descriptor.kind); +} diff --git a/src/generator/writers/writeAccessors.ts b/src/generator/writers/writeAccessors.ts index 9a42fd1d7..732d4edd5 100644 --- a/src/generator/writers/writeAccessors.ts +++ b/src/generator/writers/writeAccessors.ts @@ -9,16 +9,23 @@ import { resolveFuncTupledType } from "./resolveFuncTupledType"; import { resolveFuncType } from "./resolveFuncType"; import { resolveFuncTypeUnpack } from "./resolveFuncTypeUnpack"; -export function writeAccessors(type: TypeDescription, origin: TypeOrigin, ctx: WriterContext) { - +export function writeAccessors( + type: TypeDescription, + origin: TypeOrigin, + ctx: WriterContext, +) { // Getters for (const f of type.fields) { ctx.fun(ops.typeField(type.name, f.name, ctx), () => { - ctx.signature(`_ ${ops.typeField(type.name, f.name, ctx)}(${resolveFuncType(type, ctx)} v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.signature( + `_ ${ops.typeField(type.name, f.name, ctx)}(${resolveFuncType(type, ctx)} v)`, + ); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { - ctx.append(`var (${type.fields.map((v) => `v'${v.name}`).join(', ')}) = v;`); + ctx.append( + `var (${type.fields.map((v) => `v'${v.name}`).join(", ")}) = v;`, + ); ctx.append(`return v'${f.name};`); }); }); @@ -26,38 +33,49 @@ export function writeAccessors(type: TypeDescription, origin: TypeOrigin, ctx: W // Tensor cast ctx.fun(ops.typeTensorCast(type.name, ctx), () => { - ctx.signature(`(${resolveFuncType(type, ctx)}) ${ops.typeTensorCast(type.name, ctx)}(${resolveFuncType(type, ctx)} v)`); - ctx.context('type:' + type.name); + ctx.signature( + `(${resolveFuncType(type, ctx)}) ${ops.typeTensorCast(type.name, ctx)}(${resolveFuncType(type, ctx)} v)`, + ); + ctx.context("type:" + type.name); ctx.asm('asm "NOP"'); }); // Not null ctx.fun(ops.typeNotNull(type.name, ctx), () => { - ctx.signature(`(${resolveFuncType(type, ctx)}) ${ops.typeNotNull(type.name, ctx)}(tuple v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.signature( + `(${resolveFuncType(type, ctx)}) ${ops.typeNotNull(type.name, ctx)}(tuple v)`, + ); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { - ctx.append(`throw_if(${contractErrors.null.id}, null?(v));`) - const flatPack = resolveFuncFlatPack(type, 'vvv', ctx); + ctx.append(`throw_if(${contractErrors.null.id}, null?(v));`); + const flatPack = resolveFuncFlatPack(type, "vvv", ctx); const flatTypes = resolveFuncFlatTypes(type, ctx); - if (flatPack.length !== flatTypes.length) throw Error('Flat pack and flat types length mismatch'); + if (flatPack.length !== flatTypes.length) + throw Error("Flat pack and flat types length mismatch"); const pairs = flatPack.map((v, i) => `${flatTypes[i]} ${v}`); ctx.used(`__tact_tuple_destroy_${flatPack.length}`); - ctx.append(`var (${pairs.join(', ')}) = __tact_tuple_destroy_${flatPack.length}(v);`); - ctx.append(`return ${resolveFuncTypeUnpack(type, 'vvv', ctx)};`); + ctx.append( + `var (${pairs.join(", ")}) = __tact_tuple_destroy_${flatPack.length}(v);`, + ); + ctx.append(`return ${resolveFuncTypeUnpack(type, "vvv", ctx)};`); }); }); // As optional ctx.fun(ops.typeAsOptional(type.name, ctx), () => { - ctx.signature(`tuple ${ops.typeAsOptional(type.name, ctx)}(${resolveFuncType(type, ctx)} v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.signature( + `tuple ${ops.typeAsOptional(type.name, ctx)}(${resolveFuncType(type, ctx)} v)`, + ); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { - ctx.append(`var ${resolveFuncTypeUnpack(type, 'v', ctx)} = v;`); - const flatPack = resolveFuncFlatPack(type, 'v', ctx); + ctx.append(`var ${resolveFuncTypeUnpack(type, "v", ctx)} = v;`); + const flatPack = resolveFuncFlatPack(type, "v", ctx); ctx.used(`__tact_tuple_create_${flatPack.length}`); - ctx.append(`return __tact_tuple_create_${flatPack.length}(${flatPack.join(', ')});`); + ctx.append( + `return __tact_tuple_create_${flatPack.length}(${flatPack.join(", ")});`, + ); }); }); @@ -66,20 +84,28 @@ export function writeAccessors(type: TypeDescription, origin: TypeOrigin, ctx: W // ctx.fun(ops.typeToTuple(type.name, ctx), () => { - ctx.signature(`tuple ${ops.typeToTuple(type.name, ctx)}((${resolveFuncType(type, ctx)}) v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.signature( + `tuple ${ops.typeToTuple(type.name, ctx)}((${resolveFuncType(type, ctx)}) v)`, + ); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { - ctx.append(`var (${type.fields.map((v) => `v'${v.name}`).join(', ')}) = v;`); + ctx.append( + `var (${type.fields.map((v) => `v'${v.name}`).join(", ")}) = v;`, + ); const vars: string[] = []; for (const f of type.fields) { - if (f.type.kind === 'ref') { + if (f.type.kind === "ref") { const t = getType(ctx.ctx, f.type.name); - if (t.kind === 'struct') { + if (t.kind === "struct") { if (f.type.optional) { - vars.push(`${ops.typeToOptTuple(f.type.name, ctx)}(v'${f.name})`); + vars.push( + `${ops.typeToOptTuple(f.type.name, ctx)}(v'${f.name})`, + ); } else { - vars.push(`${ops.typeToTuple(f.type.name, ctx)}(v'${f.name})`); + vars.push( + `${ops.typeToTuple(f.type.name, ctx)}(v'${f.name})`, + ); } continue; } @@ -87,46 +113,64 @@ export function writeAccessors(type: TypeDescription, origin: TypeOrigin, ctx: W vars.push(`v'${f.name}`); } ctx.used(`__tact_tuple_create_${vars.length}`); - ctx.append(`return __tact_tuple_create_${vars.length}(${vars.join(', ')});`); + ctx.append( + `return __tact_tuple_create_${vars.length}(${vars.join(", ")});`, + ); }); }); ctx.fun(ops.typeToOptTuple(type.name, ctx), () => { ctx.signature(`tuple ${ops.typeToOptTuple(type.name, ctx)}(tuple v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { ctx.append(`if (null?(v)) { return null(); } `); - ctx.append(`return ${ops.typeToTuple(type.name, ctx)}(${ops.typeNotNull(type.name, ctx)}(v)); `); + ctx.append( + `return ${ops.typeToTuple(type.name, ctx)}(${ops.typeNotNull(type.name, ctx)}(v)); `, + ); }); }); ctx.fun(ops.typeFromTuple(type.name, ctx), () => { - ctx.signature(`(${type.fields.map((v) => resolveFuncType(v.type, ctx)).join(', ')}) ${ops.typeFromTuple(type.name, ctx)}(tuple v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.signature( + `(${type.fields.map((v) => resolveFuncType(v.type, ctx)).join(", ")}) ${ops.typeFromTuple(type.name, ctx)}(tuple v)`, + ); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { // Resolve vars const vars: string[] = []; const out: string[] = []; for (const f of type.fields) { - if (f.type.kind === 'ref') { + if (f.type.kind === "ref") { const t = getType(ctx.ctx, f.type.name); - if (t.kind === 'struct') { + if (t.kind === "struct") { vars.push(`tuple v'${f.name}`); if (f.type.optional) { - out.push(`${ops.typeFromOptTuple(f.type.name, ctx)}(v'${f.name})`); + out.push( + `${ops.typeFromOptTuple(f.type.name, ctx)}(v'${f.name})`, + ); } else { - out.push(`${ops.typeFromTuple(f.type.name, ctx)}(v'${f.name})`); + out.push( + `${ops.typeFromTuple(f.type.name, ctx)}(v'${f.name})`, + ); } continue; - } else if (t.kind === 'primitive' && t.name === 'Address') { + } else if (t.kind === "primitive" && t.name === "Address") { if (f.type.optional) { - vars.push(`${resolveFuncType(f.type, ctx)} v'${f.name}`); - out.push(`null?(v'${f.name}) ? null() : ${ctx.used(`__tact_verify_address`)}(v'${f.name})`); + vars.push( + `${resolveFuncType(f.type, ctx)} v'${f.name}`, + ); + out.push( + `null?(v'${f.name}) ? null() : ${ctx.used(`__tact_verify_address`)}(v'${f.name})`, + ); } else { - vars.push(`${resolveFuncType(f.type, ctx)} v'${f.name}`); - out.push(`${ctx.used(`__tact_verify_address`)}(v'${f.name})`); + vars.push( + `${resolveFuncType(f.type, ctx)} v'${f.name}`, + ); + out.push( + `${ctx.used(`__tact_verify_address`)}(v'${f.name})`, + ); } continue; } @@ -135,18 +179,22 @@ export function writeAccessors(type: TypeDescription, origin: TypeOrigin, ctx: W out.push(`v'${f.name}`); } ctx.used(`__tact_tuple_destroy_${vars.length}`); - ctx.append(`var (${vars.join(', ')}) = __tact_tuple_destroy_${vars.length}(v);`); - ctx.append(`return (${out.join(', ')});`); + ctx.append( + `var (${vars.join(", ")}) = __tact_tuple_destroy_${vars.length}(v);`, + ); + ctx.append(`return (${out.join(", ")});`); }); }); ctx.fun(ops.typeFromOptTuple(type.name, ctx), () => { ctx.signature(`tuple ${ops.typeFromOptTuple(type.name, ctx)}(tuple v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { ctx.append(`if (null?(v)) { return null(); } `); - ctx.append(`return ${ops.typeAsOptional(type.name, ctx)}(${ops.typeFromTuple(type.name, ctx)}(v));`); + ctx.append( + `return ${ops.typeAsOptional(type.name, ctx)}(${ops.typeFromTuple(type.name, ctx)}(v));`, + ); }); }); @@ -155,45 +203,57 @@ export function writeAccessors(type: TypeDescription, origin: TypeOrigin, ctx: W // ctx.fun(ops.typeToExternal(type.name, ctx), () => { - ctx.signature(`(${type.fields.map((v) => resolveFuncTupledType(v.type, ctx)).join(', ')}) ${ops.typeToExternal(type.name, ctx)}((${resolveFuncType(type, ctx)}) v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.signature( + `(${type.fields.map((v) => resolveFuncTupledType(v.type, ctx)).join(", ")}) ${ops.typeToExternal(type.name, ctx)}((${resolveFuncType(type, ctx)}) v)`, + ); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { - ctx.append(`var (${type.fields.map((v) => `v'${v.name}`).join(', ')}) = v; `); + ctx.append( + `var (${type.fields.map((v) => `v'${v.name}`).join(", ")}) = v; `, + ); const vars: string[] = []; for (const f of type.fields) { - if (f.type.kind === 'ref') { + if (f.type.kind === "ref") { const t = getType(ctx.ctx, f.type.name); - if (t.kind === 'struct') { + if (t.kind === "struct") { if (f.type.optional) { - vars.push(`${ops.typeToOptTuple(f.type.name, ctx)}(v'${f.name})`); + vars.push( + `${ops.typeToOptTuple(f.type.name, ctx)}(v'${f.name})`, + ); } else { - vars.push(`${ops.typeToTuple(f.type.name, ctx)}(v'${f.name})`); + vars.push( + `${ops.typeToTuple(f.type.name, ctx)}(v'${f.name})`, + ); } continue; } } vars.push(`v'${f.name}`); } - ctx.append(`return (${vars.join(', ')});`); + ctx.append(`return (${vars.join(", ")});`); }); }); ctx.fun(ops.typeToOptExternal(type.name, ctx), () => { - ctx.signature(`tuple ${ops.typeToOptExternal(type.name, ctx)}(tuple v)`); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.signature( + `tuple ${ops.typeToOptExternal(type.name, ctx)}(tuple v)`, + ); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { - ctx.append(`var loaded = ${ops.typeToOptTuple(type.name, ctx)}(v);`); + ctx.append( + `var loaded = ${ops.typeToOptTuple(type.name, ctx)}(v);`, + ); ctx.append(`if (null?(loaded)) {`); ctx.inIndent(() => { ctx.append(`return null();`); }); - ctx.append(`} else {`) + ctx.append(`} else {`); ctx.inIndent(() => { ctx.append(`return (loaded);`); }); ctx.append(`}`); }); }); -} \ No newline at end of file +} diff --git a/src/generator/writers/writeConstant.ts b/src/generator/writers/writeConstant.ts index 0decb728f..85712d31a 100644 --- a/src/generator/writers/writeConstant.ts +++ b/src/generator/writers/writeConstant.ts @@ -3,31 +3,46 @@ import { WriterContext } from "../Writer"; export function writeString(str: string, ctx: WriterContext) { const cell = beginCell().storeStringTail(str).endCell(); - return writeRawSlice('string', `String "${str}"`, cell, ctx); + return writeRawSlice("string", `String "${str}"`, cell, ctx); } export function writeStringCell(str: string, ctx: WriterContext) { const cell = beginCell().storeStringTail(str).endCell(); - return writeRawCell('string', `String "${str}"`, cell, ctx); + return writeRawCell("string", `String "${str}"`, cell, ctx); } export function writeComment(str: string, ctx: WriterContext) { const cell = beginCell().storeUint(0, 32).storeStringTail(str).endCell(); - return writeRawCell('comment', `Comment "${str}"`, cell, ctx); + return writeRawCell("comment", `Comment "${str}"`, cell, ctx); } export function writeAddress(address: Address, ctx: WriterContext) { - return writeRawSlice('address', address.toString(), beginCell().storeAddress(address).endCell(), ctx); + return writeRawSlice( + "address", + address.toString(), + beginCell().storeAddress(address).endCell(), + ctx, + ); } export function writeCell(cell: Cell, ctx: WriterContext) { - return writeRawCell('cell', 'Cell ' + cell.hash().toString('base64'), cell, ctx); + return writeRawCell( + "cell", + "Cell " + cell.hash().toString("base64"), + cell, + ctx, + ); } -function writeRawSlice(prefix: string, comment: string, cell: Cell, ctx: WriterContext) { - const h = cell.hash().toString('hex'); - const t = cell.toBoc({ idx: false }).toString('hex'); - const k = 'slice:' + prefix + ':' + h; +function writeRawSlice( + prefix: string, + comment: string, + cell: Cell, + ctx: WriterContext, +) { + const h = cell.hash().toString("hex"); + const t = cell.toBoc({ idx: false }).toString("hex"); + const k = "slice:" + prefix + ":" + h; if (ctx.isRendered(k)) { return `__gen_slice_${prefix}_${h}`; } @@ -35,16 +50,21 @@ function writeRawSlice(prefix: string, comment: string, cell: Cell, ctx: WriterC ctx.fun(`__gen_slice_${prefix}_${h}`, () => { ctx.signature(`slice __gen_slice_${prefix}_${h}()`); ctx.comment(`${comment}`); - ctx.context('constants'); + ctx.context("constants"); ctx.asm(`asm "B{${t}} B>boc { ctx.signature(`cell __gen_cell_${prefix}_${h}()`); ctx.comment(`${comment}`); - ctx.context('constants'); + ctx.context("constants"); ctx.asm(`asm "B{${t}} B>boc PUSHREF"`); }); return `__gen_cell_${prefix}_${h}`; -} \ No newline at end of file +} diff --git a/src/generator/writers/writeContract.ts b/src/generator/writers/writeContract.ts index afe7365d7..7a5ffb861 100644 --- a/src/generator/writers/writeContract.ts +++ b/src/generator/writers/writeContract.ts @@ -1,6 +1,10 @@ import { contractErrors } from "../../abi/errors"; import { enabledInline, enabledMasterchain } from "../../config/features"; -import { InitDescription, TypeDescription, TypeOrigin } from "../../types/types"; +import { + InitDescription, + TypeDescription, + TypeOrigin, +} from "../../types/types"; import { WriterContext } from "../Writer"; import { id, initId } from "./id"; import { ops } from "./ops"; @@ -12,16 +16,20 @@ import { writeGetter, writeStatement } from "./writeFunction"; import { writeInterfaces } from "./writeInterfaces"; import { writeReceiver, writeRouter } from "./writeRouter"; -export function writeStorageOps(type: TypeDescription, origin: TypeOrigin, ctx: WriterContext) { - +export function writeStorageOps( + type: TypeDescription, + origin: TypeOrigin, + ctx: WriterContext, +) { // Load function ctx.fun(ops.contractLoad(type.name, ctx), () => { - ctx.signature(`${resolveFuncType(type, ctx)} ${ops.contractLoad(type.name, ctx)}()`); - ctx.flag('impure'); + ctx.signature( + `${resolveFuncType(type, ctx)} ${ops.contractLoad(type.name, ctx)}()`, + ); + ctx.flag("impure"); // ctx.flag('inline'); - ctx.context('type:' + type.name + '$init'); + ctx.context("type:" + type.name + "$init"); ctx.body(() => { - // Load data slice ctx.append(`slice $sc = get_data().begin_parse();`); @@ -40,21 +48,26 @@ export function writeStorageOps(type: TypeDescription, origin: TypeOrigin, ctx: }); ctx.append(`} else {`); ctx.inIndent(() => { - // Allow only workchain deployments if (!enabledMasterchain(ctx.ctx)) { ctx.write(`;; Allow only workchain deployments`); - ctx.write(`throw_unless(${contractErrors.masterchainNotEnabled.id}, my_address().preload_uint(11) == 1024);`); + ctx.write( + `throw_unless(${contractErrors.masterchainNotEnabled.id}, my_address().preload_uint(11) == 1024);`, + ); } // Load arguments if (type.init!.args.length > 0) { - ctx.append(`(${type.init!.args.map((v) => resolveFuncType(v.type, ctx) + ' ' + v.name).join(', ')}) = $sc~${ops.reader(initId(type.name), ctx)}();`); + ctx.append( + `(${type.init!.args.map((v) => resolveFuncType(v.type, ctx) + " " + v.name).join(", ")}) = $sc~${ops.reader(initId(type.name), ctx)}();`, + ); ctx.append(`$sc.end_parse();`); } // Execute init function - ctx.append(`return ${ops.contractInit(type.name, ctx)}(${[...type.init!.args.map((v) => v.name)].join(', ')});`); + ctx.append( + `return ${ops.contractInit(type.name, ctx)}(${[...type.init!.args.map((v) => v.name)].join(", ")});`, + ); }); ctx.append(`}`); @@ -65,9 +78,9 @@ export function writeStorageOps(type: TypeDescription, origin: TypeOrigin, ctx: ctx.fun(ops.contractStore(type.name, ctx), () => { const sig = `() ${ops.contractStore(type.name, ctx)}(${resolveFuncType(type, ctx)} v)`; ctx.signature(sig); - ctx.flag('impure'); - ctx.flag('inline'); - ctx.context('type:' + type.name + '$init'); + ctx.flag("impure"); + ctx.flag("inline"); + ctx.context("type:" + type.name + "$init"); ctx.body(() => { ctx.append(`builder b = begin_cell();`); @@ -88,56 +101,76 @@ export function writeStorageOps(type: TypeDescription, origin: TypeOrigin, ctx: }); } -export function writeInit(t: TypeDescription, init: InitDescription, ctx: WriterContext) { +export function writeInit( + t: TypeDescription, + init: InitDescription, + ctx: WriterContext, +) { ctx.fun(ops.contractInit(t.name, ctx), () => { - const args = init.args.map((v) => resolveFuncType(v.type, ctx) + ' ' + id(v.name)); - const sig = `${resolveFuncType(t, ctx)} ${ops.contractInit(t.name, ctx)}(${args.join(', ')})`; + const args = init.args.map( + (v) => resolveFuncType(v.type, ctx) + " " + id(v.name), + ); + const sig = `${resolveFuncType(t, ctx)} ${ops.contractInit(t.name, ctx)}(${args.join(", ")})`; ctx.signature(sig); - ctx.flag('impure'); + ctx.flag("impure"); ctx.body(() => { // Unpack args for (const a of init.args) { if (!resolveFuncPrimitive(a.type, ctx)) { - ctx.append(`var (${resolveFuncTypeUnpack(a.type, id(a.name), ctx)}) = ${id(a.name)};`); + ctx.append( + `var (${resolveFuncTypeUnpack(a.type, id(a.name), ctx)}) = ${id(a.name)};`, + ); } } // Generate self initial tensor const initValues: string[] = []; for (let i = 0; i < t.fields.length; i++) { - let init = 'null()'; + let init = "null()"; if (t.fields[i].default !== undefined) { init = writeValue(t.fields[i].default!, ctx); } initValues.push(init); } - if (initValues.length > 0) { // Special case for empty contracts - ctx.append(`var (${resolveFuncTypeUnpack(t, id('self'), ctx)}) = (${initValues.join(', ')});`); + if (initValues.length > 0) { + // Special case for empty contracts + ctx.append( + `var (${resolveFuncTypeUnpack(t, id("self"), ctx)}) = (${initValues.join(", ")});`, + ); } else { - ctx.append(`tuple ${id('self')} = null();`); + ctx.append(`tuple ${id("self")} = null();`); } // Generate statements - const returns = resolveFuncTypeUnpack(t, id('self'), ctx); + const returns = resolveFuncTypeUnpack(t, id("self"), ctx); for (const s of init.ast.statements) { writeStatement(s, returns, null, ctx); } // Return result - if (init.ast.statements.length === 0 || init.ast.statements[init.ast.statements.length - 1].kind !== 'statement_return') { + if ( + init.ast.statements.length === 0 || + init.ast.statements[init.ast.statements.length - 1].kind !== + "statement_return" + ) { ctx.append(`return ${returns};`); } }); }); ctx.fun(ops.contractInitChild(t.name, ctx), () => { - const args = [`cell sys'`, ...init.args.map((v) => resolveFuncType(v.type, ctx) + ' ' + id(v.name))]; - const sig = `(cell, cell) ${ops.contractInitChild(t.name, ctx)}(${args.join(', ')})`; + const args = [ + `cell sys'`, + ...init.args.map( + (v) => resolveFuncType(v.type, ctx) + " " + id(v.name), + ), + ]; + const sig = `(cell, cell) ${ops.contractInitChild(t.name, ctx)}(${args.join(", ")})`; ctx.signature(sig); if (enabledInline(ctx.ctx)) { - ctx.flag('inline'); + ctx.flag("inline"); } - ctx.context('type:' + t.name + '$init'); + ctx.context("type:" + t.name + "$init"); ctx.body(() => { ctx.write(` slice sc' = sys'.begin_parse(); @@ -163,20 +196,32 @@ export function writeInit(t: TypeDescription, init: InitDescription, ctx: Writer ctx.append(); ctx.append(`;; Build cell`); ctx.append(`builder b = begin_cell();`); - ctx.append(`b = b.store_ref(begin_cell().store_dict(contracts).end_cell());`); + ctx.append( + `b = b.store_ref(begin_cell().store_dict(contracts).end_cell());`, + ); ctx.append(`b = b.store_int(false, 1);`); - const args = t.init!.args.length > 0 ? ['b', '(' + t.init!.args.map((a) => id(a.name)).join(', ') + ')'].join(', ') : 'b, null()'; + const args = + t.init!.args.length > 0 + ? [ + "b", + "(" + + t.init!.args.map((a) => id(a.name)).join(", ") + + ")", + ].join(", ") + : "b, null()"; ctx.append(`b = ${ops.writer(initId(t.name), ctx)}(${args});`); ctx.append(`return (mine, b.end_cell());`); }); }); } -export function writeMainContract(type: TypeDescription, abiLink: string, ctx: WriterContext) { - +export function writeMainContract( + type: TypeDescription, + abiLink: string, + ctx: WriterContext, +) { // Main field ctx.main(() => { - // Comments ctx.append(`;;`); ctx.append(`;; Receivers of a Contract ${type.name}`); @@ -197,7 +242,7 @@ export function writeMainContract(type: TypeDescription, abiLink: string, ctx: W // Getters for (const f of type.functions.values()) { if (f.isGetter) { - writeGetter(f, ctx) + writeGetter(f, ctx); } } @@ -227,24 +272,31 @@ export function writeMainContract(type: TypeDescription, abiLink: string, ctx: W ctx.append(``); // Render body - const hasExternal = type.receivers.find((v) => v.selector.kind.startsWith('external-')); - writeRouter(type, 'internal', ctx); + const hasExternal = type.receivers.find((v) => + v.selector.kind.startsWith("external-"), + ); + writeRouter(type, "internal", ctx); if (hasExternal) { - writeRouter(type, 'external', ctx); + writeRouter(type, "external", ctx); } // Render internal receiver - ctx.append(`() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {`); + ctx.append( + `() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {`, + ); ctx.inIndent(() => { - // Load context ctx.append(); ctx.append(`;; Context`); ctx.append(`var cs = in_msg_cell.begin_parse();`); ctx.append(`var msg_flags = cs~load_uint(4);`); // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool ctx.append(`var msg_bounced = -(msg_flags & 1);`); - ctx.append(`slice msg_sender_addr = ${ctx.used('__tact_verify_address')}(cs~load_msg_addr());`); - ctx.append(`__tact_context = (msg_bounced, msg_sender_addr, msg_value, cs);`); + ctx.append( + `slice msg_sender_addr = ${ctx.used("__tact_verify_address")}(cs~load_msg_addr());`, + ); + ctx.append( + `__tact_context = (msg_bounced, msg_sender_addr, msg_value, cs);`, + ); ctx.append(`__tact_context_sender = msg_sender_addr;`); ctx.append(); @@ -255,26 +307,29 @@ export function writeMainContract(type: TypeDescription, abiLink: string, ctx: W // Process operation ctx.append(`;; Handle operation`); - ctx.append(`int handled = self~${ops.contractRouter(type.name, 'internal')}(msg_bounced, in_msg);`); + ctx.append( + `int handled = self~${ops.contractRouter(type.name, "internal")}(msg_bounced, in_msg);`, + ); ctx.append(); // Throw if not handled ctx.append(`;; Throw if not handled`); - ctx.append(`throw_unless(${contractErrors.invalidMessage.id}, handled);`); + ctx.append( + `throw_unless(${contractErrors.invalidMessage.id}, handled);`, + ); ctx.append(); // Persist state ctx.append(`;; Persist state`); ctx.append(`${ops.contractStore(type.name, ctx)}(self);`); }); - ctx.append('}'); + ctx.append("}"); ctx.append(); // Render external receiver if (hasExternal) { ctx.append(`() recv_external(slice in_msg) impure {`); ctx.inIndent(() => { - // Load self ctx.append(`;; Load contract data`); ctx.append(`var self = ${ops.contractLoad(type.name, ctx)}();`); @@ -282,20 +337,24 @@ export function writeMainContract(type: TypeDescription, abiLink: string, ctx: W // Process operation ctx.append(`;; Handle operation`); - ctx.append(`int handled = self~${ops.contractRouter(type.name, 'external')}(in_msg);`); + ctx.append( + `int handled = self~${ops.contractRouter(type.name, "external")}(in_msg);`, + ); ctx.append(); // Throw if not handled ctx.append(`;; Throw if not handled`); - ctx.append(`throw_unless(handled, ${contractErrors.invalidMessage.id});`); + ctx.append( + `throw_unless(handled, ${contractErrors.invalidMessage.id});`, + ); ctx.append(); // Persist state ctx.append(`;; Persist state`); ctx.append(`${ops.contractStore(type.name, ctx)}(self);`); }); - ctx.append('}'); + ctx.append("}"); ctx.append(); } }); -} \ No newline at end of file +} diff --git a/src/generator/writers/writeExpression.spec.ts b/src/generator/writers/writeExpression.spec.ts index 8353045cc..bdeea876c 100644 --- a/src/generator/writers/writeExpression.spec.ts +++ b/src/generator/writers/writeExpression.spec.ts @@ -1,5 +1,8 @@ import { __DANGER_resetNodeId } from "../../grammar/ast"; -import { getStaticFunction, resolveDescriptors } from "../../types/resolveDescriptors"; +import { + getStaticFunction, + resolveDescriptors, +} from "../../types/resolveDescriptors"; import { WriterContext } from "../Writer"; import { writeExpression } from "./writeExpression"; import { openContext } from "../../grammar/store"; @@ -45,49 +48,53 @@ fun main() { `; const golden: string[] = [ - '1', - '2', - '($a + $b)', - '($a + ($b * $c))', - '($a + ($b / $c))', - 'true', - 'false', - '( (( (($a > 1)) ? (true) : (( (($b < 2)) ? (($c == 3)) : (false) )) )) ? (true) : ((~ ( (( (($d != 4)) ? (true) : (false) )) ? ((~ false)) : (false) ))) )', - '$global_f1($a)', - '$A$_constructor_a_b(1, 2)', + "1", + "2", + "($a + $b)", + "($a + ($b * $c))", + "($a + ($b / $c))", + "true", + "false", + "( (( (($a > 1)) ? (true) : (( (($b < 2)) ? (($c == 3)) : (false) )) )) ? (true) : ((~ ( (( (($d != 4)) ? (true) : (false) )) ? ((~ false)) : (false) ))) )", + "$global_f1($a)", + "$A$_constructor_a_b(1, 2)", `$j'a`, - '$A$_get_b($A$_constructor_a_b(1, 2))', + "$A$_get_b($A$_constructor_a_b(1, 2))", `((- $j'b) + $a)`, `(((- $j'b) + $a) + (+ $b))`, - 'null()', - '(__tact_not_null($o) + 1)', - `$A$_store_cell(($j'a, $j'b))` -] + "null()", + "(__tact_not_null($o) + 1)", + `$A$_store_cell(($j'a, $j'b))`, +]; -describe('writeExpression', () => { +describe("writeExpression", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should write expression', () => { - let ctx = openContext(new CompilerContext(), [{ code: code, path: '', origin: 'user' }], []); + it("should write expression", () => { + let ctx = openContext( + new CompilerContext(), + [{ code: code, path: "", origin: "user" }], + [], + ); ctx = resolveDescriptors(ctx); ctx = resolveStatements(ctx); - const main = getStaticFunction(ctx, 'main'); - if (main.ast.kind !== 'def_function') { - throw Error('Unexpected function kind'); + const main = getStaticFunction(ctx, "main"); + if (main.ast.kind !== "def_function") { + throw Error("Unexpected function kind"); } let i = 0; for (const s of main.ast.statements!) { - if (s.kind !== 'statement_let') { - throw Error('Unexpected statement kind'); + if (s.kind !== "statement_let") { + throw Error("Unexpected statement kind"); } - const wctx = new WriterContext(ctx, 'Contract1'); - wctx.fun('$main', () => { + const wctx = new WriterContext(ctx, "Contract1"); + wctx.fun("$main", () => { wctx.body(() => { expect(writeExpression(s.expression, wctx)).toBe(golden[i]); }); }); - i++ + i++; } }); -}); \ No newline at end of file +}); diff --git a/src/generator/writers/writeExpression.ts b/src/generator/writers/writeExpression.ts index 84c613a1f..747516fa6 100644 --- a/src/generator/writers/writeExpression.ts +++ b/src/generator/writers/writeExpression.ts @@ -1,7 +1,16 @@ import { ASTExpression, throwError } from "../../grammar/ast"; import { getExpType } from "../../types/resolveExpression"; -import { getStaticConstant, getStaticFunction, getType, hasStaticConstant } from "../../types/resolveDescriptors"; -import { FieldDescription, printTypeRef, TypeDescription } from "../../types/types"; +import { + getStaticConstant, + getStaticFunction, + getType, + hasStaticConstant, +} from "../../types/resolveDescriptors"; +import { + FieldDescription, + printTypeRef, + TypeDescription, +} from "../../types/types"; import { WriterContext } from "../Writer"; import { resolveFuncTypeUnpack } from "./resolveFuncTypeUnpack"; import { MapFunctions } from "../../abi/map"; @@ -16,17 +25,17 @@ import { tryExpressionIntrinsics } from "../intrinsics/tryExpressionIntrinsics"; import { writeCastedExpression } from "./writeFunction"; function isNull(f: ASTExpression) { - if (f.kind === 'null') { + if (f.kind === "null") { return true; } return false; } function tryExtractPath(f: ASTExpression): string[] | null { - if (f.kind === 'id') { + if (f.kind === "id") { return [f.value]; } - if (f.kind === 'op_field') { + if (f.kind === "op_field") { const p = tryExtractPath(f.src); if (p) { return [...p, f.name]; @@ -37,11 +46,14 @@ function tryExtractPath(f: ASTExpression): string[] | null { return null; } -function writeStructConstructor(type: TypeDescription, args: string[], ctx: WriterContext) { - +function writeStructConstructor( + type: TypeDescription, + args: string[], + ctx: WriterContext, +) { // Check for duplicates const name = ops.typeContsturctor(type.name, args, ctx); - const renderKey = '$constructor$' + type.name + '$' + args.join(','); + const renderKey = "$constructor$" + type.name + "$" + args.join(","); if (ctx.isRendered(renderKey)) { return name; } @@ -49,10 +61,10 @@ function writeStructConstructor(type: TypeDescription, args: string[], ctx: Writ // Generate constructor ctx.fun(name, () => { - const sig = `(${resolveFuncType(type, ctx)}) ${name}(${args.map((v) => resolveFuncType(type.fields.find((v2) => v2.name === v)!.type, ctx) + ' ' + v).join(', ')})`; + const sig = `(${resolveFuncType(type, ctx)}) ${name}(${args.map((v) => resolveFuncType(type.fields.find((v2) => v2.name === v)!.type, ctx) + " " + v).join(", ")})`; ctx.signature(sig); - ctx.flag('inline'); - ctx.context('type:' + type.name); + ctx.flag("inline"); + ctx.context("type:" + type.name); ctx.body(() => { // Create expressions const expressions = type.fields.map((v) => { @@ -62,32 +74,37 @@ function writeStructConstructor(type: TypeDescription, args: string[], ctx: Writ } else if (v.default !== undefined) { return writeValue(v.default, ctx); } else { - throw Error(`Missing argument for field "${v.name}" in struct "${type.name}"`); // Must not happen + throw Error( + `Missing argument for field "${v.name}" in struct "${type.name}"`, + ); // Must not happen } }, ctx); - ctx.append(`return (${expressions.join(', ')});`); + ctx.append(`return (${expressions.join(", ")});`); }); }); return name; } -export function writeValue(s: bigint | string | boolean | Address | Cell | null, ctx: WriterContext): string { - if (typeof s === 'bigint') { +export function writeValue( + s: bigint | string | boolean | Address | Cell | null, + ctx: WriterContext, +): string { + if (typeof s === "bigint") { return s.toString(10); } - if (typeof s === 'string') { + if (typeof s === "string") { const id = writeString(s, ctx); ctx.used(id); return `${id}()`; } - if (typeof s === 'boolean') { - return s ? 'true' : 'false'; + if (typeof s === "boolean") { + return s ? "true" : "false"; } if (Address.isAddress(s)) { const res = writeAddress(s, ctx); ctx.used(res); - return res + '()'; + return res + "()"; } if (s instanceof Cell) { const res = writeCell(s, ctx); @@ -95,13 +112,12 @@ export function writeValue(s: bigint | string | boolean | Address | Cell | null, return `${res}()`; } if (s === null) { - return 'null()'; + return "null()"; } - throw Error('Invalid value'); + throw Error("Invalid value"); } export function writeExpression(f: ASTExpression, ctx: WriterContext): string { - // // Try intrinsics // @@ -115,15 +131,15 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Boolean // - if (f.kind === 'boolean') { - return f.value ? 'true' : 'false'; + if (f.kind === "boolean") { + return f.value ? "true" : "false"; } // // Number // - if (f.kind === 'number') { + if (f.kind === "number") { return f.value.toString(10); } @@ -131,44 +147,47 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // String literal // - if (f.kind === 'string') { - const s = f.value.replace(/\\\\|\\"|\\n|\\r|\\t|\\v|\\b|\\f|\\u{([0-9A-Fa-f]+)}|\\u([0-9A-Fa-f]{4})|\\x([0-9A-Fa-f]{2})/g, (match, unicodeCodePoint, unicodeEscape, hexEscape) => { - switch (match) { - case '\\\\': - return '\\'; - case '\\"': - return '"'; - case '\\n': - return '\n'; - case '\\r': - return '\r'; - case '\\t': - return '\t'; - case '\\v': - return '\v'; - case '\\b': - return '\b'; - case '\\f': - return '\f'; - default: - // Handle Unicode code point escape - if (unicodeCodePoint) { - const codePoint = parseInt(unicodeCodePoint, 16); - return String.fromCodePoint(codePoint); - } - // Handle Unicode escape - if (unicodeEscape) { - const codeUnit = parseInt(unicodeEscape, 16); - return String.fromCharCode(codeUnit); - } - // Handle hex escape - if (hexEscape) { - const hexValue = parseInt(hexEscape, 16); - return String.fromCharCode(hexValue); - } - return match; - } - }); + if (f.kind === "string") { + const s = f.value.replace( + /\\\\|\\"|\\n|\\r|\\t|\\v|\\b|\\f|\\u{([0-9A-Fa-f]+)}|\\u([0-9A-Fa-f]{4})|\\x([0-9A-Fa-f]{2})/g, + (match, unicodeCodePoint, unicodeEscape, hexEscape) => { + switch (match) { + case "\\\\": + return "\\"; + case '\\"': + return '"'; + case "\\n": + return "\n"; + case "\\r": + return "\r"; + case "\\t": + return "\t"; + case "\\v": + return "\v"; + case "\\b": + return "\b"; + case "\\f": + return "\f"; + default: + // Handle Unicode code point escape + if (unicodeCodePoint) { + const codePoint = parseInt(unicodeCodePoint, 16); + return String.fromCodePoint(codePoint); + } + // Handle Unicode escape + if (unicodeEscape) { + const codeUnit = parseInt(unicodeEscape, 16); + return String.fromCharCode(codeUnit); + } + // Handle hex escape + if (hexEscape) { + const hexValue = parseInt(hexEscape, 16); + return String.fromCharCode(hexValue); + } + return match; + } + }, + ); const id = writeString(s, ctx); ctx.used(id); return `${id}()`; @@ -178,28 +197,28 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Null // - if (f.kind === 'null') { - return 'null()'; + if (f.kind === "null") { + return "null()"; } // // ID Reference // - if (f.kind === 'id') { + if (f.kind === "id") { const t = getExpType(ctx.ctx, f); // Handle packed type - if (t.kind === 'ref') { + if (t.kind === "ref") { const tt = getType(ctx.ctx, t.name); - if (tt.kind === 'contract' || tt.kind === 'struct') { + if (tt.kind === "contract" || tt.kind === "struct") { return resolveFuncTypeUnpack(t, id(f.value), ctx); } } - if (t.kind === 'ref_bounced') { + if (t.kind === "ref_bounced") { const tt = getType(ctx.ctx, t.name); - if (tt.kind === 'struct') { + if (tt.kind === "struct") { return resolveFuncTypeUnpack(t, id(f.value), ctx, false, true); } } @@ -218,24 +237,23 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // NOTE: We always wrap in parenthesis to avoid operator precedence issues // - if (f.kind === 'op_binary') { - + if (f.kind === "op_binary") { // Special case for non-integer types and nullable - if (f.op === '==' || f.op === '!=') { + if (f.op === "==" || f.op === "!=") { if (isNull(f.left) && isNull(f.right)) { - if (f.op === '==') { - return 'true'; + if (f.op === "==") { + return "true"; } else { - return 'false'; + return "false"; } } else if (isNull(f.left) && !isNull(f.right)) { - if (f.op === '==') { + if (f.op === "==") { return `null?(${writeExpression(f.right, ctx)})`; } else { return `(~ null?(${writeExpression(f.right, ctx)}))`; } } else if (!isNull(f.left) && isNull(f.right)) { - if (f.op === '==') { + if (f.op === "==") { return `null?(${writeExpression(f.left, ctx)})`; } else { return `(~ null?(${writeExpression(f.left, ctx)}))`; @@ -249,14 +267,14 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Case for addresses equality if ( - lt.kind === 'ref' && - rt.kind === 'ref' && - lt.name === 'Address' && - rt.name === 'Address' + lt.kind === "ref" && + rt.kind === "ref" && + lt.name === "Address" && + rt.name === "Address" ) { - let prefix = ''; - if (f.op == '!=') { - prefix = '~ '; + let prefix = ""; + if (f.op == "!=") { + prefix = "~ "; } if (lt.optional && rt.optional) { ctx.used(`__tact_slice_eq_bits_nullable`); @@ -276,12 +294,12 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Case for cells eqality if ( - lt.kind === 'ref' && - rt.kind === 'ref' && - lt.name === 'Cell' && - rt.name === 'Cell' + lt.kind === "ref" && + rt.kind === "ref" && + lt.name === "Cell" && + rt.name === "Cell" ) { - const op = f.op === '==' ? 'eq' : 'neq'; + const op = f.op === "==" ? "eq" : "neq"; if (lt.optional && rt.optional) { ctx.used(`__tact_cell_${op}_nullable`); return `__tact_cell_${op}_nullable(${writeExpression(f.left, ctx)}, ${writeExpression(f.right, ctx)})`; @@ -300,12 +318,12 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Case for slices and strings equality if ( - lt.kind === 'ref' && - rt.kind === 'ref' && + lt.kind === "ref" && + rt.kind === "ref" && lt.name === rt.name && - (lt.name === 'Slice' || lt.name === 'String') + (lt.name === "Slice" || lt.name === "String") ) { - const op = f.op === '==' ? 'eq' : 'neq'; + const op = f.op === "==" ? "eq" : "neq"; if (lt.optional && rt.optional) { ctx.used(`__tact_slice_${op}_nullable`); return `__tact_slice_${op}_nullable(${writeExpression(f.left, ctx)}, ${writeExpression(f.right, ctx)})`; @@ -323,26 +341,29 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { } // Case for maps eqality - if (lt.kind === 'map' && rt.kind === 'map') { - const op = f.op === '==' ? 'eq' : 'neq'; + if (lt.kind === "map" && rt.kind === "map") { + const op = f.op === "==" ? "eq" : "neq"; ctx.used(`__tact_cell_${op}_nullable`); return `__tact_cell_${op}_nullable(${writeExpression(f.left, ctx)}, ${writeExpression(f.right, ctx)})`; } // Check for int or boolean types - if (lt.kind !== 'ref' - || rt.kind !== 'ref' - || (lt.name !== 'Int' && lt.name !== 'Bool') - || (rt.name !== 'Int' && rt.name !== 'Bool') + if ( + lt.kind !== "ref" || + rt.kind !== "ref" || + (lt.name !== "Int" && lt.name !== "Bool") || + (rt.name !== "Int" && rt.name !== "Bool") ) { const file = f.ref.file; const loc_info = f.ref.interval.getLineAndColumn(); - throw Error(`(Internal Compiler Error) Invalid types for binary operation: ${file}:${loc_info.lineNum}:${loc_info.colNum}`); // Should be unreachable + throw Error( + `(Internal Compiler Error) Invalid types for binary operation: ${file}:${loc_info.lineNum}:${loc_info.colNum}`, + ); // Should be unreachable } // Case for ints equality - if (f.op === '==' || f.op === '!=') { - const op = f.op === '==' ? 'eq' : 'neq'; + if (f.op === "==" || f.op === "!=") { + const op = f.op === "==" ? "eq" : "neq"; if (lt.optional && rt.optional) { ctx.used(`__tact_int_${op}_nullable`); return `__tact_int_${op}_nullable(${writeExpression(f.left, ctx)}, ${writeExpression(f.right, ctx)})`; @@ -355,7 +376,7 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { ctx.used(`__tact_int_${op}_nullable_one`); return `__tact_int_${op}_nullable_one(${writeExpression(f.right, ctx)}, ${writeExpression(f.left, ctx)})`; } - if (f.op === '==') { + if (f.op === "==") { return `(${writeExpression(f.left, ctx)} == ${writeExpression(f.right, ctx)})`; } else { return `(${writeExpression(f.left, ctx)} != ${writeExpression(f.right, ctx)})`; @@ -363,47 +384,55 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { } // Case for "&&" operator - if (f.op === '&&') { + if (f.op === "&&") { return `( (${writeExpression(f.left, ctx)}) ? (${writeExpression(f.right, ctx)}) : (false) )`; } // Case for "||" operator - if (f.op === '||') { + if (f.op === "||") { return `( (${writeExpression(f.left, ctx)}) ? (true) : (${writeExpression(f.right, ctx)}) )`; } // Other ops let op: string; - if (f.op === '*') { - op = '*'; - } else if (f.op === '/') { - op = '/'; - } else if (f.op === '%') { - op = '%'; - } else if (f.op === '+') { - op = '+'; - } else if (f.op === '-') { - op = '-'; - } else if (f.op === '<') { - op = '<'; - } else if (f.op === '<=') { - op = '<='; - } else if (f.op === '>') { - op = '>'; - } else if (f.op === '>=') { - op = '>='; - } else if (f.op === '<<') { - op = '<<'; - } else if (f.op === '>>') { - op = '>>'; - } else if (f.op === '&') { - op = '&'; - } else if (f.op === '|') { - op = '|'; + if (f.op === "*") { + op = "*"; + } else if (f.op === "/") { + op = "/"; + } else if (f.op === "%") { + op = "%"; + } else if (f.op === "+") { + op = "+"; + } else if (f.op === "-") { + op = "-"; + } else if (f.op === "<") { + op = "<"; + } else if (f.op === "<=") { + op = "<="; + } else if (f.op === ">") { + op = ">"; + } else if (f.op === ">=") { + op = ">="; + } else if (f.op === "<<") { + op = "<<"; + } else if (f.op === ">>") { + op = ">>"; + } else if (f.op === "&") { + op = "&"; + } else if (f.op === "|") { + op = "|"; } else { - throwError('Unknown binary operator: ' + f.op, f.ref); + throwError("Unknown binary operator: " + f.op, f.ref); } - return '(' + writeExpression(f.left, ctx) + ' ' + op + ' ' + writeExpression(f.right, ctx) + ')'; + return ( + "(" + + writeExpression(f.left, ctx) + + " " + + op + + " " + + writeExpression(f.right, ctx) + + ")" + ); } // @@ -411,36 +440,35 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // NOTE: We always wrap in parenthesis to avoid operator precedence issues // - if (f.kind === 'op_unary') { - + if (f.kind === "op_unary") { // NOTE: Logical not is written as a bitwise not - if (f.op === '!') { - return '(~ ' + writeExpression(f.right, ctx) + ')'; + if (f.op === "!") { + return "(~ " + writeExpression(f.right, ctx) + ")"; } - if (f.op === '-') { - return '(- ' + writeExpression(f.right, ctx) + ')'; + if (f.op === "-") { + return "(- " + writeExpression(f.right, ctx) + ")"; } - if (f.op === '+') { - return '(+ ' + writeExpression(f.right, ctx) + ')'; + if (f.op === "+") { + return "(+ " + writeExpression(f.right, ctx) + ")"; } // NOTE: Assert function that ensures that the value is not null - if (f.op === '!!') { + if (f.op === "!!") { const t = getExpType(ctx.ctx, f.right); - if (t.kind === 'ref') { + if (t.kind === "ref") { const tt = getType(ctx.ctx, t.name); - if (tt.kind === 'struct') { + if (tt.kind === "struct") { return `${ops.typeNotNull(tt.name, ctx)}(${writeExpression(f.right, ctx)})`; } } - ctx.used('__tact_not_null'); - return `${ctx.used('__tact_not_null')}(${writeExpression(f.right, ctx)})`; + ctx.used("__tact_not_null"); + return `${ctx.used("__tact_not_null")}(${writeExpression(f.right, ctx)})`; } - throwError('Unknown unary operator: ' + f.op, f.ref); + throwError("Unknown unary operator: " + f.op, f.ref); } // @@ -448,12 +476,17 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // NOTE: this branch resolves "a.b", where "a" is an expression and "b" is a field name // - if (f.kind === 'op_field') { - + if (f.kind === "op_field") { // Resolve the type of the expression const src = getExpType(ctx.ctx, f.src); - if (src === null || ((src.kind !== 'ref' || src.optional) && (src.kind !== 'ref_bounced'))) { - throwError(`Cannot access field of non-struct type: ${printTypeRef(src)}`, f.ref); + if ( + src === null || + ((src.kind !== "ref" || src.optional) && src.kind !== "ref_bounced") + ) { + throwError( + `Cannot access field of non-struct type: ${printTypeRef(src)}`, + f.ref, + ); } const srcT = getType(ctx.ctx, src.name); @@ -461,22 +494,23 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { let fields: FieldDescription[]; fields = srcT.fields; - if (src.kind === 'ref_bounced') { + if (src.kind === "ref_bounced") { fields = fields.slice(0, srcT.partialFieldCount); } const field = fields.find((v) => v.name === f.name)!; const cst = srcT.constants.find((v) => v.name === f.name)!; if (!field && !cst) { - throwError(`Cannot find field "${f.name}" in struct "${srcT.name}"`, f.ref); + throwError( + `Cannot find field "${f.name}" in struct "${srcT.name}"`, + f.ref, + ); } if (field) { - // Trying to resolve field as a path const path = tryExtractPath(f); if (path) { - // Prepare path const convertedPath: string[] = []; convertedPath.push(id(path[0])); @@ -484,9 +518,9 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { const idd = convertedPath.join(`'`); // Special case for structs - if (field.type.kind === 'ref') { + if (field.type.kind === "ref") { const ft = getType(ctx.ctx, field.type.name); - if (ft.kind === 'struct' || ft.kind === 'contract') { + if (ft.kind === "struct" || ft.kind === "contract") { return resolveFuncTypeUnpack(field.type, idd, ctx); } } @@ -505,76 +539,100 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Static Function Call // - if (f.kind === 'op_static_call') { - + if (f.kind === "op_static_call") { // Check global functions if (GlobalFunctions.has(f.name)) { - return GlobalFunctions.get(f.name)!.generate(ctx, + return GlobalFunctions.get(f.name)!.generate( + ctx, f.args.map((v) => getExpType(ctx.ctx, v)), f.args, - f.ref); + f.ref, + ); } const sf = getStaticFunction(ctx.ctx, f.name); let n = ops.global(f.name); - if (sf.ast.kind === 'def_native_function') { + if (sf.ast.kind === "def_native_function") { n = sf.ast.nativeName; - if (n.startsWith('__tact')) { + if (n.startsWith("__tact")) { ctx.used(n); } } else { ctx.used(n); } - return n + '(' + f.args.map((a, i) => writeCastedExpression(a, sf.args[i].type, ctx)).join(', ') + ')'; + return ( + n + + "(" + + f.args + .map((a, i) => writeCastedExpression(a, sf.args[i].type, ctx)) + .join(", ") + + ")" + ); } // // Struct Constructor // - if (f.kind === 'op_new') { + if (f.kind === "op_new") { const src = getType(ctx.ctx, f.type); // Write a constructor - const id = writeStructConstructor(src, f.args.map((v) => v.name), ctx); + const id = writeStructConstructor( + src, + f.args.map((v) => v.name), + ctx, + ); ctx.used(id); // Write an expression - const expressions = f.args.map((v) => writeCastedExpression(v.exp, src.fields.find((v2) => v2.name === v.name)!.type, ctx), ctx); - return `${id}(${expressions.join(', ')})`; + const expressions = f.args.map( + (v) => + writeCastedExpression( + v.exp, + src.fields.find((v2) => v2.name === v.name)!.type, + ctx, + ), + ctx, + ); + return `${id}(${expressions.join(", ")})`; } // // Object-based function call // - if (f.kind === 'op_call') { - + if (f.kind === "op_call") { // Resolve source type const src = getExpType(ctx.ctx, f.src); if (src === null) { - throwError(`Cannot call function of non - direct type: ${printTypeRef(src)} `, f.ref); + throwError( + `Cannot call function of non - direct type: ${printTypeRef(src)} `, + f.ref, + ); } // Reference type - if (src.kind === 'ref') { - + if (src.kind === "ref") { if (src.optional) { - throwError(`Cannot call function of non - direct type: ${printTypeRef(src)} `, f.ref); + throwError( + `Cannot call function of non - direct type: ${printTypeRef(src)} `, + f.ref, + ); } // Render function call const t = getType(ctx.ctx, src.name); // Check struct ABI - if (t.kind === 'struct') { + if (t.kind === "struct") { if (StructFunctions.has(f.name)) { const abi = StructFunctions.get(f.name)!; return abi.generate( ctx, [src, ...f.args.map((v) => getExpType(ctx.ctx, v))], [f.src, ...f.args], - f.ref + f.ref, ); } } @@ -582,27 +640,35 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Resolve function const ff = t.functions.get(f.name)!; let name = ops.extension(src.name, f.name); - if (ff.ast.kind === 'def_function') { + if (ff.ast.kind === "def_function") { ctx.used(name); } else { name = ff.ast.nativeName; - if (name.startsWith('__tact')) { + if (name.startsWith("__tact")) { ctx.used(name); } } // Render arguments - let renderedArguments = f.args.map((a, i) => writeCastedExpression(a, ff.args[i].type, ctx)); + let renderedArguments = f.args.map((a, i) => + writeCastedExpression(a, ff.args[i].type, ctx), + ); - // Hack to replace a single struct argument to a tensor wrapper since otherwise + // Hack to replace a single struct argument to a tensor wrapper since otherwise // func would convert (int) type to just int and break mutating functions if (ff.isMutating) { if (f.args.length === 1) { const t = getExpType(ctx.ctx, f.args[0]); - if (t.kind === 'ref') { + if (t.kind === "ref") { const tt = getType(ctx.ctx, t.name); - if ((tt.kind === 'contract' || tt.kind === 'struct') && (ff.args[0].type.kind === 'ref') && (!ff.args[0].type.optional)) { - renderedArguments = [`${ops.typeTensorCast(tt.name, ctx)}(${renderedArguments[0]})`]; + if ( + (tt.kind === "contract" || tt.kind === "struct") && + ff.args[0].type.kind === "ref" && + !ff.args[0].type.optional + ) { + renderedArguments = [ + `${ops.typeTensorCast(tt.name, ctx)}(${renderedArguments[0]})`, + ]; } } } @@ -611,42 +677,50 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Render const s = writeExpression(f.src, ctx); if (ff.isMutating) { - return `${s}~${name}(${renderedArguments.join(', ')})`; + return `${s}~${name}(${renderedArguments.join(", ")})`; } else { - return `${name}(${[s, ...renderedArguments].join(', ')})`; + return `${name}(${[s, ...renderedArguments].join(", ")})`; } } // Map types - if (src.kind === 'map') { + if (src.kind === "map") { if (!MapFunctions.has(f.name)) { throwError(`Map function "${f.name}" not found`, f.ref); } const abf = MapFunctions.get(f.name)!; - return abf.generate(ctx, [src, ...f.args.map((v) => getExpType(ctx.ctx, v))], [f.src, ...f.args], f.ref); + return abf.generate( + ctx, + [src, ...f.args.map((v) => getExpType(ctx.ctx, v))], + [f.src, ...f.args], + f.ref, + ); } - if (src.kind === 'ref_bounced') { + if (src.kind === "ref_bounced") { throw Error("Unimplemented"); } - throwError(`Cannot call function of non - direct type: ${printTypeRef(src)} `, f.ref); + throwError( + `Cannot call function of non - direct type: ${printTypeRef(src)} `, + f.ref, + ); } // // Init of // - if (f.kind === 'init_of') { + if (f.kind === "init_of") { const type = getType(ctx.ctx, f.name); - return `${ops.contractInitChild(f.name, ctx)}(${['__tact_context_sys', ...f.args.map((a, i) => writeCastedExpression(a, type.init!.args[i].type, ctx))].join(', ')})`; + return `${ops.contractInitChild(f.name, ctx)}(${["__tact_context_sys", ...f.args.map((a, i) => writeCastedExpression(a, type.init!.args[i].type, ctx))].join(", ")})`; } // // Ternary operator // - if (f.kind === 'conditional') { + if (f.kind === "conditional") { return `(${writeExpression(f.condition, ctx)} ? ${writeExpression(f.thenBranch, ctx)} : ${writeExpression(f.elseBranch, ctx)})`; } @@ -654,5 +728,5 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string { // Unreachable // - throw Error('Unknown expression'); -} \ No newline at end of file + throw Error("Unknown expression"); +} diff --git a/src/generator/writers/writeFunction.ts b/src/generator/writers/writeFunction.ts index 7bcfdc4ff..fa00be1da 100644 --- a/src/generator/writers/writeFunction.ts +++ b/src/generator/writers/writeFunction.ts @@ -14,26 +14,43 @@ import { cast } from "./cast"; import { resolveFuncTupledType } from "./resolveFuncTupledType"; import { ops } from "./ops"; -export function writeCastedExpression(expression: ASTExpression, to: TypeRef, ctx: WriterContext) { +export function writeCastedExpression( + expression: ASTExpression, + to: TypeRef, + ctx: WriterContext, +) { const expr = getExpType(ctx.ctx, expression); return cast(expr, to, writeExpression(expression, ctx), ctx); // Cast for nullable } -export function unwrapExternal(targetName: string, sourceName: string, type: TypeRef, ctx: WriterContext) { - if (type.kind === 'ref') { +export function unwrapExternal( + targetName: string, + sourceName: string, + type: TypeRef, + ctx: WriterContext, +) { + if (type.kind === "ref") { const t = getType(ctx.ctx, type.name); - if (t.kind === 'struct') { + if (t.kind === "struct") { if (type.optional) { - ctx.append(`${resolveFuncType(type, ctx)} ${targetName} = ${ops.typeFromOptTuple(t.name, ctx)}(${sourceName});`); + ctx.append( + `${resolveFuncType(type, ctx)} ${targetName} = ${ops.typeFromOptTuple(t.name, ctx)}(${sourceName});`, + ); } else { - ctx.append(`${resolveFuncType(type, ctx)} ${targetName} = ${ops.typeFromTuple(t.name, ctx)}(${sourceName});`); + ctx.append( + `${resolveFuncType(type, ctx)} ${targetName} = ${ops.typeFromTuple(t.name, ctx)}(${sourceName});`, + ); } return; - } else if (t.kind === 'primitive' && t.name === 'Address') { + } else if (t.kind === "primitive" && t.name === "Address") { if (type.optional) { - ctx.append(`${resolveFuncType(type, ctx)} ${targetName} = null?(${sourceName}) ? null() : ${ctx.used(`__tact_verify_address`)}(${sourceName});`); + ctx.append( + `${resolveFuncType(type, ctx)} ${targetName} = null?(${sourceName}) ? null() : ${ctx.used(`__tact_verify_address`)}(${sourceName});`, + ); } else { - ctx.append(`${resolveFuncType(type, ctx)} ${targetName} = ${ctx.used(`__tact_verify_address`)}(${sourceName});`); + ctx.append( + `${resolveFuncType(type, ctx)} ${targetName} = ${ctx.used(`__tact_verify_address`)}(${sourceName});`, + ); } return; } @@ -41,10 +58,14 @@ export function unwrapExternal(targetName: string, sourceName: string, type: Typ ctx.append(`${resolveFuncType(type, ctx)} ${targetName} = ${sourceName};`); } -export function writeStatement(f: ASTStatement, self: string | null, returns: TypeRef | null, ctx: WriterContext) { - if (f.kind === 'statement_return') { +export function writeStatement( + f: ASTStatement, + self: string | null, + returns: TypeRef | null, + ctx: WriterContext, +) { + if (f.kind === "statement_return") { if (f.expression) { - // Format expression const result = writeCastedExpression(f.expression, returns!, ctx); @@ -62,54 +83,66 @@ export function writeStatement(f: ASTStatement, self: string | null, returns: Ty } } return; - } else if (f.kind === 'statement_let') { - + } else if (f.kind === "statement_let") { // Contract/struct case const t = resolveTypeRef(ctx.ctx, f.type); - if (t.kind === 'ref') { + if (t.kind === "ref") { const tt = getType(ctx.ctx, t.name); - if (tt.kind === 'contract' || tt.kind === 'struct') { + if (tt.kind === "contract" || tt.kind === "struct") { if (t.optional) { - ctx.append(`tuple ${id(f.name)} = ${writeCastedExpression(f.expression, t, ctx)};`); + ctx.append( + `tuple ${id(f.name)} = ${writeCastedExpression(f.expression, t, ctx)};`, + ); } else { - ctx.append(`var ${resolveFuncTypeUnpack(t, id(f.name), ctx)} = ${writeCastedExpression(f.expression, t, ctx)};`); + ctx.append( + `var ${resolveFuncTypeUnpack(t, id(f.name), ctx)} = ${writeCastedExpression(f.expression, t, ctx)};`, + ); } return; } } - ctx.append(`${resolveFuncType(t, ctx)} ${id(f.name)} = ${writeCastedExpression(f.expression, t, ctx)};`); + ctx.append( + `${resolveFuncType(t, ctx)} ${id(f.name)} = ${writeCastedExpression(f.expression, t, ctx)};`, + ); return; - } else if (f.kind === 'statement_assign') { - + } else if (f.kind === "statement_assign") { // Prepare lvalue - const path = f.path.map((v, i) => (i === 0) ? id(v.name) : v.name).join(`'`); + const path = f.path + .map((v, i) => (i === 0 ? id(v.name) : v.name)) + .join(`'`); // Contract/struct case const t = getExpType(ctx.ctx, f.path[f.path.length - 1]); - if (t.kind === 'ref') { + if (t.kind === "ref") { const tt = getType(ctx.ctx, t.name); - if (tt.kind === 'contract' || tt.kind === 'struct') { - ctx.append(`${resolveFuncTypeUnpack(t, `${path}`, ctx)} = ${writeCastedExpression(f.expression, t, ctx)};`); + if (tt.kind === "contract" || tt.kind === "struct") { + ctx.append( + `${resolveFuncTypeUnpack(t, `${path}`, ctx)} = ${writeCastedExpression(f.expression, t, ctx)};`, + ); return; } } ctx.append(`${path} = ${writeCastedExpression(f.expression, t, ctx)};`); return; - } else if (f.kind === 'statement_augmentedassign') { - const path = f.path.map((v, i) => (i === 0) ? id(v.name) : v.name).join(`'`); + } else if (f.kind === "statement_augmentedassign") { + const path = f.path + .map((v, i) => (i === 0 ? id(v.name) : v.name)) + .join(`'`); const t = getExpType(ctx.ctx, f.path[f.path.length - 1]); - ctx.append(`${path} = ${cast(t, t, `${path} ${f.op} ${writeExpression(f.expression, ctx)}`, ctx)};`); + ctx.append( + `${path} = ${cast(t, t, `${path} ${f.op} ${writeExpression(f.expression, ctx)}`, ctx)};`, + ); return; - } else if (f.kind === 'statement_condition') { + } else if (f.kind === "statement_condition") { writeCondition(f, self, false, returns, ctx); return; - } else if (f.kind === 'statement_expression') { + } else if (f.kind === "statement_expression") { const exp = writeExpression(f.expression, ctx); ctx.append(`${exp};`); return; - } else if (f.kind === 'statement_while') { + } else if (f.kind === "statement_while") { ctx.append(`while (${writeExpression(f.condition, ctx)}) {`); ctx.inIndent(() => { for (const s of f.statements) { @@ -118,7 +151,7 @@ export function writeStatement(f: ASTStatement, self: string | null, returns: Ty }); ctx.append(`}`); return; - } else if (f.kind === 'statement_until') { + } else if (f.kind === "statement_until") { ctx.append(`do {`); ctx.inIndent(() => { for (const s of f.statements) { @@ -127,7 +160,7 @@ export function writeStatement(f: ASTStatement, self: string | null, returns: Ty }); ctx.append(`} until (${writeExpression(f.condition, ctx)});`); return; - } else if (f.kind === 'statement_repeat') { + } else if (f.kind === "statement_repeat") { ctx.append(`repeat (${writeExpression(f.condition, ctx)}) {`); ctx.inIndent(() => { for (const s of f.statements) { @@ -138,11 +171,19 @@ export function writeStatement(f: ASTStatement, self: string | null, returns: Ty return; } - throw Error('Unknown statement kind'); + throw Error("Unknown statement kind"); } -function writeCondition(f: ASTCondition, self: string | null, elseif: boolean, returns: TypeRef | null, ctx: WriterContext) { - ctx.append(`${(elseif ? '} else' : '')}if (${writeExpression(f.expression, ctx)}) {`); +function writeCondition( + f: ASTCondition, + self: string | null, + elseif: boolean, + returns: TypeRef | null, + ctx: WriterContext, +) { + ctx.append( + `${elseif ? "} else" : ""}if (${writeExpression(f.expression, ctx)}) {`, + ); ctx.inIndent(() => { for (const s of f.trueStatements) { writeStatement(s, self, returns, ctx); @@ -164,9 +205,8 @@ function writeCondition(f: ASTCondition, self: string | null, elseif: boolean, r } export function writeFunction(f: FunctionDescription, ctx: WriterContext) { - // Do not write native functions - if (f.ast.kind === 'def_native_function') { + if (f.ast.kind === "def_native_function") { return; } const fd = f.ast; @@ -178,42 +218,48 @@ export function writeFunction(f: FunctionDescription, ctx: WriterContext) { let returns: string = resolveFuncType(f.returns, ctx); let returnsStr: string | null; if (self && f.isMutating) { - if (f.returns.kind !== 'void') { + if (f.returns.kind !== "void") { returns = `(${resolveFuncType(self, ctx)}, ${returns})`; } else { returns = `(${resolveFuncType(self, ctx)}, ())`; } - returnsStr = resolveFuncTypeUnpack(self, id('self'), ctx); + returnsStr = resolveFuncTypeUnpack(self, id("self"), ctx); } // Resolve function descriptor const name = self ? ops.extension(self.name, f.name) : ops.global(f.name); const args: string[] = []; if (self) { - args.push(resolveFuncType(self, ctx) + ' ' + id('self')); + args.push(resolveFuncType(self, ctx) + " " + id("self")); } for (const a of f.args) { - args.push(resolveFuncType(a.type, ctx) + ' ' + id(a.name)); + args.push(resolveFuncType(a.type, ctx) + " " + id(a.name)); } // Write function body ctx.fun(name, () => { - ctx.signature(`${returns} ${name}(${args.join(', ')})`); - ctx.flag('impure'); + ctx.signature(`${returns} ${name}(${args.join(", ")})`); + ctx.flag("impure"); if (enabledInline(ctx.ctx) || f.isInline) { - ctx.flag('inline'); + ctx.flag("inline"); } - if (f.origin === 'stdlib') { - ctx.context('stdlib'); + if (f.origin === "stdlib") { + ctx.context("stdlib"); } ctx.body(() => { // Unpack self if (self) { - ctx.append(`var (${resolveFuncTypeUnpack(self, id('self'), ctx)}) = ${id('self')};`); + ctx.append( + `var (${resolveFuncTypeUnpack(self, id("self"), ctx)}) = ${id("self")};`, + ); } for (const a of fd.args) { - if (!resolveFuncPrimitive(resolveTypeRef(ctx.ctx, a.type), ctx)) { - ctx.append(`var (${resolveFuncTypeUnpack(resolveTypeRef(ctx.ctx, a.type), id(a.name), ctx)}) = ${id(a.name)};`); + if ( + !resolveFuncPrimitive(resolveTypeRef(ctx.ctx, a.type), ctx) + ) { + ctx.append( + `var (${resolveFuncTypeUnpack(resolveTypeRef(ctx.ctx, a.type), id(a.name), ctx)}) = ${id(a.name)};`, + ); } } @@ -223,8 +269,12 @@ export function writeFunction(f: FunctionDescription, ctx: WriterContext) { } // Auto append return - if (f.self && (f.returns.kind === 'void') && f.isMutating) { - if (fd.statements!.length === 0 || fd.statements![fd.statements!.length - 1].kind !== 'statement_return') { + if (f.self && f.returns.kind === "void" && f.isMutating) { + if ( + fd.statements!.length === 0 || + fd.statements![fd.statements!.length - 1].kind !== + "statement_return" + ) { ctx.append(`return (${returnsStr}, ());`); } } @@ -233,34 +283,40 @@ export function writeFunction(f: FunctionDescription, ctx: WriterContext) { } export function writeGetter(f: FunctionDescription, ctx: WriterContext) { - // Render tensors const self = f.self ? getType(ctx.ctx, f.self) : null; if (!self) { throw new Error(`No self type for getter ${f.name}`); // Impossible } - ctx.append(`_ %${f.name}(${f.args.map((v) => resolveFuncTupledType(v.type, ctx) + ' ' + id('$' + v.name)).join(', ')}) method_id(${getMethodId(f.name)}) {`); + ctx.append( + `_ %${f.name}(${f.args.map((v) => resolveFuncTupledType(v.type, ctx) + " " + id("$" + v.name)).join(", ")}) method_id(${getMethodId(f.name)}) {`, + ); ctx.inIndent(() => { - // Unpack arguments for (const arg of f.args) { - unwrapExternal(id(arg.name), id('$' + arg.name), arg.type, ctx); + unwrapExternal(id(arg.name), id("$" + arg.name), arg.type, ctx); } // Load contract state ctx.append(`var self = ${ops.contractLoad(self.name, ctx)}();`); // Execute get method - ctx.append(`var res = self~${ctx.used(ops.extension(self.name, f.name))}(${f.args.map((v) => id(v.name)).join(', ')});`); + ctx.append( + `var res = self~${ctx.used(ops.extension(self.name, f.name))}(${f.args.map((v) => id(v.name)).join(", ")});`, + ); // Pack if needed - if (f.returns.kind === 'ref') { + if (f.returns.kind === "ref") { const t = getType(ctx.ctx, f.returns.name); - if (t.kind === 'struct') { + if (t.kind === "struct") { if (f.returns.optional) { - ctx.append(`return ${ops.typeToOptExternal(t.name, ctx)}(res);`); + ctx.append( + `return ${ops.typeToOptExternal(t.name, ctx)}(res);`, + ); } else { - ctx.append(`return ${ops.typeToExternal(t.name, ctx)}(res);`); + ctx.append( + `return ${ops.typeToExternal(t.name, ctx)}(res);`, + ); } return; } @@ -271,4 +327,4 @@ export function writeGetter(f: FunctionDescription, ctx: WriterContext) { }); ctx.append(`}`); ctx.append(); -} \ No newline at end of file +} diff --git a/src/generator/writers/writeInterfaces.ts b/src/generator/writers/writeInterfaces.ts index b39a1bb88..5173461ae 100644 --- a/src/generator/writers/writeInterfaces.ts +++ b/src/generator/writers/writeInterfaces.ts @@ -7,19 +7,20 @@ export function writeInterfaces(type: TypeDescription, ctx: WriterContext) { ctx.inIndent(() => { ctx.append(`return (`); ctx.inIndent(() => { - // Build interfaces list const interfaces: string[] = []; - interfaces.push('org.ton.introspection.v0'); + interfaces.push("org.ton.introspection.v0"); interfaces.push(...getSupportedInterfaces(type, ctx.ctx)); // Render interfaces for (let i = 0; i < interfaces.length; i++) { - ctx.append(`"${interfaces[i]}"H >> 128${i < interfaces.length - 1 ? "," : ""}`); + ctx.append( + `"${interfaces[i]}"H >> 128${i < interfaces.length - 1 ? "," : ""}`, + ); } }); ctx.append(`);`); }); ctx.append(`}`); ctx.append(); -} \ No newline at end of file +} diff --git a/src/generator/writers/writeRouter.ts b/src/generator/writers/writeRouter.ts index e45ae9c81..a35240299 100644 --- a/src/generator/writers/writeRouter.ts +++ b/src/generator/writers/writeRouter.ts @@ -8,26 +8,32 @@ import { resolveFuncType } from "./resolveFuncType"; import { resolveFuncTypeUnpack } from "./resolveFuncTypeUnpack"; import { writeStatement } from "./writeFunction"; -export function writeRouter(type: TypeDescription, kind: 'internal' | 'external', ctx: WriterContext) { - const internal = kind === 'internal'; +export function writeRouter( + type: TypeDescription, + kind: "internal" | "external", + ctx: WriterContext, +) { + const internal = kind === "internal"; if (internal) { - ctx.append(`(${resolveFuncType(type, ctx)}, int) ${ops.contractRouter(type.name, kind)}(${resolveFuncType(type, ctx)} self, int msg_bounced, slice in_msg) impure inline_ref {`); + ctx.append( + `(${resolveFuncType(type, ctx)}, int) ${ops.contractRouter(type.name, kind)}(${resolveFuncType(type, ctx)} self, int msg_bounced, slice in_msg) impure inline_ref {`, + ); } else { - ctx.append(`(${resolveFuncType(type, ctx)}, int) ${ops.contractRouter(type.name, kind)}(${resolveFuncType(type, ctx)} self, slice in_msg) impure inline_ref {`); + ctx.append( + `(${resolveFuncType(type, ctx)}, int) ${ops.contractRouter(type.name, kind)}(${resolveFuncType(type, ctx)} self, slice in_msg) impure inline_ref {`, + ); } ctx.inIndent(() => { - // Handle bounced if (internal) { ctx.append(`;; Handle bounced messages`); ctx.append(`if (msg_bounced) {`); ctx.inIndent(() => { - - const bounceReceivers = type.receivers.filter(r => { + const bounceReceivers = type.receivers.filter((r) => { return r.selector.kind === "bounce-binary"; }); - const fallbackReceiver = type.receivers.find(r => { + const fallbackReceiver = type.receivers.find((r) => { return r.selector.kind === "bounce-fallback"; }); @@ -51,41 +57,51 @@ export function writeRouter(type: TypeDescription, kind: 'internal' | 'external' for (const r of bounceReceivers) { const selector = r.selector; - if (selector.kind !== "bounce-binary") throw Error('Invalid selector type: ' + selector.kind); // Should not happen + if (selector.kind !== "bounce-binary") + throw Error("Invalid selector type: " + selector.kind); // Should not happen const allocation = getType(ctx.ctx, selector.type); - if (!allocation) throw Error('Invalid allocation: ' + selector.type); // Should not happen + if (!allocation) + throw Error("Invalid allocation: " + selector.type); // Should not happen - ctx.append(`;; Bounced handler for ${selector.type} message`); + ctx.append( + `;; Bounced handler for ${selector.type} message`, + ); ctx.append(`if (op == ${allocation.header}) {`); ctx.inIndent(() => { // Read message - ctx.append(`var msg = in_msg~${selector.bounced ? ops.readerBounced(selector.type, ctx) : ops.reader(selector.type, ctx)}();`); + ctx.append( + `var msg = in_msg~${selector.bounced ? ops.readerBounced(selector.type, ctx) : ops.reader(selector.type, ctx)}();`, + ); // Execute function - ctx.append(`self~${ops.receiveTypeBounce(type.name, selector.type)}(msg);`); + ctx.append( + `self~${ops.receiveTypeBounce(type.name, selector.type)}(msg);`, + ); // Exit - ctx.append('return (self, true);'); - }) + ctx.append("return (self, true);"); + }); ctx.append(`}`); ctx.append(); } if (fallbackReceiver) { const selector = fallbackReceiver.selector; - if (selector.kind !== "bounce-fallback") throw Error('Invalid selector type: ' + selector.kind); + if (selector.kind !== "bounce-fallback") + throw Error("Invalid selector type: " + selector.kind); // Execute function ctx.append(`;; Fallback bounce receiver`); - ctx.append(`self~${ops.receiveBounceAny(type.name)}(in_msg);`); + ctx.append( + `self~${ops.receiveBounceAny(type.name)}(in_msg);`, + ); ctx.append(); // Exit - ctx.append('return (self, true);'); + ctx.append("return (self, true);"); } else { ctx.append(`return (self, true);`); } - }); ctx.append(`}`); } @@ -106,90 +122,126 @@ export function writeRouter(type: TypeDescription, kind: 'internal' | 'external' const selector = f.selector; // Generic receiver - if (selector.kind === (internal ? 'internal-binary' : 'external-binary')) { + if ( + selector.kind === + (internal ? "internal-binary" : "external-binary") + ) { const allocation = getType(ctx.ctx, selector.type); if (!allocation.header) { - throw Error('Invalid allocation: ' + selector.type); + throw Error("Invalid allocation: " + selector.type); } ctx.append(); ctx.append(`;; Receive ${selector.type} message`); ctx.append(`if (op == ${allocation.header}) {`); ctx.inIndent(() => { - // Read message - ctx.append(`var msg = in_msg~${ops.reader(selector.type, ctx)}();`); + ctx.append( + `var msg = in_msg~${ops.reader(selector.type, ctx)}();`, + ); // Execute function - ctx.append(`self~${ops.receiveType(type.name, kind, selector.type)}(msg);`); + ctx.append( + `self~${ops.receiveType(type.name, kind, selector.type)}(msg);`, + ); // Exit - ctx.append('return (self, true);'); - }) + ctx.append("return (self, true);"); + }); ctx.append(`}`); } - if (selector.kind === (internal ? 'internal-empty' : 'external-empty')) { + if ( + selector.kind === + (internal ? "internal-empty" : "external-empty") + ) { ctx.append(); ctx.append(`;; Receive empty message`); ctx.append(`if ((op == 0) & (slice_bits(in_msg) <= 32)) {`); ctx.inIndent(() => { - // Execute function ctx.append(`self~${ops.receiveEmpty(type.name, kind)}();`); // Exit - ctx.append('return (self, true);'); - }) + ctx.append("return (self, true);"); + }); ctx.append(`}`); } } // Text resolvers - const hasComments = !!type.receivers.find((v) => internal ? (v.selector.kind === 'internal-comment' || v.selector.kind === 'internal-comment-fallback') : (v.selector.kind === 'external-comment' || v.selector.kind === 'external-comment-fallback')); + const hasComments = !!type.receivers.find((v) => + internal + ? v.selector.kind === "internal-comment" || + v.selector.kind === "internal-comment-fallback" + : v.selector.kind === "external-comment" || + v.selector.kind === "external-comment-fallback", + ); if (hasComments) { ctx.append(); ctx.append(`;; Text Receivers`); ctx.append(`if (op == 0) {`); ctx.inIndent(() => { - if (type.receivers.find((v) => v.selector.kind === (internal ? 'internal-comment' : 'external-comment'))) { + if ( + type.receivers.find( + (v) => + v.selector.kind === + (internal + ? "internal-comment" + : "external-comment"), + ) + ) { ctx.append(`var text_op = slice_hash(in_msg);`); for (const r of type.receivers) { const selector = r.selector; - if (selector.kind === (internal ? 'internal-comment' : 'external-comment')) { + if ( + selector.kind === + (internal ? "internal-comment" : "external-comment") + ) { const hash = beginCell() .storeUint(0, 32) - .storeBuffer(Buffer.from(selector.comment, 'utf8')) + .storeBuffer( + Buffer.from(selector.comment, "utf8"), + ) .endCell() .hash() - .toString('hex', 0, 64); + .toString("hex", 0, 64); ctx.append(); - ctx.append(`;; Receive "${selector.comment}" message`); + ctx.append( + `;; Receive "${selector.comment}" message`, + ); ctx.append(`if (text_op == 0x${hash}) {`); ctx.inIndent(() => { - // Execute function - ctx.append(`self~${ops.receiveText(type.name, kind, hash)}();`); + ctx.append( + `self~${ops.receiveText(type.name, kind, hash)}();`, + ); // Exit - ctx.append('return (self, true);'); - }) + ctx.append("return (self, true);"); + }); ctx.append(`}`); } } } // Comment fallback resolver - const fallback = type.receivers.find((v) => v.selector.kind === (internal ? 'internal-comment-fallback' : 'external-comment-fallback')); + const fallback = type.receivers.find( + (v) => + v.selector.kind === + (internal + ? "internal-comment-fallback" + : "external-comment-fallback"), + ); if (fallback) { - ctx.append(`if (slice_bits(in_msg) >= 32) {`); ctx.inIndent(() => { - // Execute function - ctx.append(`self~${ops.receiveAnyText(type.name, kind)}(in_msg.skip_bits(32));`); + ctx.append( + `self~${ops.receiveAnyText(type.name, kind)}(in_msg.skip_bits(32));`, + ); // Exit - ctx.append('return (self, true);'); + ctx.append("return (self, true);"); }); ctx.append(`}`); @@ -199,47 +251,65 @@ export function writeRouter(type: TypeDescription, kind: 'internal' | 'external' } // Fallback - const fallbackReceiver = type.receivers.find((v) => v.selector.kind === (internal ? 'internal-fallback' : 'external-fallback')); + const fallbackReceiver = type.receivers.find( + (v) => + v.selector.kind === + (internal ? "internal-fallback" : "external-fallback"), + ); if (fallbackReceiver) { - ctx.append(); ctx.append(`;; Receiver fallback`); // Execute function ctx.append(`self~${ops.receiveAny(type.name, kind)}(in_msg);`); - ctx.append('return (self, true);'); + ctx.append("return (self, true);"); } else { ctx.append(); - ctx.append('return (self, false);'); + ctx.append("return (self, false);"); } }); ctx.append(`}`); ctx.append(); } -export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx: WriterContext) { +export function writeReceiver( + self: TypeDescription, + f: ReceiverDescription, + ctx: WriterContext, +) { const selector = f.selector; - const selfRes = resolveFuncTypeUnpack(self, id('self'), ctx); + const selfRes = resolveFuncTypeUnpack(self, id("self"), ctx); const selfType = resolveFuncType(self, ctx); - const selfUnpack = `var ${resolveFuncTypeUnpack(self, id('self'), ctx)} = ${id('self')};`; + const selfUnpack = `var ${resolveFuncTypeUnpack(self, id("self"), ctx)} = ${id("self")};`; // Binary receiver - if (selector.kind === 'internal-binary' || selector.kind === 'external-binary') { + if ( + selector.kind === "internal-binary" || + selector.kind === "external-binary" + ) { const args = [ - selfType + ' ' + id('self'), - resolveFuncType(selector.type, ctx) + ' ' + id(selector.name) + selfType + " " + id("self"), + resolveFuncType(selector.type, ctx) + " " + id(selector.name), ]; - ctx.append(`((${selfType}), ()) ${ops.receiveType(self.name, selector.kind === 'internal-binary' ? 'internal' : 'external', selector.type)}(${args.join(', ')}) impure inline {`); + ctx.append( + `((${selfType}), ()) ${ops.receiveType(self.name, selector.kind === "internal-binary" ? "internal" : "external", selector.type)}(${args.join(", ")}) impure inline {`, + ); ctx.inIndent(() => { ctx.append(selfUnpack); - ctx.append(`var ${resolveFuncTypeUnpack(selector.type, id(selector.name), ctx)} = ${id(selector.name)};`); + ctx.append( + `var ${resolveFuncTypeUnpack(selector.type, id(selector.name), ctx)} = ${id(selector.name)};`, + ); for (const s of f.ast.statements) { writeStatement(s, selfRes, null, ctx); } - if (f.ast.statements.length === 0 || f.ast.statements[f.ast.statements.length - 1].kind !== 'statement_return') { + if ( + f.ast.statements.length === 0 || + f.ast.statements[f.ast.statements.length - 1].kind !== + "statement_return" + ) { ctx.append(`return (${selfRes}, ());`); } }); @@ -249,8 +319,13 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx } // Empty receiver - if (selector.kind === 'internal-empty' || selector.kind === 'external-empty') { - ctx.append(`((${selfType}), ()) ${ops.receiveEmpty(self.name, selector.kind === 'internal-empty' ? 'internal' : 'external')}(${(selfType + ' ' + id('self'))}) impure inline {`); + if ( + selector.kind === "internal-empty" || + selector.kind === "external-empty" + ) { + ctx.append( + `((${selfType}), ()) ${ops.receiveEmpty(self.name, selector.kind === "internal-empty" ? "internal" : "external")}(${selfType + " " + id("self")}) impure inline {`, + ); ctx.inIndent(() => { ctx.append(selfUnpack); @@ -258,7 +333,11 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx writeStatement(s, selfRes, null, ctx); } - if (f.ast.statements.length === 0 || f.ast.statements[f.ast.statements.length - 1].kind !== 'statement_return') { + if ( + f.ast.statements.length === 0 || + f.ast.statements[f.ast.statements.length - 1].kind !== + "statement_return" + ) { ctx.append(`return (${selfRes}, ());`); } }); @@ -268,14 +347,19 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx } // Comment receiver - if (selector.kind === 'internal-comment' || selector.kind === 'external-comment') { + if ( + selector.kind === "internal-comment" || + selector.kind === "external-comment" + ) { const hash = beginCell() .storeUint(0, 32) - .storeBuffer(Buffer.from(selector.comment, 'utf8')) + .storeBuffer(Buffer.from(selector.comment, "utf8")) .endCell() .hash() - .toString('hex', 0, 64); - ctx.append(`(${selfType}, ()) ${ops.receiveText(self.name, selector.kind === 'internal-comment' ? 'internal' : 'external', hash)}(${(selfType + ' ' + id('self'))}) impure inline {`); + .toString("hex", 0, 64); + ctx.append( + `(${selfType}, ()) ${ops.receiveText(self.name, selector.kind === "internal-comment" ? "internal" : "external", hash)}(${selfType + " " + id("self")}) impure inline {`, + ); ctx.inIndent(() => { ctx.append(selfUnpack); @@ -283,7 +367,11 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx writeStatement(s, selfRes, null, ctx); } - if (f.ast.statements.length === 0 || f.ast.statements[f.ast.statements.length - 1].kind !== 'statement_return') { + if ( + f.ast.statements.length === 0 || + f.ast.statements[f.ast.statements.length - 1].kind !== + "statement_return" + ) { ctx.append(`return (${selfRes}, ());`); } }); @@ -292,10 +380,14 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx return; } - // Fallback - if (selector.kind === 'internal-comment-fallback' || selector.kind === 'external-comment-fallback') { - ctx.append(`(${selfType}, ()) ${ops.receiveAnyText(self.name, selector.kind === 'internal-comment-fallback' ? 'internal' : 'external')}(${([selfType + ' ' + id('self'), 'slice ' + id(selector.name)]).join(', ')}) impure inline {`) + if ( + selector.kind === "internal-comment-fallback" || + selector.kind === "external-comment-fallback" + ) { + ctx.append( + `(${selfType}, ()) ${ops.receiveAnyText(self.name, selector.kind === "internal-comment-fallback" ? "internal" : "external")}(${[selfType + " " + id("self"), "slice " + id(selector.name)].join(", ")}) impure inline {`, + ); ctx.inIndent(() => { ctx.append(selfUnpack); @@ -303,7 +395,11 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx writeStatement(s, selfRes, null, ctx); } - if (f.ast.statements.length === 0 || f.ast.statements[f.ast.statements.length - 1].kind !== 'statement_return') { + if ( + f.ast.statements.length === 0 || + f.ast.statements[f.ast.statements.length - 1].kind !== + "statement_return" + ) { ctx.append(`return (${selfRes}, ());`); } }); @@ -313,8 +409,10 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx } // Fallback - if (selector.kind === 'internal-fallback') { - ctx.append(`(${selfType}, ()) ${ops.receiveAny(self.name, selector.kind === 'internal-fallback' ? 'internal' : 'external')}(${selfType} ${id('self')}, slice ${id(selector.name)}) impure inline {`); + if (selector.kind === "internal-fallback") { + ctx.append( + `(${selfType}, ()) ${ops.receiveAny(self.name, selector.kind === "internal-fallback" ? "internal" : "external")}(${selfType} ${id("self")}, slice ${id(selector.name)}) impure inline {`, + ); ctx.inIndent(() => { ctx.append(selfUnpack); @@ -322,7 +420,11 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx writeStatement(s, selfRes, null, ctx); } - if (f.ast.statements.length === 0 || f.ast.statements[f.ast.statements.length - 1].kind !== 'statement_return') { + if ( + f.ast.statements.length === 0 || + f.ast.statements[f.ast.statements.length - 1].kind !== + "statement_return" + ) { ctx.append(`return (${selfRes}, ());`); } }); @@ -332,8 +434,10 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx } // Bounced - if (selector.kind === 'bounce-fallback') { - ctx.append(`(${selfType}, ()) ${ops.receiveBounceAny(self.name)}(${selfType} ${id('self')}, slice ${id(selector.name)}) impure inline {`); + if (selector.kind === "bounce-fallback") { + ctx.append( + `(${selfType}, ()) ${ops.receiveBounceAny(self.name)}(${selfType} ${id("self")}, slice ${id(selector.name)}) impure inline {`, + ); ctx.inIndent(() => { ctx.append(selfUnpack); @@ -341,7 +445,11 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx writeStatement(s, selfRes, null, ctx); } - if (f.ast.statements.length === 0 || f.ast.statements[f.ast.statements.length - 1].kind !== 'statement_return') { + if ( + f.ast.statements.length === 0 || + f.ast.statements[f.ast.statements.length - 1].kind !== + "statement_return" + ) { ctx.append(`return (${selfRes}, ());`); } }); @@ -350,21 +458,31 @@ export function writeReceiver(self: TypeDescription, f: ReceiverDescription, ctx return; } - if (selector.kind === 'bounce-binary') { + if (selector.kind === "bounce-binary") { const args = [ - selfType + ' ' + id('self'), - resolveFuncType(selector.type, ctx, false, selector.bounced) + ' ' + id(selector.name) + selfType + " " + id("self"), + resolveFuncType(selector.type, ctx, false, selector.bounced) + + " " + + id(selector.name), ]; - ctx.append(`((${selfType}), ()) ${ops.receiveTypeBounce(self.name, selector.type)}(${args.join(', ')}) impure inline {`); + ctx.append( + `((${selfType}), ()) ${ops.receiveTypeBounce(self.name, selector.type)}(${args.join(", ")}) impure inline {`, + ); ctx.inIndent(() => { ctx.append(selfUnpack); - ctx.append(`var ${resolveFuncTypeUnpack(selector.type, id(selector.name), ctx, false, selector.bounced)} = ${id(selector.name)};`); + ctx.append( + `var ${resolveFuncTypeUnpack(selector.type, id(selector.name), ctx, false, selector.bounced)} = ${id(selector.name)};`, + ); for (const s of f.ast.statements) { writeStatement(s, selfRes, null, ctx); } - if (f.ast.statements.length === 0 || f.ast.statements[f.ast.statements.length - 1].kind !== 'statement_return') { + if ( + f.ast.statements.length === 0 || + f.ast.statements[f.ast.statements.length - 1].kind !== + "statement_return" + ) { ctx.append(`return (${selfRes}, ());`); } }); diff --git a/src/generator/writers/writeSerialization.spec.ts b/src/generator/writers/writeSerialization.spec.ts index c65926ea4..c1f0e7fc7 100644 --- a/src/generator/writers/writeSerialization.spec.ts +++ b/src/generator/writers/writeSerialization.spec.ts @@ -1,7 +1,14 @@ import { __DANGER_resetNodeId } from "../../grammar/ast"; import { CompilerContext } from "../../context"; -import { getAllocation, resolveAllocations } from "../../storage/resolveAllocation"; -import { getAllTypes, getType, resolveDescriptors } from "../../types/resolveDescriptors"; +import { + getAllocation, + resolveAllocations, +} from "../../storage/resolveAllocation"; +import { + getAllTypes, + getType, + resolveDescriptors, +} from "../../types/resolveDescriptors"; import { WriterContext } from "../Writer"; import { writeParser, writeSerializer } from "./writeSerialization"; import { writeStdlib } from "./writeStdlib"; @@ -48,26 +55,42 @@ struct C { } `; -describe('writeSerialization', () => { +describe("writeSerialization", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - for (const s of ['A', 'B', 'C']) { - it('should write serializer for ' + s, () => { - let ctx = openContext(new CompilerContext(), [{ code, path: '', origin: 'user' }], []); + for (const s of ["A", "B", "C"]) { + it("should write serializer for " + s, () => { + let ctx = openContext( + new CompilerContext(), + [{ code, path: "", origin: "user" }], + [], + ); ctx = resolveDescriptors(ctx); ctx = resolveAllocations(ctx); const wctx = new WriterContext(ctx, s); writeStdlib(wctx); - writeSerializer(getType(ctx, s).name, false, getAllocation(ctx, s), 'user', wctx); + writeSerializer( + getType(ctx, s).name, + false, + getAllocation(ctx, s), + "user", + wctx, + ); for (const t of Object.values(getAllTypes(ctx))) { - if (t.kind === 'contract' || t.kind === 'struct') { - writeAccessors(t, 'user', wctx); + if (t.kind === "contract" || t.kind === "struct") { + writeAccessors(t, "user", wctx); } } - writeParser(getType(ctx, s).name, false, getAllocation(ctx, s), 'user', wctx); + writeParser( + getType(ctx, s).name, + false, + getAllocation(ctx, s), + "user", + wctx, + ); const extracted = wctx.extract(true); expect(extracted).toMatchSnapshot(); }); } -}); \ No newline at end of file +}); diff --git a/src/generator/writers/writeSerialization.ts b/src/generator/writers/writeSerialization.ts index 8a3d05ac7..2c7ef7460 100644 --- a/src/generator/writers/writeSerialization.ts +++ b/src/generator/writers/writeSerialization.ts @@ -1,7 +1,7 @@ import { contractErrors } from "../../abi/errors"; import { AllocationCell, AllocationOperation } from "../../storage/operation"; import { StorageAllocation } from "../../storage/StorageAllocation"; -import { getType } from '../../types/resolveDescriptors'; +import { getType } from "../../types/resolveDescriptors"; import { TypeOrigin } from "../../types/types"; import { WriterContext } from "../Writer"; import { ops } from "./ops"; @@ -14,22 +14,37 @@ const SMALL_STRUCT_MAX_FIELDS = 5; // Serializer // -export function writeSerializer(name: string, forceInline: boolean, allocation: StorageAllocation, origin: TypeOrigin, ctx: WriterContext) { +export function writeSerializer( + name: string, + forceInline: boolean, + allocation: StorageAllocation, + origin: TypeOrigin, + ctx: WriterContext, +) { const isSmall = allocation.ops.length <= SMALL_STRUCT_MAX_FIELDS; // Write to builder ctx.fun(ops.writer(name, ctx), () => { - ctx.signature(`builder ${ops.writer(name, ctx)}(builder build_0, ${resolveFuncTypeFromAbi(allocation.ops.map((v) => v.type), ctx)} v)`); + ctx.signature( + `builder ${ops.writer(name, ctx)}(builder build_0, ${resolveFuncTypeFromAbi( + allocation.ops.map((v) => v.type), + ctx, + )} v)`, + ); if (forceInline || isSmall) { - ctx.flag('inline'); + ctx.flag("inline"); } - ctx.context('type:' + name); + ctx.context("type:" + name); ctx.body(() => { if (allocation.ops.length > 0) { - ctx.append(`var ${resolveFuncTypeFromAbiUnpack(`v`, allocation.ops, ctx)} = v;`) + ctx.append( + `var ${resolveFuncTypeFromAbiUnpack(`v`, allocation.ops, ctx)} = v;`, + ); } if (allocation.header) { - ctx.append(`build_0 = store_uint(build_0, ${allocation.header.value}, ${allocation.header.bits});`); + ctx.append( + `build_0 = store_uint(build_0, ${allocation.header.value}, ${allocation.header.bits});`, + ); } writeSerializerCell(allocation.root, 0, ctx); ctx.append(`return build_0;`); @@ -38,20 +53,31 @@ export function writeSerializer(name: string, forceInline: boolean, allocation: // Write to cell ctx.fun(ops.writerCell(name, ctx), () => { - ctx.signature(`cell ${ops.writerCell(name, ctx)}(${resolveFuncTypeFromAbi(allocation.ops.map((v) => v.type), ctx)} v)`); - ctx.flag('inline'); - ctx.context('type:' + name); + ctx.signature( + `cell ${ops.writerCell(name, ctx)}(${resolveFuncTypeFromAbi( + allocation.ops.map((v) => v.type), + ctx, + )} v)`, + ); + ctx.flag("inline"); + ctx.context("type:" + name); ctx.body(() => { - ctx.append(`return ${ops.writer(name, ctx)}(begin_cell(), v).end_cell();`); - }) + ctx.append( + `return ${ops.writer(name, ctx)}(begin_cell(), v).end_cell();`, + ); + }); }); } -export function writeOptionalSerializer(name: string, origin: TypeOrigin, ctx: WriterContext) { +export function writeOptionalSerializer( + name: string, + origin: TypeOrigin, + ctx: WriterContext, +) { ctx.fun(ops.writerCellOpt(name, ctx), () => { ctx.signature(`cell ${ops.writerCellOpt(name, ctx)}(tuple v)`); - ctx.flag('inline'); - ctx.context('type:' + name); + ctx.flag("inline"); + ctx.context("type:" + name); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -63,8 +89,11 @@ export function writeOptionalSerializer(name: string, origin: TypeOrigin, ctx: W }); } -function writeSerializerCell(cell: AllocationCell, gen: number, ctx: WriterContext) { - +function writeSerializerCell( + cell: AllocationCell, + gen: number, + ctx: WriterContext, +) { // Write fields for (const f of cell.ops) { writeSerializerField(f, gen, ctx); @@ -74,161 +103,229 @@ function writeSerializerCell(cell: AllocationCell, gen: number, ctx: WriterConte if (cell.next) { ctx.append(`var build_${gen + 1} = begin_cell();`); writeSerializerCell(cell.next, gen + 1, ctx); - ctx.append(`build_${gen} = store_ref(build_${gen}, build_${gen + 1}.end_cell());`); + ctx.append( + `build_${gen} = store_ref(build_${gen}, build_${gen + 1}.end_cell());`, + ); } } -function writeSerializerField(f: AllocationOperation, gen: number, ctx: WriterContext) { +function writeSerializerField( + f: AllocationOperation, + gen: number, + ctx: WriterContext, +) { const fieldName = `v'${f.name}`; const op = f.op; - if (op.kind === 'int') { + if (op.kind === "int") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_int(${fieldName}, ${op.bits}) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_int(${fieldName}, ${op.bits}) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_int(${fieldName}, ${op.bits});`); + ctx.append( + `build_${gen} = build_${gen}.store_int(${fieldName}, ${op.bits});`, + ); } return; } - if (op.kind === 'uint') { + if (op.kind === "uint") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_uint(${fieldName}, ${op.bits}) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_uint(${fieldName}, ${op.bits}) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_uint(${fieldName}, ${op.bits});`); + ctx.append( + `build_${gen} = build_${gen}.store_uint(${fieldName}, ${op.bits});`, + ); } return; } - if (op.kind === 'coins') { + if (op.kind === "coins") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_coins(${fieldName}) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_coins(${fieldName}) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_coins(${fieldName});`); + ctx.append( + `build_${gen} = build_${gen}.store_coins(${fieldName});`, + ); } return; } - if (op.kind === 'boolean') { + if (op.kind === "boolean") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_int(${fieldName}, 1) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_int(${fieldName}, 1) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_int(${fieldName}, 1);`); + ctx.append( + `build_${gen} = build_${gen}.store_int(${fieldName}, 1);`, + ); } return; } - if (op.kind === 'address') { + if (op.kind === "address") { if (op.optional) { ctx.used(`__tact_store_address_opt`); - ctx.append(`build_${gen} = __tact_store_address_opt(build_${gen}, ${fieldName});`); + ctx.append( + `build_${gen} = __tact_store_address_opt(build_${gen}, ${fieldName});`, + ); } else { ctx.used(`__tact_store_address`); - ctx.append(`build_${gen} = __tact_store_address(build_${gen}, ${fieldName});`); + ctx.append( + `build_${gen} = __tact_store_address(build_${gen}, ${fieldName});`, + ); } return; } - if (op.kind === 'cell') { - if (op.format === 'default') { + if (op.kind === "cell") { + if (op.format === "default") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_ref(${fieldName}) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_ref(${fieldName}) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_ref(${fieldName});`); + ctx.append( + `build_${gen} = build_${gen}.store_ref(${fieldName});`, + ); } - } else if (op.format === 'remainder') { + } else if (op.format === "remainder") { if (op.optional) { - throw Error('Impossible'); + throw Error("Impossible"); } - ctx.append(`build_${gen} = build_${gen}.store_slice(${fieldName}.begin_parse());`); + ctx.append( + `build_${gen} = build_${gen}.store_slice(${fieldName}.begin_parse());`, + ); } else { - throw Error('Impossible'); + throw Error("Impossible"); } return; } - if (op.kind === 'slice') { - if (op.format === 'default') { + if (op.kind === "slice") { + if (op.format === "default") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_ref(begin_cell().store_slice(${fieldName}).end_cell()) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_ref(begin_cell().store_slice(${fieldName}).end_cell()) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_ref(begin_cell().store_slice(${fieldName}).end_cell());`); + ctx.append( + `build_${gen} = build_${gen}.store_ref(begin_cell().store_slice(${fieldName}).end_cell());`, + ); } - } else if (op.format === 'remainder') { + } else if (op.format === "remainder") { if (op.optional) { - throw Error('Impossible'); + throw Error("Impossible"); } - ctx.append(`build_${gen} = build_${gen}.store_slice(${fieldName});`); + ctx.append( + `build_${gen} = build_${gen}.store_slice(${fieldName});`, + ); } else { - throw Error('Impossible'); + throw Error("Impossible"); } return; } - if (op.kind === 'builder') { - if (op.format === 'default') { + if (op.kind === "builder") { + if (op.format === "default") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_ref(begin_cell().store_slice(${fieldName}.end_cell().begin_parse()).end_cell()) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_ref(begin_cell().store_slice(${fieldName}.end_cell().begin_parse()).end_cell()) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_ref(begin_cell().store_slice(${fieldName}.end_cell().begin_parse()).end_cell());`); + ctx.append( + `build_${gen} = build_${gen}.store_ref(begin_cell().store_slice(${fieldName}.end_cell().begin_parse()).end_cell());`, + ); } - } else if (op.format === 'remainder') { + } else if (op.format === "remainder") { if (op.optional) { - throw Error('Impossible'); + throw Error("Impossible"); } - ctx.append(`build_${gen} = build_${gen}.store_slice(${fieldName}.end_cell().begin_parse());`); + ctx.append( + `build_${gen} = build_${gen}.store_slice(${fieldName}.end_cell().begin_parse());`, + ); } else { - throw Error('Impossible'); + throw Error("Impossible"); } return; } - if (op.kind === 'string') { + if (op.kind === "string") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_ref(begin_cell().store_slice(${fieldName}).end_cell()) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_ref(begin_cell().store_slice(${fieldName}).end_cell()) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_ref(begin_cell().store_slice(${fieldName}).end_cell());`); + ctx.append( + `build_${gen} = build_${gen}.store_ref(begin_cell().store_slice(${fieldName}).end_cell());`, + ); } return; } - if (op.kind === 'fixed-bytes') { + if (op.kind === "fixed-bytes") { if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_slice(${fieldName}) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).store_slice(${fieldName}) : build_${gen}.store_int(false, 1);`, + ); } else { - ctx.append(`build_${gen} = build_${gen}.store_slice(${fieldName});`); + ctx.append( + `build_${gen} = build_${gen}.store_slice(${fieldName});`, + ); } return; } - if (op.kind === 'map') { + if (op.kind === "map") { ctx.append(`build_${gen} = build_${gen}.store_dict(${fieldName});`); return; } - if (op.kind === 'struct') { + if (op.kind === "struct") { if (op.ref) { - throw Error('Not implemented'); + throw Error("Not implemented"); } if (op.optional) { - ctx.append(`build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).${ops.writer(op.type, ctx)}(${ops.typeNotNull(op.type, ctx)}(${fieldName})) : build_${gen}.store_int(false, 1);`); + ctx.append( + `build_${gen} = ~ null?(${fieldName}) ? build_${gen}.store_int(true, 1).${ops.writer(op.type, ctx)}(${ops.typeNotNull(op.type, ctx)}(${fieldName})) : build_${gen}.store_int(false, 1);`, + ); } else { const ff = getType(ctx.ctx, op.type).fields.map((f) => f.abi); - ctx.append(`build_${gen} = ${ops.writer(op.type, ctx)}(build_${gen}, ${resolveFuncTypeFromAbiUnpack(fieldName, ff, ctx)});`); + ctx.append( + `build_${gen} = ${ops.writer(op.type, ctx)}(build_${gen}, ${resolveFuncTypeFromAbiUnpack(fieldName, ff, ctx)});`, + ); } return; } - throw Error('Unsupported field kind: ' + op.kind); + throw Error("Unsupported field kind: " + op.kind); } // // Parser // -export function writeParser(name: string, forceInline: boolean, allocation: StorageAllocation, origin: TypeOrigin, ctx: WriterContext) { +export function writeParser( + name: string, + forceInline: boolean, + allocation: StorageAllocation, + origin: TypeOrigin, + ctx: WriterContext, +) { const isSmall = allocation.ops.length <= SMALL_STRUCT_MAX_FIELDS; ctx.fun(ops.reader(name, ctx), () => { - ctx.signature(`(slice, (${resolveFuncTypeFromAbi(allocation.ops.map((v) => v.type), ctx)})) ${ops.reader(name, ctx)}(slice sc_0)`); + ctx.signature( + `(slice, (${resolveFuncTypeFromAbi( + allocation.ops.map((v) => v.type), + ctx, + )})) ${ops.reader(name, ctx)}(slice sc_0)`, + ); if (forceInline || isSmall) { - ctx.flag('inline'); + ctx.flag("inline"); } - ctx.context('type:' + name); + ctx.context("type:" + name); ctx.body(() => { - // Check prefix if (allocation.header) { - ctx.append(`throw_unless(${contractErrors.invalidPrefix.id}, sc_0~load_uint(${allocation.header.bits}) == ${allocation.header.value});`); + ctx.append( + `throw_unless(${contractErrors.invalidPrefix.id}, sc_0~load_uint(${allocation.header.bits}) == ${allocation.header.value});`, + ); } // Write cell parser @@ -238,26 +335,40 @@ export function writeParser(name: string, forceInline: boolean, allocation: Stor if (allocation.ops.length === 0) { ctx.append(`return (sc_0, null());`); } else { - ctx.append(`return (sc_0, (${allocation.ops.map((v) => `v'${v.name}`).join(', ')}));`); + ctx.append( + `return (sc_0, (${allocation.ops.map((v) => `v'${v.name}`).join(", ")}));`, + ); } }); }); } -export function writeBouncedParser(name: string, forceInline: boolean, allocation: StorageAllocation, origin: TypeOrigin, ctx: WriterContext) { +export function writeBouncedParser( + name: string, + forceInline: boolean, + allocation: StorageAllocation, + origin: TypeOrigin, + ctx: WriterContext, +) { const isSmall = allocation.ops.length <= SMALL_STRUCT_MAX_FIELDS; ctx.fun(ops.readerBounced(name, ctx), () => { - ctx.signature(`(slice, (${resolveFuncTypeFromAbi(allocation.ops.map((v) => v.type), ctx)})) ${ops.readerBounced(name, ctx)}(slice sc_0)`); + ctx.signature( + `(slice, (${resolveFuncTypeFromAbi( + allocation.ops.map((v) => v.type), + ctx, + )})) ${ops.readerBounced(name, ctx)}(slice sc_0)`, + ); if (forceInline || isSmall) { - ctx.flag('inline'); + ctx.flag("inline"); } - ctx.context('type:' + name); + ctx.context("type:" + name); ctx.body(() => { - // Check prefix if (allocation.header) { - ctx.append(`throw_unless(${contractErrors.invalidPrefix.id}, sc_0~load_uint(${allocation.header.bits}) == ${allocation.header.value});`); + ctx.append( + `throw_unless(${contractErrors.invalidPrefix.id}, sc_0~load_uint(${allocation.header.bits}) == ${allocation.header.value});`, + ); } // Write cell parser @@ -267,17 +378,23 @@ export function writeBouncedParser(name: string, forceInline: boolean, allocatio if (allocation.ops.length === 0) { ctx.append(`return (sc_0, null());`); } else { - ctx.append(`return (sc_0, (${allocation.ops.map((v) => `v'${v.name}`).join(', ')}));`); + ctx.append( + `return (sc_0, (${allocation.ops.map((v) => `v'${v.name}`).join(", ")}));`, + ); } }); }); } -export function writeOptionalParser(name: string, origin: TypeOrigin, ctx: WriterContext) { +export function writeOptionalParser( + name: string, + origin: TypeOrigin, + ctx: WriterContext, +) { ctx.fun(ops.readerOpt(name, ctx), () => { ctx.signature(`tuple ${ops.readerOpt(name, ctx)}(cell cl)`); - ctx.flag('inline'); - ctx.context('type:' + name); + ctx.flag("inline"); + ctx.context("type:" + name); ctx.body(() => { ctx.write(` if (null?(cl)) { @@ -290,8 +407,11 @@ export function writeOptionalParser(name: string, origin: TypeOrigin, ctx: Write }); } -function writeCellParser(cell: AllocationCell, gen: number, ctx: WriterContext): number { - +function writeCellParser( + cell: AllocationCell, + gen: number, + ctx: WriterContext, +): number { // Write current fields for (const f of cell.ops) { writeFieldParser(f, gen, ctx); @@ -306,44 +426,56 @@ function writeCellParser(cell: AllocationCell, gen: number, ctx: WriterContext): } } -function writeFieldParser(f: AllocationOperation, gen: number, ctx: WriterContext) { +function writeFieldParser( + f: AllocationOperation, + gen: number, + ctx: WriterContext, +) { const op = f.op; const varName = `var v'${f.name}`; // Handle int - if (op.kind === 'int') { + if (op.kind === "int") { if (op.optional) { - ctx.append(`${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_int(${op.bits}) : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_int(${op.bits}) : null();`, + ); } else { ctx.append(`${varName} = sc_${gen}~load_int(${op.bits});`); } return; } - if (op.kind === 'uint') { + if (op.kind === "uint") { if (op.optional) { - ctx.append(`${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_uint(${op.bits}) : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_uint(${op.bits}) : null();`, + ); } else { ctx.append(`${varName} = sc_${gen}~load_uint(${op.bits});`); } return; } - if (op.kind === 'coins') { + if (op.kind === "coins") { if (op.optional) { - ctx.append(`${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_coins() : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_coins() : null();`, + ); } else { ctx.append(`${varName} = sc_${gen}~load_coins();`); } return; } - if (op.kind === 'boolean') { + if (op.kind === "boolean") { if (op.optional) { - ctx.append(`${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_int(1) : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_int(1) : null();`, + ); } else { ctx.append(`${varName} = sc_${gen}~load_int(1);`); } return; } - if (op.kind === 'address') { + if (op.kind === "address") { if (op.optional) { ctx.used(`__tact_load_address_opt`); ctx.append(`${varName} = sc_${gen}~__tact_load_address_opt();`); @@ -353,33 +485,39 @@ function writeFieldParser(f: AllocationOperation, gen: number, ctx: WriterContex } return; } - if (op.kind === 'cell') { + if (op.kind === "cell") { if (op.optional) { - if (op.format !== 'default') { + if (op.format !== "default") { throw new Error(`Impossible`); } - ctx.append(`${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_ref() : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_ref() : null();`, + ); } else { - if (op.format === 'default') { + if (op.format === "default") { ctx.append(`${varName} = sc_${gen}~load_ref();`); - } else if (op.format === 'remainder') { - ctx.append(`${varName} = begin_cell().store_slice(sc_${gen}).end_cell();`); + } else if (op.format === "remainder") { + ctx.append( + `${varName} = begin_cell().store_slice(sc_${gen}).end_cell();`, + ); } else { throw new Error(`Impossible`); } } return; } - if (op.kind === 'slice') { + if (op.kind === "slice") { if (op.optional) { - if (op.format !== 'default') { + if (op.format !== "default") { throw new Error(`Impossible`); } - ctx.append(`${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_ref().begin_parse() : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_ref().begin_parse() : null();`, + ); } else { - if (op.format === 'default') { + if (op.format === "default") { ctx.append(`${varName} = sc_${gen}~load_ref().begin_parse();`); - } else if (op.format === 'remainder') { + } else if (op.format === "remainder") { ctx.append(`${varName} = sc_${gen};`); } else { throw new Error(`Impossible`); @@ -387,16 +525,20 @@ function writeFieldParser(f: AllocationOperation, gen: number, ctx: WriterContex } return; } - if (op.kind === 'builder') { + if (op.kind === "builder") { if (op.optional) { - if (op.format !== 'default') { + if (op.format !== "default") { throw new Error(`Impossible`); } - ctx.append(`${varName} = sc_${gen}~load_int(1) ? begin_cell().store_slice(sc_${gen}~load_ref().begin_parse()) : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? begin_cell().store_slice(sc_${gen}~load_ref().begin_parse()) : null();`, + ); } else { - if (op.format === 'default') { - ctx.append(`${varName} = begin_cell().store_slice(sc_${gen}~load_ref().begin_parse());`); - } else if (op.format === 'remainder') { + if (op.format === "default") { + ctx.append( + `${varName} = begin_cell().store_slice(sc_${gen}~load_ref().begin_parse());`, + ); + } else if (op.format === "remainder") { ctx.append(`${varName} = begin_cell().store_slice(sc_${gen});`); } else { throw new Error(`Impossible`); @@ -404,42 +546,50 @@ function writeFieldParser(f: AllocationOperation, gen: number, ctx: WriterContex } return; } - if (op.kind === 'string') { + if (op.kind === "string") { if (op.optional) { - ctx.append(`${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_ref().begin_parse() : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_ref().begin_parse() : null();`, + ); } else { ctx.append(`${varName} = sc_${gen}~load_ref().begin_parse();`); } return; } - if (op.kind === 'fixed-bytes') { + if (op.kind === "fixed-bytes") { if (op.optional) { - ctx.append(`${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_bits(${op.bytes * 8}) : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? sc_${gen}~load_bits(${op.bytes * 8}) : null();`, + ); } else { ctx.append(`${varName} = sc_${gen}~load_bits(${op.bytes * 8});`); } return; } - if (op.kind === 'map') { + if (op.kind === "map") { ctx.append(`${varName} = sc_${gen}~load_dict();`); return; } - if (op.kind === 'struct') { + if (op.kind === "struct") { if (op.optional) { if (op.ref) { - throw Error('Not implemented'); + throw Error("Not implemented"); } else { - ctx.append(`${varName} = sc_${gen}~load_int(1) ? ${ops.typeAsOptional(op.type, ctx)}(sc_${gen}~${ops.reader(op.type, ctx)}()) : null();`); + ctx.append( + `${varName} = sc_${gen}~load_int(1) ? ${ops.typeAsOptional(op.type, ctx)}(sc_${gen}~${ops.reader(op.type, ctx)}()) : null();`, + ); } } else { if (op.ref) { - throw Error('Not implemented'); + throw Error("Not implemented"); } else { - ctx.append(`${varName} = sc_${gen}~${ops.reader(op.type, ctx)}();`); + ctx.append( + `${varName} = sc_${gen}~${ops.reader(op.type, ctx)}();`, + ); } } return; } - throw Error('Unsupported field kind: ' + op.kind); -} \ No newline at end of file + throw Error("Unsupported field kind: " + op.kind); +} diff --git a/src/generator/writers/writeStdlib.ts b/src/generator/writers/writeStdlib.ts index e41c29b48..b97366e72 100644 --- a/src/generator/writers/writeStdlib.ts +++ b/src/generator/writers/writeStdlib.ts @@ -3,25 +3,24 @@ import { enabledMasterchain } from "../../config/features"; import { WriterContext } from "../Writer"; export function writeStdlib(ctx: WriterContext) { - // // stdlib extension functions // - ctx.skip('__tact_set'); - ctx.skip('__tact_nop'); - ctx.skip('__tact_str_to_slice'); - ctx.skip('__tact_slice_to_str'); - ctx.skip('__tact_address_to_slice'); + ctx.skip("__tact_set"); + ctx.skip("__tact_nop"); + ctx.skip("__tact_str_to_slice"); + ctx.skip("__tact_slice_to_str"); + ctx.skip("__tact_address_to_slice"); // // Addresses // - ctx.fun('__tact_verify_address', () => { + ctx.fun("__tact_verify_address", () => { ctx.signature(`slice __tact_verify_address(slice address)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` throw_unless(${contractErrors.invalidAddress.id}, address.slice_bits() == 267); @@ -41,13 +40,13 @@ export function writeStdlib(ctx: WriterContext) { ctx.write(` return address; `); - }) + }); }); - ctx.fun('__tact_load_address', () => { + ctx.fun("__tact_load_address", () => { ctx.signature(`(slice, slice) __tact_load_address(slice cs)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` slice raw = cs~load_msg_addr(); @@ -56,10 +55,10 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_load_address_opt', () => { + ctx.fun("__tact_load_address_opt", () => { ctx.signature(`(slice, slice) __tact_load_address_opt(slice cs)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` slice raw = cs~load_msg_addr(); @@ -72,10 +71,10 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_store_address', () => { + ctx.fun("__tact_store_address", () => { ctx.signature(`builder __tact_store_address(builder b, slice address)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return b.store_slice(${ctx.used(`__tact_verify_address`)}(address)); @@ -83,10 +82,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_store_address_opt', () => { - ctx.signature(`builder __tact_store_address_opt(builder b, slice address)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_store_address_opt", () => { + ctx.signature( + `builder __tact_store_address_opt(builder b, slice address)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(address)) { @@ -99,10 +100,10 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_create_address', () => { + ctx.fun("__tact_create_address", () => { ctx.signature(`slice __tact_create_address(int chain, int hash)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var b = begin_cell(); @@ -116,10 +117,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_compute_contract_address', () => { - ctx.signature(`slice __tact_compute_contract_address(int chain, cell code, cell data)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_compute_contract_address", () => { + ctx.signature( + `slice __tact_compute_contract_address(int chain, cell code, cell data)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var b = begin_cell(); @@ -136,8 +139,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_my_balance`, () => { ctx.signature(`int __tact_my_balance()`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return pair_first(get_balance()); @@ -145,80 +148,91 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_not_null', () => { + ctx.fun("__tact_not_null", () => { ctx.signature(`forall X -> X __tact_not_null(X x)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { - ctx.write(`throw_if(${contractErrors.null.id}, null?(x)); return x;`); + ctx.write( + `throw_if(${contractErrors.null.id}, null?(x)); return x;`, + ); }); - }); - ctx.fun('__tact_dict_delete', () => { - ctx.signature(`(cell, int) __tact_dict_delete(cell dict, int key_len, slice index)`); - ctx.context('stdlib'); + ctx.fun("__tact_dict_delete", () => { + ctx.signature( + `(cell, int) __tact_dict_delete(cell dict, int key_len, slice index)`, + ); + ctx.context("stdlib"); ctx.asm(`asm(index dict key_len) "DICTDEL"`); }); - ctx.fun('__tact_dict_set_ref', () => { - ctx.signature(`((cell), ()) __tact_dict_set_ref(cell dict, int key_len, slice index, cell value)`); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_ref", () => { + ctx.signature( + `((cell), ()) __tact_dict_set_ref(cell dict, int key_len, slice index, cell value)`, + ); + ctx.context("stdlib"); ctx.asm(`asm(value index dict key_len) "DICTSETREF"`); }); - ctx.fun('__tact_dict_get', () => { - ctx.signature(`(slice, int) __tact_dict_get(cell dict, int key_len, slice index)`); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get", () => { + ctx.signature( + `(slice, int) __tact_dict_get(cell dict, int key_len, slice index)`, + ); + ctx.context("stdlib"); ctx.asm(`asm(index dict key_len) "DICTGET" "NULLSWAPIFNOT"`); }); - ctx.fun('__tact_dict_get_ref', () => { - ctx.signature(`(cell, int) __tact_dict_get_ref(cell dict, int key_len, slice index)`); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get_ref", () => { + ctx.signature( + `(cell, int) __tact_dict_get_ref(cell dict, int key_len, slice index)`, + ); + ctx.context("stdlib"); ctx.asm(`asm(index dict key_len) "DICTGETREF" "NULLSWAPIFNOT"`); }); - ctx.fun('__tact_debug', () => { + ctx.fun("__tact_debug", () => { ctx.signature(`forall X -> () __tact_debug(X value)`); - ctx.flag('impure'); - ctx.context('stdlib'); + ctx.flag("impure"); + ctx.context("stdlib"); ctx.asm(`asm "s0 DUMP" "DROP"`); }); - ctx.fun('__tact_debug_str', () => { + ctx.fun("__tact_debug_str", () => { ctx.signature(`() __tact_debug_str(slice value)`); - ctx.flag('impure'); - ctx.context('stdlib'); + ctx.flag("impure"); + ctx.context("stdlib"); ctx.asm(`asm "STRDUMP" "DROP"`); }); - ctx.fun('__tact_debug_bool', () => { + ctx.fun("__tact_debug_bool", () => { ctx.signature(`() __tact_debug_bool(int value)`); - ctx.flag('impure'); - ctx.context('stdlib'); + ctx.flag("impure"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (value) { - ${ctx.used('__tact_debug_str')}("true"); + ${ctx.used("__tact_debug_str")}("true"); } else { - ${ctx.used('__tact_debug_str')}("false"); + ${ctx.used("__tact_debug_str")}("false"); } `); }); }); - ctx.fun('__tact_preload_offset', () => { - ctx.signature(`(slice) __tact_preload_offset(slice s, int offset, int bits)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_preload_offset", () => { + ctx.signature( + `(slice) __tact_preload_offset(slice s, int offset, int bits)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.asm(`asm "SDSUBSTR"`); }); - ctx.fun('__tact_crc16', () => { + ctx.fun("__tact_crc16", () => { ctx.signature(`(slice) __tact_crc16(slice data)`); - ctx.flag('inline_ref'); - ctx.context('stdlib'); + ctx.flag("inline_ref"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` slice new_data = begin_cell() @@ -250,9 +264,9 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_base64_encode', () => { + ctx.fun("__tact_base64_encode", () => { ctx.signature(`(slice) __tact_base64_encode(slice data)`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` slice chars = "4142434445464748494A4B4C4D4E4F505152535455565758595A6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435363738392D5F"s; @@ -264,10 +278,10 @@ export function writeStdlib(ctx: WriterContext) { int n = (bs1 << 16) | (bs2 << 8) | bs3; res = res - .store_slice(${ctx.used('__tact_preload_offset')}(chars, ((n >> 18) & 63) * 8, 8)) - .store_slice(${ctx.used('__tact_preload_offset')}(chars, ((n >> 12) & 63) * 8, 8)) - .store_slice(${ctx.used('__tact_preload_offset')}(chars, ((n >> 6) & 63) * 8, 8)) - .store_slice(${ctx.used('__tact_preload_offset')}(chars, ((n ) & 63) * 8, 8)); + .store_slice(${ctx.used("__tact_preload_offset")}(chars, ((n >> 18) & 63) * 8, 8)) + .store_slice(${ctx.used("__tact_preload_offset")}(chars, ((n >> 12) & 63) * 8, 8)) + .store_slice(${ctx.used("__tact_preload_offset")}(chars, ((n >> 6) & 63) * 8, 8)) + .store_slice(${ctx.used("__tact_preload_offset")}(chars, ((n ) & 63) * 8, 8)); } return res.end_cell().begin_parse(); @@ -275,9 +289,9 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_address_to_userfriendly', () => { + ctx.fun("__tact_address_to_userfriendly", () => { ctx.signature(`(slice) __tact_address_to_userfriendly(slice address)`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` (int wc, int hash) = address.parse_std_addr(); @@ -288,51 +302,51 @@ export function writeStdlib(ctx: WriterContext) { .store_uint(hash, 256) .end_cell().begin_parse(); - slice checksum = ${ctx.used('__tact_crc16')}(user_friendly_address); + slice checksum = ${ctx.used("__tact_crc16")}(user_friendly_address); slice user_friendly_address_with_checksum = begin_cell() .store_slice(user_friendly_address) .store_slice(checksum) .end_cell().begin_parse(); - return ${ctx.used('__tact_base64_encode')}(user_friendly_address_with_checksum); + return ${ctx.used("__tact_base64_encode")}(user_friendly_address_with_checksum); `); }); }); - ctx.fun('__tact_debug_address', () => { + ctx.fun("__tact_debug_address", () => { ctx.signature(`() __tact_debug_address(slice address)`); - ctx.flag('impure'); - ctx.context('stdlib'); + ctx.flag("impure"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` - ${ctx.used('__tact_debug_str')}(${ctx.used('__tact_address_to_userfriendly')}(address)); + ${ctx.used("__tact_debug_str")}(${ctx.used("__tact_address_to_userfriendly")}(address)); `); }); }); - ctx.fun('__tact_context_get', () => { + ctx.fun("__tact_context_get", () => { ctx.signature(`(int, slice, int, slice) __tact_context_get()`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(`return __tact_context;`); }); }); - ctx.fun('__tact_context_get_sender', () => { + ctx.fun("__tact_context_get_sender", () => { ctx.signature(`slice __tact_context_get_sender()`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(`return __tact_context_sender;`); }); }); - ctx.fun('__tact_prepare_random', () => { + ctx.fun("__tact_prepare_random", () => { ctx.signature(`() __tact_prepare_random()`); - ctx.flag('impure'); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("impure"); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(__tact_randomized)) { @@ -343,10 +357,10 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_store_bool', () => { + ctx.fun("__tact_store_bool", () => { ctx.signature(`builder __tact_store_bool(builder b, int v)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return b.store_int(v, 1); @@ -354,15 +368,15 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_to_tuple', () => { + ctx.fun("__tact_to_tuple", () => { ctx.signature(`forall X -> tuple __tact_to_tuple(X x)`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.asm(`asm "NOP"`); }); - ctx.fun('__tact_from_tuple', () => { + ctx.fun("__tact_from_tuple", () => { ctx.signature(`forall X -> X __tact_from_tuple(tuple x)`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.asm(`asm "NOP"`); }); @@ -370,10 +384,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Int -> Int // - ctx.fun('__tact_dict_set_int_int', () => { - ctx.signature(`(cell, ()) __tact_dict_set_int_int(cell d, int kl, int k, int v, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_int_int", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_int_int(cell d, int kl, int k, int v, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -386,10 +402,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_int_int', () => { - ctx.signature(`int __tact_dict_get_int_int(cell d, int kl, int k, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get_int_int", () => { + ctx.signature( + `int __tact_dict_get_int_int(cell d, int kl, int k, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = idict_get?(d, kl, k); @@ -406,10 +424,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Int -> Int // - ctx.fun('__tact_dict_set_int_uint', () => { - ctx.signature(`(cell, ()) __tact_dict_set_int_uint(cell d, int kl, int k, int v, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_int_uint", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_int_uint(cell d, int kl, int k, int v, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -422,10 +442,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_int_uint', () => { - ctx.signature(`int __tact_dict_get_int_uint(cell d, int kl, int k, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get_int_uint", () => { + ctx.signature( + `int __tact_dict_get_int_uint(cell d, int kl, int k, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = idict_get?(d, kl, k); @@ -442,10 +464,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Uint -> Int // - ctx.fun('__tact_dict_set_uint_int', () => { - ctx.signature(`(cell, ()) __tact_dict_set_uint_int(cell d, int kl, int k, int v, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_uint_int", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_uint_int(cell d, int kl, int k, int v, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -458,10 +482,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_uint_int', () => { - ctx.signature(`int __tact_dict_get_uint_int(cell d, int kl, int k, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get_uint_int", () => { + ctx.signature( + `int __tact_dict_get_uint_int(cell d, int kl, int k, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = udict_get?(d, kl, k); @@ -478,10 +504,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Uint -> Uint // - ctx.fun('__tact_dict_set_uint_uint', () => { - ctx.signature(`(cell, ()) __tact_dict_set_uint_uint(cell d, int kl, int k, int v, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_uint_uint", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_uint_uint(cell d, int kl, int k, int v, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -494,10 +522,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_uint_uint', () => { - ctx.signature(`int __tact_dict_get_uint_uint(cell d, int kl, int k, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get_uint_uint", () => { + ctx.signature( + `int __tact_dict_get_uint_uint(cell d, int kl, int k, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = udict_get?(d, kl, k); @@ -514,10 +544,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Int -> Cell // - ctx.fun('__tact_dict_set_int_cell', () => { - ctx.signature(`(cell, ()) __tact_dict_set_int_cell(cell d, int kl, int k, cell v)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_int_cell", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_int_cell(cell d, int kl, int k, cell v)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -530,10 +562,10 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_int_cell', () => { + ctx.fun("__tact_dict_get_int_cell", () => { ctx.signature(`cell __tact_dict_get_int_cell(cell d, int kl, int k)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = idict_get_ref?(d, kl, k); @@ -546,15 +578,16 @@ export function writeStdlib(ctx: WriterContext) { }); }); - // // Dict Uint -> Cell // - ctx.fun('__tact_dict_set_uint_cell', () => { - ctx.signature(`(cell, ()) __tact_dict_set_uint_cell(cell d, int kl, int k, cell v)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_uint_cell", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_uint_cell(cell d, int kl, int k, cell v)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -567,10 +600,10 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_uint_cell', () => { + ctx.fun("__tact_dict_get_uint_cell", () => { ctx.signature(`cell __tact_dict_get_uint_cell(cell d, int kl, int k)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = udict_get_ref?(d, kl, k); @@ -587,10 +620,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Int -> Slice // - ctx.fun('__tact_dict_set_int_slice', () => { - ctx.signature(`(cell, ()) __tact_dict_set_int_slice(cell d, int kl, int k, slice v)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_int_slice", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_int_slice(cell d, int kl, int k, slice v)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -603,10 +638,10 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_int_slice', () => { + ctx.fun("__tact_dict_get_int_slice", () => { ctx.signature(`slice __tact_dict_get_int_slice(cell d, int kl, int k)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = idict_get?(d, kl, k); @@ -623,10 +658,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Uint -> Slice // - ctx.fun('__tact_dict_set_uint_slice', () => { - ctx.signature(`(cell, ()) __tact_dict_set_uint_slice(cell d, int kl, int k, slice v)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_uint_slice", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_uint_slice(cell d, int kl, int k, slice v)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -639,10 +676,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_uint_slice', () => { - ctx.signature(`slice __tact_dict_get_uint_slice(cell d, int kl, int k)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get_uint_slice", () => { + ctx.signature( + `slice __tact_dict_get_uint_slice(cell d, int kl, int k)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = udict_get?(d, kl, k); @@ -659,10 +698,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Slice -> Int // - ctx.fun('__tact_dict_set_slice_int', () => { - ctx.signature(`(cell, ()) __tact_dict_set_slice_int(cell d, int kl, slice k, int v, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_slice_int", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_slice_int(cell d, int kl, slice k, int v, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -675,10 +716,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_slice_int', () => { - ctx.signature(`int __tact_dict_get_slice_int(cell d, int kl, slice k, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get_slice_int", () => { + ctx.signature( + `int __tact_dict_get_slice_int(cell d, int kl, slice k, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = ${ctx.used(`__tact_dict_get`)}(d, kl, k); @@ -695,10 +738,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Slice -> UInt // - ctx.fun('__tact_dict_set_slice_uint', () => { - ctx.signature(`(cell, ()) __tact_dict_set_slice_uint(cell d, int kl, slice k, int v, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_slice_uint", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_slice_uint(cell d, int kl, slice k, int v, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -711,10 +756,12 @@ export function writeStdlib(ctx: WriterContext) { }); }); - ctx.fun('__tact_dict_get_slice_uint', () => { - ctx.signature(`int __tact_dict_get_slice_uint(cell d, int kl, slice k, int vl)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_get_slice_uint", () => { + ctx.signature( + `int __tact_dict_get_slice_uint(cell d, int kl, slice k, int vl)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = ${ctx.used(`__tact_dict_get`)}(d, kl, k); @@ -731,10 +778,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Slice -> Cell // - ctx.fun('__tact_dict_set_slice_cell', () => { - ctx.signature(`(cell, ()) __tact_dict_set_slice_cell(cell d, int kl, slice k, cell v)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_slice_cell", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_slice_cell(cell d, int kl, slice k, cell v)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -748,9 +797,11 @@ export function writeStdlib(ctx: WriterContext) { }); ctx.fun(`__tact_dict_get_slice_cell`, () => { - ctx.signature(`cell __tact_dict_get_slice_cell(cell d, int kl, slice k)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.signature( + `cell __tact_dict_get_slice_cell(cell d, int kl, slice k)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = ${ctx.used(`__tact_dict_get_ref`)}(d, kl, k); @@ -767,10 +818,12 @@ export function writeStdlib(ctx: WriterContext) { // Dict Slice -> Slice // - ctx.fun('__tact_dict_set_slice_slice', () => { - ctx.signature(`(cell, ()) __tact_dict_set_slice_slice(cell d, int kl, slice k, slice v)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.fun("__tact_dict_set_slice_slice", () => { + ctx.signature( + `(cell, ()) __tact_dict_set_slice_slice(cell d, int kl, slice k, slice v)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (null?(v)) { @@ -784,9 +837,11 @@ export function writeStdlib(ctx: WriterContext) { }); ctx.fun(`__tact_dict_get_slice_slice`, () => { - ctx.signature(`slice __tact_dict_get_slice_slice(cell d, int kl, slice k)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.signature( + `slice __tact_dict_get_slice_slice(cell d, int kl, slice k)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (r, ok) = ${ctx.used(`__tact_dict_get`)}(d, kl, k); @@ -801,40 +856,42 @@ export function writeStdlib(ctx: WriterContext) { // // Address - // + // ctx.fun(`__tact_slice_eq_bits`, () => { ctx.signature(`int __tact_slice_eq_bits(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return equal_slice_bits(a, b); - `) + `); }); }); ctx.fun(`__tact_slice_eq_bits_nullable_one`, () => { - ctx.signature(`int __tact_slice_eq_bits_nullable_one(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.signature( + `int __tact_slice_eq_bits_nullable_one(slice a, slice b)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (null?(a)) ? (false) : (equal_slice_bits(a, b)); - `) + `); }); }); ctx.fun(`__tact_slice_eq_bits_nullable`, () => { ctx.signature(`int __tact_slice_eq_bits_nullable(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var a_is_null = null?(a); var b_is_null = null?(b); return ( a_is_null & b_is_null ) ? ( true ) : ( ( ( ~ a_is_null ) & ( ~ b_is_null ) ) ? ( equal_slice_bits(a, b) ) : ( false ) ); - `) + `); }); }); @@ -844,8 +901,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_int_eq_nullable_one`, () => { ctx.signature(`int __tact_int_eq_nullable_one(int a, int b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (null?(a)) ? (false) : (a == b); @@ -855,8 +912,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_int_neq_nullable_one`, () => { ctx.signature(`int __tact_int_neq_nullable_one(int a, int b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (null?(a)) ? (true) : (a != b); @@ -866,8 +923,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_int_eq_nullable`, () => { ctx.signature(`int __tact_int_eq_nullable(int a, int b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var a_is_null = null?(a); @@ -879,8 +936,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_int_neq_nullable`, () => { ctx.signature(`int __tact_int_neq_nullable(int a, int b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var a_is_null = null?(a); @@ -892,12 +949,12 @@ export function writeStdlib(ctx: WriterContext) { // // Cell Eq - // + // ctx.fun(`__tact_cell_eq`, () => { ctx.signature(`int __tact_cell_eq(cell a, cell b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (a.cell_hash() == b.cell_hash()); @@ -907,8 +964,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_cell_neq`, () => { ctx.signature(`int __tact_cell_neq(cell a, cell b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (a.cell_hash() != b.cell_hash()); @@ -918,8 +975,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_cell_eq_nullable_one`, () => { ctx.signature(`int __tact_cell_eq_nullable_one(cell a, cell b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (null?(a)) ? (false) : (a.cell_hash() == b.cell_hash()); @@ -929,8 +986,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_cell_neq_nullable_one`, () => { ctx.signature(`int __tact_cell_neq_nullable_one(cell a, cell b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (null?(a)) ? (true) : (a.cell_hash() != b.cell_hash()); @@ -940,8 +997,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_cell_eq_nullable`, () => { ctx.signature(`int __tact_cell_eq_nullable(cell a, cell b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var a_is_null = null?(a); @@ -953,8 +1010,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_cell_neq_nullable`, () => { ctx.signature(`int __tact_cell_neq_nullable(cell a, cell b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var a_is_null = null?(a); @@ -970,8 +1027,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_slice_eq`, () => { ctx.signature(`int __tact_slice_eq(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (a.slice_hash() == b.slice_hash()); @@ -981,8 +1038,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_slice_neq`, () => { ctx.signature(`int __tact_slice_neq(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (a.slice_hash() != b.slice_hash()); @@ -992,8 +1049,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_slice_eq_nullable_one`, () => { ctx.signature(`int __tact_slice_eq_nullable_one(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (null?(a)) ? (false) : (a.slice_hash() == b.slice_hash()); @@ -1003,8 +1060,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_slice_neq_nullable_one`, () => { ctx.signature(`int __tact_slice_neq_nullable_one(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return (null?(a)) ? (true) : (a.slice_hash() != b.slice_hash()); @@ -1014,8 +1071,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_slice_eq_nullable`, () => { ctx.signature(`int __tact_slice_eq_nullable(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var a_is_null = null?(a); @@ -1027,8 +1084,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_slice_neq_nullable`, () => { ctx.signature(`int __tact_slice_neq_nullable(slice a, slice b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var a_is_null = null?(a); @@ -1043,9 +1100,11 @@ export function writeStdlib(ctx: WriterContext) { // ctx.fun(`__tact_dict_set_code`, () => { - ctx.signature(`cell __tact_dict_set_code(cell dict, int id, cell code)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.signature( + `cell __tact_dict_set_code(cell dict, int id, cell code)`, + ); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return udict_set_ref(dict, 16, id, code); @@ -1055,8 +1114,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_dict_get_code`, () => { ctx.signature(`cell __tact_dict_get_code(cell dict, int id)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var (data, ok) = udict_get_ref?(dict, 16, id); @@ -1072,16 +1131,16 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_tuple_create_0`, () => { ctx.signature(`tuple __tact_tuple_create_0()`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.asm(`asm "NIL"`); }); ctx.fun(`__tact_tuple_destroy_0`, () => { ctx.signature(`() __tact_tuple_destroy_0()`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { - ctx.append(`return ();`) - }) + ctx.append(`return ();`); + }); }); for (let i = 1; i < 64; i++) { @@ -1090,8 +1149,10 @@ export function writeStdlib(ctx: WriterContext) { for (let j = 0; j < i; j++) { args.push(`X${j}`); } - ctx.signature(`forall ${args.join(', ')} -> tuple __tact_tuple_create_${i}((${args.join(', ')}) v)`); - ctx.context('stdlib'); + ctx.signature( + `forall ${args.join(", ")} -> tuple __tact_tuple_create_${i}((${args.join(", ")}) v)`, + ); + ctx.context("stdlib"); ctx.asm(`asm "${i} TUPLE"`); }); ctx.fun(`__tact_tuple_destroy_${i}`, () => { @@ -1099,8 +1160,10 @@ export function writeStdlib(ctx: WriterContext) { for (let j = 0; j < i; j++) { args.push(`X${j}`); } - ctx.signature(`forall ${args.join(', ')} -> (${args.join(', ')}) __tact_tuple_destroy_${i}(tuple v)`); - ctx.context('stdlib'); + ctx.signature( + `forall ${args.join(", ")} -> (${args.join(", ")}) __tact_tuple_destroy_${i}(tuple v)`, + ); + ctx.context("stdlib"); ctx.asm(`asm "${i} UNTUPLE"`); }); } @@ -1111,42 +1174,41 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_string_builder_start_comment`, () => { ctx.signature(`tuple __tact_string_builder_start_comment()`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` - return ${ctx.used('__tact_string_builder_start')}(begin_cell().store_uint(0, 32)); + return ${ctx.used("__tact_string_builder_start")}(begin_cell().store_uint(0, 32)); `); }); }); ctx.fun(`__tact_string_builder_start_tail_string`, () => { ctx.signature(`tuple __tact_string_builder_start_tail_string()`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` - return ${ctx.used('__tact_string_builder_start')}(begin_cell().store_uint(0, 8)); + return ${ctx.used("__tact_string_builder_start")}(begin_cell().store_uint(0, 8)); `); }); }); ctx.fun(`__tact_string_builder_start_string`, () => { ctx.signature(`tuple __tact_string_builder_start_string()`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` - return ${ctx.used('__tact_string_builder_start')}(begin_cell()); + return ${ctx.used("__tact_string_builder_start")}(begin_cell()); `); }); - }); ctx.fun(`__tact_string_builder_start`, () => { ctx.signature(`tuple __tact_string_builder_start(builder b)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` return tpush(tpush(empty_tuple(), b), null()); @@ -1156,8 +1218,8 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_string_builder_end`, () => { ctx.signature(`cell __tact_string_builder_end(tuple builders)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` (builder b, tuple tail) = uncons(builders); @@ -1173,18 +1235,20 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_string_builder_end_slice`, () => { ctx.signature(`slice __tact_string_builder_end_slice(tuple builders)`); - ctx.flag('inline'); - ctx.context('stdlib'); + ctx.flag("inline"); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` - return ${ctx.used('__tact_string_builder_end')}(builders).begin_parse(); + return ${ctx.used("__tact_string_builder_end")}(builders).begin_parse(); `); }); }); ctx.fun(`__tact_string_builder_append`, () => { - ctx.signature(`((tuple), ()) __tact_string_builder_append(tuple builders, slice sc)`); - ctx.context('stdlib'); + ctx.signature( + `((tuple), ()) __tact_string_builder_append(tuple builders, slice sc)`, + ); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` int sliceRefs = slice_refs(sc); @@ -1228,11 +1292,13 @@ export function writeStdlib(ctx: WriterContext) { }); ctx.fun(`__tact_string_builder_append_not_mut`, () => { - ctx.signature(`(tuple) __tact_string_builder_append_not_mut(tuple builders, slice sc)`); - ctx.context('stdlib'); + ctx.signature( + `(tuple) __tact_string_builder_append_not_mut(tuple builders, slice sc)`, + ); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` - builders~${ctx.used('__tact_string_builder_append')}(sc); + builders~${ctx.used("__tact_string_builder_append")}(sc); return builders; `); }); @@ -1240,7 +1306,7 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_int_to_string`, () => { ctx.signature(`slice __tact_int_to_string(int src)`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` var b = begin_cell(); @@ -1285,7 +1351,7 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_float_to_string`, () => { ctx.signature(`slice __tact_float_to_string(int src, int digits)`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` throw_if(${contractErrors.invalidArgument.id}, (digits <= 0) | (digits > 77)); @@ -1339,13 +1405,13 @@ export function writeStdlib(ctx: WriterContext) { ctx.fun(`__tact_log2`, () => { ctx.signature(`int __tact_log2(int num)`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.asm(`asm "UBITSIZE DEC"`); }); ctx.fun(`__tact_log`, () => { ctx.signature(`int __tact_log(int num, int base)`); - ctx.context('stdlib'); + ctx.context("stdlib"); ctx.body(() => { ctx.write(` if (num < base) { @@ -1360,4 +1426,4 @@ export function writeStdlib(ctx: WriterContext) { `); }); }); -} \ No newline at end of file +} diff --git a/src/grammar/ast.ts b/src/grammar/ast.ts index 005c82e2c..b76924e79 100644 --- a/src/grammar/ast.ts +++ b/src/grammar/ast.ts @@ -1,123 +1,122 @@ -import { Interval as RawInterval, Node as RawNode } from 'ohm-js'; -import { TactSyntaxError } from '../errors'; -import { TypeOrigin } from '../types/types'; +import { Interval as RawInterval, Node as RawNode } from "ohm-js"; +import { TactSyntaxError } from "../errors"; +import { TypeOrigin } from "../types/types"; export class ASTRef { - - static merge(...refs: ASTRef[]) { - if (refs.length === 0) { - throw Error('Cannot merge 0 refs'); - } - let r = refs[0].#interval; - const file = refs[0].#file; - for (let i = 1; i < refs.length; i++) { - r = r.coverageWith(r, refs[i].#interval); - } - return new ASTRef(r, file); + static merge(...refs: ASTRef[]) { + if (refs.length === 0) { + throw Error("Cannot merge 0 refs"); + } + let r = refs[0].#interval; + const file = refs[0].#file; + for (let i = 1; i < refs.length; i++) { + r = r.coverageWith(r, refs[i].#interval); } + return new ASTRef(r, file); + } - readonly #interval: RawInterval; - readonly #file: string | null; + readonly #interval: RawInterval; + readonly #file: string | null; - constructor(interval: RawInterval, file: string | null) { - this.#interval = interval; - this.#file = file; - } + constructor(interval: RawInterval, file: string | null) { + this.#interval = interval; + this.#file = file; + } - get file() { - return this.#file; - } + get file() { + return this.#file; + } - get contents() { - return this.#interval.contents; - } + get contents() { + return this.#interval.contents; + } - get interval() { - return this.#interval; - } + get interval() { + return this.#interval; + } } export type ASTPrimitive = { - kind: 'primitive', - origin: TypeOrigin, - id: number, - name: string, - ref: ASTRef -} + kind: "primitive"; + origin: TypeOrigin; + id: number; + name: string; + ref: ASTRef; +}; // // Values // export type ASTNumber = { - kind: 'number', - id: number, - value: bigint, - ref: ASTRef -} + kind: "number"; + id: number; + value: bigint; + ref: ASTRef; +}; export type ASTID = { - kind: 'id', - id: number, - value: string, - ref: ASTRef -} + kind: "id"; + id: number; + value: string; + ref: ASTRef; +}; export type ASTBoolean = { - kind: 'boolean', - id: number, - value: boolean, - ref: ASTRef -} + kind: "boolean"; + id: number; + value: boolean; + ref: ASTRef; +}; export type ASTString = { - kind: 'string', - id: number, - value: string, - ref: ASTRef -} + kind: "string"; + id: number; + value: string; + ref: ASTRef; +}; export type ASTNull = { - kind: 'null', - id: number, - ref: ASTRef -} + kind: "null"; + id: number; + ref: ASTRef; +}; export type ASTLvalueRef = { - kind: 'lvalue_ref', - id: number, - name: string, - ref: ASTRef -} + kind: "lvalue_ref"; + id: number; + name: string; + ref: ASTRef; +}; // // Types // export type ASTTypeRefSimple = { - kind: 'type_ref_simple', - id: number, - name: string, - optional: boolean, - ref: ASTRef -} + kind: "type_ref_simple"; + id: number; + name: string; + optional: boolean; + ref: ASTRef; +}; export type ASTTypeRefMap = { - kind: 'type_ref_map', - id: number, - key: string, - keyAs: string | null, - value: string, - valueAs: string | null, - ref: ASTRef -} + kind: "type_ref_map"; + id: number; + key: string; + keyAs: string | null; + value: string; + valueAs: string | null; + ref: ASTRef; +}; export type ASTTypeRefBounced = { - kind: 'type_ref_bounced', - id: number, - name: string, - ref: ASTRef -} + kind: "type_ref_bounced"; + id: number; + name: string; + ref: ASTRef; +}; export type ASTTypeRef = ASTTypeRefSimple | ASTTypeRefMap | ASTTypeRefBounced; @@ -126,521 +125,640 @@ export type ASTTypeRef = ASTTypeRefSimple | ASTTypeRefMap | ASTTypeRefBounced; // export type ASTOpBinary = { - kind: 'op_binary', - id: number, - op: '+' | '-' | '*' | '/' | '!=' | '>' | '<' | '>=' | '<=' | '==' | '&&' | '||' | '%' | '<<' | '>>' | '&' | '|', - left: ASTExpression, - right: ASTExpression, - ref: ASTRef -} + kind: "op_binary"; + id: number; + op: + | "+" + | "-" + | "*" + | "/" + | "!=" + | ">" + | "<" + | ">=" + | "<=" + | "==" + | "&&" + | "||" + | "%" + | "<<" + | ">>" + | "&" + | "|"; + left: ASTExpression; + right: ASTExpression; + ref: ASTRef; +}; export type ASTOpUnary = { - kind: 'op_unary', - id: number, - op: '+' | '-' | '!' | '!!', - right: ASTExpression, - ref: ASTRef -} + kind: "op_unary"; + id: number; + op: "+" | "-" | "!" | "!!"; + right: ASTExpression; + ref: ASTRef; +}; export type ASTOpField = { - kind: 'op_field' - id: number, - src: ASTExpression, - name: string, - ref: ASTRef -} + kind: "op_field"; + id: number; + src: ASTExpression; + name: string; + ref: ASTRef; +}; export type ASTOpCall = { - kind: 'op_call' - id: number, - src: ASTExpression, - name: string, - args: ASTExpression[], - ref: ASTRef -} + kind: "op_call"; + id: number; + src: ASTExpression; + name: string; + args: ASTExpression[]; + ref: ASTRef; +}; export type ASTOpCallStatic = { - kind: 'op_static_call' - id: number, - name: string, - args: ASTExpression[], - ref: ASTRef -} + kind: "op_static_call"; + id: number; + name: string; + args: ASTExpression[]; + ref: ASTRef; +}; export type ASTOpNew = { - kind: 'op_new' - id: number, - type: string, - args: ASTNewParameter[], - ref: ASTRef -} + kind: "op_new"; + id: number; + type: string; + args: ASTNewParameter[]; + ref: ASTRef; +}; export type ASTNewParameter = { - kind: 'new_parameter' - id: number, - name: string, - exp: ASTExpression, - ref: ASTRef -} + kind: "new_parameter"; + id: number; + name: string; + exp: ASTExpression; + ref: ASTRef; +}; export type ASTInitOf = { - kind: 'init_of' - id: number, - name: string, - args: ASTExpression[], - ref: ASTRef -} + kind: "init_of"; + id: number; + name: string; + args: ASTExpression[]; + ref: ASTRef; +}; export type ASTConditional = { - kind: 'conditional' - id: number, - condition: ASTExpression, - thenBranch: ASTExpression, - elseBranch: ASTExpression, - ref: ASTRef -} + kind: "conditional"; + id: number; + condition: ASTExpression; + thenBranch: ASTExpression; + elseBranch: ASTExpression; + ref: ASTRef; +}; // // Program // export type ASTProgram = { - kind: 'program', - id: number, - entries: (ASTStruct | ASTContract | ASTPrimitive | ASTFunction | ASTNativeFunction | ASTTrait | ASTProgramImport | ASTConstant)[] -} + kind: "program"; + id: number; + entries: ( + | ASTStruct + | ASTContract + | ASTPrimitive + | ASTFunction + | ASTNativeFunction + | ASTTrait + | ASTProgramImport + | ASTConstant + )[]; +}; export type ASTProgramImport = { - kind: 'program_import', - id: number, - path: ASTString, - ref: ASTRef -} + kind: "program_import"; + id: number; + path: ASTString; + ref: ASTRef; +}; export type ASTStruct = { - kind: 'def_struct', - origin: TypeOrigin, - id: number, - name: string, - message: boolean, - prefix: number | null, - fields: ASTField[], - ref: ASTRef -} + kind: "def_struct"; + origin: TypeOrigin; + id: number; + name: string; + message: boolean; + prefix: number | null; + fields: ASTField[]; + ref: ASTRef; +}; export type ASTTrait = { - kind: 'def_trait', - origin: TypeOrigin, - id: number, - name: string, - traits: ASTString[], - attributes: ASTContractAttribute[], - declarations: (ASTField | ASTFunction | ASTReceive | ASTConstant)[], - ref: ASTRef -} + kind: "def_trait"; + origin: TypeOrigin; + id: number; + name: string; + traits: ASTString[]; + attributes: ASTContractAttribute[]; + declarations: (ASTField | ASTFunction | ASTReceive | ASTConstant)[]; + ref: ASTRef; +}; export type ASTField = { - kind: 'def_field', - id: number, - name: string, - type: ASTTypeRef, - init: ASTExpression | null, - as: string | null, - ref: ASTRef -} + kind: "def_field"; + id: number; + name: string; + type: ASTTypeRef; + init: ASTExpression | null; + as: string | null; + ref: ASTRef; +}; export type ASTConstant = { - kind: 'def_constant', - id: number, - name: string, - type: ASTTypeRef, - value: ASTExpression | null, - attributes: ASTConstantAttribute[], - ref: ASTRef -} + kind: "def_constant"; + id: number; + name: string; + type: ASTTypeRef; + value: ASTExpression | null; + attributes: ASTConstantAttribute[]; + ref: ASTRef; +}; export type ASTConstantAttribute = - | { type: 'virtual', ref: ASTRef } - | { type: 'overrides', ref: ASTRef } - | { type: 'abstract', ref: ASTRef }; + | { type: "virtual"; ref: ASTRef } + | { type: "overrides"; ref: ASTRef } + | { type: "abstract"; ref: ASTRef }; -export type ASTContractAttribute = { type: 'interface', name: ASTString, ref: ASTRef }; +export type ASTContractAttribute = { + type: "interface"; + name: ASTString; + ref: ASTRef; +}; export type ASTContract = { - kind: 'def_contract', - origin: TypeOrigin, - id: number, - name: string, - traits: ASTString[], - attributes: ASTContractAttribute[], - declarations: (ASTField | ASTFunction | ASTInitFunction | ASTReceive | ASTConstant)[], - ref: ASTRef -} + kind: "def_contract"; + origin: TypeOrigin; + id: number; + name: string; + traits: ASTString[]; + attributes: ASTContractAttribute[]; + declarations: ( + | ASTField + | ASTFunction + | ASTInitFunction + | ASTReceive + | ASTConstant + )[]; + ref: ASTRef; +}; export type ASTArgument = { - kind: 'def_argument', - id: number, - name: string, - type: ASTTypeRef, - ref: ASTRef -} + kind: "def_argument"; + id: number; + name: string; + type: ASTTypeRef; + ref: ASTRef; +}; export type ASTFunctionAttribute = - | { type: 'get', ref: ASTRef } - | { type: 'mutates', ref: ASTRef } - | { type: 'extends', ref: ASTRef } - | { type: 'virtual', ref: ASTRef } - | { type: 'abstract', ref: ASTRef } - | { type: 'overrides', ref: ASTRef } - | { type: 'inline', ref: ASTRef }; + | { type: "get"; ref: ASTRef } + | { type: "mutates"; ref: ASTRef } + | { type: "extends"; ref: ASTRef } + | { type: "virtual"; ref: ASTRef } + | { type: "abstract"; ref: ASTRef } + | { type: "overrides"; ref: ASTRef } + | { type: "inline"; ref: ASTRef }; export type ASTFunction = { - kind: 'def_function', - origin: TypeOrigin, - id: number, - attributes: ASTFunctionAttribute[], - name: string, - return: ASTTypeRef | null, - args: ASTArgument[], - statements: ASTStatement[] | null, - ref: ASTRef -} + kind: "def_function"; + origin: TypeOrigin; + id: number; + attributes: ASTFunctionAttribute[]; + name: string; + return: ASTTypeRef | null; + args: ASTArgument[]; + statements: ASTStatement[] | null; + ref: ASTRef; +}; export type ASTReceive = { - kind: 'def_receive', - id: number, - selector: { - kind: 'internal-simple', arg: ASTArgument - } | { - kind: 'internal-fallback' - } | { - kind: 'internal-comment', comment: ASTString - } | { - kind: 'bounce', arg: ASTArgument - } | { - kind: 'external-simple', arg: ASTArgument - } | { - kind: 'external-fallback' - } | { - kind: 'external-comment', comment: ASTString - }, - statements: ASTStatement[], - ref: ASTRef -} + kind: "def_receive"; + id: number; + selector: + | { + kind: "internal-simple"; + arg: ASTArgument; + } + | { + kind: "internal-fallback"; + } + | { + kind: "internal-comment"; + comment: ASTString; + } + | { + kind: "bounce"; + arg: ASTArgument; + } + | { + kind: "external-simple"; + arg: ASTArgument; + } + | { + kind: "external-fallback"; + } + | { + kind: "external-comment"; + comment: ASTString; + }; + statements: ASTStatement[]; + ref: ASTRef; +}; export type ASTNativeFunction = { - kind: 'def_native_function', - origin: TypeOrigin, - id: number, - attributes: ASTFunctionAttribute[], - name: string, - nativeName: string, - return: ASTTypeRef | null, - args: ASTArgument[], - ref: ASTRef -} + kind: "def_native_function"; + origin: TypeOrigin; + id: number; + attributes: ASTFunctionAttribute[]; + name: string; + nativeName: string; + return: ASTTypeRef | null; + args: ASTArgument[]; + ref: ASTRef; +}; export type ASTInitFunction = { - kind: 'def_init_function', - id: number, - args: ASTArgument[], - statements: ASTStatement[], - ref: ASTRef -} + kind: "def_init_function"; + id: number; + args: ASTArgument[]; + statements: ASTStatement[]; + ref: ASTRef; +}; // // Statements // export type ASTStatementLet = { - kind: 'statement_let', - id: number, - name: string, - type: ASTTypeRef, - expression: ASTExpression, - ref: ASTRef -} + kind: "statement_let"; + id: number; + name: string; + type: ASTTypeRef; + expression: ASTExpression; + ref: ASTRef; +}; export type ASTStatementReturn = { - kind: 'statement_return', - id: number, - expression: ASTExpression | null, - ref: ASTRef -} + kind: "statement_return"; + id: number; + expression: ASTExpression | null; + ref: ASTRef; +}; export type ASTStatementExpression = { - kind: 'statement_expression', - id: number, - expression: ASTExpression, - ref: ASTRef -} + kind: "statement_expression"; + id: number; + expression: ASTExpression; + ref: ASTRef; +}; export type ASTSTatementAssign = { - kind: 'statement_assign', - id: number, - path: ASTLvalueRef[], - expression: ASTExpression, - ref: ASTRef -} + kind: "statement_assign"; + id: number; + path: ASTLvalueRef[]; + expression: ASTExpression; + ref: ASTRef; +}; export type ASTSTatementAugmentedAssign = { - kind: 'statement_augmentedassign', - id: number, - op: '+' | '-' | '*' | '/' | '%', - path: ASTLvalueRef[], - expression: ASTExpression, - ref: ASTRef -} + kind: "statement_augmentedassign"; + id: number; + op: "+" | "-" | "*" | "/" | "%"; + path: ASTLvalueRef[]; + expression: ASTExpression; + ref: ASTRef; +}; export type ASTCondition = { - kind: 'statement_condition', - id: number, - expression: ASTExpression, - trueStatements: ASTStatement[], - falseStatements: ASTStatement[] | null; - elseif: ASTCondition | null, - ref: ASTRef, -} + kind: "statement_condition"; + id: number; + expression: ASTExpression; + trueStatements: ASTStatement[]; + falseStatements: ASTStatement[] | null; + elseif: ASTCondition | null; + ref: ASTRef; +}; export type ASTStatementWhile = { - kind: 'statement_while' - id: number, - condition: ASTExpression, - statements: ASTStatement[], - ref: ASTRef -} + kind: "statement_while"; + id: number; + condition: ASTExpression; + statements: ASTStatement[]; + ref: ASTRef; +}; export type ASTStatementUntil = { - kind: 'statement_until' - id: number, - condition: ASTExpression, - statements: ASTStatement[], - ref: ASTRef -} + kind: "statement_until"; + id: number; + condition: ASTExpression; + statements: ASTStatement[]; + ref: ASTRef; +}; export type ASTStatementRepeat = { - kind: 'statement_repeat' - id: number, - condition: ASTExpression, - statements: ASTStatement[], - ref: ASTRef -} - + kind: "statement_repeat"; + id: number; + condition: ASTExpression; + statements: ASTStatement[]; + ref: ASTRef; +}; // // Unions // -export type ASTStatement = ASTStatementLet | ASTStatementReturn | ASTStatementExpression | ASTSTatementAssign | ASTSTatementAugmentedAssign | ASTCondition | ASTStatementWhile | ASTStatementUntil | ASTStatementRepeat; -export type ASTNode = ASTExpression | ASTStruct | ASTField | ASTContract | ASTArgument | ASTFunction | ASTOpCall | ASTStatementLet | ASTStatementReturn | ASTProgram | ASTPrimitive | ASTOpCallStatic | ASTStatementExpression | ASTNativeFunction | ASTSTatementAssign | ASTSTatementAugmentedAssign | ASTOpNew | ASTNewParameter | ASTTypeRef | ASTNull | ASTCondition | ASTInitFunction | ASTStatementWhile | ASTStatementUntil | ASTStatementRepeat | ASTReceive | ASTLvalueRef | ASTString | ASTTrait | ASTProgramImport | ASTInitOf | ASTConstant; -export type ASTExpression = ASTOpBinary | ASTOpUnary | ASTOpField | ASTNumber | ASTID | ASTBoolean | ASTOpCall | ASTOpCallStatic | ASTOpNew | ASTNull | ASTLvalueRef | ASTInitOf | ASTString | ASTConditional; +export type ASTStatement = + | ASTStatementLet + | ASTStatementReturn + | ASTStatementExpression + | ASTSTatementAssign + | ASTSTatementAugmentedAssign + | ASTCondition + | ASTStatementWhile + | ASTStatementUntil + | ASTStatementRepeat; +export type ASTNode = + | ASTExpression + | ASTStruct + | ASTField + | ASTContract + | ASTArgument + | ASTFunction + | ASTOpCall + | ASTStatementLet + | ASTStatementReturn + | ASTProgram + | ASTPrimitive + | ASTOpCallStatic + | ASTStatementExpression + | ASTNativeFunction + | ASTSTatementAssign + | ASTSTatementAugmentedAssign + | ASTOpNew + | ASTNewParameter + | ASTTypeRef + | ASTNull + | ASTCondition + | ASTInitFunction + | ASTStatementWhile + | ASTStatementUntil + | ASTStatementRepeat + | ASTReceive + | ASTLvalueRef + | ASTString + | ASTTrait + | ASTProgramImport + | ASTInitOf + | ASTConstant; +export type ASTExpression = + | ASTOpBinary + | ASTOpUnary + | ASTOpField + | ASTNumber + | ASTID + | ASTBoolean + | ASTOpCall + | ASTOpCallStatic + | ASTOpNew + | ASTNull + | ASTLvalueRef + | ASTInitOf + | ASTString + | ASTConditional; export type ASTType = ASTPrimitive | ASTStruct | ASTContract | ASTTrait; // eslint-disable-next-line @typescript-eslint/no-explicit-any type DistributiveOmit = T extends any - ? Omit - : never; + ? Omit + : never; let nextId = 1; -export function createNode(src: DistributiveOmit): ASTNode { - return Object.freeze(Object.assign({ id: nextId++ }, src)); +export function createNode(src: DistributiveOmit): ASTNode { + return Object.freeze(Object.assign({ id: nextId++ }, src)); } export function cloneASTNode(src: T): T { - return { ...src, id: nextId++ }; + return { ...src, id: nextId++ }; } export function __DANGER_resetNodeId() { - nextId = 1; + nextId = 1; } let currentFile: string | null = null; export function inFile(path: string, callback: () => T) { - currentFile = path; - const r = callback(); - currentFile = null; - return r; + currentFile = path; + const r = callback(); + currentFile = null; + return r; } export function createRef(s: RawNode, ...extra: RawNode[]): ASTRef { - let i = s.source; - if (extra.length > 0) { - i = i.coverageWith(...extra.map((e) => e.source)); - } - return new ASTRef(i, currentFile); + let i = s.source; + if (extra.length > 0) { + i = i.coverageWith(...extra.map((e) => e.source)); + } + return new ASTRef(i, currentFile); } export function throwError(message: string, ref: ASTRef): never { - if (ref.file) { - const lc = ref.interval.getLineAndColumn() as { lineNum: number, colNum: number }; - throw new TactSyntaxError(ref.file + ':' + lc.lineNum + ':' + lc.colNum + ': ' + message + '\n' + ref.interval.getLineAndColumnMessage(), ref); - } else { - throw new TactSyntaxError(message + ref.interval.getLineAndColumnMessage(), ref); - } + if (ref.file) { + const lc = ref.interval.getLineAndColumn() as { + lineNum: number; + colNum: number; + }; + throw new TactSyntaxError( + ref.file + + ":" + + lc.lineNum + + ":" + + lc.colNum + + ": " + + message + + "\n" + + ref.interval.getLineAndColumnMessage(), + ref, + ); + } else { + throw new TactSyntaxError( + message + ref.interval.getLineAndColumnMessage(), + ref, + ); + } } export function traverse(node: ASTNode, callback: (node: ASTNode) => void) { - callback(node); - - // - // Program - // - - if (node.kind === 'program') { - for (const e of node.entries) { - traverse(e, callback); - } - } - if (node.kind === 'def_contract') { - for (const e of node.declarations) { - traverse(e, callback); - } - } - if (node.kind === 'def_struct') { - for (const e of node.fields) { - traverse(e, callback); - } - } - if (node.kind === 'def_trait') { - for (const e of node.declarations) { - traverse(e, callback); - } - } - - // - // Functions - // - - if (node.kind === 'def_function') { - for (const e of node.args) { - traverse(e, callback); - } - if (node.statements) { - for (const e of node.statements) { - traverse(e, callback); - } - } - } - if (node.kind === 'def_init_function') { - for (const e of node.args) { - traverse(e, callback); - } - for (const e of node.statements) { - traverse(e, callback); - } - } - if (node.kind === 'def_receive') { - for (const e of node.statements) { - traverse(e, callback); - } - } - if (node.kind === 'def_native_function') { - for (const e of node.args) { - traverse(e, callback); - } - } - if (node.kind === 'def_field') { - if (node.init) { - traverse(node.init, callback); - } - } - if (node.kind === 'def_constant') { - if (node.value) { - traverse(node.value, callback); - } - } - - // - // Statements - // - - if (node.kind === 'statement_let') { - traverse(node.type, callback); - traverse(node.expression, callback); - } - if (node.kind === 'statement_return') { - if (node.expression) { - traverse(node.expression, callback); - } - } - if (node.kind === 'statement_expression') { - traverse(node.expression, callback); - } - if (node.kind === 'statement_assign') { - for (const e of node.path) { - traverse(e, callback); - } - traverse(node.expression, callback); - } - if (node.kind === 'statement_augmentedassign') { - for (const e of node.path) { - traverse(e, callback); - } - traverse(node.expression, callback); - } - if (node.kind === 'statement_condition') { - traverse(node.expression, callback); - for (const e of node.trueStatements) { - traverse(e, callback); - } - if (node.falseStatements) { - for (const e of node.falseStatements) { - traverse(e, callback); - } - } - if (node.elseif) { - traverse(node.elseif, callback); - } - } - if (node.kind === 'statement_while') { - traverse(node.condition, callback); - for (const e of node.statements) { - traverse(e, callback); - } - } - if (node.kind === 'statement_until') { - traverse(node.condition, callback); - for (const e of node.statements) { - traverse(e, callback); - } - } - if (node.kind === 'statement_repeat') { - traverse(node.condition, callback); - for (const e of node.statements) { - traverse(e, callback); - } - } - if (node.kind === 'op_binary') { - traverse(node.left, callback); - traverse(node.right, callback); - } - if (node.kind === 'op_unary') { - traverse(node.right, callback); - } - if (node.kind === 'op_field') { - traverse(node.src, callback); - } - if (node.kind === 'op_call') { - traverse(node.src, callback); - for (const e of node.args) { - traverse(e, callback); - } - } - if (node.kind === 'op_static_call') { - for (const e of node.args) { - traverse(e, callback); - } - } - if (node.kind === 'op_new') { - for (const e of node.args) { - traverse(e, callback); - } - } - if (node.kind === 'new_parameter') { - traverse(node.exp, callback); - } - if (node.kind === 'conditional') { - traverse(node.condition, callback); - traverse(node.thenBranch, callback); - traverse(node.elseBranch, callback); - } -} \ No newline at end of file + callback(node); + + // + // Program + // + + if (node.kind === "program") { + for (const e of node.entries) { + traverse(e, callback); + } + } + if (node.kind === "def_contract") { + for (const e of node.declarations) { + traverse(e, callback); + } + } + if (node.kind === "def_struct") { + for (const e of node.fields) { + traverse(e, callback); + } + } + if (node.kind === "def_trait") { + for (const e of node.declarations) { + traverse(e, callback); + } + } + + // + // Functions + // + + if (node.kind === "def_function") { + for (const e of node.args) { + traverse(e, callback); + } + if (node.statements) { + for (const e of node.statements) { + traverse(e, callback); + } + } + } + if (node.kind === "def_init_function") { + for (const e of node.args) { + traverse(e, callback); + } + for (const e of node.statements) { + traverse(e, callback); + } + } + if (node.kind === "def_receive") { + for (const e of node.statements) { + traverse(e, callback); + } + } + if (node.kind === "def_native_function") { + for (const e of node.args) { + traverse(e, callback); + } + } + if (node.kind === "def_field") { + if (node.init) { + traverse(node.init, callback); + } + } + if (node.kind === "def_constant") { + if (node.value) { + traverse(node.value, callback); + } + } + + // + // Statements + // + + if (node.kind === "statement_let") { + traverse(node.type, callback); + traverse(node.expression, callback); + } + if (node.kind === "statement_return") { + if (node.expression) { + traverse(node.expression, callback); + } + } + if (node.kind === "statement_expression") { + traverse(node.expression, callback); + } + if (node.kind === "statement_assign") { + for (const e of node.path) { + traverse(e, callback); + } + traverse(node.expression, callback); + } + if (node.kind === "statement_augmentedassign") { + for (const e of node.path) { + traverse(e, callback); + } + traverse(node.expression, callback); + } + if (node.kind === "statement_condition") { + traverse(node.expression, callback); + for (const e of node.trueStatements) { + traverse(e, callback); + } + if (node.falseStatements) { + for (const e of node.falseStatements) { + traverse(e, callback); + } + } + if (node.elseif) { + traverse(node.elseif, callback); + } + } + if (node.kind === "statement_while") { + traverse(node.condition, callback); + for (const e of node.statements) { + traverse(e, callback); + } + } + if (node.kind === "statement_until") { + traverse(node.condition, callback); + for (const e of node.statements) { + traverse(e, callback); + } + } + if (node.kind === "statement_repeat") { + traverse(node.condition, callback); + for (const e of node.statements) { + traverse(e, callback); + } + } + if (node.kind === "op_binary") { + traverse(node.left, callback); + traverse(node.right, callback); + } + if (node.kind === "op_unary") { + traverse(node.right, callback); + } + if (node.kind === "op_field") { + traverse(node.src, callback); + } + if (node.kind === "op_call") { + traverse(node.src, callback); + for (const e of node.args) { + traverse(e, callback); + } + } + if (node.kind === "op_static_call") { + for (const e of node.args) { + traverse(e, callback); + } + } + if (node.kind === "op_new") { + for (const e of node.args) { + traverse(e, callback); + } + } + if (node.kind === "new_parameter") { + traverse(node.exp, callback); + } + if (node.kind === "conditional") { + traverse(node.condition, callback); + traverse(node.thenBranch, callback); + traverse(node.elseBranch, callback); + } +} diff --git a/src/grammar/checkConstAttributes.ts b/src/grammar/checkConstAttributes.ts index 5e1d45b17..1a2a06d7a 100644 --- a/src/grammar/checkConstAttributes.ts +++ b/src/grammar/checkConstAttributes.ts @@ -1,20 +1,24 @@ import { ASTConstantAttribute, ASTRef, throwError } from "./ast"; -export function checkConstAttributes(isAbstract: boolean, attributes: ASTConstantAttribute[], ref: ASTRef) { - const k = new Set(); - for (const a of attributes) { - if (k.has(a.type)) { - throwError(`Duplicate function attribute ${a.type}`, a.ref); - } - k.add(a.type); +export function checkConstAttributes( + isAbstract: boolean, + attributes: ASTConstantAttribute[], + ref: ASTRef, +) { + const k = new Set(); + for (const a of attributes) { + if (k.has(a.type)) { + throwError(`Duplicate function attribute ${a.type}`, a.ref); } - if (isAbstract) { - if (!k.has('abstract')) { - throwError(`Abstract function doesn't have abstract modifier`, ref); - } - } else { - if (k.has('abstract')) { - throwError(`Non abstract function have abstract modifier`, ref); - } + k.add(a.type); + } + if (isAbstract) { + if (!k.has("abstract")) { + throwError(`Abstract function doesn't have abstract modifier`, ref); } -} \ No newline at end of file + } else { + if (k.has("abstract")) { + throwError(`Non abstract function have abstract modifier`, ref); + } + } +} diff --git a/src/grammar/checkFunctionAttributes.ts b/src/grammar/checkFunctionAttributes.ts index 7dd339e10..a0c21357b 100644 --- a/src/grammar/checkFunctionAttributes.ts +++ b/src/grammar/checkFunctionAttributes.ts @@ -1,20 +1,24 @@ import { ASTFunctionAttribute, ASTRef, throwError } from "./ast"; -export function checkFunctionAttributes(isAbstract: boolean, attrs: ASTFunctionAttribute[], ref: ASTRef) { - const k = new Set(); - for (const a of attrs) { - if (k.has(a.type)) { - throwError(`Duplicate function attribute ${a.type}`, a.ref); - } - k.add(a.type); +export function checkFunctionAttributes( + isAbstract: boolean, + attrs: ASTFunctionAttribute[], + ref: ASTRef, +) { + const k = new Set(); + for (const a of attrs) { + if (k.has(a.type)) { + throwError(`Duplicate function attribute ${a.type}`, a.ref); } - if (isAbstract) { - if (!k.has('abstract')) { - throwError(`Abstract function doesn't have abstract modifier`, ref); - } - } else { - if (k.has('abstract')) { - throwError(`Non abstract function have abstract modifier`, ref); - } + k.add(a.type); + } + if (isAbstract) { + if (!k.has("abstract")) { + throwError(`Abstract function doesn't have abstract modifier`, ref); } -} \ No newline at end of file + } else { + if (k.has("abstract")) { + throwError(`Non abstract function have abstract modifier`, ref); + } + } +} diff --git a/src/grammar/checkVariableName.ts b/src/grammar/checkVariableName.ts index 8e6fd5413..829e73186 100644 --- a/src/grammar/checkVariableName.ts +++ b/src/grammar/checkVariableName.ts @@ -1,10 +1,10 @@ import { ASTRef, throwError } from "./ast"; export function checkVariableName(name: string, ref: ASTRef) { - if (name.startsWith('__gen')) { - throwError(`Variable name cannot start with "__gen"`, ref); - } - if (name.startsWith('__tact')) { - throwError(`Variable name cannot start with "__tact"`, ref); - } -} \ No newline at end of file + if (name.startsWith("__gen")) { + throwError(`Variable name cannot start with "__gen"`, ref); + } + if (name.startsWith("__tact")) { + throwError(`Variable name cannot start with "__tact"`, ref); + } +} diff --git a/src/grammar/clone.ts b/src/grammar/clone.ts index a8a452cf2..34f8b0f3d 100644 --- a/src/grammar/clone.ts +++ b/src/grammar/clone.ts @@ -1,149 +1,150 @@ import { ASTNode, cloneASTNode } from "./ast"; export function cloneNode(src: T): T { - if (src.kind === 'boolean') { - return cloneASTNode(src); - } else if (src.kind === 'id') { - return cloneASTNode(src); - } else if (src.kind === 'null') { - return cloneASTNode(src); - } else if (src.kind === 'number') { - return cloneASTNode(src); - } else if (src.kind === 'string') { - return cloneASTNode(src); - } else if (src.kind === 'lvalue_ref') { - return cloneASTNode(src); - } else if (src.kind === 'statement_assign') { - return cloneASTNode({ - ...src, - path: src.path.map(cloneNode), - expression: cloneNode(src.expression), - }); - } else if (src.kind === 'statement_augmentedassign') { - return cloneASTNode({ - ...src, - path: src.path.map(cloneNode), - expression: cloneNode(src.expression), - }); - } else if (src.kind === 'statement_let') { - return cloneASTNode({ - ...src, - type: cloneASTNode(src.type), - expression: cloneNode(src.expression), - }); - } else if (src.kind === 'statement_condition') { - return cloneASTNode({ - ...src, - expression: cloneNode(src.expression), - trueStatements: src.trueStatements.map(cloneNode), - falseStatements: src.falseStatements ? src.falseStatements.map(cloneNode) : null, - elseif: src.elseif ? cloneNode(src.elseif) : null, - }); - } else if (src.kind === 'new_parameter') { - return cloneASTNode({ - ...src, - exp: cloneNode(src.exp), - }); - } else if (src.kind === 'statement_expression') { - return cloneASTNode({ - ...src, - expression: cloneNode(src.expression), - }); - } else if (src.kind === 'op_binary') { - return cloneASTNode({ - ...src, - left: cloneNode(src.left), - right: cloneNode(src.right), - }); - } else if (src.kind === 'op_unary') { - return cloneASTNode({ - ...src, - right: cloneNode(src.right), - }); - } else if (src.kind === 'op_new') { - return cloneASTNode({ - ...src, - args: src.args.map(cloneNode), - }); - } else if (src.kind === 'op_call') { - return cloneASTNode({ - ...src, - src: cloneNode(src.src), - args: src.args.map(cloneNode), - }); - } else if (src.kind === 'op_field') { - return cloneASTNode({ - ...src, - src: cloneNode(src.src), - }); - } else if (src.kind === 'op_static_call') { - return cloneASTNode({ - ...src, - args: src.args.map(cloneNode), - }); - } else if (src.kind === 'conditional') { - return cloneASTNode({ - ...src, - condition: cloneNode(src.condition), - thenBranch: cloneNode(src.thenBranch), - elseBranch: cloneNode(src.elseBranch), - }); - } else if (src.kind === 'statement_return') { - return cloneASTNode({ - ...src, - expression: src.expression ? cloneNode(src.expression) : null, + if (src.kind === "boolean") { + return cloneASTNode(src); + } else if (src.kind === "id") { + return cloneASTNode(src); + } else if (src.kind === "null") { + return cloneASTNode(src); + } else if (src.kind === "number") { + return cloneASTNode(src); + } else if (src.kind === "string") { + return cloneASTNode(src); + } else if (src.kind === "lvalue_ref") { + return cloneASTNode(src); + } else if (src.kind === "statement_assign") { + return cloneASTNode({ + ...src, + path: src.path.map(cloneNode), + expression: cloneNode(src.expression), + }); + } else if (src.kind === "statement_augmentedassign") { + return cloneASTNode({ + ...src, + path: src.path.map(cloneNode), + expression: cloneNode(src.expression), + }); + } else if (src.kind === "statement_let") { + return cloneASTNode({ + ...src, + type: cloneASTNode(src.type), + expression: cloneNode(src.expression), + }); + } else if (src.kind === "statement_condition") { + return cloneASTNode({ + ...src, + expression: cloneNode(src.expression), + trueStatements: src.trueStatements.map(cloneNode), + falseStatements: src.falseStatements + ? src.falseStatements.map(cloneNode) + : null, + elseif: src.elseif ? cloneNode(src.elseif) : null, + }); + } else if (src.kind === "new_parameter") { + return cloneASTNode({ + ...src, + exp: cloneNode(src.exp), + }); + } else if (src.kind === "statement_expression") { + return cloneASTNode({ + ...src, + expression: cloneNode(src.expression), + }); + } else if (src.kind === "op_binary") { + return cloneASTNode({ + ...src, + left: cloneNode(src.left), + right: cloneNode(src.right), + }); + } else if (src.kind === "op_unary") { + return cloneASTNode({ + ...src, + right: cloneNode(src.right), + }); + } else if (src.kind === "op_new") { + return cloneASTNode({ + ...src, + args: src.args.map(cloneNode), + }); + } else if (src.kind === "op_call") { + return cloneASTNode({ + ...src, + src: cloneNode(src.src), + args: src.args.map(cloneNode), + }); + } else if (src.kind === "op_field") { + return cloneASTNode({ + ...src, + src: cloneNode(src.src), + }); + } else if (src.kind === "op_static_call") { + return cloneASTNode({ + ...src, + args: src.args.map(cloneNode), + }); + } else if (src.kind === "conditional") { + return cloneASTNode({ + ...src, + condition: cloneNode(src.condition), + thenBranch: cloneNode(src.thenBranch), + elseBranch: cloneNode(src.elseBranch), + }); + } else if (src.kind === "statement_return") { + return cloneASTNode({ + ...src, + expression: src.expression ? cloneNode(src.expression) : null, + }); + } else if (src.kind === "statement_repeat") { + return cloneASTNode({ + ...src, + condition: cloneNode(src.condition), + statements: src.statements.map(cloneNode), + }); + } else if (src.kind === "statement_until") { + return cloneASTNode({ + ...src, + condition: cloneNode(src.condition), + statements: src.statements.map(cloneNode), + }); + } else if (src.kind === "statement_while") { + return cloneASTNode({ + ...src, + condition: cloneNode(src.condition), + statements: src.statements.map(cloneNode), + }); + } else if (src.kind === "def_function") { + return cloneASTNode({ + ...src, + statements: src.statements ? src.statements.map(cloneNode) : null, + args: src.args.map(cloneNode), + }); + } else if (src.kind === "def_native_function") { + return cloneASTNode({ + ...src, + args: src.args.map(cloneNode), + }); + } else if (src.kind === "def_receive") { + return cloneASTNode({ + ...src, + statements: src.statements.map(cloneNode), + }); + } else if (src.kind === "def_argument") { + return cloneASTNode({ + ...src, + type: cloneASTNode(src.type), + }); + } else if (src.kind === "init_of") { + return cloneASTNode({ + ...src, + args: src.args.map(cloneNode), + }); + } else if (src.kind === "def_constant") { + return cloneASTNode({ + ...src, + value: src.value ? cloneNode(src.value) : src.value, + }); + } - }); - } else if (src.kind === 'statement_repeat') { - return cloneASTNode({ - ...src, - condition: cloneNode(src.condition), - statements: src.statements.map(cloneNode), - }); - } else if (src.kind === 'statement_until') { - return cloneASTNode({ - ...src, - condition: cloneNode(src.condition), - statements: src.statements.map(cloneNode), - }); - } else if (src.kind === 'statement_while') { - return cloneASTNode({ - ...src, - condition: cloneNode(src.condition), - statements: src.statements.map(cloneNode), - }); - } else if (src.kind === 'def_function') { - return cloneASTNode({ - ...src, - statements: src.statements ? src.statements.map(cloneNode) : null, - args: src.args.map(cloneNode) - }); - } else if (src.kind === 'def_native_function') { - return cloneASTNode({ - ...src, - args: src.args.map(cloneNode) - }); - } else if (src.kind === 'def_receive') { - return cloneASTNode({ - ...src, - statements: src.statements.map(cloneNode), - }); - } else if (src.kind === 'def_argument') { - return cloneASTNode({ - ...src, - type: cloneASTNode(src.type), - }); - } else if (src.kind === 'init_of') { - return cloneASTNode({ - ...src, - args: src.args.map(cloneNode), - }); - } else if (src.kind === 'def_constant') { - return cloneASTNode({ - ...src, - value: src.value ? cloneNode(src.value) : src.value, - }); - } - - throw Error('Not implemented for ' + src.kind); -} \ No newline at end of file + throw Error("Not implemented for " + src.kind); +} diff --git a/src/grammar/grammar.spec.ts b/src/grammar/grammar.spec.ts index 04c62d32d..17505ff2b 100644 --- a/src/grammar/grammar.spec.ts +++ b/src/grammar/grammar.spec.ts @@ -3,25 +3,26 @@ import { ASTRef, __DANGER_resetNodeId } from "./ast"; import { loadCases } from "../utils/loadCases"; expect.addSnapshotSerializer({ - test: (src) => src instanceof ASTRef, - print: (src) => `${(src as ASTRef).contents}` + test: (src) => src instanceof ASTRef, + print: (src) => `${(src as ASTRef).contents}`, }); -describe('grammar', () => { +describe("grammar", () => { + beforeEach(() => { + __DANGER_resetNodeId(); + }); - beforeEach(() => { - __DANGER_resetNodeId(); + for (const r of loadCases(__dirname + "/test/")) { + it("should parse " + r.name, () => { + expect(parse(r.code, "", "user")).toMatchSnapshot(); }); + } - for (const r of loadCases(__dirname + "/test/")) { - it('should parse ' + r.name, () => { - expect(parse(r.code, '', 'user')).toMatchSnapshot(); - }); - } - - for (const r of loadCases(__dirname + "/test-failed/")) { - it('should fail ' + r.name, () => { - expect(() => parse(r.code, '', 'user')).toThrowErrorMatchingSnapshot(); - }); - } -}); \ No newline at end of file + for (const r of loadCases(__dirname + "/test-failed/")) { + it("should fail " + r.name, () => { + expect(() => + parse(r.code, "", "user"), + ).toThrowErrorMatchingSnapshot(); + }); + } +}); diff --git a/src/grammar/grammar.ts b/src/grammar/grammar.ts index 91c631751..6106499bc 100644 --- a/src/grammar/grammar.ts +++ b/src/grammar/grammar.ts @@ -1,11 +1,24 @@ -import rawGrammar from './grammar.ohm-bundle'; -import { ASTConstantAttribute, ASTContractAttribute, ASTFunctionAttribute, ASTNode, ASTProgram, ASTRef, ASTString, ASTTypeRef, createNode, createRef, inFile, throwError } from './ast'; -import { checkVariableName } from './checkVariableName'; -import { TactSyntaxError } from './../errors'; -import { MatchResult } from 'ohm-js'; -import { TypeOrigin } from '../types/types'; -import { checkFunctionAttributes } from './checkFunctionAttributes'; -import { checkConstAttributes } from './checkConstAttributes'; +import rawGrammar from "./grammar.ohm-bundle"; +import { + ASTConstantAttribute, + ASTContractAttribute, + ASTFunctionAttribute, + ASTNode, + ASTProgram, + ASTRef, + ASTString, + ASTTypeRef, + createNode, + createRef, + inFile, + throwError, +} from "./ast"; +import { checkVariableName } from "./checkVariableName"; +import { TactSyntaxError } from "./../errors"; +import { MatchResult } from "ohm-js"; +import { TypeOrigin } from "../types/types"; +import { checkFunctionAttributes } from "./checkFunctionAttributes"; +import { checkConstAttributes } from "./checkConstAttributes"; let ctx: { origin: TypeOrigin } | null; @@ -13,798 +26,1165 @@ let ctx: { origin: TypeOrigin } | null; const semantics = rawGrammar.createSemantics(); // Resolve program -semantics.addOperation('resolve_program', { - Program(arg0) { - return createNode({ - kind: 'program', - entries: arg0.children.map((v) => v.resolve_program_item()) - }); - }, +semantics.addOperation("resolve_program", { + Program(arg0) { + return createNode({ + kind: "program", + entries: arg0.children.map((v) => v.resolve_program_item()), + }); + }, }); // Resolve program items -semantics.addOperation('resolve_program_item', { - ProgramImport(_arg0, arg1, _arg2) { - const pp = arg1.resolve_expression() as ASTString; - if (pp.value.indexOf('\\') >= 0) { - throwError('Import path can\'t contain "\\"', createRef(arg1)); - } - return createNode({ - kind: 'program_import', - path: arg1.resolve_expression(), - ref: createRef(this) - }); - }, - Primitive(_arg0, arg1, _arg2) { - checkVariableName(arg1.sourceString, createRef(arg1)); - return createNode({ - kind: 'primitive', - origin: ctx!.origin, - name: arg1.sourceString, - ref: createRef(this) - }); - }, - Struct_originary(_arg0, arg1, _arg2, arg3, _arg4) { - checkVariableName(arg1.sourceString, createRef(arg1)); - return createNode({ - kind: 'def_struct', - origin: ctx!.origin, - name: arg1.sourceString, - fields: arg3.children.map((v) => v.resolve_declaration()), - prefix: null, - message: false, - ref: createRef(this) - }) - }, - Struct_message(_arg0, arg1, _arg2, arg3, _arg4) { - checkVariableName(arg1.sourceString, createRef(arg1)); - return createNode({ - kind: 'def_struct', - origin: ctx!.origin, - name: arg1.sourceString, - fields: arg3.children.map((v) => v.resolve_declaration()), - prefix: null, - message: true, - ref: createRef(this) - }) - }, - Struct_messageWithId(_arg0, arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { - checkVariableName(arg1.sourceString, createRef(arg1)); - return createNode({ - kind: 'def_struct', - origin: ctx!.origin, - name: arg4.sourceString, - fields: arg6.children.map((v) => v.resolve_declaration()), - prefix: parseInt(arg2.sourceString), - message: true, - ref: createRef(this) - }) - }, - Contract_simple(arg0, _arg1, arg2, _arg3, arg4, _arg5) { - checkVariableName(arg2.sourceString, createRef(arg2)); - return createNode({ - kind: 'def_contract', - origin: ctx!.origin, - name: arg2.sourceString, - attributes: arg0.children.map((v) => v.resolve_contract_attributes()), - declarations: arg4.children.map((v) => v.resolve_declaration()), - traits: [], - ref: createRef(this) - }) - }, - Contract_withTraits(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { - checkVariableName(arg2.sourceString, createRef(arg2)); - return createNode({ - kind: 'def_contract', - origin: ctx!.origin, - name: arg2.sourceString, - attributes: arg0.children.map((v) => v.resolve_contract_attributes()), - declarations: arg6.children.map((v) => v.resolve_declaration()), - traits: arg4.asIteration().children.map((v) => v.resolve_expression()), - ref: createRef(this) - }) - }, - Trait_originary(arg0, _arg1, arg2, _arg3, arg4, _arg5) { - checkVariableName(arg2.sourceString, createRef(arg2)); - return createNode({ - kind: 'def_trait', - origin: ctx!.origin, - name: arg2.sourceString, - attributes: arg0.children.map((v) => v.resolve_contract_attributes()), - declarations: arg4.children.map((v) => v.resolve_declaration()), - traits: [], - ref: createRef(this) - }) - }, - Trait_withTraits(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { - checkVariableName(arg2.sourceString, createRef(arg2)); - return createNode({ - kind: 'def_trait', - origin: ctx!.origin, - name: arg2.sourceString, - attributes: arg0.children.map((v) => v.resolve_contract_attributes()), - declarations: arg6.children.map((v) => v.resolve_declaration()), - traits: arg4.asIteration().children.map((v) => v.resolve_expression()), - ref: createRef(this) - }) - }, - StaticFunction(arg0) { - return arg0.resolve_declaration(); - }, - NativeFunction(arg0) { - return arg0.resolve_declaration(); - }, - Constant_withValue(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { - const attributes = arg0.children.map((v) => v.resolve_const_attributes()) as ASTConstantAttribute[]; - checkConstAttributes(false, attributes, createRef(this)); - return createNode({ - kind: 'def_constant', - name: arg2.sourceString, - type: arg4.resolve_expression(), - value: arg6.resolve_expression(), - attributes, - ref: createRef(this) - }) - }, - Constant_withEmpty(arg0, _arg1, arg2, _arg3, arg4, _arg5) { - const attributes = arg0.children.map((v) => v.resolve_const_attributes()) as ASTConstantAttribute[]; - checkConstAttributes(true, attributes, createRef(this)); - return createNode({ - kind: 'def_constant', - name: arg2.sourceString, - type: arg4.resolve_expression(), - value: null, - attributes, - ref: createRef(this) - }) - }, +semantics.addOperation("resolve_program_item", { + ProgramImport(_arg0, arg1, _arg2) { + const pp = arg1.resolve_expression() as ASTString; + if (pp.value.indexOf("\\") >= 0) { + throwError('Import path can\'t contain "\\"', createRef(arg1)); + } + return createNode({ + kind: "program_import", + path: arg1.resolve_expression(), + ref: createRef(this), + }); + }, + Primitive(_arg0, arg1, _arg2) { + checkVariableName(arg1.sourceString, createRef(arg1)); + return createNode({ + kind: "primitive", + origin: ctx!.origin, + name: arg1.sourceString, + ref: createRef(this), + }); + }, + Struct_originary(_arg0, arg1, _arg2, arg3, _arg4) { + checkVariableName(arg1.sourceString, createRef(arg1)); + return createNode({ + kind: "def_struct", + origin: ctx!.origin, + name: arg1.sourceString, + fields: arg3.children.map((v) => v.resolve_declaration()), + prefix: null, + message: false, + ref: createRef(this), + }); + }, + Struct_message(_arg0, arg1, _arg2, arg3, _arg4) { + checkVariableName(arg1.sourceString, createRef(arg1)); + return createNode({ + kind: "def_struct", + origin: ctx!.origin, + name: arg1.sourceString, + fields: arg3.children.map((v) => v.resolve_declaration()), + prefix: null, + message: true, + ref: createRef(this), + }); + }, + Struct_messageWithId(_arg0, arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { + checkVariableName(arg1.sourceString, createRef(arg1)); + return createNode({ + kind: "def_struct", + origin: ctx!.origin, + name: arg4.sourceString, + fields: arg6.children.map((v) => v.resolve_declaration()), + prefix: parseInt(arg2.sourceString), + message: true, + ref: createRef(this), + }); + }, + Contract_simple(arg0, _arg1, arg2, _arg3, arg4, _arg5) { + checkVariableName(arg2.sourceString, createRef(arg2)); + return createNode({ + kind: "def_contract", + origin: ctx!.origin, + name: arg2.sourceString, + attributes: arg0.children.map((v) => v.resolve_contract_attributes()), + declarations: arg4.children.map((v) => v.resolve_declaration()), + traits: [], + ref: createRef(this), + }); + }, + Contract_withTraits(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { + checkVariableName(arg2.sourceString, createRef(arg2)); + return createNode({ + kind: "def_contract", + origin: ctx!.origin, + name: arg2.sourceString, + attributes: arg0.children.map((v) => v.resolve_contract_attributes()), + declarations: arg6.children.map((v) => v.resolve_declaration()), + traits: arg4.asIteration().children.map((v) => v.resolve_expression()), + ref: createRef(this), + }); + }, + Trait_originary(arg0, _arg1, arg2, _arg3, arg4, _arg5) { + checkVariableName(arg2.sourceString, createRef(arg2)); + return createNode({ + kind: "def_trait", + origin: ctx!.origin, + name: arg2.sourceString, + attributes: arg0.children.map((v) => v.resolve_contract_attributes()), + declarations: arg4.children.map((v) => v.resolve_declaration()), + traits: [], + ref: createRef(this), + }); + }, + Trait_withTraits(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { + checkVariableName(arg2.sourceString, createRef(arg2)); + return createNode({ + kind: "def_trait", + origin: ctx!.origin, + name: arg2.sourceString, + attributes: arg0.children.map((v) => v.resolve_contract_attributes()), + declarations: arg6.children.map((v) => v.resolve_declaration()), + traits: arg4.asIteration().children.map((v) => v.resolve_expression()), + ref: createRef(this), + }); + }, + StaticFunction(arg0) { + return arg0.resolve_declaration(); + }, + NativeFunction(arg0) { + return arg0.resolve_declaration(); + }, + Constant_withValue(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { + const attributes = arg0.children.map((v) => + v.resolve_const_attributes(), + ) as ASTConstantAttribute[]; + checkConstAttributes(false, attributes, createRef(this)); + return createNode({ + kind: "def_constant", + name: arg2.sourceString, + type: arg4.resolve_expression(), + value: arg6.resolve_expression(), + attributes, + ref: createRef(this), + }); + }, + Constant_withEmpty(arg0, _arg1, arg2, _arg3, arg4, _arg5) { + const attributes = arg0.children.map((v) => + v.resolve_const_attributes(), + ) as ASTConstantAttribute[]; + checkConstAttributes(true, attributes, createRef(this)); + return createNode({ + kind: "def_constant", + name: arg2.sourceString, + type: arg4.resolve_expression(), + value: null, + attributes, + ref: createRef(this), + }); + }, }); // Resolve attributes -semantics.addOperation('resolve_attributes', { - FunctionAttribute_getter(_arg0) { - return { type: 'get', ref: createRef(this) }; - }, - FunctionAttribute_extends(_arg0) { - return { type: 'extends', ref: createRef(this) }; - }, - FunctionAttribute_mutates(_arg0) { - return { type: 'mutates', ref: createRef(this) }; - }, - FunctionAttribute_override(_arg0) { - return { type: 'overrides', ref: createRef(this) }; - }, - FunctionAttribute_inline(_arg0) { - return { type: 'inline', ref: createRef(this) }; - }, - FunctionAttribute_virtual(_arg0) { - return { type: 'virtual', ref: createRef(this) }; - }, - FunctionAttribute_abstract(_arg0) { - return { type: 'abstract', ref: createRef(this) }; - }, +semantics.addOperation("resolve_attributes", { + FunctionAttribute_getter(_arg0) { + return { type: "get", ref: createRef(this) }; + }, + FunctionAttribute_extends(_arg0) { + return { type: "extends", ref: createRef(this) }; + }, + FunctionAttribute_mutates(_arg0) { + return { type: "mutates", ref: createRef(this) }; + }, + FunctionAttribute_override(_arg0) { + return { type: "overrides", ref: createRef(this) }; + }, + FunctionAttribute_inline(_arg0) { + return { type: "inline", ref: createRef(this) }; + }, + FunctionAttribute_virtual(_arg0) { + return { type: "virtual", ref: createRef(this) }; + }, + FunctionAttribute_abstract(_arg0) { + return { type: "abstract", ref: createRef(this) }; + }, }); // Resolve const attributes -semantics.addOperation('resolve_const_attributes', { - ConstantAttribute_override(_arg0) { - return { type: 'overrides', ref: createRef(this) }; - }, - ConstantAttribute_virtual(_arg0) { - return { type: 'virtual', ref: createRef(this) }; - }, - ConstantAttribute_abstract(_arg0) { - return { type: 'abstract', ref: createRef(this) }; - }, +semantics.addOperation("resolve_const_attributes", { + ConstantAttribute_override(_arg0) { + return { type: "overrides", ref: createRef(this) }; + }, + ConstantAttribute_virtual(_arg0) { + return { type: "virtual", ref: createRef(this) }; + }, + ConstantAttribute_abstract(_arg0) { + return { type: "abstract", ref: createRef(this) }; + }, }); // Resolve contract -semantics.addOperation('resolve_contract_attributes', { - ContractAttribute_interface(_arg0, _arg1, arg2, _arg3) { - return { type: 'interface', name: arg2.resolve_expression(), ref: createRef(this) }; - } +semantics.addOperation("resolve_contract_attributes", { + ContractAttribute_interface(_arg0, _arg1, arg2, _arg3) { + return { + type: "interface", + name: arg2.resolve_expression(), + ref: createRef(this), + }; + }, }); // Struct and class declarations -semantics.addOperation('resolve_declaration', { - Field_default(arg0, _arg1, arg2, _arg3) { - return createNode({ - kind: 'def_field', - name: arg0.sourceString, - type: arg2.resolve_expression(), - as: null, - init: null, - ref: createRef(this) - }) - }, - Field_defaultWithInit(arg0, _arg1, arg2, _arg3, arg4, _arg5) { - const tr = (arg2.resolve_expression() as ASTTypeRef); - return createNode({ - kind: 'def_field', - name: arg0.sourceString, - type: tr, - as: null, - init: arg4.resolve_expression(), - ref: createRef(this) - }) - }, - Field_withSerialization(arg0, _arg1, arg2, _arg3, arg4, _arg5) { - return createNode({ - kind: 'def_field', - name: arg0.sourceString, - type: arg2.resolve_expression(), - as: arg4.sourceString, - init: null, - ref: createRef(this) - }) - }, - Field_withSerializationAndInit(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { - const tr = (arg2.resolve_expression() as ASTTypeRef); - return createNode({ - kind: 'def_field', - name: arg0.sourceString, - type: tr, - as: arg4.sourceString, - init: arg6.resolve_expression(), - ref: createRef(this) - }) - }, - Constant_withValue(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { - const attributes = arg0.children.map((v) => v.resolve_const_attributes()) as ASTConstantAttribute[]; - checkConstAttributes(false, attributes, createRef(this)); - return createNode({ - kind: 'def_constant', - name: arg2.sourceString, - type: arg4.resolve_expression(), - value: arg6.resolve_expression(), - attributes, - ref: createRef(this) - }) - }, - Constant_withEmpty(arg0, _arg1, arg2, _arg3, arg4, _) { - const attributes = arg0.children.map((v) => v.resolve_const_attributes()) as ASTConstantAttribute[]; - checkConstAttributes(true, attributes, createRef(this)); - return createNode({ - kind: 'def_constant', - name: arg2.sourceString, - type: arg4.resolve_expression(), - value: null, - attributes, - ref: createRef(this) - }) - }, - FunctionArg(arg0, _arg1, arg2) { - checkVariableName(arg0.sourceString, createRef(arg0)); - return createNode({ - kind: 'def_argument', - name: arg0.sourceString, - type: arg2.resolve_expression(), - ref: createRef(this) - }) - }, - Function_withType(arg0, _arg1, arg2, _arg3, arg4, arg5, _arg6, _arg7, arg8, _arg9, arg10, _arg11) { - if (arg4.source.contents === '' && arg5.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg5)); - } +semantics.addOperation("resolve_declaration", { + Field_default(arg0, _arg1, arg2, _arg3) { + return createNode({ + kind: "def_field", + name: arg0.sourceString, + type: arg2.resolve_expression(), + as: null, + init: null, + ref: createRef(this), + }); + }, + Field_defaultWithInit(arg0, _arg1, arg2, _arg3, arg4, _arg5) { + const tr = arg2.resolve_expression() as ASTTypeRef; + return createNode({ + kind: "def_field", + name: arg0.sourceString, + type: tr, + as: null, + init: arg4.resolve_expression(), + ref: createRef(this), + }); + }, + Field_withSerialization(arg0, _arg1, arg2, _arg3, arg4, _arg5) { + return createNode({ + kind: "def_field", + name: arg0.sourceString, + type: arg2.resolve_expression(), + as: arg4.sourceString, + init: null, + ref: createRef(this), + }); + }, + Field_withSerializationAndInit( + arg0, + _arg1, + arg2, + _arg3, + arg4, + _arg5, + arg6, + _arg7, + ) { + const tr = arg2.resolve_expression() as ASTTypeRef; + return createNode({ + kind: "def_field", + name: arg0.sourceString, + type: tr, + as: arg4.sourceString, + init: arg6.resolve_expression(), + ref: createRef(this), + }); + }, + Constant_withValue(arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7) { + const attributes = arg0.children.map((v) => + v.resolve_const_attributes(), + ) as ASTConstantAttribute[]; + checkConstAttributes(false, attributes, createRef(this)); + return createNode({ + kind: "def_constant", + name: arg2.sourceString, + type: arg4.resolve_expression(), + value: arg6.resolve_expression(), + attributes, + ref: createRef(this), + }); + }, + Constant_withEmpty(arg0, _arg1, arg2, _arg3, arg4, _) { + const attributes = arg0.children.map((v) => + v.resolve_const_attributes(), + ) as ASTConstantAttribute[]; + checkConstAttributes(true, attributes, createRef(this)); + return createNode({ + kind: "def_constant", + name: arg2.sourceString, + type: arg4.resolve_expression(), + value: null, + attributes, + ref: createRef(this), + }); + }, + FunctionArg(arg0, _arg1, arg2) { + checkVariableName(arg0.sourceString, createRef(arg0)); + return createNode({ + kind: "def_argument", + name: arg0.sourceString, + type: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + Function_withType( + arg0, + _arg1, + arg2, + _arg3, + arg4, + arg5, + _arg6, + _arg7, + arg8, + _arg9, + arg10, + _arg11, + ) { + if (arg4.source.contents === "" && arg5.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg5), + ); + } - const attributes = arg0.children.map((v) => v.resolve_attributes()) as ASTFunctionAttribute[]; - checkVariableName(arg2.sourceString, createRef(arg2)); - checkFunctionAttributes(false, attributes, createRef(this)); - return createNode({ - kind: 'def_function', - origin: ctx!.origin, - attributes, - name: arg2.sourceString, - return: arg8.resolve_expression(), - args: arg4.asIteration().children.map((v) => v.resolve_declaration()), - statements: arg10.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - Function_withVoid(arg0, _arg1, arg2, _arg3, arg4, arg5, _arg6, _arg7, arg8, _arg9) { - if (arg4.source.contents === '' && arg5.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg5)); - } + const attributes = arg0.children.map((v) => + v.resolve_attributes(), + ) as ASTFunctionAttribute[]; + checkVariableName(arg2.sourceString, createRef(arg2)); + checkFunctionAttributes(false, attributes, createRef(this)); + return createNode({ + kind: "def_function", + origin: ctx!.origin, + attributes, + name: arg2.sourceString, + return: arg8.resolve_expression(), + args: arg4.asIteration().children.map((v) => v.resolve_declaration()), + statements: arg10.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + Function_withVoid( + arg0, + _arg1, + arg2, + _arg3, + arg4, + arg5, + _arg6, + _arg7, + arg8, + _arg9, + ) { + if (arg4.source.contents === "" && arg5.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg5), + ); + } - const attributes = arg0.children.map((v) => v.resolve_attributes()) as ASTFunctionAttribute[]; - checkVariableName(arg2.sourceString, createRef(arg2)); - checkFunctionAttributes(false, attributes, createRef(this)); - return createNode({ - kind: 'def_function', - origin: ctx!.origin, - attributes, - name: arg2.sourceString, - return: null, - args: arg4.asIteration().children.map((v) => v.resolve_declaration()), - statements: arg8.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - Function_abstractVoid(arg0, _arg1, arg2, _arg3, arg4, arg5, _arg6, _arg7) { - if (arg4.source.contents === '' && arg5.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg5)); - } + const attributes = arg0.children.map((v) => + v.resolve_attributes(), + ) as ASTFunctionAttribute[]; + checkVariableName(arg2.sourceString, createRef(arg2)); + checkFunctionAttributes(false, attributes, createRef(this)); + return createNode({ + kind: "def_function", + origin: ctx!.origin, + attributes, + name: arg2.sourceString, + return: null, + args: arg4.asIteration().children.map((v) => v.resolve_declaration()), + statements: arg8.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + Function_abstractVoid(arg0, _arg1, arg2, _arg3, arg4, arg5, _arg6, _arg7) { + if (arg4.source.contents === "" && arg5.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg5), + ); + } - const attributes = arg0.children.map((v) => v.resolve_attributes()) as ASTFunctionAttribute[]; - checkVariableName(arg2.sourceString, createRef(arg2)); - checkFunctionAttributes(true, attributes, createRef(this)); - return createNode({ - kind: 'def_function', - origin: ctx!.origin, - attributes, - name: arg2.sourceString, - return: null, - args: arg4.asIteration().children.map((v) => v.resolve_declaration()), - statements: null, - ref: createRef(this) - }) - }, - Function_abstractType(arg0, _arg1, arg2, _arg3, arg4, arg5, _arg6, _arg7, arg8, _arg9) { - if (arg4.source.contents === '' && arg5.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg5)); - } + const attributes = arg0.children.map((v) => + v.resolve_attributes(), + ) as ASTFunctionAttribute[]; + checkVariableName(arg2.sourceString, createRef(arg2)); + checkFunctionAttributes(true, attributes, createRef(this)); + return createNode({ + kind: "def_function", + origin: ctx!.origin, + attributes, + name: arg2.sourceString, + return: null, + args: arg4.asIteration().children.map((v) => v.resolve_declaration()), + statements: null, + ref: createRef(this), + }); + }, + Function_abstractType( + arg0, + _arg1, + arg2, + _arg3, + arg4, + arg5, + _arg6, + _arg7, + arg8, + _arg9, + ) { + if (arg4.source.contents === "" && arg5.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg5), + ); + } + + const attributes = arg0.children.map((v) => + v.resolve_attributes(), + ) as ASTFunctionAttribute[]; + checkVariableName(arg2.sourceString, createRef(arg2)); + checkFunctionAttributes(true, attributes, createRef(this)); + return createNode({ + kind: "def_function", + origin: ctx!.origin, + attributes, + name: arg2.sourceString, + return: arg8.resolve_expression(), + args: arg4.asIteration().children.map((v) => v.resolve_declaration()), + statements: null, + ref: createRef(this), + }); + }, + NativeFunction_withType( + _arg0, + _arg1, + arg2, + _arg3, + arg4, + arg5, + arg6, + _arg7, + arg8, + arg9, + _arg10, + _arg11, + arg12, + _arg13, + ) { + if (arg8.source.contents === "" && arg9.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg9), + ); + } + + checkVariableName(arg5.sourceString, createRef(arg5)); + return createNode({ + kind: "def_native_function", + origin: ctx!.origin, + attributes: arg4.children.map((v) => v.resolve_attributes()), + name: arg6.sourceString, + nativeName: arg2.sourceString, + return: arg12.resolve_expression(), + args: arg8.asIteration().children.map((v) => v.resolve_declaration()), + ref: createRef(this), + }); + }, + NativeFunction_withVoid( + _arg0, + _arg1, + arg2, + _arg3, + arg4, + arg5, + arg6, + _arg7, + arg8, + arg9, + _arg10, + _arg11, + ) { + if (arg8.source.contents === "" && arg9.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg9), + ); + } - const attributes = arg0.children.map((v) => v.resolve_attributes()) as ASTFunctionAttribute[]; - checkVariableName(arg2.sourceString, createRef(arg2)); - checkFunctionAttributes(true, attributes, createRef(this)); - return createNode({ - kind: 'def_function', - origin: ctx!.origin, - attributes, - name: arg2.sourceString, - return: arg8.resolve_expression(), - args: arg4.asIteration().children.map((v) => v.resolve_declaration()), - statements: null, - ref: createRef(this) - }) - }, - NativeFunction_withType(_arg0, _arg1, arg2, _arg3, arg4, arg5, arg6, _arg7, arg8, arg9, _arg10, _arg11, arg12, _arg13) { - if (arg8.source.contents === '' && arg9.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg9)); - } - - checkVariableName(arg5.sourceString, createRef(arg5)); - return createNode({ - kind: 'def_native_function', - origin: ctx!.origin, - attributes: arg4.children.map((v) => v.resolve_attributes()), - name: arg6.sourceString, - nativeName: arg2.sourceString, - return: arg12.resolve_expression(), - args: arg8.asIteration().children.map((v) => v.resolve_declaration()), - ref: createRef(this) - }) - }, - NativeFunction_withVoid(_arg0, _arg1, arg2, _arg3, arg4, arg5, arg6, _arg7, arg8, arg9, _arg10, _arg11) { - if (arg8.source.contents === '' && arg9.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg9)); - } - - checkVariableName(arg5.sourceString, createRef(arg5)); - return createNode({ - kind: 'def_native_function', - origin: ctx!.origin, - attributes: arg4.children.map((v) => v.resolve_attributes()), - name: arg6.sourceString, - nativeName: arg2.sourceString, - return: null, - args: arg8.asIteration().children.map((v) => v.resolve_declaration()), - ref: createRef(this) - }) - }, - ContractInit(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { - return createNode({ - kind: 'def_init_function', - args: arg2.asIteration().children.map((v) => v.resolve_declaration()), - statements: arg5.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - ReceiveFunction_simple(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { - return createNode({ - kind: 'def_receive', - selector: { kind: 'internal-simple', arg: arg2.resolve_declaration() }, - statements: arg5.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - ReceiveFunction_empty(_arg0, _arg1, _arg2, _arg3, arg4, _arg5) { - return createNode({ - kind: 'def_receive', - selector: { kind: 'internal-fallback' }, - statements: arg4.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - ReceiveFunction_comment(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { - return createNode({ - kind: 'def_receive', - selector: { kind: 'internal-comment', comment: arg2.resolve_expression() }, - statements: arg5.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - ReceiveFunction_bounced(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { - return createNode({ - kind: 'def_receive', - selector: { kind: 'bounce', arg: arg2.resolve_declaration() }, - statements: arg5.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - ReceiveFunction_externalSimple(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { - return createNode({ - kind: 'def_receive', - selector: { kind: 'external-simple', arg: arg2.resolve_declaration() }, - statements: arg5.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - ReceiveFunction_externalComment(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { - return createNode({ - kind: 'def_receive', - selector: { kind: 'external-comment', comment: arg2.resolve_expression() }, - statements: arg5.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, + checkVariableName(arg5.sourceString, createRef(arg5)); + return createNode({ + kind: "def_native_function", + origin: ctx!.origin, + attributes: arg4.children.map((v) => v.resolve_attributes()), + name: arg6.sourceString, + nativeName: arg2.sourceString, + return: null, + args: arg8.asIteration().children.map((v) => v.resolve_declaration()), + ref: createRef(this), + }); + }, + ContractInit(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { + return createNode({ + kind: "def_init_function", + args: arg2.asIteration().children.map((v) => v.resolve_declaration()), + statements: arg5.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + ReceiveFunction_simple(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { + return createNode({ + kind: "def_receive", + selector: { kind: "internal-simple", arg: arg2.resolve_declaration() }, + statements: arg5.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + ReceiveFunction_empty(_arg0, _arg1, _arg2, _arg3, arg4, _arg5) { + return createNode({ + kind: "def_receive", + selector: { kind: "internal-fallback" }, + statements: arg4.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + ReceiveFunction_comment(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { + return createNode({ + kind: "def_receive", + selector: { + kind: "internal-comment", + comment: arg2.resolve_expression(), + }, + statements: arg5.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + ReceiveFunction_bounced(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { + return createNode({ + kind: "def_receive", + selector: { kind: "bounce", arg: arg2.resolve_declaration() }, + statements: arg5.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + ReceiveFunction_externalSimple( + _arg0, + _arg1, + arg2, + _arg3, + _arg4, + arg5, + _arg6, + ) { + return createNode({ + kind: "def_receive", + selector: { kind: "external-simple", arg: arg2.resolve_declaration() }, + statements: arg5.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + ReceiveFunction_externalComment( + _arg0, + _arg1, + arg2, + _arg3, + _arg4, + arg5, + _arg6, + ) { + return createNode({ + kind: "def_receive", + selector: { + kind: "external-comment", + comment: arg2.resolve_expression(), + }, + statements: arg5.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, }); // Statements -semantics.addOperation('resolve_statement', { - StatementLet(_arg0, arg1, _arg2, arg3, _arg4, arg5, _arg6) { - checkVariableName(arg1.sourceString, createRef(arg1)); +semantics.addOperation("resolve_statement", { + StatementLet(_arg0, arg1, _arg2, arg3, _arg4, arg5, _arg6) { + checkVariableName(arg1.sourceString, createRef(arg1)); - return createNode({ - kind: 'statement_let', - name: arg1.sourceString, - type: arg3.resolve_expression(), - expression: arg5.resolve_expression(), - ref: createRef(this) - }) - }, - StatementReturn_withExpression(_arg0, arg1, _arg2) { - return createNode({ - kind: 'statement_return', - expression: arg1.resolve_expression(), - ref: createRef(this) - }) - }, - StatementReturn_withoutExpression(_arg0, _arg1) { - return createNode({ - kind: 'statement_return', - expression: null, - ref: createRef(this) - }) - }, - StatementExpression(arg0, _arg1) { - return createNode({ - kind: 'statement_expression', - expression: arg0.resolve_expression(), - ref: createRef(this) - }) - }, - StatementAssign(arg0, _arg1, arg2, _arg3) { - return createNode({ - kind: 'statement_assign', - path: arg0.resolve_lvalue(), - expression: arg2.resolve_expression(), - ref: createRef(this) - }) - }, - StatementAugmentedAssignAdd(arg0, _arg1, arg2, _arg3) { - return createNode({ - kind: 'statement_augmentedassign', - path: arg0.resolve_lvalue(), - op: '+', - expression: arg2.resolve_expression(), - ref: createRef(this) - }) - }, - StatementAugmentedAssignSub(arg0, _arg1, arg2, _arg3) { - return createNode({ - kind: 'statement_augmentedassign', - path: arg0.resolve_lvalue(), - op: '-', - expression: arg2.resolve_expression(), - ref: createRef(this) - }) - }, - StatementAugmentedAssignMul(arg0, _arg1, arg2, _arg3) { - return createNode({ - kind: 'statement_augmentedassign', - path: arg0.resolve_lvalue(), - op: '*', - expression: arg2.resolve_expression(), - ref: createRef(this) - }) - }, - StatementAugmentedAssignDiv(arg0, _arg1, arg2, _arg3) { - return createNode({ - kind: 'statement_augmentedassign', - path: arg0.resolve_lvalue(), - op: '/', - expression: arg2.resolve_expression(), - ref: createRef(this) - }) - }, - StatementAugmentedAssignRem(arg0, _arg1, arg2, _arg3) { - return createNode({ - kind: 'statement_augmentedassign', - path: arg0.resolve_lvalue(), - op: '%', - expression: arg2.resolve_expression(), - ref: createRef(this) - }) - }, - StatementCondition_simple(_arg0, arg1, _arg2, arg3, _arg4) { - return createNode({ - kind: 'statement_condition', - expression: arg1.resolve_expression(), - trueStatements: arg3.children.map((v) => v.resolve_statement()), - falseStatements: null, - elseif: null, - ref: createRef(this) - }) - }, - StatementCondition_withElse(_arg0, arg1, _arg2, arg3, _arg4, _arg5, _arg6, arg7, _arg8) { - return createNode({ - kind: 'statement_condition', - expression: arg1.resolve_expression(), - trueStatements: arg3.children.map((v) => v.resolve_statement()), - falseStatements: arg7.children.map((v) => v.resolve_statement()), - elseif: null, - ref: createRef(this) - }) - }, - StatementCondition_withElseIf(_arg0, arg1, _arg2, arg3, _arg4, _arg5, arg6) { - return createNode({ - kind: 'statement_condition', - expression: arg1.resolve_expression(), - trueStatements: arg3.children.map((v) => v.resolve_statement()), - falseStatements: null, - elseif: arg6.resolve_statement(), - ref: createRef(this) - }) - }, - StatementWhile(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { - return createNode({ - kind: 'statement_while', - condition: arg2.resolve_expression(), - statements: arg5.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - StatementRepeat(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { - return createNode({ - kind: 'statement_repeat', - condition: arg2.resolve_expression(), - statements: arg5.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, - StatementUntil(_arg0, _arg1, arg2, _arg3, _arg4, _arg5, arg6, _arg7, _arg8) { - return createNode({ - kind: 'statement_until', - condition: arg6.resolve_expression(), - statements: arg2.children.map((v) => v.resolve_statement()), - ref: createRef(this) - }) - }, + return createNode({ + kind: "statement_let", + name: arg1.sourceString, + type: arg3.resolve_expression(), + expression: arg5.resolve_expression(), + ref: createRef(this), + }); + }, + StatementReturn_withExpression(_arg0, arg1, _arg2) { + return createNode({ + kind: "statement_return", + expression: arg1.resolve_expression(), + ref: createRef(this), + }); + }, + StatementReturn_withoutExpression(_arg0, _arg1) { + return createNode({ + kind: "statement_return", + expression: null, + ref: createRef(this), + }); + }, + StatementExpression(arg0, _arg1) { + return createNode({ + kind: "statement_expression", + expression: arg0.resolve_expression(), + ref: createRef(this), + }); + }, + StatementAssign(arg0, _arg1, arg2, _arg3) { + return createNode({ + kind: "statement_assign", + path: arg0.resolve_lvalue(), + expression: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + StatementAugmentedAssignAdd(arg0, _arg1, arg2, _arg3) { + return createNode({ + kind: "statement_augmentedassign", + path: arg0.resolve_lvalue(), + op: "+", + expression: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + StatementAugmentedAssignSub(arg0, _arg1, arg2, _arg3) { + return createNode({ + kind: "statement_augmentedassign", + path: arg0.resolve_lvalue(), + op: "-", + expression: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + StatementAugmentedAssignMul(arg0, _arg1, arg2, _arg3) { + return createNode({ + kind: "statement_augmentedassign", + path: arg0.resolve_lvalue(), + op: "*", + expression: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + StatementAugmentedAssignDiv(arg0, _arg1, arg2, _arg3) { + return createNode({ + kind: "statement_augmentedassign", + path: arg0.resolve_lvalue(), + op: "/", + expression: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + StatementAugmentedAssignRem(arg0, _arg1, arg2, _arg3) { + return createNode({ + kind: "statement_augmentedassign", + path: arg0.resolve_lvalue(), + op: "%", + expression: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + StatementCondition_simple(_arg0, arg1, _arg2, arg3, _arg4) { + return createNode({ + kind: "statement_condition", + expression: arg1.resolve_expression(), + trueStatements: arg3.children.map((v) => v.resolve_statement()), + falseStatements: null, + elseif: null, + ref: createRef(this), + }); + }, + StatementCondition_withElse( + _arg0, + arg1, + _arg2, + arg3, + _arg4, + _arg5, + _arg6, + arg7, + _arg8, + ) { + return createNode({ + kind: "statement_condition", + expression: arg1.resolve_expression(), + trueStatements: arg3.children.map((v) => v.resolve_statement()), + falseStatements: arg7.children.map((v) => v.resolve_statement()), + elseif: null, + ref: createRef(this), + }); + }, + StatementCondition_withElseIf(_arg0, arg1, _arg2, arg3, _arg4, _arg5, arg6) { + return createNode({ + kind: "statement_condition", + expression: arg1.resolve_expression(), + trueStatements: arg3.children.map((v) => v.resolve_statement()), + falseStatements: null, + elseif: arg6.resolve_statement(), + ref: createRef(this), + }); + }, + StatementWhile(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { + return createNode({ + kind: "statement_while", + condition: arg2.resolve_expression(), + statements: arg5.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + StatementRepeat(_arg0, _arg1, arg2, _arg3, _arg4, arg5, _arg6) { + return createNode({ + kind: "statement_repeat", + condition: arg2.resolve_expression(), + statements: arg5.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, + StatementUntil(_arg0, _arg1, arg2, _arg3, _arg4, _arg5, arg6, _arg7, _arg8) { + return createNode({ + kind: "statement_until", + condition: arg6.resolve_expression(), + statements: arg2.children.map((v) => v.resolve_statement()), + ref: createRef(this), + }); + }, }); // LValue -semantics.addOperation('resolve_lvalue', { - LValue_single(arg0) { - return [createNode({ - kind: 'lvalue_ref', - name: arg0.sourceString, - ref: createRef(this) - })]; - }, - LValue_more(arg0, arg1, arg2) { - return [createNode({ - kind: 'lvalue_ref', - name: arg0.sourceString, - ref: createRef(arg0, arg1) - }), ...arg2.resolve_lvalue()]; - } +semantics.addOperation("resolve_lvalue", { + LValue_single(arg0) { + return [ + createNode({ + kind: "lvalue_ref", + name: arg0.sourceString, + ref: createRef(this), + }), + ]; + }, + LValue_more(arg0, arg1, arg2) { + return [ + createNode({ + kind: "lvalue_ref", + name: arg0.sourceString, + ref: createRef(arg0, arg1), + }), + ...arg2.resolve_lvalue(), + ]; + }, }); // Expressions -semantics.addOperation('resolve_expression', { - - // Literals - integerLiteral(n) { - return createNode({ kind: 'number', value: BigInt(n.sourceString.replaceAll('_', '')), ref: createRef(this) }); // Parses dec, hex, and bin numbers - }, - boolLiteral(arg0) { - return createNode({ kind: 'boolean', value: arg0.sourceString === 'true', ref: createRef(this) }); - }, - id(arg0, arg1) { - return createNode({ kind: 'id', value: arg0.sourceString + arg1.sourceString, ref: createRef(this) }); - }, - funcId(arg0, arg1) { - return createNode({ kind: 'id', value: arg0.sourceString + arg1.sourceString, ref: createRef(this) }); - }, - null(_arg0) { - return createNode({ kind: 'null', ref: createRef(this) }); - }, - stringLiteral(_arg0, arg1, _arg2) { - return createNode({ kind: 'string', value: arg1.sourceString, ref: createRef(this) }); - }, - - // TypeRefs - Type_optional(arg0, _arg1) { - return createNode({ kind: 'type_ref_simple', name: arg0.sourceString, optional: true, ref: createRef(this) }); - }, - Type_required(arg0) { - return createNode({ kind: 'type_ref_simple', name: arg0.sourceString, optional: false, ref: createRef(this) }); - }, - Type_map(_arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7, arg8, _arg9) { +semantics.addOperation("resolve_expression", { + // Literals + integerLiteral(n) { + return createNode({ + kind: "number", + value: BigInt(n.sourceString.replaceAll("_", "")), + ref: createRef(this), + }); // Parses dec, hex, and bin numbers + }, + boolLiteral(arg0) { + return createNode({ + kind: "boolean", + value: arg0.sourceString === "true", + ref: createRef(this), + }); + }, + id(arg0, arg1) { + return createNode({ + kind: "id", + value: arg0.sourceString + arg1.sourceString, + ref: createRef(this), + }); + }, + funcId(arg0, arg1) { + return createNode({ + kind: "id", + value: arg0.sourceString + arg1.sourceString, + ref: createRef(this), + }); + }, + null(_arg0) { + return createNode({ kind: "null", ref: createRef(this) }); + }, + stringLiteral(_arg0, arg1, _arg2) { + return createNode({ + kind: "string", + value: arg1.sourceString, + ref: createRef(this), + }); + }, - return createNode({ - kind: 'type_ref_map', - key: arg2.sourceString, - keyAs: arg4.numChildren === 1 ? arg4.children[0].sourceString : null, - value: arg6.sourceString, - valueAs: arg8.numChildren === 1 ? arg8.children[0].sourceString : null, - ref: createRef(this) - }); - }, - Type_bounced(_arg0, _arg1, arg2, _arg3) { - return createNode({ kind: 'type_ref_bounced', name: arg2.sourceString, ref: createRef(this) }); - }, + // TypeRefs + Type_optional(arg0, _arg1) { + return createNode({ + kind: "type_ref_simple", + name: arg0.sourceString, + optional: true, + ref: createRef(this), + }); + }, + Type_required(arg0) { + return createNode({ + kind: "type_ref_simple", + name: arg0.sourceString, + optional: false, + ref: createRef(this), + }); + }, + Type_map(_arg0, _arg1, arg2, _arg3, arg4, _arg5, arg6, _arg7, arg8, _arg9) { + return createNode({ + kind: "type_ref_map", + key: arg2.sourceString, + keyAs: arg4.numChildren === 1 ? arg4.children[0].sourceString : null, + value: arg6.sourceString, + valueAs: arg8.numChildren === 1 ? arg8.children[0].sourceString : null, + ref: createRef(this), + }); + }, + Type_bounced(_arg0, _arg1, arg2, _arg3) { + return createNode({ + kind: "type_ref_bounced", + name: arg2.sourceString, + ref: createRef(this), + }); + }, - // Binary - ExpressionAdd_add(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '+', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionAdd_sub(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '-', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionMul_div(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '/', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionMul_mul(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '*', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionMul_rem(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '%', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionCompare_eq(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '==', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionCompare_not(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '!=', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionCompare_gt(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '>', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionCompare_gte(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '>=', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionCompare_lt(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '<', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionCompare_lte(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '<=', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionOr_or(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '||', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionAnd_and(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '&&', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionBinary_shr(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '>>', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionBinary_shl(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '<<', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionBinary_bin_and(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '&', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionBinary_bin_or(arg0, _arg1, arg2) { - return createNode({ kind: 'op_binary', op: '|', left: arg0.resolve_expression(), right: arg2.resolve_expression(), ref: createRef(this) }); - }, + // Binary + ExpressionAdd_add(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "+", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionAdd_sub(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "-", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionMul_div(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "/", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionMul_mul(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "*", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionMul_rem(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "%", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionCompare_eq(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "==", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionCompare_not(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "!=", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionCompare_gt(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: ">", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionCompare_gte(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: ">=", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionCompare_lt(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "<", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionCompare_lte(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "<=", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionOr_or(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "||", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionAnd_and(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "&&", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionBinary_shr(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: ">>", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionBinary_shl(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "<<", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionBinary_bin_and(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "&", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionBinary_bin_or(arg0, _arg1, arg2) { + return createNode({ + kind: "op_binary", + op: "|", + left: arg0.resolve_expression(), + right: arg2.resolve_expression(), + ref: createRef(this), + }); + }, - // Unary - ExpressionUnary_add(_arg0, arg1) { - return createNode({ kind: 'op_unary', op: '+', right: arg1.resolve_expression(), ref: createRef(this) }); - }, - ExpressionUnary_neg(_arg0, arg1) { - return createNode({ kind: 'op_unary', op: '-', right: arg1.resolve_expression(), ref: createRef(this) }); - }, - ExpressionUnary_not(_arg0, arg1) { - return createNode({ kind: 'op_unary', op: '!', right: arg1.resolve_expression(), ref: createRef(this) }); - }, - ExpressionBracket(_arg0, arg1, _arg2) { - return arg1.resolve_expression(); - }, - ExpressionUnarySuffix_notNull(arg0, _arg1) { - return createNode({ kind: 'op_unary', op: '!!', right: arg0.resolve_expression(), ref: createRef(this) }); - }, + // Unary + ExpressionUnary_add(_arg0, arg1) { + return createNode({ + kind: "op_unary", + op: "+", + right: arg1.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionUnary_neg(_arg0, arg1) { + return createNode({ + kind: "op_unary", + op: "-", + right: arg1.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionUnary_not(_arg0, arg1) { + return createNode({ + kind: "op_unary", + op: "!", + right: arg1.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionBracket(_arg0, arg1, _arg2) { + return arg1.resolve_expression(); + }, + ExpressionUnarySuffix_notNull(arg0, _arg1) { + return createNode({ + kind: "op_unary", + op: "!!", + right: arg0.resolve_expression(), + ref: createRef(this), + }); + }, - // Access - ExpressionField(arg0, _arg1, arg2) { - return createNode({ kind: 'op_field', src: arg0.resolve_expression(), name: arg2.sourceString, ref: createRef(this) }); - }, - ExpressionCall(arg0, _arg1, arg2, _arg3, arg4, arg5, _arg6) { - if (arg4.source.contents === '' && arg5.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg5)); - } + // Access + ExpressionField(arg0, _arg1, arg2) { + return createNode({ + kind: "op_field", + src: arg0.resolve_expression(), + name: arg2.sourceString, + ref: createRef(this), + }); + }, + ExpressionCall(arg0, _arg1, arg2, _arg3, arg4, arg5, _arg6) { + if (arg4.source.contents === "" && arg5.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg5), + ); + } - return createNode({ kind: 'op_call', src: arg0.resolve_expression(), name: arg2.sourceString, args: arg4.asIteration().children.map((v) => v.resolve_expression()), ref: createRef(this) }); - }, - ExpressionStaticCall(arg0, _arg1, arg2, arg3, _arg4) { - if (arg2.source.contents === '' && arg3.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg3)); - } + return createNode({ + kind: "op_call", + src: arg0.resolve_expression(), + name: arg2.sourceString, + args: arg4.asIteration().children.map((v) => v.resolve_expression()), + ref: createRef(this), + }); + }, + ExpressionStaticCall(arg0, _arg1, arg2, arg3, _arg4) { + if (arg2.source.contents === "" && arg3.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg3), + ); + } - return createNode({ kind: 'op_static_call', name: arg0.sourceString, args: arg2.asIteration().children.map((v) => v.resolve_expression()), ref: createRef(this) }); - }, - ExpressionNew(arg0, _arg1, arg2, arg3, _arg4) { - if (arg2.source.contents === '' && arg3.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg3)); - } + return createNode({ + kind: "op_static_call", + name: arg0.sourceString, + args: arg2.asIteration().children.map((v) => v.resolve_expression()), + ref: createRef(this), + }); + }, + ExpressionNew(arg0, _arg1, arg2, arg3, _arg4) { + if (arg2.source.contents === "" && arg3.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg3), + ); + } - return createNode({ kind: 'op_new', type: arg0.sourceString, args: arg2.asIteration().children.map((v) => v.resolve_expression()), ref: createRef(this) }); - }, - NewParameter(arg0, _arg1, arg2) { - return createNode({ kind: 'new_parameter', name: arg0.sourceString, exp: arg2.resolve_expression(), ref: createRef(this) }); - }, - ExpressionInitOf(_arg0, arg1, _arg2, arg3, arg4, _arg5) { - if (arg3.source.contents === '' && arg4.sourceString === ',') { - throwError('Empty parameter list should not have a dangling comma.', createRef(arg4)); - } + return createNode({ + kind: "op_new", + type: arg0.sourceString, + args: arg2.asIteration().children.map((v) => v.resolve_expression()), + ref: createRef(this), + }); + }, + NewParameter(arg0, _arg1, arg2) { + return createNode({ + kind: "new_parameter", + name: arg0.sourceString, + exp: arg2.resolve_expression(), + ref: createRef(this), + }); + }, + ExpressionInitOf(_arg0, arg1, _arg2, arg3, arg4, _arg5) { + if (arg3.source.contents === "" && arg4.sourceString === ",") { + throwError( + "Empty parameter list should not have a dangling comma.", + createRef(arg4), + ); + } - return createNode({ kind: 'init_of', name: arg1.sourceString, args: arg3.asIteration().children.map((v) => v.resolve_expression()), ref: createRef(this) }); - }, + return createNode({ + kind: "init_of", + name: arg1.sourceString, + args: arg3.asIteration().children.map((v) => v.resolve_expression()), + ref: createRef(this), + }); + }, - // Ternary conditional - ExpressionConditional_ternary(arg0, _arg1, arg2, _arg3, arg4) { - return createNode({ kind: 'conditional', condition: arg0.resolve_expression(), thenBranch: arg2.resolve_expression(), elseBranch: arg4.resolve_expression(), ref: createRef(this) }); - }, + // Ternary conditional + ExpressionConditional_ternary(arg0, _arg1, arg2, _arg3, arg4) { + return createNode({ + kind: "conditional", + condition: arg0.resolve_expression(), + thenBranch: arg2.resolve_expression(), + elseBranch: arg4.resolve_expression(), + ref: createRef(this), + }); + }, }); function throwMatchError(matchResult: MatchResult, path: string): never { - const interval = matchResult.getInterval(); - const lc = interval.getLineAndColumn() as { lineNum: number, colNum: number }; - const msg = interval.getLineAndColumnMessage(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const message = path + ':' + lc.lineNum + ':' + lc.colNum + ': Syntax error: expected ' + (matchResult as any).getExpectedText() + ' \n' + msg; - throw new TactSyntaxError(message, new ASTRef(interval, path)); + const interval = matchResult.getInterval(); + const lc = interval.getLineAndColumn() as { lineNum: number; colNum: number }; + const msg = interval.getLineAndColumnMessage(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const message = + path + + ":" + + lc.lineNum + + ":" + + lc.colNum + + ": Syntax error: expected " + + (matchResult as any).getExpectedText() + + " \n" + + msg; + throw new TactSyntaxError(message, new ASTRef(interval, path)); } -export function parse(src: string, path: string, origin: TypeOrigin): ASTProgram { - return inFile(path, () => { - const matchResult = rawGrammar.match(src); - if (matchResult.failed()) { - throwMatchError(matchResult, path); - } - ctx = { origin }; - try { - return semantics(matchResult).resolve_program(); - } finally { - ctx = null; - } - }); +export function parse( + src: string, + path: string, + origin: TypeOrigin, +): ASTProgram { + return inFile(path, () => { + const matchResult = rawGrammar.match(src); + if (matchResult.failed()) { + throwMatchError(matchResult, path); + } + ctx = { origin }; + try { + return semantics(matchResult).resolve_program(); + } finally { + ctx = null; + } + }); } -export function parseImports(src: string, path: string, origin: TypeOrigin): string[] { - const r = parse(src, path, origin); - const imports: string[] = []; - let hasExpression = false; - for (const e of r.entries) { - if (e.kind === 'program_import') { - if (hasExpression) { - throwError('Import must be at the top of the file', e.ref); - } - imports.push(e.path.value); - } else { - hasExpression = true; - } +export function parseImports( + src: string, + path: string, + origin: TypeOrigin, +): string[] { + const r = parse(src, path, origin); + const imports: string[] = []; + let hasExpression = false; + for (const e of r.entries) { + if (e.kind === "program_import") { + if (hasExpression) { + throwError("Import must be at the top of the file", e.ref); + } + imports.push(e.path.value); + } else { + hasExpression = true; } - return imports; -} \ No newline at end of file + } + return imports; +} diff --git a/src/grammar/store.ts b/src/grammar/store.ts index f1c187fe1..6807f9c86 100644 --- a/src/grammar/store.ts +++ b/src/grammar/store.ts @@ -4,57 +4,79 @@ import { parse } from "./grammar"; import { TypeOrigin } from "../types/types"; type ASTStore = { - sources: { code: string, path: string }[], - funcSources: { code: string, path: string }[], - functions: (ASTFunction | ASTNativeFunction)[], - constants: ASTConstant[], - types: ASTType[] + sources: { code: string; path: string }[]; + funcSources: { code: string; path: string }[]; + functions: (ASTFunction | ASTNativeFunction)[]; + constants: ASTConstant[]; + types: ASTType[]; }; const store = createContextStore(); export function getRawAST(ctx: CompilerContext) { - const r = store.get(ctx, 'types'); - if (!r) { - throw Error('No AST found in context'); - } - return r; + const r = store.get(ctx, "types"); + if (!r) { + throw Error("No AST found in context"); + } + return r; } -export function openContext(ctx: CompilerContext, - sources: { code: string, path: string, origin: TypeOrigin }[], - funcSources: { code: string, path: string }[] +export function openContext( + ctx: CompilerContext, + sources: { code: string; path: string; origin: TypeOrigin }[], + funcSources: { code: string; path: string }[], ) { - const asts = sources.map(source => parse(source.code, source.path, source.origin)); - const types: ASTType[] = []; - const functions: (ASTNativeFunction | ASTFunction)[] = []; - const constants: ASTConstant[] = []; - for (const a of asts) { - for (const e of a.entries) { - if (e.kind === 'def_struct' || e.kind === 'def_contract' || e.kind === 'def_trait' || e.kind === 'primitive') { - types.push(e); - } else if (e.kind === 'def_function' || e.kind === 'def_native_function') { - functions.push(e); - } else if (e.kind === 'def_constant') { - constants.push(e); - } - } + const asts = sources.map((source) => + parse(source.code, source.path, source.origin), + ); + const types: ASTType[] = []; + const functions: (ASTNativeFunction | ASTFunction)[] = []; + const constants: ASTConstant[] = []; + for (const a of asts) { + for (const e of a.entries) { + if ( + e.kind === "def_struct" || + e.kind === "def_contract" || + e.kind === "def_trait" || + e.kind === "primitive" + ) { + types.push(e); + } else if ( + e.kind === "def_function" || + e.kind === "def_native_function" + ) { + functions.push(e); + } else if (e.kind === "def_constant") { + constants.push(e); + } } - ctx = store.set(ctx, 'types', { sources, funcSources, functions, constants, types }); - return ctx; + } + ctx = store.set(ctx, "types", { + sources, + funcSources, + functions, + constants, + types, + }); + return ctx; } // Creates a mock context with the given AST elements needed for testing // purposes export function openMockContext( - ctx: CompilerContext, - types: ASTType[], - functions: (ASTNativeFunction|ASTFunction)[], - constants: ASTConstant[], + ctx: CompilerContext, + types: ASTType[], + functions: (ASTNativeFunction | ASTFunction)[], + constants: ASTConstant[], ) { - const sources: {code: string, path: string}[] = []; - const funcSources: {code: string, path: string}[] = []; - ctx = store.set(ctx, 'types', - {sources, funcSources, functions, constants, types}); + const sources: { code: string; path: string }[] = []; + const funcSources: { code: string; path: string }[] = []; + ctx = store.set(ctx, "types", { + sources, + funcSources, + functions, + constants, + types, + }); return ctx; } diff --git a/src/imports/parseImportPath.spec.ts b/src/imports/parseImportPath.spec.ts index f5b1bc2c9..3536e2898 100644 --- a/src/imports/parseImportPath.spec.ts +++ b/src/imports/parseImportPath.spec.ts @@ -1,20 +1,20 @@ -import { parseImportPath } from './parseImportPath'; +import { parseImportPath } from "./parseImportPath"; -describe('parseImportPath', () => { - it('should reject non-relative imports', () => { - const res = parseImportPath('some_name'); +describe("parseImportPath", () => { + it("should reject non-relative imports", () => { + const res = parseImportPath("some_name"); expect(res).toBeNull(); }); - it('should reject non-file imports', () => { - const res = parseImportPath('./some_name/'); + it("should reject non-file imports", () => { + const res = parseImportPath("./some_name/"); expect(res).toBeNull(); }); - it('should parse single imports', () => { - const res = parseImportPath('./import'); - expect(res).toMatchObject(['import']); + it("should parse single imports", () => { + const res = parseImportPath("./import"); + expect(res).toMatchObject(["import"]); }); - it('should parse multiple imports', () => { - const res = parseImportPath('./import/second'); - expect(res).toMatchObject(['import', 'second']); + it("should parse multiple imports", () => { + const res = parseImportPath("./import/second"); + expect(res).toMatchObject(["import", "second"]); }); -}); \ No newline at end of file +}); diff --git a/src/imports/parseImportPath.ts b/src/imports/parseImportPath.ts index 874154ad3..450925c09 100644 --- a/src/imports/parseImportPath.ts +++ b/src/imports/parseImportPath.ts @@ -1,11 +1,11 @@ import normalize from "path-normalize"; export function parseImportPath(src: string) { - if (!(src.startsWith('./') || src.startsWith('../'))) { + if (!(src.startsWith("./") || src.startsWith("../"))) { return null; } - if (src.endsWith('/')) { + if (src.endsWith("/")) { return null; } - return normalize(src).split('/'); + return normalize(src).split("/"); } diff --git a/src/imports/resolveImports.spec.ts b/src/imports/resolveImports.spec.ts index 8772f916d..25b805cbf 100644 --- a/src/imports/resolveImports.spec.ts +++ b/src/imports/resolveImports.spec.ts @@ -1,45 +1,80 @@ -import { resolveImports } from './resolveImports'; -import { createNodeFileSystem } from '../vfs/createNodeFileSystem'; -import path from 'path'; +import { resolveImports } from "./resolveImports"; +import { createNodeFileSystem } from "../vfs/createNodeFileSystem"; +import path from "path"; -describe('resolveImports', () => { - it('should resolve imports', () => { - const project = createNodeFileSystem(path.resolve(__dirname, '__testdata', 'project')); - const stdlib = createNodeFileSystem(path.resolve(__dirname, '__testdata', 'stdlib')); +describe("resolveImports", () => { + it("should resolve imports", () => { + const project = createNodeFileSystem( + path.resolve(__dirname, "__testdata", "project"), + ); + const stdlib = createNodeFileSystem( + path.resolve(__dirname, "__testdata", "stdlib"), + ); const resolved = resolveImports({ project, stdlib, - entrypoint: './main.tact' + entrypoint: "./main.tact", }); expect(resolved).toMatchObject({ - "func": [ + func: [ { - "code": "", - "path": path.resolve(__dirname, '__testdata', 'stdlib', 'stdlib2.fc'), + code: "", + path: path.resolve( + __dirname, + "__testdata", + "stdlib", + "stdlib2.fc", + ), }, ], - "tact": [ + tact: [ { - "code": "import \"./stdlib2.fc\";", - "path": path.resolve(__dirname, '__testdata', 'stdlib', 'stdlib.tact'), + code: 'import "./stdlib2.fc";', + path: path.resolve( + __dirname, + "__testdata", + "stdlib", + "stdlib.tact", + ), }, { - "code": "", - "path": path.resolve(__dirname, '__testdata', 'project', 'imported.tact'), + code: "", + path: path.resolve( + __dirname, + "__testdata", + "project", + "imported.tact", + ), }, { - "code": "import \"../imported_from_subfolder\";", - "path": path.resolve(__dirname, '__testdata', 'project', 'subfolder', 'import_from_parent.tact'), + code: 'import "../imported_from_subfolder";', + path: path.resolve( + __dirname, + "__testdata", + "project", + "subfolder", + "import_from_parent.tact", + ), }, { - "code": "", - "path": path.resolve(__dirname, '__testdata', 'project', 'imported_from_subfolder.tact'), + code: "", + path: path.resolve( + __dirname, + "__testdata", + "project", + "imported_from_subfolder.tact", + ), }, { - "code": "import \"./imported\"; import \"./subfolder/import_from_parent\";", - "path": path.resolve(__dirname, '__testdata', 'project', 'main.tact'), + code: 'import "./imported"; import "./subfolder/import_from_parent";', + path: path.resolve( + __dirname, + "__testdata", + "project", + "main.tact", + ), }, ], }); }); -}); \ No newline at end of file +}); diff --git a/src/imports/resolveImports.ts b/src/imports/resolveImports.ts index 8f09f31f4..01056afe9 100644 --- a/src/imports/resolveImports.ts +++ b/src/imports/resolveImports.ts @@ -1,10 +1,13 @@ -import { parseImports } from '../grammar/grammar'; -import { TypeOrigin } from '../types/types'; -import { VirtualFileSystem } from '../vfs/VirtualFileSystem'; -import { resolveLibrary } from './resolveLibrary'; - -export function resolveImports(args: { entrypoint: string, project: VirtualFileSystem, stdlib: VirtualFileSystem }) { +import { parseImports } from "../grammar/grammar"; +import { TypeOrigin } from "../types/types"; +import { VirtualFileSystem } from "../vfs/VirtualFileSystem"; +import { resolveLibrary } from "./resolveLibrary"; +export function resolveImports(args: { + entrypoint: string; + project: VirtualFileSystem; + stdlib: VirtualFileSystem; +}) { // // Load stdlib and entrypoint // @@ -12,7 +15,7 @@ export function resolveImports(args: { entrypoint: string, project: VirtualFileS // const stdlibFuncPath = args.stdlib.resolve('./stdlib.fc'); // const stdlibFunc = args.stdlib.readFile(stdlibFuncPath).toString(); - const stdlibTactPath = args.stdlib.resolve('stdlib.tact'); + const stdlibTactPath = args.stdlib.resolve("stdlib.tact"); const stdlibTact = args.stdlib.readFile(stdlibTactPath).toString(); const codePath = args.project.resolve(args.entrypoint); @@ -22,27 +25,28 @@ export function resolveImports(args: { entrypoint: string, project: VirtualFileS // Resolve all imports // - const importedTact: { code: string, path: string, origin: TypeOrigin }[] = []; - const importedFunc: { code: string, path: string, origin: TypeOrigin }[] = []; + const importedTact: { code: string; path: string; origin: TypeOrigin }[] = + []; + const importedFunc: { code: string; path: string; origin: TypeOrigin }[] = + []; const processed = new Set(); - const pending: { code: string, path: string, origin: TypeOrigin }[] = []; + const pending: { code: string; path: string; origin: TypeOrigin }[] = []; function processImports(source: string, path: string, origin: TypeOrigin) { const imp = parseImports(source, path, origin); for (const i of imp) { - // Resolve library const resolved = resolveLibrary({ path: path, name: i, project: args.project, - stdlib: args.stdlib + stdlib: args.stdlib, }); if (!resolved.ok) { throw new Error(`Could not resolve import ${i} in ${path}`); } // Check if already imported - if (resolved.kind === 'func') { + if (resolved.kind === "func") { if (importedFunc.find((v) => v.path === resolved.path)) { continue; } @@ -53,11 +57,12 @@ export function resolveImports(args: { entrypoint: string, project: VirtualFileS } // Load code - const vfs = resolved.source === 'project' ? args.project : args.stdlib; + const vfs = + resolved.source === "project" ? args.project : args.stdlib; const code: string = vfs.readFile(resolved.path).toString(); // Add to imports - if (resolved.kind === 'func') { + if (resolved.kind === "func") { importedFunc.push({ code, path: resolved.path, origin }); } else { if (!processed.has(resolved.path)) { @@ -69,24 +74,26 @@ export function resolveImports(args: { entrypoint: string, project: VirtualFileS } // Run resolve - importedTact.push({ code: stdlibTact, path: stdlibTactPath, origin: 'stdlib' }); - processImports(stdlibTact, stdlibTactPath, 'stdlib'); - processImports(code, codePath, 'user'); + importedTact.push({ + code: stdlibTact, + path: stdlibTactPath, + origin: "stdlib", + }); + processImports(stdlibTact, stdlibTactPath, "stdlib"); + processImports(code, codePath, "user"); while (pending.length > 0) { const p = pending.shift()!; importedTact.push(p); processImports(p.code, p.path, p.origin); } - importedTact.push({ code: code, path: codePath, origin: 'user' }); // To keep order same as before refactoring + importedTact.push({ code: code, path: codePath, origin: "user" }); // To keep order same as before refactoring // Assemble result return { - tact: [ - ...importedTact, - ], + tact: [...importedTact], func: [ // { code: stdlibFunc, path: stdlibFuncPath }, - ...importedFunc - ] + ...importedFunc, + ], }; -} \ No newline at end of file +} diff --git a/src/imports/resolveLibrary.spec.ts b/src/imports/resolveLibrary.spec.ts index e86ca8965..e28868b8b 100644 --- a/src/imports/resolveLibrary.spec.ts +++ b/src/imports/resolveLibrary.spec.ts @@ -1,86 +1,86 @@ -import { createVirtualFileSystem } from '../vfs/createVirtualFileSystem'; -import { resolveLibrary } from './resolveLibrary'; +import { createVirtualFileSystem } from "../vfs/createVirtualFileSystem"; +import { resolveLibrary } from "./resolveLibrary"; -describe('resolveLibrary', () => { - it('should resolve imports', () => { - const project = createVirtualFileSystem('/project', { - ['main.tact']: '', - ['import.tact']: '', - ['main.fc']: '' +describe("resolveLibrary", () => { + it("should resolve imports", () => { + const project = createVirtualFileSystem("/project", { + ["main.tact"]: "", + ["import.tact"]: "", + ["main.fc"]: "", }); - const stdlib = createVirtualFileSystem('@stdlib', { - ['libs/config.tact']: '', - ['libs/config/import.tact']: '' + const stdlib = createVirtualFileSystem("@stdlib", { + ["libs/config.tact"]: "", + ["libs/config/import.tact"]: "", }); // Resolve stdlib import let resolved = resolveLibrary({ - path: '/project/main.tact', - name: '@stdlib/config', + path: "/project/main.tact", + name: "@stdlib/config", project, - stdlib + stdlib, }); if (!resolved.ok) { - throw Error('Unable to resolve library'); + throw Error("Unable to resolve library"); } - expect(resolved.path).toBe('@stdlib/libs/config.tact'); - expect(resolved.source).toBe('stdlib'); - expect(resolved.kind).toBe('tact'); + expect(resolved.path).toBe("@stdlib/libs/config.tact"); + expect(resolved.source).toBe("stdlib"); + expect(resolved.kind).toBe("tact"); // Resolve import func file resolved = resolveLibrary({ - path: '/project/main.tact', - name: './main.fc', + path: "/project/main.tact", + name: "./main.fc", project, - stdlib + stdlib, }); if (!resolved.ok) { - throw Error('Unable to resolve library'); + throw Error("Unable to resolve library"); } - expect(resolved.path).toBe('/project/main.fc'); - expect(resolved.source).toBe('project'); - expect(resolved.kind).toBe('func'); + expect(resolved.path).toBe("/project/main.fc"); + expect(resolved.source).toBe("project"); + expect(resolved.kind).toBe("func"); // Resolve import tact file resolved = resolveLibrary({ - path: '/project/main.tact', - name: './import', + path: "/project/main.tact", + name: "./import", project, - stdlib + stdlib, }); if (!resolved.ok) { - throw Error('Unable to resolve library'); + throw Error("Unable to resolve library"); } - expect(resolved.path).toBe('/project/import.tact'); - expect(resolved.source).toBe('project'); - expect(resolved.kind).toBe('tact'); + expect(resolved.path).toBe("/project/import.tact"); + expect(resolved.source).toBe("project"); + expect(resolved.kind).toBe("tact"); // Resolve import tact file resolved = resolveLibrary({ - path: '/project/main.tact', - name: './import.tact', + path: "/project/main.tact", + name: "./import.tact", project, - stdlib + stdlib, }); if (!resolved.ok) { - throw Error('Unable to resolve library'); + throw Error("Unable to resolve library"); } - expect(resolved.path).toBe('/project/import.tact'); - expect(resolved.source).toBe('project'); - expect(resolved.kind).toBe('tact'); + expect(resolved.path).toBe("/project/import.tact"); + expect(resolved.source).toBe("project"); + expect(resolved.kind).toBe("tact"); // Resolve import internal stdlib file resolved = resolveLibrary({ - path: '@stdlib/libs/import.tact', - name: './config/import', + path: "@stdlib/libs/import.tact", + name: "./config/import", project, - stdlib + stdlib, }); if (!resolved.ok) { - throw Error('Unable to resolve library'); + throw Error("Unable to resolve library"); } - expect(resolved.path).toBe('@stdlib/libs/config/import.tact'); - expect(resolved.source).toBe('stdlib'); - expect(resolved.kind).toBe('tact'); + expect(resolved.path).toBe("@stdlib/libs/config/import.tact"); + expect(resolved.source).toBe("stdlib"); + expect(resolved.kind).toBe("tact"); }); -}); \ No newline at end of file +}); diff --git a/src/imports/resolveLibrary.ts b/src/imports/resolveLibrary.ts index b0f24aae6..f0af5065c 100644 --- a/src/imports/resolveLibrary.ts +++ b/src/imports/resolveLibrary.ts @@ -2,35 +2,36 @@ import { VirtualFileSystem } from "../vfs/VirtualFileSystem"; import { parseImportPath } from "./parseImportPath"; export type ResolveLibraryArgs = { - path: string, - name: string, - project: VirtualFileSystem, - stdlib: VirtualFileSystem + path: string; + name: string; + project: VirtualFileSystem; + stdlib: VirtualFileSystem; }; -export type ResolveLibraryResult = { - ok: true, - path: string; - kind: 'func' | 'tact'; - source: 'project' | 'stdlib'; -} | { - ok: false -} +export type ResolveLibraryResult = + | { + ok: true; + path: string; + kind: "func" | "tact"; + source: "project" | "stdlib"; + } + | { + ok: false; + }; export function resolveLibrary(args: ResolveLibraryArgs): ResolveLibraryResult { - // Stdlib resolving // NOTE: We are handling stdlib resolving here, because we need to enforce the stdlib import before anything else // to avoid hijacking the stdlib imports - if (args.name.startsWith('@stdlib/')) { - const libraryName = args.name.substring('@stdlib/'.length); - const libraryPath = parseImportPath('./' + libraryName + '.tact'); + if (args.name.startsWith("@stdlib/")) { + const libraryName = args.name.substring("@stdlib/".length); + const libraryPath = parseImportPath("./" + libraryName + ".tact"); if (!libraryPath) { return { ok: false }; } - const tactFile = args.stdlib.resolve('libs', ...libraryPath); + const tactFile = args.stdlib.resolve("libs", ...libraryPath); if (args.stdlib.exists(tactFile)) { - return { ok: true, path: tactFile, source: 'stdlib', kind: 'tact' }; + return { ok: true, path: tactFile, source: "stdlib", kind: "tact" }; } else { return { ok: false }; } @@ -38,13 +39,14 @@ export function resolveLibrary(args: ResolveLibraryArgs): ResolveLibraryResult { // Resolve vfs let vfs: VirtualFileSystem; - let source: 'project' | 'stdlib'; - if (args.path.startsWith(args.stdlib.root)) { // NOTE: stdlib checked first to avoid hijacking stdlib imports + let source: "project" | "stdlib"; + if (args.path.startsWith(args.stdlib.root)) { + // NOTE: stdlib checked first to avoid hijacking stdlib imports vfs = args.stdlib; - source = 'stdlib'; + source = "stdlib"; } else if (args.path.startsWith(args.project.root)) { vfs = args.project; - source = 'project'; + source = "project"; } else { return { ok: false }; } @@ -52,9 +54,9 @@ export function resolveLibrary(args: ResolveLibraryArgs): ResolveLibraryResult { // Resolving relative file let importName = args.name; - const kind: 'tact' | 'func' = importName.endsWith('.fc') ? 'func' : 'tact'; - if (!importName.endsWith('.tact') && !importName.endsWith('.fc')) { - importName = importName + '.tact'; + const kind: "tact" | "func" = importName.endsWith(".fc") ? "func" : "tact"; + if (!importName.endsWith(".tact") && !importName.endsWith(".fc")) { + importName = importName + ".tact"; } // Resolve import @@ -62,11 +64,11 @@ export function resolveLibrary(args: ResolveLibraryArgs): ResolveLibraryResult { if (!parsedImport) { return { ok: false }; } - const resolvedPath = vfs.resolve(workingDirectory, '..', ...parsedImport); + const resolvedPath = vfs.resolve(workingDirectory, "..", ...parsedImport); if (vfs.exists(resolvedPath)) { return { ok: true, path: resolvedPath, source, kind }; } // Nothing matched return { ok: false }; -} \ No newline at end of file +} diff --git a/src/logger.ts b/src/logger.ts index 97ca08e44..f50888ee6 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -5,5 +5,5 @@ export type TactLogger = { export const consoleLogger: TactLogger = { log: console.log, - error: console.error -}; \ No newline at end of file + error: console.error, +}; diff --git a/src/main.ts b/src/main.ts index e255d1c60..88816f83d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,27 +2,19 @@ export { Config, ConfigProject, parseConfig, - verifyConfig -} from './config/parseConfig'; + verifyConfig, +} from "./config/parseConfig"; -export { - PackageFileFormat -} from './packaging/fileFormat'; +export { PackageFileFormat } from "./packaging/fileFormat"; -export { - build -} from './pipeline/build'; +export { build } from "./pipeline/build"; -export { - VirtualFileSystem -} from './vfs/VirtualFileSystem'; +export { VirtualFileSystem } from "./vfs/VirtualFileSystem"; -export { - createVirtualFileSystem -} from './vfs/createVirtualFileSystem'; +export { createVirtualFileSystem } from "./vfs/createVirtualFileSystem"; -export * from './browser'; -export * from './verify'; -export * from './logger'; -export * from './errors'; -export * from './check'; \ No newline at end of file +export * from "./browser"; +export * from "./verify"; +export * from "./logger"; +export * from "./errors"; +export * from "./check"; diff --git a/src/node.ts b/src/node.ts index b3e248c3e..cd7b1ebcb 100644 --- a/src/node.ts +++ b/src/node.ts @@ -1,36 +1,37 @@ -import path from 'path'; -import fs from 'fs'; +import path from "path"; +import fs from "fs"; import { Config, parseConfig } from "./config/parseConfig"; -import { createNodeFileSystem } from './vfs/createNodeFileSystem'; -import { build } from './pipeline/build'; -import { consoleLogger } from './logger'; - -export async function run(args: { configPath: string, projectNames?: string[] }) { +import { createNodeFileSystem } from "./vfs/createNodeFileSystem"; +import { build } from "./pipeline/build"; +import { consoleLogger } from "./logger"; +export async function run(args: { + configPath: string; + projectNames?: string[]; +}) { // Load config const resolvedPath = path.resolve(args.configPath); const rootPath = path.dirname(resolvedPath); let config: Config; if (!fs.existsSync(resolvedPath)) { - console.warn('Unable to find config file at ' + resolvedPath); + console.warn("Unable to find config file at " + resolvedPath); return false; } try { - config = parseConfig(fs.readFileSync(resolvedPath, 'utf8')); + config = parseConfig(fs.readFileSync(resolvedPath, "utf8")); } catch (e) { console.log(e); - console.warn('Unable to parse config file at ' + resolvedPath); + console.warn("Unable to parse config file at " + resolvedPath); return false; } // Resolve projects let projects = config.projects; if (args.projectNames && args.projectNames.length > 0) { - // Check that all proejct names are valid for (const pp of args.projectNames) { if (!projects.find((v) => v.name === pp)) { - console.warn('Unable to find project ' + pp); + console.warn("Unable to find project " + pp); return false; } } @@ -39,20 +40,28 @@ export async function run(args: { configPath: string, projectNames?: string[] }) projects = projects.filter((v) => args.projectNames!.includes(v.name)); } if (projects.length === 0) { - console.warn('No projects to compile'); + console.warn("No projects to compile"); return false; } - // Compile + // Compile let success = true; const project = createNodeFileSystem(rootPath, false); - const stdlib = createNodeFileSystem(path.resolve(__dirname, '..', 'stdlib'), false); // Improves developer experience + const stdlib = createNodeFileSystem( + path.resolve(__dirname, "..", "stdlib"), + false, + ); // Improves developer experience for (const config of projects) { - console.log('💼 Compiling project ' + config.name + '...'); - const built = await build({ config, project, stdlib, logger: consoleLogger }); + console.log("💼 Compiling project " + config.name + "..."); + const built = await build({ + config, + project, + stdlib, + logger: consoleLogger, + }); success = success && built; } return success; } -export { createNodeFileSystem } from './vfs/createNodeFileSystem'; \ No newline at end of file +export { createNodeFileSystem } from "./vfs/createNodeFileSystem"; diff --git a/src/packaging/fileFormat.ts b/src/packaging/fileFormat.ts index 13924a910..a177d5df1 100644 --- a/src/packaging/fileFormat.ts +++ b/src/packaging/fileFormat.ts @@ -1,42 +1,60 @@ -import { z } from 'zod'; +import { z } from "zod"; export const typeFormat = z.union([ z.object({ - kind: z.literal('simple'), + kind: z.literal("simple"), type: z.string(), optional: z.boolean().optional().nullable(), - format: z.union([z.boolean(), z.number(), z.string()]).optional().nullable() + format: z + .union([z.boolean(), z.number(), z.string()]) + .optional() + .nullable(), }), z.object({ - kind: z.literal('dict'), - format: z.union([z.boolean(), z.number(), z.string()]).optional().nullable(), + kind: z.literal("dict"), + format: z + .union([z.boolean(), z.number(), z.string()]) + .optional() + .nullable(), key: z.string(), - keyFormat: z.union([z.boolean(), z.number(), z.string()]).optional().nullable(), + keyFormat: z + .union([z.boolean(), z.number(), z.string()]) + .optional() + .nullable(), value: z.string(), - valueFormat: z.union([z.boolean(), z.number(), z.string()]).optional().nullable(), + valueFormat: z + .union([z.boolean(), z.number(), z.string()]) + .optional() + .nullable(), }), ]); export const initFormat = z.object({ - kind: z.literal('direct'), - args: z.array(z.object({ - name: z.string(), - type: typeFormat - })), - prefix: z.object({ - bits: z.number(), - value: z.number() - }).optional(), - deployment: z.union([z.object({ - kind: z.literal('direct'), - }), z.object({ - kind: z.literal('system-cell'), - system: z.string() - })]), + kind: z.literal("direct"), + args: z.array( + z.object({ + name: z.string(), + type: typeFormat, + }), + ), + prefix: z + .object({ + bits: z.number(), + value: z.number(), + }) + .optional(), + deployment: z.union([ + z.object({ + kind: z.literal("direct"), + }), + z.object({ + kind: z.literal("system-cell"), + system: z.string(), + }), + ]), }); export const fileFormat = z.object({ - // Contract name, code and abi name: z.string(), code: z.string(), @@ -52,8 +70,8 @@ export const fileFormat = z.object({ compiler: z.object({ name: z.string(), version: z.string(), - parameters: z.string().optional().nullable() - }) + parameters: z.string().optional().nullable(), + }), }); -export type PackageFileFormat = z.infer; \ No newline at end of file +export type PackageFileFormat = z.infer; diff --git a/src/packaging/packageCode.ts b/src/packaging/packageCode.ts index 90fd712e1..d2d0e8c77 100644 --- a/src/packaging/packageCode.ts +++ b/src/packaging/packageCode.ts @@ -3,4 +3,4 @@ import { fileFormat, PackageFileFormat } from "./fileFormat"; export function packageCode(pkg: PackageFileFormat) { const parsed = fileFormat.parse(pkg); return JSON.stringify(parsed); -} \ No newline at end of file +} diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index addc409c2..a10cdf012 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -1,59 +1,61 @@ -import { beginCell, Cell, Dictionary } from '@ton/core'; -import { decompileAll } from '@tact-lang/opcode'; -import { writeTypescript } from '../bindings/writeTypescript'; -import { featureEnable } from '../config/features'; +import { beginCell, Cell, Dictionary } from "@ton/core"; +import { decompileAll } from "@tact-lang/opcode"; +import { writeTypescript } from "../bindings/writeTypescript"; +import { featureEnable } from "../config/features"; import { ConfigProject } from "../config/parseConfig"; import { CompilerContext } from "../context"; -import { funcCompile } from '../func/funcCompile'; -import { writeReport } from '../generator/writeReport'; -import { getRawAST } from '../grammar/store'; -import files from '../imports/stdlib'; -import { consoleLogger, TactLogger } from '../logger'; -import { PackageFileFormat } from '../packaging/fileFormat'; -import { packageCode } from '../packaging/packageCode'; -import { createABITypeRefFromTypeRef } from '../types/resolveABITypeRef'; -import { getContracts, getType } from '../types/resolveDescriptors'; -import { errorToString } from '../utils/errorToString'; -import { posixNormalize } from '../utils/filePath'; -import { createVirtualFileSystem } from '../vfs/createVirtualFileSystem'; -import { VirtualFileSystem } from '../vfs/VirtualFileSystem'; -import { compile } from './compile'; +import { funcCompile } from "../func/funcCompile"; +import { writeReport } from "../generator/writeReport"; +import { getRawAST } from "../grammar/store"; +import files from "../imports/stdlib"; +import { consoleLogger, TactLogger } from "../logger"; +import { PackageFileFormat } from "../packaging/fileFormat"; +import { packageCode } from "../packaging/packageCode"; +import { createABITypeRefFromTypeRef } from "../types/resolveABITypeRef"; +import { getContracts, getType } from "../types/resolveDescriptors"; +import { errorToString } from "../utils/errorToString"; +import { posixNormalize } from "../utils/filePath"; +import { createVirtualFileSystem } from "../vfs/createVirtualFileSystem"; +import { VirtualFileSystem } from "../vfs/VirtualFileSystem"; +import { compile } from "./compile"; import { precompile } from "./precompile"; -import { getCompilerVersion } from './version'; +import { getCompilerVersion } from "./version"; export async function build(args: { - config: ConfigProject, - project: VirtualFileSystem, - stdlib: string | VirtualFileSystem, - logger?: TactLogger | null | undefined + config: ConfigProject; + project: VirtualFileSystem; + stdlib: string | VirtualFileSystem; + logger?: TactLogger | null | undefined; }) { - const { config, project } = args; - const stdlib = (typeof args.stdlib === 'string') ? createVirtualFileSystem(args.stdlib, files) : args.stdlib; + const stdlib = + typeof args.stdlib === "string" + ? createVirtualFileSystem(args.stdlib, files) + : args.stdlib; const logger: TactLogger = args.logger || consoleLogger; // Configure context let ctx: CompilerContext = new CompilerContext({ shared: {} }); const cfg: string = JSON.stringify({ entrypoint: config.path, - options: (config.options || {}) + options: config.options || {}, }); if (config.options) { if (config.options.debug) { - logger.error(' > 👀 Enabling debug'); - ctx = featureEnable(ctx, 'debug'); + logger.error(" > 👀 Enabling debug"); + ctx = featureEnable(ctx, "debug"); } if (config.options.masterchain) { - logger.error(' > 👀 Enabling masterchain'); - ctx = featureEnable(ctx, 'masterchain'); + logger.error(" > 👀 Enabling masterchain"); + ctx = featureEnable(ctx, "masterchain"); } if (config.options.external) { - logger.error(' > 👀 Enabling external'); - ctx = featureEnable(ctx, 'external'); + logger.error(" > 👀 Enabling external"); + ctx = featureEnable(ctx, "external"); } if (config.options.experimental && config.options.experimental.inline) { - logger.error(' > 👀 Enabling inline'); - ctx = featureEnable(ctx, 'inline'); + logger.error(" > 👀 Enabling inline"); + ctx = featureEnable(ctx, "inline"); } } @@ -61,7 +63,7 @@ export async function build(args: { try { ctx = precompile(ctx, project, stdlib, config.path); } catch (e) { - logger.error('Tact compilation failed'); + logger.error("Tact compilation failed"); logger.error(errorToString(e)); return false; } @@ -70,67 +72,89 @@ export async function build(args: { let ok = true; const built: { [key: string]: { - codeBoc: Buffer, + codeBoc: Buffer; // codeFunc: string, // codeFift: string, // codeFiftDecompiled: string, - abi: string - } + abi: string; + }; } = {}; for (const contract of getContracts(ctx)) { - const pathAbi = project.resolve(config.output, config.name + '_' + contract + ".abi"); - + const pathAbi = project.resolve( + config.output, + config.name + "_" + contract + ".abi", + ); - const pathCodeBoc = project.resolve(config.output, config.name + '_' + contract + ".code.boc"); - const pathCodeFif = project.resolve(config.output, config.name + '_' + contract + ".code.fif"); - const pathCodeFifDec = project.resolve(config.output, config.name + '_' + contract + ".code.rev.fif"); - let codeFc: { path: string, content: string }[]; + const pathCodeBoc = project.resolve( + config.output, + config.name + "_" + contract + ".code.boc", + ); + const pathCodeFif = project.resolve( + config.output, + config.name + "_" + contract + ".code.fif", + ); + const pathCodeFifDec = project.resolve( + config.output, + config.name + "_" + contract + ".code.rev.fif", + ); + let codeFc: { path: string; content: string }[]; let codeEntrypoint: string; // Compiling contract to func - logger.log(' > ' + contract + ': tact compiler'); + logger.log(" > " + contract + ": tact compiler"); let abi: string; try { - const res = await compile(ctx, contract, config.name + '_' + contract); + const res = await compile( + ctx, + contract, + config.name + "_" + contract, + ); for (const files of res.output.files) { const ffc = project.resolve(config.output, files.name); project.writeFile(ffc, files.code); } project.writeFile(pathAbi, res.output.abi); abi = res.output.abi; - codeFc = res.output.files.map((v) => ({ path: posixNormalize(project.resolve(config.output, v.name)), content: v.code })); + codeFc = res.output.files.map((v) => ({ + path: posixNormalize(project.resolve(config.output, v.name)), + content: v.code, + })); codeEntrypoint = res.output.entrypoint; } catch (e) { - logger.error('Tact compilation failed'); + logger.error("Tact compilation failed"); logger.error(errorToString(e)); ok = false; continue; } // Compiling contract to TVM - logger.log(' > ' + contract + ': func compiler'); + logger.log(" > " + contract + ": func compiler"); let codeBoc: Buffer; try { - const stdlibPath = stdlib.resolve('stdlib.fc'); + const stdlibPath = stdlib.resolve("stdlib.fc"); const stdlibCode = stdlib.readFile(stdlibPath).toString(); - const stdlibExPath = stdlib.resolve('stdlib_ex.fc'); + const stdlibExPath = stdlib.resolve("stdlib_ex.fc"); const stdlibExCode = stdlib.readFile(stdlibExPath).toString(); const c = await funcCompile({ entries: [ stdlibPath, stdlibExPath, - posixNormalize(project.resolve(config.output, codeEntrypoint)) + posixNormalize( + project.resolve(config.output, codeEntrypoint), + ), ], - sources: [{ - path: stdlibPath, - content: stdlibCode - }, { - path: stdlibExPath, - content: stdlibExCode, - }, - ...codeFc + sources: [ + { + path: stdlibPath, + content: stdlibCode, + }, + { + path: stdlibExPath, + content: stdlibExCode, + }, + ...codeFc, ], - logger + logger, }); if (!c.ok) { logger.error(c.log); @@ -141,20 +165,20 @@ export async function build(args: { project.writeFile(pathCodeBoc, c.output); codeBoc = c.output; } catch (e) { - logger.error('FunC compiler crashed'); + logger.error("FunC compiler crashed"); logger.error(errorToString(e)); ok = false; continue; } // Fift decompiler for generated code debug - logger.log(' > ' + contract + ': fift decompiler'); + logger.log(" > " + contract + ": fift decompiler"); let codeFiftDecompiled: string; try { codeFiftDecompiled = decompileAll({ src: codeBoc }); project.writeFile(pathCodeFifDec, codeFiftDecompiled); } catch (e) { - logger.error('Fift decompiler crashed'); + logger.error("Fift decompiler crashed"); logger.error(errorToString(e)); ok = false; continue; @@ -166,34 +190,37 @@ export async function build(args: { codeBoc, // codeFift, // codeFiftDecompiled, - abi + abi, }; } if (!ok) { - logger.log('💥 Compilation failed. Skipping packaging'); + logger.log("💥 Compilation failed. Skipping packaging"); return false; } // Package - logger.log(' > Packaging'); + logger.log(" > Packaging"); const contracts = getContracts(ctx); const packages: PackageFileFormat[] = []; for (const contract of contracts) { - logger.log(' > ' + contract); + logger.log(" > " + contract); const artifacts = built[contract]; if (!artifacts) { - logger.error(' > ' + contract + ': no artifacts found'); + logger.error(" > " + contract + ": no artifacts found"); return false; } // System cell - const depends = Dictionary.empty(Dictionary.Keys.Uint(16), Dictionary.Values.Cell()); + const depends = Dictionary.empty( + Dictionary.Keys.Uint(16), + Dictionary.Values.Cell(), + ); const ct = getType(ctx, contract); depends.set(ct.uid, Cell.fromBoc(built[ct.name].codeBoc)[0]); // Mine for (const c of ct.dependsOn) { const cd = built[c.name]; if (!cd) { - logger.error(' > ' + cd + ': no artifacts found'); + logger.error(" > " + cd + ": no artifacts found"); return false; } depends.set(c.uid, Cell.fromBoc(cd.codeBoc)[0]); @@ -204,8 +231,13 @@ export async function build(args: { const sources: { [key: string]: string } = {}; const rawAst = getRawAST(ctx); for (const source of [...rawAst.funcSources, ...rawAst.sources]) { - if (source.path.startsWith(project.root) && !source.path.startsWith(stdlib.root)) { - sources[source.path.slice(project.root.length)] = Buffer.from(source.code).toString('base64'); + if ( + source.path.startsWith(project.root) && + !source.path.startsWith(stdlib.root) + ) { + sources[source.path.slice(project.root.length)] = Buffer.from( + source.code, + ).toString("base64"); } } @@ -213,38 +245,49 @@ export async function build(args: { const pkg: PackageFileFormat = { name: contract, abi: artifacts.abi, - code: artifacts.codeBoc.toString('base64'), + code: artifacts.codeBoc.toString("base64"), init: { - kind: 'direct', - args: getType(ctx, contract).init!.args.map((v) => ({ name: v.name, type: createABITypeRefFromTypeRef(v.type, v.ref) })), + kind: "direct", + args: getType(ctx, contract).init!.args.map((v) => ({ + name: v.name, + type: createABITypeRefFromTypeRef(v.type, v.ref), + })), prefix: { bits: 1, value: 0, }, deployment: { - kind: 'system-cell', - system: systemCell.toBoc().toString('base64') + kind: "system-cell", + system: systemCell.toBoc().toString("base64"), }, }, sources, compiler: { - name: 'tact', + name: "tact", version: getCompilerVersion(), - parameters: cfg - } + parameters: cfg, + }, }; const pkgData = packageCode(pkg); - const pathPkg = project.resolve(config.output, config.name + '_' + contract + ".pkg"); + const pathPkg = project.resolve( + config.output, + config.name + "_" + contract + ".pkg", + ); project.writeFile(pathPkg, pkgData); packages.push(pkg); } // Bindings - logger.log(' > Bindings'); + logger.log(" > Bindings"); for (const pkg of packages) { - logger.log(' > ' + pkg.name); - if (pkg.init.deployment.kind !== 'system-cell') { - logger.error(' > ' + pkg.name + ': unsupported deployment kind ' + pkg.init.deployment.kind); + logger.log(" > " + pkg.name); + if (pkg.init.deployment.kind !== "system-cell") { + logger.error( + " > " + + pkg.name + + ": unsupported deployment kind " + + pkg.init.deployment.kind, + ); return false; } try { @@ -252,30 +295,39 @@ export async function build(args: { code: pkg.code, prefix: pkg.init.prefix, system: pkg.init.deployment.system, - args: pkg.init.args + args: pkg.init.args, }); - project.writeFile(project.resolve(config.output, config.name + '_' + pkg.name + ".ts"), bindingsServer); + project.writeFile( + project.resolve( + config.output, + config.name + "_" + pkg.name + ".ts", + ), + bindingsServer, + ); } catch (e) { - logger.error('Bindings compiler crashed'); + logger.error("Bindings compiler crashed"); logger.error(errorToString(e)); return false; } } // Reports - logger.log(' > Reports'); + logger.log(" > Reports"); for (const pkg of packages) { - logger.log(' > ' + pkg.name); + logger.log(" > " + pkg.name); try { const report = writeReport(ctx, pkg); - const pathBindings = project.resolve(config.output, config.name + '_' + pkg.name + ".md"); + const pathBindings = project.resolve( + config.output, + config.name + "_" + pkg.name + ".md", + ); project.writeFile(pathBindings, report); } catch (e) { - logger.error('Report generation crashed'); + logger.error("Report generation crashed"); logger.error(errorToString(e)); return false; } } return true; -} \ No newline at end of file +} diff --git a/src/pipeline/compile.ts b/src/pipeline/compile.ts index 4c7ff7a88..84fde8302 100644 --- a/src/pipeline/compile.ts +++ b/src/pipeline/compile.ts @@ -2,9 +2,13 @@ import { CompilerContext } from "../context"; import { createABI } from "../generator/createABI"; import { writeProgram } from "../generator/writeProgram"; -export async function compile(ctx: CompilerContext, name: string, basename: string) { +export async function compile( + ctx: CompilerContext, + name: string, + basename: string, +) { const abi = createABI(ctx, name); const output = await writeProgram(ctx, abi, basename); const cOutput = output; return { output: cOutput, ctx }; -} \ No newline at end of file +} diff --git a/src/pipeline/precompile.ts b/src/pipeline/precompile.ts index 23d217738..7d951e873 100644 --- a/src/pipeline/precompile.ts +++ b/src/pipeline/precompile.ts @@ -1,22 +1,26 @@ import { CompilerContext } from "../context"; -import { resolveDescriptors } from '../types/resolveDescriptors'; +import { resolveDescriptors } from "../types/resolveDescriptors"; import { resolveAllocations } from "../storage/resolveAllocation"; import { openContext } from "../grammar/store"; import { resolveStatements } from "../types/resolveStatements"; import { resolveErrors } from "../types/resolveErrors"; -import { resolveSignatures } from '../types/resolveSignatures'; -import { resolveImports } from '../imports/resolveImports'; +import { resolveSignatures } from "../types/resolveSignatures"; +import { resolveImports } from "../imports/resolveImports"; import { VirtualFileSystem } from "../vfs/VirtualFileSystem"; -export function precompile(ctx: CompilerContext, project: VirtualFileSystem, stdlib: VirtualFileSystem, entrypoint: string) { - +export function precompile( + ctx: CompilerContext, + project: VirtualFileSystem, + stdlib: VirtualFileSystem, + entrypoint: string, +) { // Load all sources const imported = resolveImports({ entrypoint, project, stdlib }); // Perform initial compiler steps ctx = openContext(ctx, imported.tact, imported.func); - // First load type descriptors and check that + // First load type descriptors and check that // they all have valid signatures ctx = resolveDescriptors(ctx); @@ -34,4 +38,4 @@ export function precompile(ctx: CompilerContext, project: VirtualFileSystem, std // Prepared context return ctx; -} \ No newline at end of file +} diff --git a/src/pipeline/version.ts b/src/pipeline/version.ts index ec50a7516..8b1c1abe9 100644 --- a/src/pipeline/version.ts +++ b/src/pipeline/version.ts @@ -1,4 +1,4 @@ -const version = require('../../package.json').version; +const version = require("../../package.json").version; let __DANGER__VersionNumberDisabled = false; @@ -8,7 +8,7 @@ export function __DANGER__disableVersionNumber() { export function getCompilerVersion() { if (__DANGER__VersionNumberDisabled) { - return 'invalid'; + return "invalid"; } return version as string; -} \ No newline at end of file +} diff --git a/src/storage/StorageAllocation.ts b/src/storage/StorageAllocation.ts index 5e6d1a75c..a1a8f58f4 100644 --- a/src/storage/StorageAllocation.ts +++ b/src/storage/StorageAllocation.ts @@ -2,7 +2,7 @@ import { AllocationCell, AllocationOperation } from "./operation"; export type StorageAllocation = { ops: AllocationOperation[]; - header: { value: number, bits: number } | null; - size: { bits: number, refs: number }; + header: { value: number; bits: number } | null; + size: { bits: number; refs: number }; root: AllocationCell; -}; \ No newline at end of file +}; diff --git a/src/storage/allocator.ts b/src/storage/allocator.ts index 470fef44a..fde6c2dcf 100644 --- a/src/storage/allocator.ts +++ b/src/storage/allocator.ts @@ -1,42 +1,53 @@ import { ABITypeRef } from "@ton/core"; -import { AllocationCell, AllocationOperation, AllocationOperationType } from "./operation"; +import { + AllocationCell, + AllocationOperation, + AllocationOperationType, +} from "./operation"; const ALLOCATOR_RESERVE_BIT = 1; const ALLOCATOR_RESERVE_REF = 1; -export function getOperationSize(src: AllocationOperationType): { bits: number, refs: number } { - if (src.kind === 'int' || src.kind === 'uint') { +export function getOperationSize(src: AllocationOperationType): { + bits: number; + refs: number; +} { + if (src.kind === "int" || src.kind === "uint") { return { bits: src.bits + (src.optional ? 1 : 0), refs: 0 }; - } else if (src.kind === 'coins') { + } else if (src.kind === "coins") { return { bits: 124 + (src.optional ? 1 : 0), refs: 0 }; - } else if (src.kind === 'boolean') { + } else if (src.kind === "boolean") { return { bits: 1 + (src.optional ? 1 : 0), refs: 0 }; - } else if (src.kind === 'address') { + } else if (src.kind === "address") { return { bits: 267, refs: 0 }; - } else if (src.kind === 'cell' || src.kind === 'slice' || src.kind === 'builder') { - if (src.format === 'default') { + } else if ( + src.kind === "cell" || + src.kind === "slice" || + src.kind === "builder" + ) { + if (src.format === "default") { if (src.optional) { return { bits: 1, refs: 1 }; } else { return { bits: 0, refs: 1 }; } - } else if (src.format === 'remainder') { + } else if (src.format === "remainder") { if (src.optional) { - throw new Error('Remainder cell cannot be optional'); + throw new Error("Remainder cell cannot be optional"); } return { bits: 0, refs: 0 }; } else { - throw new Error('Unsupported format'); + throw new Error("Unsupported format"); } - } else if (src.kind === 'string') { + } else if (src.kind === "string") { if (src.optional) { return { bits: 1, refs: 1 }; } else { return { bits: 0, refs: 1 }; } - } else if (src.kind === 'map') { + } else if (src.kind === "map") { return { bits: 1, refs: 1 }; - } else if (src.kind === 'struct') { + } else if (src.kind === "struct") { if (src.ref) { if (src.optional) { return { bits: 1, refs: 1 }; @@ -50,134 +61,251 @@ export function getOperationSize(src: AllocationOperationType): { bits: number, return { bits: src.size.bits, refs: src.size.refs }; } } - } else if (src.kind === 'fixed-bytes') { - return { bits: (src.bytes * 8) + (src.optional ? 1 : 0), refs: 0 }; + } else if (src.kind === "fixed-bytes") { + return { bits: src.bytes * 8 + (src.optional ? 1 : 0), refs: 0 }; } - throw new Error('Unsupported operation'); + throw new Error("Unsupported operation"); } -export function getAllocationOperationFromField(src: ABITypeRef, structLoader: (name: string) => { bits: number, refs: number }): AllocationOperationType { - +export function getAllocationOperationFromField( + src: ABITypeRef, + structLoader: (name: string) => { bits: number; refs: number }, +): AllocationOperationType { // Reference types - if (src.kind === 'simple') { + if (src.kind === "simple") { // let td = getType(ctx, src.type); // Handle primitive types // if (td.kind === 'primitive') { - if (src.type === 'int') { + if (src.type === "int") { if (src.format === 8) { - return { kind: 'int', bits: 8, optional: src.optional ? src.optional : false }; + return { + kind: "int", + bits: 8, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 16) { - return { kind: 'int', bits: 16, optional: src.optional ? src.optional : false }; + return { + kind: "int", + bits: 16, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 32) { - return { kind: 'int', bits: 32, optional: src.optional ? src.optional : false }; + return { + kind: "int", + bits: 32, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 64) { - return { kind: 'int', bits: 64, optional: src.optional ? src.optional : false }; + return { + kind: "int", + bits: 64, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 128) { - return { kind: 'int', bits: 128, optional: src.optional ? src.optional : false }; + return { + kind: "int", + bits: 128, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 256) { - return { kind: 'int', bits: 256, optional: src.optional ? src.optional : false }; + return { + kind: "int", + bits: 256, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 257) { - return { kind: 'int', bits: 257, optional: src.optional ? src.optional : false }; + return { + kind: "int", + bits: 257, + optional: src.optional ? src.optional : false, + }; } else if (src.format !== null && src.format !== undefined) { - throw Error('Unsupported int format ' + src.format); + throw Error("Unsupported int format " + src.format); } - return { kind: 'int', bits: 257, optional: src.optional ? src.optional : false }; + return { + kind: "int", + bits: 257, + optional: src.optional ? src.optional : false, + }; } - if (src.type === 'uint') { + if (src.type === "uint") { if (src.format === 8) { - return { kind: 'uint', bits: 8, optional: src.optional ? src.optional : false }; + return { + kind: "uint", + bits: 8, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 16) { - return { kind: 'uint', bits: 16, optional: src.optional ? src.optional : false }; + return { + kind: "uint", + bits: 16, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 32) { - return { kind: 'uint', bits: 32, optional: src.optional ? src.optional : false }; + return { + kind: "uint", + bits: 32, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 64) { - return { kind: 'uint', bits: 64, optional: src.optional ? src.optional : false }; + return { + kind: "uint", + bits: 64, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 128) { - return { kind: 'uint', bits: 128, optional: src.optional ? src.optional : false }; + return { + kind: "uint", + bits: 128, + optional: src.optional ? src.optional : false, + }; } else if (src.format === 256) { - return { kind: 'uint', bits: 256, optional: src.optional ? src.optional : false }; - } else if (src.format === 'coins') { - return { kind: 'coins', optional: src.optional ? src.optional : false }; + return { + kind: "uint", + bits: 256, + optional: src.optional ? src.optional : false, + }; + } else if (src.format === "coins") { + return { + kind: "coins", + optional: src.optional ? src.optional : false, + }; } else if (src.format !== null && src.format !== undefined) { - throw Error('Unsupported int format ' + src.format); + throw Error("Unsupported int format " + src.format); } - return { kind: 'uint', bits: 256, optional: src.optional ? src.optional : false }; + return { + kind: "uint", + bits: 256, + optional: src.optional ? src.optional : false, + }; } - if (src.type === 'bool') { + if (src.type === "bool") { if (src.format !== null && src.format !== undefined) { - throw Error('Unsupported bool format ' + src.format); + throw Error("Unsupported bool format " + src.format); } - return { kind: 'boolean', optional: src.optional ? src.optional : false }; + return { + kind: "boolean", + optional: src.optional ? src.optional : false, + }; } - if (src.type === 'cell') { - if (src.format === 'remainder') { - return { kind: 'cell', optional: src.optional ? src.optional : false, format: 'remainder' }; + if (src.type === "cell") { + if (src.format === "remainder") { + return { + kind: "cell", + optional: src.optional ? src.optional : false, + format: "remainder", + }; } else if (src.format !== null && src.format !== undefined) { - throw Error('Unsupported cell format ' + src.format); + throw Error("Unsupported cell format " + src.format); } - return { kind: 'cell', optional: src.optional ? src.optional : false, format: 'default' }; + return { + kind: "cell", + optional: src.optional ? src.optional : false, + format: "default", + }; } - if (src.type === 'slice') { - if (src.format === 'remainder') { - return { kind: 'slice', optional: src.optional ? src.optional : false, format: 'remainder' }; + if (src.type === "slice") { + if (src.format === "remainder") { + return { + kind: "slice", + optional: src.optional ? src.optional : false, + format: "remainder", + }; } else if (src.format !== null && src.format !== undefined) { - throw Error('Unsupported slice format ' + src.format); + throw Error("Unsupported slice format " + src.format); } - return { kind: 'slice', optional: src.optional ? src.optional : false, format: 'default' }; + return { + kind: "slice", + optional: src.optional ? src.optional : false, + format: "default", + }; } - if (src.type === 'builder') { - if (src.format === 'remainder') { - return { kind: 'builder', optional: src.optional ? src.optional : false, format: 'remainder' }; + if (src.type === "builder") { + if (src.format === "remainder") { + return { + kind: "builder", + optional: src.optional ? src.optional : false, + format: "remainder", + }; } else if (src.format !== null && src.format !== undefined) { - throw Error('Unsupported slice format ' + src.format); + throw Error("Unsupported slice format " + src.format); } - return { kind: 'builder', optional: src.optional ? src.optional : false, format: 'default' }; + return { + kind: "builder", + optional: src.optional ? src.optional : false, + format: "default", + }; } - if (src.type === 'address') { - return { kind: 'address', optional: src.optional ? src.optional : false }; + if (src.type === "address") { + return { + kind: "address", + optional: src.optional ? src.optional : false, + }; } - if (src.type === 'fixed-bytes') { + if (src.type === "fixed-bytes") { if (src.format === 32 || src.format === 64) { - return { kind: 'fixed-bytes', bytes: src.format, optional: src.optional ? src.optional : false }; + return { + kind: "fixed-bytes", + bytes: src.format, + optional: src.optional ? src.optional : false, + }; } else { - throw Error('Unsupported fixed-bytes format ' + src.format); + throw Error("Unsupported fixed-bytes format " + src.format); } } - if (src.type === 'string') { + if (src.type === "string") { if (src.format !== null && src.format !== undefined) { - throw Error('Unsupported string format ' + src.format); + throw Error("Unsupported string format " + src.format); } - return { kind: 'string', optional: src.optional ? src.optional : false }; + return { + kind: "string", + optional: src.optional ? src.optional : false, + }; } // Struct types const size = structLoader(src.type); - if (src.format === 'ref') { - return { kind: 'struct', type: src.type, ref: true, optional: src.optional ? src.optional : false, size }; + if (src.format === "ref") { + return { + kind: "struct", + type: src.type, + ref: true, + optional: src.optional ? src.optional : false, + size, + }; } else if (src.format !== undefined && src.format !== null) { - throw Error('Unsupported struct format ' + src.format); + throw Error("Unsupported struct format " + src.format); } else { - return { kind: 'struct', type: src.type, ref: false, optional: src.optional ? src.optional : false, size }; + return { + kind: "struct", + type: src.type, + ref: false, + optional: src.optional ? src.optional : false, + size, + }; } } // Map - if (src.kind === 'dict') { + if (src.kind === "dict") { if (src.format !== null && src.format !== undefined) { - throw Error('Unsupported map format ' + src.format); + throw Error("Unsupported map format " + src.format); } - return { kind: 'map' }; + return { kind: "map" }; } - throw new Error('Unsupported operation'); + throw new Error("Unsupported operation"); } -function allocateSegment(ops: AllocationOperation[], bits: number, refs: number): AllocationCell { - +function allocateSegment( + ops: AllocationOperation[], + bits: number, + refs: number, +): AllocationCell { const fields: AllocationOperation[] = []; let next: AllocationCell | null = null; - const used: { bits: number, refs: number } = { bits: 0, refs: 0 }; + const used: { bits: number; refs: number } = { bits: 0, refs: 0 }; for (let i = 0; i < ops.length; i++) { const op = ops[i]; @@ -185,7 +313,11 @@ function allocateSegment(ops: AllocationOperation[], bits: number, refs: number) // Check if we can fit this operation if (size.bits > bits || size.refs > refs) { - next = allocateSegment(ops.slice(i), 1023 - ALLOCATOR_RESERVE_BIT, 4 - ALLOCATOR_RESERVE_REF); + next = allocateSegment( + ops.slice(i), + 1023 - ALLOCATOR_RESERVE_BIT, + 4 - ALLOCATOR_RESERVE_REF, + ); break; } @@ -200,10 +332,17 @@ function allocateSegment(ops: AllocationOperation[], bits: number, refs: number) return { ops: fields, size: used, - next + next, }; } -export function allocate(type: { reserved: { bits: number, refs: number }, ops: AllocationOperation[] }): AllocationCell { - return allocateSegment(type.ops, 1023 - type.reserved.bits - ALLOCATOR_RESERVE_BIT, 4 - type.reserved.refs - ALLOCATOR_RESERVE_REF); -} \ No newline at end of file +export function allocate(type: { + reserved: { bits: number; refs: number }; + ops: AllocationOperation[]; +}): AllocationCell { + return allocateSegment( + type.ops, + 1023 - type.reserved.bits - ALLOCATOR_RESERVE_BIT, + 4 - type.reserved.refs - ALLOCATOR_RESERVE_REF, + ); +} diff --git a/src/storage/operation.ts b/src/storage/operation.ts index f1cc26644..7b18fb466 100644 --- a/src/storage/operation.ts +++ b/src/storage/operation.ts @@ -1,55 +1,66 @@ import { ABITypeRef } from "@ton/core"; export type AllocationCell = { - ops: AllocationOperation[], - size: { bits: number, refs: number } - next: AllocationCell | null + ops: AllocationOperation[]; + size: { bits: number; refs: number }; + next: AllocationCell | null; }; export type AllocationOperation = { name: string; type: ABITypeRef; op: AllocationOperationType; -} +}; -export type AllocationOperationType = { - kind: 'int' | 'uint', - bits: number, - optional: boolean -} | { - kind: 'boolean', - optional: boolean -} | { - kind: 'coins', - optional: boolean -} | { - kind: 'address', - optional: boolean -} | { - kind: 'struct', - type: string, - ref: boolean, - optional: boolean, - size: { bits: number, refs: number } -} | { - kind: 'cell', - optional: boolean, - format: 'default' | 'remainder' -} | { - kind: 'slice', - optional: boolean, - format: 'default' | 'remainder' -} | { - kind: 'builder', - optional: boolean, - format: 'default' | 'remainder' -} | { - kind: 'map' -} | { - kind: 'string', - optional: boolean -} | { - kind: 'fixed-bytes', - bytes: number, - optional: boolean -} \ No newline at end of file +export type AllocationOperationType = + | { + kind: "int" | "uint"; + bits: number; + optional: boolean; + } + | { + kind: "boolean"; + optional: boolean; + } + | { + kind: "coins"; + optional: boolean; + } + | { + kind: "address"; + optional: boolean; + } + | { + kind: "struct"; + type: string; + ref: boolean; + optional: boolean; + size: { bits: number; refs: number }; + } + | { + kind: "cell"; + optional: boolean; + format: "default" | "remainder"; + } + | { + kind: "slice"; + optional: boolean; + format: "default" | "remainder"; + } + | { + kind: "builder"; + optional: boolean; + format: "default" | "remainder"; + } + | { + kind: "map"; + } + | { + kind: "string"; + optional: boolean; + } + | { + kind: "fixed-bytes"; + bytes: number; + optional: boolean; + }; diff --git a/src/storage/resolveAllocation.spec.ts b/src/storage/resolveAllocation.spec.ts index 87d6f5121..4af8847df 100644 --- a/src/storage/resolveAllocation.spec.ts +++ b/src/storage/resolveAllocation.spec.ts @@ -1,15 +1,15 @@ -import fs from 'fs'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { resolveDescriptors } from '../types/resolveDescriptors'; -import { getAllocations, resolveAllocations } from './resolveAllocation'; -import { openContext } from '../grammar/store'; -import { resolveStatements } from '../types/resolveStatements'; -import { CompilerContext } from '../context'; -import { resolveSignatures } from '../types/resolveSignatures'; -import path from 'path'; +import fs from "fs"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { resolveDescriptors } from "../types/resolveDescriptors"; +import { getAllocations, resolveAllocations } from "./resolveAllocation"; +import { openContext } from "../grammar/store"; +import { resolveStatements } from "../types/resolveStatements"; +import { CompilerContext } from "../context"; +import { resolveSignatures } from "../types/resolveSignatures"; +import path from "path"; -const stdlibPath = path.resolve(__dirname, '../../stdlib/std/primitives.tact'); -const stdlib = fs.readFileSync(stdlibPath, 'utf-8'); +const stdlibPath = path.resolve(__dirname, "../../stdlib/std/primitives.tact"); +const stdlib = fs.readFileSync(stdlibPath, "utf-8"); const src = ` trait BaseTrait { @@ -60,16 +60,23 @@ contract Sample { } `; -describe('resolveAllocation', () => { +describe("resolveAllocation", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should write program', () => { - let ctx = openContext(new CompilerContext(), [{ code: stdlib, path: stdlibPath, origin: 'stdlib' }, { code: src, path: '', origin: 'user' }], []); + it("should write program", () => { + let ctx = openContext( + new CompilerContext(), + [ + { code: stdlib, path: stdlibPath, origin: "stdlib" }, + { code: src, path: "", origin: "user" }, + ], + [], + ); ctx = resolveDescriptors(ctx); ctx = resolveSignatures(ctx); ctx = resolveStatements(ctx); ctx = resolveAllocations(ctx); expect(getAllocations(ctx)).toMatchSnapshot(); }); -}); \ No newline at end of file +}); diff --git a/src/storage/resolveAllocation.ts b/src/storage/resolveAllocation.ts index 67b0cb530..da33ef54b 100644 --- a/src/storage/resolveAllocation.ts +++ b/src/storage/resolveAllocation.ts @@ -13,26 +13,31 @@ const store = createContextStore(); export function getAllocation(ctx: CompilerContext, name: string) { const t = store.get(ctx, name); if (!t) { - throw Error('Allocation for ' + name + ' not found'); + throw Error("Allocation for " + name + " not found"); } return t; } export function getAllocations(ctx: CompilerContext) { - return getSortedTypes(ctx).map((v) => ({ allocation: getAllocation(ctx, v.name), type: v })); + return getSortedTypes(ctx).map((v) => ({ + allocation: getAllocation(ctx, v.name), + type: v, + })); } export function getSortedTypes(ctx: CompilerContext) { - const types = Object.values(getAllTypes(ctx)).filter((v) => v.kind === 'struct' || v.kind === 'contract'); - let structs = types.filter(t => t.kind === 'struct'); + const types = Object.values(getAllTypes(ctx)).filter( + (v) => v.kind === "struct" || v.kind === "contract", + ); + let structs = types.filter((t) => t.kind === "struct"); const refs = (src: TypeDescription) => { - const res: TypeDescription[] = [] + const res: TypeDescription[] = []; const t = new Set(); for (const f of src.fields) { const r = f.type; - if (r.kind === 'ref') { + if (r.kind === "ref") { const tp = getType(ctx, r.name); - if (tp.kind === 'struct') { + if (tp.kind === "struct") { if (!t.has(tp.name)) { t.add(r.name); res.push(tp); @@ -41,23 +46,21 @@ export function getSortedTypes(ctx: CompilerContext) { } } return res; - } + }; structs = topologicalSort(structs, refs); - structs = [...structs, ...types.filter((v) => v.kind === 'contract')]; + structs = [...structs, ...types.filter((v) => v.kind === "contract")]; return structs; } export function resolveAllocations(ctx: CompilerContext) { - // Load topological order of structs and contracts const types = getSortedTypes(ctx); // Generate allocations for (const s of types) { - // Reserve bits let reserveBits = 0; - let header: { value: number, bits: number } | null = null; + let header: { value: number; bits: number } | null = null; if (s.header !== null) { reserveBits += 32; // Header size header = { value: s.header, bits: 32 }; @@ -65,7 +68,7 @@ export function resolveAllocations(ctx: CompilerContext) { // Reserver refs let reserveRefs = 0; - if (s.kind === 'contract') { + if (s.kind === "contract") { reserveRefs += 1; // Internal state } @@ -76,7 +79,10 @@ export function resolveAllocations(ctx: CompilerContext) { const op = { name: f.name, type: f.abi.type, - op: getAllocationOperationFromField(f.abi.type, (name) => getAllocation(ctx, name)!.size) + op: getAllocationOperationFromField( + f.abi.type, + (name) => getAllocation(ctx, name)!.size, + ), }; ops.push(op); if (i < s.partialFieldCount) { @@ -85,8 +91,14 @@ export function resolveAllocations(ctx: CompilerContext) { } // Perform allocation - const root = allocate({ ops, reserved: { bits: reserveBits, refs: reserveRefs } }); - const partialRoot = allocate({ ops: partialOps, reserved: { bits: reserveBits, refs: reserveRefs } }); + const root = allocate({ + ops, + reserved: { bits: reserveBits, refs: reserveRefs }, + }); + const partialRoot = allocate({ + ops: partialOps, + reserved: { bits: reserveBits, refs: reserveRefs }, + }); // Store allocation const allocation: StorageAllocation = { @@ -95,28 +107,27 @@ export function resolveAllocations(ctx: CompilerContext) { header, size: { bits: root.size.bits + reserveBits, - refs: root.size.refs + reserveRefs - } + refs: root.size.refs + reserveRefs, + }, }; - + const partialAllocation: StorageAllocation = { ops: partialOps, root: partialRoot, header, size: { bits: root.size.bits + reserveBits, - refs: root.size.refs + reserveRefs - } + refs: root.size.refs + reserveRefs, + }, }; - + ctx = store.set(ctx, s.name, allocation); ctx = store.set(ctx, toBounced(s.name), partialAllocation); } - + // Generate init allocations for (const s of types) { - if (s.kind === 'contract' && s.init) { - + if (s.kind === "contract" && s.init) { // Reserve bits and refs let reserveBits = 0; let reserveRefs = 0; @@ -134,12 +145,18 @@ export function resolveAllocations(ctx: CompilerContext) { ops.push({ name: f.name, type: abiType, - op: getAllocationOperationFromField(abiType, (name) => getAllocation(ctx, name)!.size) + op: getAllocationOperationFromField( + abiType, + (name) => getAllocation(ctx, name)!.size, + ), }); } // Perform allocation - const root = allocate({ ops, reserved: { bits: reserveBits, refs: reserveRefs } }); // Better allocation? + const root = allocate({ + ops, + reserved: { bits: reserveBits, refs: reserveRefs }, + }); // Better allocation? // Store allocation const allocation: StorageAllocation = { @@ -148,12 +165,12 @@ export function resolveAllocations(ctx: CompilerContext) { header: null, size: { bits: root.size.bits + reserveBits, - refs: root.size.refs + reserveRefs - } + refs: root.size.refs + reserveRefs, + }, }; ctx = store.set(ctx, initId(s.name), allocation); } } return ctx; -} \ No newline at end of file +} diff --git a/src/test/bugs.spec.ts b/src/test/bugs.spec.ts index 972f390b4..685ec3159 100644 --- a/src/test/bugs.spec.ts +++ b/src/test/bugs.spec.ts @@ -1,26 +1,46 @@ -import { beginCell, toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { SampleJetton } from './bugs/output/bugs_SampleJetton'; -import { JettonDefaultWallet } from './bugs/output/bugs_JettonDefaultWallet'; +import { beginCell, toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { SampleJetton } from "./bugs/output/bugs_SampleJetton"; +import { JettonDefaultWallet } from "./bugs/output/bugs_JettonDefaultWallet"; -describe('bugs', () => { +describe("bugs", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should deploy contract correctly', async () => { - + it("should deploy contract correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); - const contract = system.open(await SampleJetton.fromInit(treasure.address, beginCell().endCell(), toNano('100'))); - const target = system.open(await JettonDefaultWallet.fromInit(contract.address, treasure.address)); + const treasure = system.treasure("treasure"); + const contract = system.open( + await SampleJetton.fromInit( + treasure.address, + beginCell().endCell(), + toNano("100"), + ), + ); + const target = system.open( + await JettonDefaultWallet.fromInit( + contract.address, + treasure.address, + ), + ); const tracker = system.track(target.address); - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Mint', receiver: treasure.address, amount: toNano('10') }); + await contract.send( + treasure, + { value: toNano("10") }, + { + $$type: "Mint", + receiver: treasure.address, + amount: toNano("10"), + }, + ); await system.run(); - expect(contract.abi.errors!['31733'].message).toStrictEqual('condition can`t be...') + expect(contract.abi.errors!["31733"].message).toStrictEqual( + "condition can`t be...", + ); expect(tracker.collect()).toMatchSnapshot(); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-bounced-routing.spec.ts b/src/test/feature-bounced-routing.spec.ts index 1b6a8c341..943a2fdc8 100644 --- a/src/test/feature-bounced-routing.spec.ts +++ b/src/test/feature-bounced-routing.spec.ts @@ -1,43 +1,50 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { SampleContract2 } from './features/output/bounced-routing_SampleContract2'; -import { SampleContract } from './features/output/bounced-routing_SampleContract'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { SampleContract2 } from "./features/output/bounced-routing_SampleContract2"; +import { SampleContract } from "./features/output/bounced-routing_SampleContract"; -describe('feature-strings', () => { +describe("feature-strings", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should bounce based on type router', async () => { - + it("should bounce based on type router", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await SampleContract.fromInit()); const contract2 = system.open(await SampleContract2.fromInit()); // Deploy - await contract.send(treasure, { value: toNano('10') }, null); - await contract2.send(treasure, { value: toNano('10') }, null); + await contract.send(treasure, { value: toNano("10") }, null); + await contract2.send(treasure, { value: toNano("10") }, null); await system.run(); - expect(await contract.getAmount()).toBe(100n) + expect(await contract.getAmount()).toBe(100n); - await contract.send(treasure, { value: toNano('10') }, { - $$type: 'EntryFirst', - amountToAdd: 10n, - toAddress: contract2.address - }); - await system.run() + await contract.send( + treasure, + { value: toNano("10") }, + { + $$type: "EntryFirst", + amountToAdd: 10n, + toAddress: contract2.address, + }, + ); + await system.run(); expect(await contract.getAmount()).toBe(98n); - await contract.send(treasure, { value: toNano('10') }, { - $$type: 'EntrySecond', - amountToAdd: 10n, - toAddress: contract2.address - }); - await system.run() + await contract.send( + treasure, + { value: toNano("10") }, + { + $$type: "EntrySecond", + amountToAdd: 10n, + toAddress: contract2.address, + }, + ); + await system.run(); expect(await contract.getAmount()).toBe(94n); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-debug.spec.ts b/src/test/feature-debug.spec.ts index 202250367..1d85243c9 100644 --- a/src/test/feature-debug.spec.ts +++ b/src/test/feature-debug.spec.ts @@ -1,33 +1,33 @@ -import { Address, toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { Debug } from './features/output/debug_Debug'; +import { Address, toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { Debug } from "./features/output/debug_Debug"; -describe('feature-debug', () => { +describe("feature-debug", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should dump values correctly', async () => { + it("should dump values correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await Debug.fromInit()); const logger = system.log(contract.address); await contract.send( treasure, - { value: toNano('10') }, - { $$type: 'Deploy', queryId: 0n } + { value: toNano("10") }, + { $$type: "Deploy", queryId: 0n }, ); await system.run(); logger.reset(); - await contract.send(treasure, { value: toNano('10') }, 'Debug'); + await contract.send(treasure, { value: toNano("10") }, "Debug"); await system.run(); const res = logger.collect(); const debugLogs = res.slice( - res.indexOf('=== DEBUG LOGS ===') + 19, - res.indexOf('=== VM LOGS ===') - 2 + res.indexOf("=== DEBUG LOGS ===") + 19, + res.indexOf("=== VM LOGS ===") - 2, ); expect(debugLogs).toStrictEqual(`stack(2 values) : 10000000000 () Hello world! @@ -37,7 +37,7 @@ false null ${contract.address.toString({ bounceable: true })} ${Address.parseRaw( - '0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8' + "0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8", )}`); }); }); diff --git a/src/test/feature-deep.spec.ts b/src/test/feature-deep.spec.ts index f29d9bb8e..5a4d4d6af 100644 --- a/src/test/feature-deep.spec.ts +++ b/src/test/feature-deep.spec.ts @@ -1,19 +1,18 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { A } from './features/output/deep_A'; -import { B } from './features/output/deep_B'; -import { C } from './features/output/deep_C'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { A } from "./features/output/deep_A"; +import { B } from "./features/output/deep_B"; +import { C } from "./features/output/deep_C"; -describe('feature-random', () => { +describe("feature-random", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should chain deep sequences correctly', async () => { - + it("should chain deep sequences correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contractA = system.open(await A.fromInit()); const contractB = system.open(await B.fromInit(contractA.address)); const contractC = system.open(await C.fromInit(contractB.address)); @@ -23,7 +22,7 @@ describe('feature-random', () => { expect(trackA.address.toString({ testOnly: true })).toMatchSnapshot(); expect(trackB.address.toString({ testOnly: true })).toMatchSnapshot(); expect(trackC.address.toString({ testOnly: true })).toMatchSnapshot(); - await contractA.send(treasure, { value: toNano('10') }, "Message"); + await contractA.send(treasure, { value: toNano("10") }, "Message"); await system.run(); const nextA = await contractA.getGetNext(); expect(nextA.code.equals(contractB.init!.code!)).toBe(true); @@ -37,4 +36,4 @@ describe('feature-random', () => { expect(trackB.collect()).toMatchSnapshot(); expect(trackC.collect()).toMatchSnapshot(); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-dns.spec.ts b/src/test/feature-dns.spec.ts index c7d545ae2..90538cffd 100644 --- a/src/test/feature-dns.spec.ts +++ b/src/test/feature-dns.spec.ts @@ -1,13 +1,13 @@ -import { OpenedContract, beginCell, toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { DNSTester } from './features/output/dns_DNSTester'; +import { OpenedContract, beginCell, toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { DNSTester } from "./features/output/dns_DNSTester"; function convertToInternal(src: string) { - if (src === '.') { + if (src === ".") { return Buffer.alloc(1, 0); } - const parts = src.split('.').map((x) => Buffer.from(x)); + const parts = src.split(".").map((x) => Buffer.from(x)); let res = Buffer.alloc(0); for (let i = 0; i < parts.length; i++) { if (i > 0) { @@ -31,72 +31,92 @@ function convertToInternal(src: string) { // return res; } -describe('feature-dns', () => { - +describe("feature-dns", () => { let contract: OpenedContract; beforeEach(async () => { __DANGER_resetNodeId(); const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); contract = system.open(await DNSTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Deploy', queryId: 0n }); + await contract.send( + treasure, + { value: toNano("10") }, + { $$type: "Deploy", queryId: 0n }, + ); await system.run(); }); const invalidNames = [ - '..', - 'a..b', - 'a.b..c', - 'a.b.c..', - 'a.!b', - 'a.-b', - 'a.b-', - '_a.b', - 'a..b', - 'a b', - 'A.b' + "..", + "a..b", + "a.b..c", + "a.b.c..", + "a.!b", + "a.-b", + "a.b-", + "_a.b", + "a..b", + "a b", + "A.b", ]; const validNames = [ - '.', - 'ton.dns', - 'a.b', - 'a.b.c', - 'a.b.c.d', - 'a.b.c.', - 'ton-dns.com', - 'ton-dns.com.hello', + ".", + "ton.dns", + "a.b", + "a.b.c", + "a.b.c.d", + "a.b.c.", + "ton-dns.com", + "ton-dns.com.hello", ]; const equalNormalized = [ - ['ton.dns', 't0n.dns'], - ['t1n.dns', 'tln.dns'], + ["ton.dns", "t0n.dns"], + ["t1n.dns", "tln.dns"], ]; const notEqualNormalized = [ - ['ton.dns', 'tan.dns'], - ['t1n.dns', 'tin.dns'], + ["ton.dns", "tan.dns"], + ["t1n.dns", "tin.dns"], ]; for (let i = 0; i < invalidNames.length; i++) { it(`should fail on invalid name: ${invalidNames[i]}`, async () => { - expect(await contract.getStringToInternal(invalidNames[i])).toBe(null); + expect(await contract.getStringToInternal(invalidNames[i])).toBe( + null, + ); const internalAddress = convertToInternal(invalidNames[i]); - expect(await contract.getDnsInternalVerify(beginCell().storeBuffer(internalAddress).endCell())).toBe(false); + expect( + await contract.getDnsInternalVerify( + beginCell().storeBuffer(internalAddress).endCell(), + ), + ).toBe(false); }); } for (let i = 0; i < validNames.length; i++) { it(`should convert valid name: ${validNames[i]}`, async () => { const data = (await contract.getStringToInternal(validNames[i]))!; - const received = data.beginParse().loadBuffer(data.bits.length / 8).toString('hex'); - expect(received).toBe(convertToInternal(validNames[i].endsWith('.') && validNames[i] !== '.' ? validNames[i].slice(0, validNames[i].length - 1) : validNames[i]).toString('hex')); + const received = data + .beginParse() + .loadBuffer(data.bits.length / 8) + .toString("hex"); + expect(received).toBe( + convertToInternal( + validNames[i].endsWith(".") && validNames[i] !== "." + ? validNames[i].slice(0, validNames[i].length - 1) + : validNames[i], + ).toString("hex"), + ); }); } for (let i = 0; i < validNames.length; i++) { - if (validNames[i] !== '.') { + if (validNames[i] !== ".") { it(`should convert valid name: ${validNames[i]}`, async () => { - const data = (await contract.getStringToInternal(validNames[i]))!; + const data = (await contract.getStringToInternal( + validNames[i], + ))!; expect(await contract.getDnsInternalVerify(data)).toBe(true); }); } @@ -104,66 +124,124 @@ describe('feature-dns', () => { for (let i = 0; i < equalNormalized.length; i++) { it(`should convert equal normalized names: ${equalNormalized[i][0]} ${equalNormalized[i][1]}`, async () => { - let data1 = (await contract.getStringToInternal(equalNormalized[i][0]))!; + let data1 = (await contract.getStringToInternal( + equalNormalized[i][0], + ))!; data1 = await contract.getInternalNormalize(data1); - const received1 = data1.beginParse().loadBuffer(data1.bits.length / 8).toString('hex'); - let data2 = (await contract.getStringToInternal(equalNormalized[i][1]))!; + const received1 = data1 + .beginParse() + .loadBuffer(data1.bits.length / 8) + .toString("hex"); + let data2 = (await contract.getStringToInternal( + equalNormalized[i][1], + ))!; data2 = await contract.getInternalNormalize(data2); - const received2 = data2.beginParse().loadBuffer(data2.bits.length / 8).toString('hex'); + const received2 = data2 + .beginParse() + .loadBuffer(data2.bits.length / 8) + .toString("hex"); expect(received1).toBe(received2); expect(received1.length).toBe(received2.length); }); } for (let i = 0; i < notEqualNormalized.length; i++) { it(`should convert not equal normalized names: ${notEqualNormalized[i][0]} ${notEqualNormalized[i][1]}`, async () => { - let data1 = (await contract.getStringToInternal(notEqualNormalized[i][0]))!; + let data1 = (await contract.getStringToInternal( + notEqualNormalized[i][0], + ))!; data1 = await contract.getInternalNormalize(data1); - const received1 = data1.beginParse().loadBuffer(data1.bits.length / 8).toString('hex'); - let data2 = (await contract.getStringToInternal(notEqualNormalized[i][1]))!; + const received1 = data1 + .beginParse() + .loadBuffer(data1.bits.length / 8) + .toString("hex"); + let data2 = (await contract.getStringToInternal( + notEqualNormalized[i][1], + ))!; data2 = await contract.getInternalNormalize(data2); - const received2 = data2.beginParse().loadBuffer(data2.bits.length / 8).toString('hex'); + const received2 = data2 + .beginParse() + .loadBuffer(data2.bits.length / 8) + .toString("hex"); expect(received1).not.toBe(received2); expect(received1.length).toBe(received2.length); }); } for (let i = 0; i < validNames.length; i++) { - it('should resolve name ' + validNames[i], async () => { + it("should resolve name " + validNames[i], async () => { const internalAddress = convertToInternal(validNames[i]); - const resolved = (await contract.getDnsresolve(beginCell().storeBuffer(internalAddress).endCell(), 1n))!; + const resolved = (await contract.getDnsresolve( + beginCell().storeBuffer(internalAddress).endCell(), + 1n, + ))!; expect(resolved.prefix).toBe(BigInt(internalAddress.length * 8)); - if (validNames[i] === '.') { + if (validNames[i] === ".") { expect(resolved.record!.bits.length).toBe(0); expect(resolved.record!.refs.length).toBe(0); - } else if (validNames[i].endsWith('.')) { - expect(resolved.record!.beginParse().loadBuffer(internalAddress.length - 1).toString('hex')).toBe(internalAddress.subarray(1).toString('hex')); + } else if (validNames[i].endsWith(".")) { + expect( + resolved + .record!.beginParse() + .loadBuffer(internalAddress.length - 1) + .toString("hex"), + ).toBe(internalAddress.subarray(1).toString("hex")); } else { - expect(resolved.record!.beginParse().loadBuffer(internalAddress.length).toString('hex')).toBe(internalAddress.toString('hex')); + expect( + resolved + .record!.beginParse() + .loadBuffer(internalAddress.length) + .toString("hex"), + ).toBe(internalAddress.toString("hex")); } }); } for (let i = 0; i < invalidNames.length; i++) { - it('should not resolve name ' + invalidNames[i], async () => { + it("should not resolve name " + invalidNames[i], async () => { const internalAddress = convertToInternal(invalidNames[i]); - await expect(contract.getDnsresolve(beginCell().storeBuffer(internalAddress).endCell(), 1n)).rejects.toThrowError(); + await expect( + contract.getDnsresolve( + beginCell().storeBuffer(internalAddress).endCell(), + 1n, + ), + ).rejects.toThrowError(); }); } for (let i = 0; i < validNames.length; i++) { - if (validNames[i].endsWith('.')) { + if (validNames[i].endsWith(".")) { continue; } - it('should resolve name with leading zero ' + validNames[i], async () => { - const internalAddress = convertToInternal(validNames[i]); - const resolved = (await contract.getDnsresolve(beginCell().storeBuffer(Buffer.concat([Buffer.alloc(1, 0), internalAddress])).endCell(), 1n))!; - expect(resolved.prefix).toBe(BigInt(internalAddress.length * 8 + 8)); - if (validNames[i] === '.') { - expect(resolved.record!.bits.length).toBe(0); - expect(resolved.record!.refs.length).toBe(0); - } else { - expect(resolved.record!.beginParse().loadBuffer(internalAddress.length).toString('hex')).toBe(internalAddress.toString('hex')); - } - }); + it( + "should resolve name with leading zero " + validNames[i], + async () => { + const internalAddress = convertToInternal(validNames[i]); + const resolved = (await contract.getDnsresolve( + beginCell() + .storeBuffer( + Buffer.concat([ + Buffer.alloc(1, 0), + internalAddress, + ]), + ) + .endCell(), + 1n, + ))!; + expect(resolved.prefix).toBe( + BigInt(internalAddress.length * 8 + 8), + ); + if (validNames[i] === ".") { + expect(resolved.record!.bits.length).toBe(0); + expect(resolved.record!.refs.length).toBe(0); + } else { + expect( + resolved + .record!.beginParse() + .loadBuffer(internalAddress.length) + .toString("hex"), + ).toBe(internalAddress.toString("hex")); + } + }, + ); } -}); \ No newline at end of file +}); diff --git a/src/test/feature-implicit-init.spec.ts b/src/test/feature-implicit-init.spec.ts index de50a1197..3632c03ed 100644 --- a/src/test/feature-implicit-init.spec.ts +++ b/src/test/feature-implicit-init.spec.ts @@ -1,13 +1,13 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { MyContract } from './features/output/implicit-init_MyContract'; -import { run } from '../node'; -import { consoleLogger } from '../logger'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { MyContract } from "./features/output/implicit-init_MyContract"; +import { run } from "../node"; +import { consoleLogger } from "../logger"; -describe('feature-send', () => { +describe("feature-send", () => { beforeAll(() => { - jest.spyOn(consoleLogger, 'error').mockImplementation(() => {}); + jest.spyOn(consoleLogger, "error").mockImplementation(() => {}); }); beforeEach(() => { @@ -22,53 +22,53 @@ describe('feature-send', () => { (consoleLogger.error as jest.Mock).mockClear(); }); - it('should deploy', async () => { + it("should deploy", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MyContract.fromInit()); const tracker = system.track(contract.address); await contract.send( treasure, - { value: toNano('1') }, - { $$type: 'Deploy', queryId: 0n } + { value: toNano("1") }, + { $$type: "Deploy", queryId: 0n }, ); await system.run(); expect(await contract.getGetCounter()).toBe(0n); expect(tracker.collect()).toMatchSnapshot(); }); - it('should increment counter', async () => { + it("should increment counter", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MyContract.fromInit()); await contract.send( treasure, - { value: toNano('1') }, - { $$type: 'Deploy', queryId: 0n } + { value: toNano("1") }, + { $$type: "Deploy", queryId: 0n }, ); await system.run(); // Test expect(await contract.getGetCounter()).toBe(0n); const tracker = system.track(contract); - await contract.send(treasure, { value: toNano('1') }, 'increment'); + await contract.send(treasure, { value: toNano("1") }, "increment"); await system.run(); expect(await contract.getGetCounter()).toBe(1n); - await contract.send(treasure, { value: toNano('1') }, 'increment'); + await contract.send(treasure, { value: toNano("1") }, "increment"); await system.run(); expect(await contract.getGetCounter()).toBe(2n); expect(tracker.collect()).toMatchSnapshot(); }); - it('should not compile with uninitialized storage fields', async () => { + it("should not compile with uninitialized storage fields", async () => { const result = await run({ - configPath: __dirname + '/test-tact.config.json', - projectNames: ['implicit-init-2'], + configPath: __dirname + "/test-tact.config.json", + projectNames: ["implicit-init-2"], }); expect((consoleLogger.error as jest.Mock).mock.lastCall[0]).toContain( - 'Field test_field is not set' + "Field test_field is not set", ); expect(result).toBe(false); }); diff --git a/src/test/feature-instrinsics.spec.ts b/src/test/feature-instrinsics.spec.ts index 2341bcd18..77f50f696 100644 --- a/src/test/feature-instrinsics.spec.ts +++ b/src/test/feature-instrinsics.spec.ts @@ -1,39 +1,66 @@ -import { Address, beginCell, Cell, toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { IntrinsicsTester } from './features/output/intrinsics_IntrinsicsTester'; -import { sha256_sync } from '@ton/crypto'; +import { Address, beginCell, Cell, toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { IntrinsicsTester } from "./features/output/intrinsics_IntrinsicsTester"; +import { sha256_sync } from "@ton/crypto"; -describe('feature-instrinsics', () => { +describe("feature-instrinsics", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should return correct instinsic results', async () => { + it("should return correct instinsic results", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await IntrinsicsTester.fromInit()); - system.name(contract, 'contract'); - await contract.send(treasure, { value: toNano('10') }, 'Deploy'); + system.name(contract, "contract"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); // Compile-time constants - expect(await contract.getGetTons()).toBe(toNano('10.1234')); - expect(await contract.getGetTons2()).toBe(toNano('10.1234')); - expect(await contract.getGetString()).toBe('Hello world'); - expect(await contract.getGetString2()).toBe('Hello world'); - expect((await contract.getGetAddress()).equals(Address.parse('EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N'))).toBe(true); - expect((await contract.getGetAddress2()).equals(Address.parse('EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N'))).toBe(true); - expect((await contract.getGetCell()).equals(Cell.fromBase64('te6cckEBAQEADgAAGEhlbGxvIHdvcmxkIXgtxbw='))).toBe(true); - expect((await contract.getGetCell2()).equals(Cell.fromBase64('te6cckEBAQEADgAAGEhlbGxvIHdvcmxkIXgtxbw='))).toBe(true); + expect(await contract.getGetTons()).toBe(toNano("10.1234")); + expect(await contract.getGetTons2()).toBe(toNano("10.1234")); + expect(await contract.getGetString()).toBe("Hello world"); + expect(await contract.getGetString2()).toBe("Hello world"); + expect( + (await contract.getGetAddress()).equals( + Address.parse( + "EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N", + ), + ), + ).toBe(true); + expect( + (await contract.getGetAddress2()).equals( + Address.parse( + "EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N", + ), + ), + ).toBe(true); + expect( + (await contract.getGetCell()).equals( + Cell.fromBase64("te6cckEBAQEADgAAGEhlbGxvIHdvcmxkIXgtxbw="), + ), + ).toBe(true); + expect( + (await contract.getGetCell2()).equals( + Cell.fromBase64("te6cckEBAQEADgAAGEhlbGxvIHdvcmxkIXgtxbw="), + ), + ).toBe(true); expect(await contract.getGetPow()).toBe(512n); expect(await contract.getGetPow2()).toBe(512n); // Compile-time optimizations - expect((await contract.getGetComment()).equals(beginCell().storeUint(0, 32).storeStringTail('Hello world').endCell())).toBe(true); + expect( + (await contract.getGetComment()).equals( + beginCell() + .storeUint(0, 32) + .storeStringTail("Hello world") + .endCell(), + ), + ).toBe(true); // Compile-time send/emit optimizations const tracker = system.track(contract); - await contract.send(treasure, { value: toNano(1) }, 'emit_1'); + await contract.send(treasure, { value: toNano(1) }, "emit_1"); await system.run(); // Check that the contract emitted the correct message @@ -42,11 +69,15 @@ describe('feature-instrinsics', () => { // Check sha256 function sha256(src: string | Buffer) { - return BigInt('0x' + sha256_sync(src).toString('hex')); + return BigInt("0x" + sha256_sync(src).toString("hex")); } - expect(await contract.getGetHash()).toBe(sha256('hello world')); - expect(await contract.getGetHash2()).toBe(sha256('hello world')); - expect(await contract.getGetHash3(beginCell().storeStringTail('sometest').endCell())).toBe(sha256('sometest')); - expect(await contract.getGetHash4('wallet')).toBe(sha256('wallet')); + expect(await contract.getGetHash()).toBe(sha256("hello world")); + expect(await contract.getGetHash2()).toBe(sha256("hello world")); + expect( + await contract.getGetHash3( + beginCell().storeStringTail("sometest").endCell(), + ), + ).toBe(sha256("sometest")); + expect(await contract.getGetHash4("wallet")).toBe(sha256("wallet")); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-integer-literals.spec.ts b/src/test/feature-integer-literals.spec.ts index c361b42d8..e9285479d 100644 --- a/src/test/feature-integer-literals.spec.ts +++ b/src/test/feature-integer-literals.spec.ts @@ -1,18 +1,18 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { IntegerLiteralsTester } from './features/output/integer-literals_IntegerLiteralsTester'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { IntegerLiteralsTester } from "./features/output/integer-literals_IntegerLiteralsTester"; -describe('feature-integer-literals', () => { +describe("feature-integer-literals", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should implement integer literals correctly', async () => { + it("should implement integer literals correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await IntegerLiteralsTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, null); + await contract.send(treasure, { value: toNano("10") }, null); await system.run(); // Check methods diff --git a/src/test/feature-map.spec.ts b/src/test/feature-map.spec.ts index ebee31532..806683fa4 100644 --- a/src/test/feature-map.spec.ts +++ b/src/test/feature-map.spec.ts @@ -1,9 +1,12 @@ -import { randomAddress } from './utils/randomAddress'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { MapTestContract, SomeStruct } from './features/output/maps_MapTestContract'; -import { ContractSystem } from '@tact-lang/emulator'; -import { beginCell, toNano } from '@ton/core'; -import { ComputeError } from '@ton/core'; +import { randomAddress } from "./utils/randomAddress"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { + MapTestContract, + SomeStruct, +} from "./features/output/maps_MapTestContract"; +import { ContractSystem } from "@tact-lang/emulator"; +import { beginCell, toNano } from "@ton/core"; +import { ComputeError } from "@ton/core"; function strEq(a: SomeStruct | null, b: SomeStruct | null) { if (a === null || b === null) { @@ -12,7 +15,7 @@ function strEq(a: SomeStruct | null, b: SomeStruct | null) { return a.value === b.value; } -describe('feature-map', () => { +describe("feature-map", () => { /* eslint-disable */ let globalCoverage: any; beforeAll(() => { @@ -21,20 +24,19 @@ describe('feature-map', () => { }); afterAll(() => { (globalThis as any).__ton_coverage__ = globalCoverage; - }) + }); /* eslint-enable */ beforeEach(() => { __DANGER_resetNodeId(); }); - it('should implement maps correctly', async () => { + it("should implement maps correctly", async () => { jest.setTimeout(2 * 60000); try { - // Init contract const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MapTestContract.fromInit()); - await contract.send(treasure, { value: toNano('10') }, null); + await contract.send(treasure, { value: toNano("10") }, null); await system.run(); // Initial state @@ -95,7 +97,6 @@ describe('feature-map', () => { keys.push(10102312312312312312312n); keys.push(-10102312312312312312312n); for (const k of keys) { - // Check keys to be empty expect(await contract.getIntMap1Value(k)).toBeNull(); expect(await contract.getIntMap2Value(k)).toBeNull(); @@ -105,86 +106,234 @@ describe('feature-map', () => { // Set keys const valueInt = k * 10n; const valueBool = k < 0n; - const addr = randomAddress(0, 'addr-' + k.toString(10)); + const addr = randomAddress(0, "addr-" + k.toString(10)); const valueCell = beginCell().storeUint(123123, 128).endCell(); - const valueStruct: SomeStruct = { $$type: 'SomeStruct', value: 10012312n }; - const valueAddr = randomAddress(0, 'value-' + k.toString(10)); + const valueStruct: SomeStruct = { + $$type: "SomeStruct", + value: 10012312n, + }; + const valueAddr = randomAddress(0, "value-" + k.toString(10)); const keySmall = k % 100n; const keySmallAbs = (k > 0 ? k : -k) % 100n; const valueSmall = k % 100n; const valueSmallAbs = (k > 0 ? k : -k) % 100n; - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap1', key: k, value: valueInt }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap2', key: k, value: valueBool }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap3', key: k, value: valueCell }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap4', key: k, value: valueStruct }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap5', key: k, value: valueAddr }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap6', key: keySmall, value: valueInt }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetUIntMap7', key: keySmallAbs, value: valueInt }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap8', key: k, value: valueSmall }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetUIntMap9', key: k, value: valueSmallAbs }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap1', key: addr, value: valueInt }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap2', key: addr, value: valueBool }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap3', key: addr, value: valueCell }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap4', key: addr, value: valueStruct }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap5', key: addr, value: valueAddr }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap6', key: addr, value: valueSmall }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap7', key: addr, value: valueSmallAbs }); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap1", key: k, value: valueInt }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap2", key: k, value: valueBool }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap3", key: k, value: valueCell }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap4", key: k, value: valueStruct }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap5", key: k, value: valueAddr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap6", key: keySmall, value: valueInt }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { + $$type: "SetUIntMap7", + key: keySmallAbs, + value: valueInt, + }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap8", key: k, value: valueSmall }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetUIntMap9", key: k, value: valueSmallAbs }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap1", key: addr, value: valueInt }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap2", key: addr, value: valueBool }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap3", key: addr, value: valueCell }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap4", key: addr, value: valueStruct }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap5", key: addr, value: valueAddr }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap6", key: addr, value: valueSmall }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap7", key: addr, value: valueSmallAbs }, + ); await system.run(); // Check value set - expect((await contract.getIntMap1Value(k))).toBe(valueInt); + expect(await contract.getIntMap1Value(k)).toBe(valueInt); expect((await contract.getIntMap2Value(k))!).toBe(valueBool); - expect((await contract.getIntMap3Value(k))!.equals(valueCell)).toBe(true); - expect(strEq((await contract.getIntMap4Value(k))!, valueStruct)).toBe(true); - expect((await contract.getIntMap5Value(k))!.equals(valueAddr)).toBe(true); - expect((await contract.getIntMap6_1Value(keySmall))).toBe(valueInt); - expect((await contract.getIntMap6_2Value(keySmall))).toBe(valueInt); - expect((await contract.getIntMap6_3Value(keySmall))).toBe(valueInt); - expect((await contract.getIntMap6_4Value(keySmall))).toBe(valueInt); - expect((await contract.getIntMap6_5Value(keySmall))).toBe(valueInt); - expect((await contract.getIntMap6_6Value(keySmall))).toBe(valueInt); - expect((await contract.getIntMap6_7Value(keySmall))).toBe(valueInt); - expect((await contract.getIntMap7_1Value(keySmallAbs))).toBe(valueInt); - expect((await contract.getIntMap7_2Value(keySmallAbs))).toBe(valueInt); - expect((await contract.getIntMap7_3Value(keySmallAbs))).toBe(valueInt); - expect((await contract.getIntMap7_4Value(keySmallAbs))).toBe(valueInt); - expect((await contract.getIntMap7_5Value(keySmallAbs))).toBe(valueInt); - expect((await contract.getIntMap7_6Value(keySmallAbs))).toBe(valueInt); - expect((await contract.getIntMap8_1Value(k))).toBe(valueSmall); - expect((await contract.getIntMap8_2Value(k))).toBe(valueSmall); - expect((await contract.getIntMap8_3Value(k))).toBe(valueSmall); - expect((await contract.getIntMap8_4Value(k))).toBe(valueSmall); - expect((await contract.getIntMap8_5Value(k))).toBe(valueSmall); - expect((await contract.getIntMap8_6Value(k))).toBe(valueSmall); - expect((await contract.getIntMap8_7Value(k))).toBe(valueSmall); - expect((await contract.getIntMap9_1Value(k))).toBe(valueSmallAbs); - expect((await contract.getIntMap9_2Value(k))).toBe(valueSmallAbs); - expect((await contract.getIntMap9_3Value(k))).toBe(valueSmallAbs); - expect((await contract.getIntMap9_4Value(k))).toBe(valueSmallAbs); - expect((await contract.getIntMap9_5Value(k))).toBe(valueSmallAbs); - expect((await contract.getIntMap9_6Value(k))).toBe(valueSmallAbs); - expect((await contract.getIntMap10Value(keySmall, valueInt))).toBe(valueInt * 7n); - expect((await contract.getIntMap11Value(keySmallAbs, valueInt))).toBe(valueInt * 6n); - expect((await contract.getIntMap12Value(k, valueSmall))).toBe(valueSmall * 7n); - expect((await contract.getIntMap13Value(k, valueSmallAbs))).toBe(valueSmallAbs * 7n); - expect((await contract.getAddrMap1Value(addr))).toBe(valueInt); - expect((await contract.getAddrMap2Value(addr))!).toBe(valueBool); - expect((await contract.getAddrMap3Value(addr))!.equals(valueCell)).toBe(true); - expect(strEq((await contract.getAddrMap4Value(addr))!, valueStruct)).toBe(true); - expect((await contract.getAddrMap5Value(addr))!.equals(valueAddr)).toBe(true); - expect((await contract.getAddrMap6_1Value(addr))).toBe(valueSmall); - expect((await contract.getAddrMap6_2Value(addr))).toBe(valueSmall); - expect((await contract.getAddrMap6_3Value(addr))).toBe(valueSmall); - expect((await contract.getAddrMap6_4Value(addr))).toBe(valueSmall); - expect((await contract.getAddrMap6_5Value(addr))).toBe(valueSmall); - expect((await contract.getAddrMap6_6Value(addr))).toBe(valueSmall); - expect((await contract.getAddrMap6_7Value(addr))).toBe(valueSmall); - expect((await contract.getAddrMap7_1Value(addr))).toBe(valueSmallAbs); - expect((await contract.getAddrMap7_2Value(addr))).toBe(valueSmallAbs); - expect((await contract.getAddrMap7_3Value(addr))).toBe(valueSmallAbs); - expect((await contract.getAddrMap7_4Value(addr))).toBe(valueSmallAbs); - expect((await contract.getAddrMap7_5Value(addr))).toBe(valueSmallAbs); - expect((await contract.getAddrMap7_6Value(addr))).toBe(valueSmallAbs); + expect( + (await contract.getIntMap3Value(k))!.equals(valueCell), + ).toBe(true); + expect( + strEq((await contract.getIntMap4Value(k))!, valueStruct), + ).toBe(true); + expect( + (await contract.getIntMap5Value(k))!.equals(valueAddr), + ).toBe(true); + expect(await contract.getIntMap6_1Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_2Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_3Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_4Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_5Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_6Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap6_7Value(keySmall)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_1Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_2Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_3Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_4Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_5Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap7_6Value(keySmallAbs)).toBe( + valueInt, + ); + expect(await contract.getIntMap8_1Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_2Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_3Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_4Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_5Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_6Value(k)).toBe(valueSmall); + expect(await contract.getIntMap8_7Value(k)).toBe(valueSmall); + expect(await contract.getIntMap9_1Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_2Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_3Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_4Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_5Value(k)).toBe(valueSmallAbs); + expect(await contract.getIntMap9_6Value(k)).toBe(valueSmallAbs); + expect( + await contract.getIntMap10Value(keySmall, valueInt), + ).toBe(valueInt * 7n); + expect( + await contract.getIntMap11Value(keySmallAbs, valueInt), + ).toBe(valueInt * 6n); + expect(await contract.getIntMap12Value(k, valueSmall)).toBe( + valueSmall * 7n, + ); + expect(await contract.getIntMap13Value(k, valueSmallAbs)).toBe( + valueSmallAbs * 7n, + ); + expect(await contract.getAddrMap1Value(addr)).toBe(valueInt); + expect((await contract.getAddrMap2Value(addr))!).toBe( + valueBool, + ); + expect( + (await contract.getAddrMap3Value(addr))!.equals(valueCell), + ).toBe(true); + expect( + strEq( + (await contract.getAddrMap4Value(addr))!, + valueStruct, + ), + ).toBe(true); + expect( + (await contract.getAddrMap5Value(addr))!.equals(valueAddr), + ).toBe(true); + expect(await contract.getAddrMap6_1Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_2Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_3Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_4Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_5Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_6Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap6_7Value(addr)).toBe( + valueSmall, + ); + expect(await contract.getAddrMap7_1Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_2Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_3Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_4Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_5Value(addr)).toBe( + valueSmallAbs, + ); + expect(await contract.getAddrMap7_6Value(addr)).toBe( + valueSmallAbs, + ); // Sizes expect((await contract.getIntMap1()).size).toBe(1); @@ -224,74 +373,150 @@ describe('feature-map', () => { expect((await contract.getAddrMap4()).size).toBe(1); // Clear keys - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap1', key: k, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap2', key: k, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap3', key: k, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap4', key: k, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap5', key: k, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap6', key: keySmall, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetUIntMap7', key: keySmallAbs, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetIntMap8', key: k, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetUIntMap9', key: k, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap1', key: addr, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap2', key: addr, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap3', key: addr, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap4', key: addr, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap5', key: addr, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap6', key: addr, value: null }); - await contract.send(treasure, { value: toNano(1) }, { $$type: 'SetAddrMap7', key: addr, value: null }); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap1", key: k, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap2", key: k, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap3", key: k, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap4", key: k, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap5", key: k, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap6", key: keySmall, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetUIntMap7", key: keySmallAbs, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetIntMap8", key: k, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetUIntMap9", key: k, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap1", key: addr, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap2", key: addr, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap3", key: addr, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap4", key: addr, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap5", key: addr, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap6", key: addr, value: null }, + ); + await contract.send( + treasure, + { value: toNano(1) }, + { $$type: "SetAddrMap7", key: addr, value: null }, + ); await system.run(); // Check value cleared - expect((await contract.getIntMap1Value(k))).toBeNull(); - expect((await contract.getIntMap2Value(k))).toBeNull(); - expect((await contract.getIntMap3Value(k))).toBeNull(); - expect((await contract.getIntMap4Value(k))).toBeNull(); - expect((await contract.getIntMap5Value(k))).toBeNull(); - expect((await contract.getIntMap6_1Value(keySmall))).toBe(null); - expect((await contract.getIntMap6_2Value(keySmall))).toBe(null); - expect((await contract.getIntMap6_3Value(keySmall))).toBe(null); - expect((await contract.getIntMap6_4Value(keySmall))).toBe(null); - expect((await contract.getIntMap6_5Value(keySmall))).toBe(null); - expect((await contract.getIntMap6_6Value(keySmall))).toBe(null); - expect((await contract.getIntMap6_7Value(keySmall))).toBe(null); - expect((await contract.getIntMap7_1Value(keySmallAbs))).toBe(null); - expect((await contract.getIntMap7_2Value(keySmallAbs))).toBe(null); - expect((await contract.getIntMap7_3Value(keySmallAbs))).toBe(null); - expect((await contract.getIntMap7_4Value(keySmallAbs))).toBe(null); - expect((await contract.getIntMap7_5Value(keySmallAbs))).toBe(null); - expect((await contract.getIntMap7_6Value(keySmallAbs))).toBe(null); - expect((await contract.getIntMap8_1Value(k))).toBe(null); - expect((await contract.getIntMap8_2Value(k))).toBe(null); - expect((await contract.getIntMap8_3Value(k))).toBe(null); - expect((await contract.getIntMap8_4Value(k))).toBe(null); - expect((await contract.getIntMap8_5Value(k))).toBe(null); - expect((await contract.getIntMap8_6Value(k))).toBe(null); - expect((await contract.getIntMap8_7Value(k))).toBe(null); - expect((await contract.getIntMap9_1Value(k))).toBe(null); - expect((await contract.getIntMap9_2Value(k))).toBe(null); - expect((await contract.getIntMap9_3Value(k))).toBe(null); - expect((await contract.getIntMap9_4Value(k))).toBe(null); - expect((await contract.getIntMap9_5Value(k))).toBe(null); - expect((await contract.getIntMap9_6Value(k))).toBe(null); - expect((await contract.getAddrMap1Value(addr))).toBeNull(); - expect((await contract.getAddrMap2Value(addr))).toBeNull(); - expect((await contract.getAddrMap3Value(addr))).toBeNull(); - expect((await contract.getAddrMap4Value(addr))).toBeNull(); - expect((await contract.getAddrMap5Value(addr))).toBeNull(); - expect((await contract.getAddrMap6_1Value(addr))).toBe(null); - expect((await contract.getAddrMap6_2Value(addr))).toBe(null); - expect((await contract.getAddrMap6_3Value(addr))).toBe(null); - expect((await contract.getAddrMap6_4Value(addr))).toBe(null); - expect((await contract.getAddrMap6_5Value(addr))).toBe(null); - expect((await contract.getAddrMap6_6Value(addr))).toBe(null); - expect((await contract.getAddrMap6_7Value(addr))).toBe(null); - expect((await contract.getAddrMap7_1Value(addr))).toBe(null); - expect((await contract.getAddrMap7_2Value(addr))).toBe(null); - expect((await contract.getAddrMap7_3Value(addr))).toBe(null); - expect((await contract.getAddrMap7_4Value(addr))).toBe(null); - expect((await contract.getAddrMap7_5Value(addr))).toBe(null); - expect((await contract.getAddrMap7_6Value(addr))).toBe(null); + expect(await contract.getIntMap1Value(k)).toBeNull(); + expect(await contract.getIntMap2Value(k)).toBeNull(); + expect(await contract.getIntMap3Value(k)).toBeNull(); + expect(await contract.getIntMap4Value(k)).toBeNull(); + expect(await contract.getIntMap5Value(k)).toBeNull(); + expect(await contract.getIntMap6_1Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_2Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_3Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_4Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_5Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_6Value(keySmall)).toBe(null); + expect(await contract.getIntMap6_7Value(keySmall)).toBe(null); + expect(await contract.getIntMap7_1Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_2Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_3Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_4Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_5Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap7_6Value(keySmallAbs)).toBe( + null, + ); + expect(await contract.getIntMap8_1Value(k)).toBe(null); + expect(await contract.getIntMap8_2Value(k)).toBe(null); + expect(await contract.getIntMap8_3Value(k)).toBe(null); + expect(await contract.getIntMap8_4Value(k)).toBe(null); + expect(await contract.getIntMap8_5Value(k)).toBe(null); + expect(await contract.getIntMap8_6Value(k)).toBe(null); + expect(await contract.getIntMap8_7Value(k)).toBe(null); + expect(await contract.getIntMap9_1Value(k)).toBe(null); + expect(await contract.getIntMap9_2Value(k)).toBe(null); + expect(await contract.getIntMap9_3Value(k)).toBe(null); + expect(await contract.getIntMap9_4Value(k)).toBe(null); + expect(await contract.getIntMap9_5Value(k)).toBe(null); + expect(await contract.getIntMap9_6Value(k)).toBe(null); + expect(await contract.getAddrMap1Value(addr)).toBeNull(); + expect(await contract.getAddrMap2Value(addr)).toBeNull(); + expect(await contract.getAddrMap3Value(addr)).toBeNull(); + expect(await contract.getAddrMap4Value(addr)).toBeNull(); + expect(await contract.getAddrMap5Value(addr)).toBeNull(); + expect(await contract.getAddrMap6_1Value(addr)).toBe(null); + expect(await contract.getAddrMap6_2Value(addr)).toBe(null); + expect(await contract.getAddrMap6_3Value(addr)).toBe(null); + expect(await contract.getAddrMap6_4Value(addr)).toBe(null); + expect(await contract.getAddrMap6_5Value(addr)).toBe(null); + expect(await contract.getAddrMap6_6Value(addr)).toBe(null); + expect(await contract.getAddrMap6_7Value(addr)).toBe(null); + expect(await contract.getAddrMap7_1Value(addr)).toBe(null); + expect(await contract.getAddrMap7_2Value(addr)).toBe(null); + expect(await contract.getAddrMap7_3Value(addr)).toBe(null); + expect(await contract.getAddrMap7_4Value(addr)).toBe(null); + expect(await contract.getAddrMap7_5Value(addr)).toBe(null); + expect(await contract.getAddrMap7_6Value(addr)).toBe(null); } } catch (e) { if (e instanceof ComputeError) { @@ -302,4 +527,4 @@ describe('feature-map', () => { throw e; } }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-masterchain.spec.ts b/src/test/feature-masterchain.spec.ts index 39af00255..29d80be48 100644 --- a/src/test/feature-masterchain.spec.ts +++ b/src/test/feature-masterchain.spec.ts @@ -1,10 +1,10 @@ -import { Address, beginCell, toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { MasterchainTester } from './features/output/masterchain_MasterchainTester'; -import { MasterchainTester as EnabledTester } from './features/output/masterchain-allow_MasterchainTester'; +import { Address, beginCell, toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { MasterchainTester } from "./features/output/masterchain_MasterchainTester"; +import { MasterchainTester as EnabledTester } from "./features/output/masterchain-allow_MasterchainTester"; -describe('feature-masterchain', () => { +describe("feature-masterchain", () => { beforeEach(() => { __DANGER_resetNodeId(); }); @@ -13,39 +13,39 @@ describe('feature-masterchain', () => { // Deployment and simple message receiving // - it('should deploy to the workchain', async () => { + it("should deploy to the workchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); const tracker = system.track(contract.address); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); expect(tracker.collect()).toMatchSnapshot(); }); - it('should deploy to the workchain when masterchain enabled', async () => { + it("should deploy to the workchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await EnabledTester.fromInit()); const tracker = system.track(contract.address); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); expect(tracker.collect()).toMatchSnapshot(); }); - it('should not deploy to the workchain from masterchain', async () => { + it("should not deploy to the workchain from masterchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure', -1); + const treasure = system.treasure("treasure", -1); const contract = system.open(await MasterchainTester.fromInit()); const tracker = system.track(contract.address); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); expect(tracker.collect()).toMatchSnapshot(); }); - it('should deploy to the workchain from masterchain when masterchain enabled', async () => { + it("should deploy to the workchain from masterchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure', -1); + const treasure = system.treasure("treasure", -1); const contract = system.open(await EnabledTester.fromInit()); const tracker = system.track(contract.address); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); expect(tracker.collect()).toMatchSnapshot(); }); @@ -54,125 +54,153 @@ describe('feature-masterchain', () => { // newAddress // - it('should create address for the workchain', async () => { + it("should create address for the workchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); await contract.getCreateAddress(0n, 0n); }); - it('should not create address for the masterchain', async () => { + it("should not create address for the masterchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); - await expect(contract.getCreateAddress(-1n, 0n)).rejects.toThrowError('Masterchain support is not enabled for this contract'); + await expect(contract.getCreateAddress(-1n, 0n)).rejects.toThrowError( + "Masterchain support is not enabled for this contract", + ); }); - it('should create address for the masterchain when masterchain enabled', async () => { + it("should create address for the masterchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await EnabledTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); await contract.getCreateAddress(-1n, 0n); }); - it('should not create address for invalid workchain', async () => { + it("should not create address for invalid workchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); - await expect(contract.getCreateAddress(10n, 0n)).rejects.toThrowError('Invalid address'); + await expect(contract.getCreateAddress(10n, 0n)).rejects.toThrowError( + "Invalid address", + ); }); // // loadAddress // - it('should load address for the workchain', async () => { + it("should load address for the workchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(0, Buffer.alloc(32, 0)); - expect((await contract.getParseAddress(beginCell().storeAddress(addr).endCell())).equals(addr)).toBe(true); + expect( + ( + await contract.getParseAddress( + beginCell().storeAddress(addr).endCell(), + ) + ).equals(addr), + ).toBe(true); }); - it('should not load address for the masterchain', async () => { + it("should not load address for the masterchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(-1, Buffer.alloc(32, 0)); - expect(contract.getParseAddress(beginCell().storeAddress(addr).endCell())).rejects.toThrowError('Masterchain support is not enabled for this contract'); + expect( + contract.getParseAddress(beginCell().storeAddress(addr).endCell()), + ).rejects.toThrowError( + "Masterchain support is not enabled for this contract", + ); }); - it('should load address for the workchain when masterchain enabled', async () => { + it("should load address for the workchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await EnabledTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(0, Buffer.alloc(32, 0)); - expect((await contract.getParseAddress(beginCell().storeAddress(addr).endCell())).equals(addr)).toBe(true); + expect( + ( + await contract.getParseAddress( + beginCell().storeAddress(addr).endCell(), + ) + ).equals(addr), + ).toBe(true); }); - it('should load address for the masterchain when masterchain enabled', async () => { + it("should load address for the masterchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await EnabledTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(-1, Buffer.alloc(32, 0)); - expect((await contract.getParseAddress(beginCell().storeAddress(addr).endCell())).equals(addr)).toBe(true); + expect( + ( + await contract.getParseAddress( + beginCell().storeAddress(addr).endCell(), + ) + ).equals(addr), + ).toBe(true); }); // // argument of get method // - it('should handle address in get argument for the workchain', async () => { + it("should handle address in get argument for the workchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(0, Buffer.alloc(32, 0)); await contract.getSerializeAddress(addr); }); - it('should not handle address in get argument for the masterchain', async () => { + it("should not handle address in get argument for the masterchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(-1, Buffer.alloc(32, 0)); - expect(contract.getSerializeAddress(addr)).rejects.toThrowError('Masterchain support is not enabled for this contract'); + expect(contract.getSerializeAddress(addr)).rejects.toThrowError( + "Masterchain support is not enabled for this contract", + ); }); - it('should handle address in get argument for the workchain when masterchain enabled', async () => { + it("should handle address in get argument for the workchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await EnabledTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(0, Buffer.alloc(32, 0)); await contract.getSerializeAddress(addr); }); - it('should handle address in get argument for the masterchain when masterchain enabled', async () => { + it("should handle address in get argument for the masterchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await EnabledTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(-1, Buffer.alloc(32, 0)); await contract.getSerializeAddress(addr); @@ -182,46 +210,78 @@ describe('feature-masterchain', () => { // argument of get method in struct // - it('should handle address in get argument struct for the workchain', async () => { + it("should handle address in get argument struct for the workchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(0, Buffer.alloc(32, 0)); - await contract.getHandleStruct({ $$type: 'TestMessage', address: addr, address2: null }); - await contract.getHandleStruct({ $$type: 'TestMessage', address: addr, address2: addr }); + await contract.getHandleStruct({ + $$type: "TestMessage", + address: addr, + address2: null, + }); + await contract.getHandleStruct({ + $$type: "TestMessage", + address: addr, + address2: addr, + }); }); - it('should not handle address in get argument struct for the masterchain', async () => { + it("should not handle address in get argument struct for the masterchain", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MasterchainTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(-1, Buffer.alloc(32, 0)); const addr2 = new Address(0, Buffer.alloc(32, 0)); - expect(contract.getHandleStruct({ $$type: 'TestMessage', address: addr, address2: null })).rejects.toThrowError('Masterchain support is not enabled for this contract'); - expect(contract.getHandleStruct({ $$type: 'TestMessage', address: addr2, address2: addr })).rejects.toThrowError('Masterchain support is not enabled for this contract'); + expect( + contract.getHandleStruct({ + $$type: "TestMessage", + address: addr, + address2: null, + }), + ).rejects.toThrowError( + "Masterchain support is not enabled for this contract", + ); + expect( + contract.getHandleStruct({ + $$type: "TestMessage", + address: addr2, + address2: addr, + }), + ).rejects.toThrowError( + "Masterchain support is not enabled for this contract", + ); }); - it('should handle address in get argument struct for the workchain when masterchain enabled', async () => { + it("should handle address in get argument struct for the workchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await EnabledTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(0, Buffer.alloc(32, 0)); - await contract.getHandleStruct({ $$type: 'TestMessage', address: addr, address2: addr }); + await contract.getHandleStruct({ + $$type: "TestMessage", + address: addr, + address2: addr, + }); }); - it('should handle address in get argument struct for the masterchain when masterchain enabled', async () => { + it("should handle address in get argument struct for the masterchain when masterchain enabled", async () => { const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await EnabledTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, "Deploy"); + await contract.send(treasure, { value: toNano("10") }, "Deploy"); await system.run(); const addr = new Address(-1, Buffer.alloc(32, 0)); - await contract.getHandleStruct({ $$type: 'TestMessage', address: addr, address2: addr }); + await contract.getHandleStruct({ + $$type: "TestMessage", + address: addr, + address2: addr, + }); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-math.spec.ts b/src/test/feature-math.spec.ts index 740df83b4..981d80b58 100644 --- a/src/test/feature-math.spec.ts +++ b/src/test/feature-math.spec.ts @@ -1,19 +1,19 @@ -import { beginCell, Dictionary, toNano } from '@ton/core'; -import { ContractSystem, randomAddress } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { MathTester } from './features/output/math_MathTester'; +import { beginCell, Dictionary, toNano } from "@ton/core"; +import { ContractSystem, randomAddress } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { MathTester } from "./features/output/math_MathTester"; -describe('feature-math', () => { +describe("feature-math", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should perform math operations correctly', async () => { + it("should perform math operations correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await MathTester.fromInit()); - const addressA = randomAddress('a'); - const addressB = randomAddress('b'); + const addressA = randomAddress("a"); + const addressB = randomAddress("b"); const cellA = beginCell().storeUint(0, 32).endCell(); const cellB = beginCell().storeUint(1, 32).endCell(); const sliceA = beginCell() @@ -24,14 +24,14 @@ describe('feature-math', () => { .storeBit(1) .storeRef(beginCell().storeBit(1).endCell()) .endCell(); - const stringA = 'foo'; - const stringB = 'bar'; + const stringA = "foo"; + const stringB = "bar"; const dictA = Dictionary.empty().set(0n, 0n); const dictB = Dictionary.empty().set(0n, 2n); await contract.send( treasure, - { value: toNano('10') }, - { $$type: 'Deploy', queryId: 0n } + { value: toNano("10") }, + { $$type: "Deploy", queryId: 0n }, ); await system.run(); @@ -342,14 +342,16 @@ describe('feature-math', () => { // Test advanced math operations for (let num = 1n; num <= 100n; num++) { expect(await contract.getLog2(num)).toBe( - BigInt(Math.floor(Math.log2(Number(num)))) + BigInt(Math.floor(Math.log2(Number(num)))), ); } for (let num = 1n; num <= 10n; num++) { for (let base = 2n; base <= 10; base++) { const logarithm = BigInt( - Math.floor(Math.log2(Number(num)) / Math.log2(Number(base))) + Math.floor( + Math.log2(Number(num)) / Math.log2(Number(base)), + ), ); expect(await contract.getLog(num, base)).toBe(logarithm); } @@ -367,7 +369,7 @@ describe('feature-math', () => { for (let num = maxint - 10n; num <= maxint; num++) { for (let base = 2; base <= 10; base++) { expect(await contract.getLog(num, BigInt(base))).toBe( - BigInt(num.toString(base).length - 1) + BigInt(num.toString(base).length - 1), ); } } @@ -382,7 +384,7 @@ describe('feature-math', () => { for (let num = maxint / 2n - 5n; num <= maxint / 2n + 5n; num++) { for (let base = 2; base <= 10; base++) { expect(await contract.getLog(num, BigInt(base))).toBe( - BigInt(num.toString(base).length - 1) + BigInt(num.toString(base).length - 1), ); } } diff --git a/src/test/feature-optionals.spec.ts b/src/test/feature-optionals.spec.ts index baf91d377..036827b9b 100644 --- a/src/test/feature-optionals.spec.ts +++ b/src/test/feature-optionals.spec.ts @@ -1,11 +1,14 @@ -import { randomAddress } from './utils/randomAddress'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { ContractWithOptionals, SomeGenericStruct, StructWithOptionals } from './features/output/optionals_ContractWithOptionals'; -import { Address, beginCell, Cell, toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; +import { randomAddress } from "./utils/randomAddress"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { + ContractWithOptionals, + SomeGenericStruct, + StructWithOptionals, +} from "./features/output/optionals_ContractWithOptionals"; +import { Address, beginCell, Cell, toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; function strEq2(a: StructWithOptionals | null, b: StructWithOptionals | null) { - // Null checks if (a === null && b === null) { return true; @@ -103,83 +106,131 @@ function strEq(a: SomeGenericStruct | null, b: SomeGenericStruct | null) { return true; } -describe('features', () => { +describe("features", () => { beforeEach(() => { __DANGER_resetNodeId(); }); const eV = { - $$type: 'SomeGenericStruct' as const, + $$type: "SomeGenericStruct" as const, value1: 1n, value2: 2n, value3: 3n, value4: 4n, - value5: 5n + value5: 5n, }; const ev2: StructWithOptionals = { - $$type: 'StructWithOptionals' as const, + $$type: "StructWithOptionals" as const, a: 1n, b: true, c: null, - d: randomAddress(0, 'address1'), + d: randomAddress(0, "address1"), e: eV, }; const ev3: StructWithOptionals = { - $$type: 'StructWithOptionals' as const, + $$type: "StructWithOptionals" as const, a: 1n, b: true, c: null, d: null, e: null, }; - const cases: { a: bigint | null, b: boolean | null, c: Cell | null, d: Address | null, e: SomeGenericStruct | null, f: StructWithOptionals | null }[] = []; + const cases: { + a: bigint | null; + b: boolean | null; + c: Cell | null; + d: Address | null; + e: SomeGenericStruct | null; + f: StructWithOptionals | null; + }[] = []; cases.push({ a: null, b: null, c: null, d: null, e: null, f: null }); - cases.push({ a: 10n, b: true, c: null, d: randomAddress(0, 'address1'), e: eV, f: ev2 }); - cases.push({ a: -10n, b: false, c: null, d: randomAddress(-1, 'address2'), e: null, f: ev2 }); - cases.push({ a: -10n, b: false, c: beginCell().storeAddress(randomAddress(0, 'asdasd')).endCell(), d: randomAddress(-1, 'address2'), e: null, f: ev3 }); + cases.push({ + a: 10n, + b: true, + c: null, + d: randomAddress(0, "address1"), + e: eV, + f: ev2, + }); + cases.push({ + a: -10n, + b: false, + c: null, + d: randomAddress(-1, "address2"), + e: null, + f: ev2, + }); + cases.push({ + a: -10n, + b: false, + c: beginCell().storeAddress(randomAddress(0, "asdasd")).endCell(), + d: randomAddress(-1, "address2"), + e: null, + f: ev3, + }); for (let i = 0; i < cases.length; i++) { - - it('should handle case #' + i, async () => { + it("should handle case #" + i, async () => { const cs = cases[i]; // Init contract const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); - const contract = system.open(await ContractWithOptionals.fromInit(cs.a, cs.b, cs.c, cs.d, cs.e, cs.f)); - await contract.send(treasure, { value: toNano('10') }, null); + const treasure = system.treasure("treasure"); + const contract = system.open( + await ContractWithOptionals.fromInit( + cs.a, + cs.b, + cs.c, + cs.d, + cs.e, + cs.f, + ), + ); + await contract.send(treasure, { value: toNano("10") }, null); await system.run(); // Check inputs if (cs.a !== null) { expect(await contract.getNotNullA()).toBe(cs.a); } else { - await expect(() => contract.getNotNullA()).rejects.toThrowError('Null reference exception'); + await expect(() => contract.getNotNullA()).rejects.toThrowError( + "Null reference exception", + ); } if (cs.b !== null) { expect((await contract.getNotNullB()) === cs.b).toBe(true); } else { - await expect(() => contract.getNotNullB()).rejects.toThrowError('Null reference exception'); + await expect(() => contract.getNotNullB()).rejects.toThrowError( + "Null reference exception", + ); } if (cs.c !== null) { expect((await contract.getNotNullC()).equals(cs.c)).toBe(true); } else { - await expect(() => contract.getNotNullC()).rejects.toThrowError('Null reference exception'); + await expect(() => contract.getNotNullC()).rejects.toThrowError( + "Null reference exception", + ); } if (cs.d !== null) { expect((await contract.getNotNullD()).equals(cs.d)).toBe(true); } else { - await expect(() => contract.getNotNullD()).rejects.toThrowError('Null reference exception'); + await expect(() => contract.getNotNullD()).rejects.toThrowError( + "Null reference exception", + ); } if (cs.e !== null) { - expect(strEq((await contract.getNotNullE()), cs.e)).toBe(true); + expect(strEq(await contract.getNotNullE(), cs.e)).toBe(true); } else { - await expect(() => contract.getNotNullE()).rejects.toThrowError('Null reference exception'); + await expect(() => contract.getNotNullE()).rejects.toThrowError( + "Null reference exception", + ); } if (cs.f !== null) { - expect(strEq2((await contract.getNotNullF()), cs.f)).toBe(true); + expect(strEq2(await contract.getNotNullF(), cs.f)).toBe(true); } else { - await expect(() => contract.getNotNullF()).rejects.toThrowError('Null reference exception'); + await expect(() => contract.getNotNullF()).rejects.toThrowError( + "Null reference exception", + ); } // Check inputs @@ -189,6 +240,6 @@ describe('features', () => { expect(await contract.getIsNotNullD()).toBe(cs.d !== null); expect(await contract.getIsNotNullE()).toBe(cs.e !== null); expect(await contract.getIsNotNullF()).toBe(cs.f !== null); - }) + }); } -}); \ No newline at end of file +}); diff --git a/src/test/feature-ordering.spec.ts b/src/test/feature-ordering.spec.ts index a16596139..54a8a3c97 100644 --- a/src/test/feature-ordering.spec.ts +++ b/src/test/feature-ordering.spec.ts @@ -1,19 +1,22 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { A } from './features/output/ordering_A'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { A } from "./features/output/ordering_A"; -describe('feature-ordering', () => { +describe("feature-ordering", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should implement constructor ordering correctly', async () => { - + it("should implement constructor ordering correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await A.fromInit(treasure.address)); - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Deploy', queryId: 0n }); + await contract.send( + treasure, + { value: toNano("10") }, + { $$type: "Deploy", queryId: 0n }, + ); await system.run(); // Check order @@ -22,4 +25,4 @@ describe('feature-ordering', () => { expect(res.v2).toBe(2n); expect(res.v3).toBe(1n); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-random.spec.ts b/src/test/feature-random.spec.ts index 1bab2ea85..774da3bea 100644 --- a/src/test/feature-random.spec.ts +++ b/src/test/feature-random.spec.ts @@ -1,26 +1,31 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { RandomContract } from './features/output/random_RandomContract'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { RandomContract } from "./features/output/random_RandomContract"; -describe('feature-random', () => { +describe("feature-random", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should implement random correctly', async () => { - + it("should implement random correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await RandomContract.fromInit()); - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Deploy', queryId: 0n }); + await contract.send( + treasure, + { value: toNano("10") }, + { $$type: "Deploy", queryId: 0n }, + ); await system.run(); // Check random // NOTE: This values are generated by emulator and deterministic // this values also ensure that randomize_lt was called // since without it it would be different - expect(await contract.getRandomInt()).toBe(12029244659758160506229899028078921673473662712472979861368849515350569944843n); + expect(await contract.getRandomInt()).toBe( + 12029244659758160506229899028078921673473662712472979861368849515350569944843n, + ); expect(await contract.getRandom(0n, 10000n)).toBe(1038n); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-send.spec.ts b/src/test/feature-send.spec.ts index 0c85c88b5..28ce0def5 100644 --- a/src/test/feature-send.spec.ts +++ b/src/test/feature-send.spec.ts @@ -1,40 +1,49 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { SendTester } from './features/output/send_SendTester'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { SendTester } from "./features/output/send_SendTester"; -describe('feature-send', () => { +describe("feature-send", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should send reply correctly', async () => { - + it("should send reply correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await SendTester.fromInit()); const tracker = system.track(contract.address); - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Deploy', queryId: 0n }); + await contract.send( + treasure, + { value: toNano("10") }, + { $$type: "Deploy", queryId: 0n }, + ); await system.run(); expect(tracker.collect()).toMatchSnapshot(); - await contract.send(treasure, { value: toNano('10') }, 'Hello'); + await contract.send(treasure, { value: toNano("10") }, "Hello"); await system.run(); expect(tracker.collect()).toMatchSnapshot(); }); - it('should bounce on unknown message', async () => { - + it("should bounce on unknown message", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await SendTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, { $$type: 'Deploy', queryId: 0n }); + await contract.send( + treasure, + { value: toNano("10") }, + { $$type: "Deploy", queryId: 0n }, + ); await system.run(); // Test const tracker = system.track(contract); - await system.provider(contract).internal(treasure, { value: toNano('10'), body: 'Unknown string' }); + await system.provider(contract).internal(treasure, { + value: toNano("10"), + body: "Unknown string", + }); await system.run(); expect(tracker.collect()).toMatchSnapshot(); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-serialization.spec.ts b/src/test/feature-serialization.spec.ts index 9bf0fdbf0..a25f38d77 100644 --- a/src/test/feature-serialization.spec.ts +++ b/src/test/feature-serialization.spec.ts @@ -1,20 +1,29 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { SerializationTester2 } from './features/output/serialization-2_SerializationTester2'; -import { SerializationTester } from './features/output/serialization_SerializationTester'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { SerializationTester2 } from "./features/output/serialization-2_SerializationTester2"; +import { SerializationTester } from "./features/output/serialization_SerializationTester"; -describe('feature-serialization', () => { +describe("feature-serialization", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - // // Simple case // { - const cases: { a: bigint, b: bigint, c: bigint, d: bigint, e: bigint, f: bigint, g: bigint, h: bigint, i: bigint }[] = []; + const cases: { + a: bigint; + b: bigint; + c: bigint; + d: bigint; + e: bigint; + f: bigint; + g: bigint; + h: bigint; + i: bigint; + }[] = []; cases.push({ a: 1n, b: 2n, @@ -24,19 +33,30 @@ describe('feature-serialization', () => { f: 6n, g: 7n, h: 8n, - i: 9n + i: 9n, }); for (let i = 0; i < cases.length; i++) { - - it('should handle case #' + i, async () => { + it("should handle case #" + i, async () => { const cs = cases[i]; // Init contract const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); - const contract = system.open(await SerializationTester.fromInit(cs.a, cs.b, cs.c, cs.d, cs.e, cs.f, cs.g, cs.h, cs.i)); - await contract.send(treasure, { value: toNano('10') }, null); + const treasure = system.treasure("treasure"); + const contract = system.open( + await SerializationTester.fromInit( + cs.a, + cs.b, + cs.c, + cs.d, + cs.e, + cs.f, + cs.g, + cs.h, + cs.i, + ), + ); + await contract.send(treasure, { value: toNano("10") }, null); await system.run(); // Check inputs @@ -49,7 +69,7 @@ describe('feature-serialization', () => { expect(await contract.getGetG()).toBe(cs.g); expect(await contract.getGetH()).toBe(cs.h); expect(await contract.getGetI()).toBe(cs.i); - }) + }); } } @@ -57,35 +77,54 @@ describe('feature-serialization', () => { // Cases with references // { - const cases: { a: { $$type: 'Vars', a: bigint, b: bigint, c: bigint, d: bigint, e: bigint }, b: { $$type: 'Vars', a: bigint, b: bigint, c: bigint, d: bigint, e: bigint } }[] = []; + const cases: { + a: { + $$type: "Vars"; + a: bigint; + b: bigint; + c: bigint; + d: bigint; + e: bigint; + }; + b: { + $$type: "Vars"; + a: bigint; + b: bigint; + c: bigint; + d: bigint; + e: bigint; + }; + }[] = []; cases.push({ a: { - $$type: 'Vars', + $$type: "Vars", a: 1n, b: 2n, c: 3n, d: 4n, - e: 5n + e: 5n, }, b: { - $$type: 'Vars', + $$type: "Vars", a: 6n, b: 7n, c: 8n, d: 9n, - e: 10n - } - }) + e: 10n, + }, + }); for (let i = 0; i < cases.length; i++) { - it('should handle case-2 #' + i, async () => { + it("should handle case-2 #" + i, async () => { const cs = cases[i]; // Init contract const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); - const contract = system.open(await SerializationTester2.fromInit(cs.a, cs.b)); - await contract.send(treasure, { value: toNano('10') }, null); + const treasure = system.treasure("treasure"); + const contract = system.open( + await SerializationTester2.fromInit(cs.a, cs.b), + ); + await contract.send(treasure, { value: toNano("10") }, null); await system.run(); // Checl values @@ -119,4 +158,4 @@ describe('feature-serialization', () => { }); } } -}); \ No newline at end of file +}); diff --git a/src/test/feature-strings.spec.ts b/src/test/feature-strings.spec.ts index 5d70cec1e..92cbebd8a 100644 --- a/src/test/feature-strings.spec.ts +++ b/src/test/feature-strings.spec.ts @@ -1,66 +1,99 @@ -import { beginCell, toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { StringsTester } from './features/output/strings_StringsTester'; +import { beginCell, toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { StringsTester } from "./features/output/strings_StringsTester"; -describe('feature-strings', () => { +describe("feature-strings", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should implement strings correctly', async () => { - + it("should implement strings correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await StringsTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, null); + await contract.send(treasure, { value: toNano("10") }, null); await system.run(); // Check methods - expect(await contract.getConstantString()).toBe('test string'); - expect(await contract.getConstantStringUnicode()).toBe('привет мир 👀'); - const l = 'привет мир 👀 привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀'; + expect(await contract.getConstantString()).toBe("test string"); + expect(await contract.getConstantStringUnicode()).toBe("привет мир 👀"); + const l = + "привет мир 👀 привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀"; expect(await contract.getConstantStringUnicodeLong()).toBe(l); - expect((await contract.getDynamicStringCell()).equals(beginCell().storeStringTail('Hello!').endCell())).toBe(true); - expect((await contract.getDynamicStringCell2()).equals(beginCell().storeStringTail('Hello, World!').endCell())).toBe(true); - expect((await contract.getDynamicCommentCell()).equals(beginCell().storeUint(0, 32).storeStringTail('Something something world!').endCell())).toBe(true); - const l2 = 'Hello!привет мир 👀 привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀'; - expect((await contract.getDynamicCommentCellLarge()).equals(beginCell().storeStringTail(l2).endCell())).toBe(true); - expect((await contract.getDynamicCommentStringLarge())).toEqual(l2); - expect((await contract.getStringWithNumber())).toEqual('Hello, your balance: 123'); - expect((await contract.getStringWithLargeNumber())).toEqual('Hello, your balance: 1000000000000000000000000000000000000000000000000000000000000'); - expect((await contract.getStringWithNegativeNumber())).toEqual('Hello, your balance: -123'); - expect((await contract.getStringWithFloat())).toEqual('9.5'); + expect( + (await contract.getDynamicStringCell()).equals( + beginCell().storeStringTail("Hello!").endCell(), + ), + ).toBe(true); + expect( + (await contract.getDynamicStringCell2()).equals( + beginCell().storeStringTail("Hello, World!").endCell(), + ), + ).toBe(true); + expect( + (await contract.getDynamicCommentCell()).equals( + beginCell() + .storeUint(0, 32) + .storeStringTail("Something something world!") + .endCell(), + ), + ).toBe(true); + const l2 = + "Hello!привет мир 👀 привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀привет мир 👀"; + expect( + (await contract.getDynamicCommentCellLarge()).equals( + beginCell().storeStringTail(l2).endCell(), + ), + ).toBe(true); + expect(await contract.getDynamicCommentStringLarge()).toEqual(l2); + expect(await contract.getStringWithNumber()).toEqual( + "Hello, your balance: 123", + ); + expect(await contract.getStringWithLargeNumber()).toEqual( + "Hello, your balance: 1000000000000000000000000000000000000000000000000000000000000", + ); + expect(await contract.getStringWithNegativeNumber()).toEqual( + "Hello, your balance: -123", + ); + expect(await contract.getStringWithFloat()).toEqual("9.5"); const base = await contract.getBase64(); - expect(base.beginParse().loadBuffer(base.bits.length / 8).toString()).toEqual('Many hands make light work.'); + expect( + base + .beginParse() + .loadBuffer(base.bits.length / 8) + .toString(), + ).toEqual("Many hands make light work."); const b64cases = [ - 'SGVsbG8gV29ybGQ=', - 'li7dzDacuo67Jg7mtqEm2TRuOMU=', - 'FKIhdgaG5LGKiEtF1vHy4f3y700zaD6QwDS3IrNVGzNp2rY+1LFWTK6D44AyiC1n8uWz1itkYMZF0/aKDK0Yjg==', - 'AA==' + "SGVsbG8gV29ybGQ=", + "li7dzDacuo67Jg7mtqEm2TRuOMU=", + "FKIhdgaG5LGKiEtF1vHy4f3y700zaD6QwDS3IrNVGzNp2rY+1LFWTK6D44AyiC1n8uWz1itkYMZF0/aKDK0Yjg==", + "AA==", ]; for (const b of b64cases) { - const s = Buffer.from(b, 'base64'); + const s = Buffer.from(b, "base64"); const r = await contract.getProcessBase64(b); const d = r.beginParse().loadBuffer(r.bits.length / 8); - expect(d.toString('hex')).toEqual(s.toString('hex')); + expect(d.toString("hex")).toEqual(s.toString("hex")); } expect(await contract.getStringWithEscapedChars1()).toBe( - "test \n \n \\ \\\n \"string\"" + 'test \n \n \\ \\\n "string"', ); expect(await contract.getStringWithEscapedChars2()).toEqual( - "test \n test \t test \r test \b test \f test \" test ' test \\ \\\\ \"_\" \"\" test" + 'test \n test \t test \r test \b test \f test " test \' test \\ \\\\ "_" "" test', ); expect(await contract.getStringWithEscapedChars3()).toEqual( - "test \\n test \\t test \\r test \\\\b\b test \\f test \\\" test \\' test \v \v \\\\ \\\\\\\\ \\\"_\\\" \\\"\\\" test" + 'test \\n test \\t test \\r test \\\\b\b test \\f test \\" test \\\' test \v \v \\\\ \\\\\\\\ \\"_\\" \\"\\" test', ); expect(await contract.getStringWithEscapedChars4()).toEqual( - "\u{2028}\u{2029} \u0044 \x41\x42\x43" + "\u{2028}\u{2029} \u0044 \x41\x42\x43", ); - expect(await contract.getStringWithAddress()).toEqual('EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN'); + expect(await contract.getStringWithAddress()).toEqual( + "EQBKgXCNLPexWhs2L79kiARR1phGH1LwXxRbNsCFF9doc2lN", + ); }); -}); \ No newline at end of file +}); diff --git a/src/test/feature-ternary.spec.ts b/src/test/feature-ternary.spec.ts index 5e42938e1..11ee59eeb 100644 --- a/src/test/feature-ternary.spec.ts +++ b/src/test/feature-ternary.spec.ts @@ -1,19 +1,18 @@ -import { toNano } from '@ton/core'; -import { ContractSystem } from '@tact-lang/emulator'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { TernaryTester } from './features/output/ternary_TernaryTester'; +import { toNano } from "@ton/core"; +import { ContractSystem } from "@tact-lang/emulator"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { TernaryTester } from "./features/output/ternary_TernaryTester"; -describe('feature-ternary', () => { +describe("feature-ternary", () => { beforeEach(() => { __DANGER_resetNodeId(); }); - it('should implement ternary operator correctly', async () => { - + it("should implement ternary operator correctly", async () => { // Init const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await TernaryTester.fromInit()); - await contract.send(treasure, { value: toNano('10') }, null); + await contract.send(treasure, { value: toNano("10") }, null); await system.run(); // Check methods @@ -48,4 +47,4 @@ describe('feature-ternary', () => { expect(await contract.getTest10(0n)).toEqual(45n); expect(await contract.getTest10(42n)).toEqual(45n); }); -}); \ No newline at end of file +}); diff --git a/src/test/integration.spec.ts b/src/test/integration.spec.ts index 770b503c3..12895f0c9 100644 --- a/src/test/integration.spec.ts +++ b/src/test/integration.spec.ts @@ -1,29 +1,34 @@ -import fs from 'fs'; -import { CompilerContext } from '../context'; -import { __DANGER_resetNodeId } from '../grammar/ast'; -import { loadCases } from '../utils/loadCases'; -import { precompile } from '../pipeline/precompile'; -import { compile } from '../pipeline/compile'; -import { getContracts } from '../types/resolveDescriptors'; -import { createNodeFileSystem } from '../vfs/createNodeFileSystem'; -import { createVirtualFileSystem } from '../vfs/createVirtualFileSystem'; -import files from '../imports/stdlib'; +import fs from "fs"; +import { CompilerContext } from "../context"; +import { __DANGER_resetNodeId } from "../grammar/ast"; +import { loadCases } from "../utils/loadCases"; +import { precompile } from "../pipeline/precompile"; +import { compile } from "../pipeline/compile"; +import { getContracts } from "../types/resolveDescriptors"; +import { createNodeFileSystem } from "../vfs/createNodeFileSystem"; +import { createVirtualFileSystem } from "../vfs/createVirtualFileSystem"; +import files from "../imports/stdlib"; -describe('integration', () => { +describe("integration", () => { beforeEach(() => { __DANGER_resetNodeId(); }); for (const r of loadCases(__dirname + "/contracts/")) { - it('should resolve expressions for ' + r.name, async () => { + it("should resolve expressions for " + r.name, async () => { let ctx = new CompilerContext({ shared: {} }); const project = createNodeFileSystem(__dirname + "/contracts/"); - const stdlib = createVirtualFileSystem('@stdlib', files); - ctx = precompile(ctx, project, stdlib, r.name + '.tact'); + const stdlib = createVirtualFileSystem("@stdlib", files); + ctx = precompile(ctx, project, stdlib, r.name + ".tact"); const contract = getContracts(ctx)[0]; - const res = await compile(ctx, contract, r.name + '_' + contract); + const res = await compile(ctx, contract, r.name + "_" + contract); for (const f of res.output.files) { - expect(f.code).toEqual(fs.readFileSync(__dirname + "/contracts/output/" + f.name, 'utf8')); + expect(f.code).toEqual( + fs.readFileSync( + __dirname + "/contracts/output/" + f.name, + "utf8", + ), + ); } }); } -}); \ No newline at end of file +}); diff --git a/src/test/stdlib.spec.ts b/src/test/stdlib.spec.ts index 174f311b3..ecc34711d 100644 --- a/src/test/stdlib.spec.ts +++ b/src/test/stdlib.spec.ts @@ -1,28 +1,27 @@ import { beginCell, toNano } from "@ton/core"; import { ContractSystem } from "@tact-lang/emulator"; -import { StdlibTest } from './contracts/output/stdlib_StdlibTest'; - -describe('stdlib', () => { - it('should execute slice methods correctly', async () => { +import { StdlibTest } from "./contracts/output/stdlib_StdlibTest"; +describe("stdlib", () => { + it("should execute slice methods correctly", async () => { // Create and deploy contract const system = await ContractSystem.create(); - const treasure = system.treasure('treasure'); + const treasure = system.treasure("treasure"); const contract = system.open(await StdlibTest.fromInit()); - await contract.send(treasure, { value: toNano('10') }, null); + await contract.send(treasure, { value: toNano("10") }, null); await system.run(); - + // Execute slice methods const slice = beginCell() .storeBit(1) .storeBit(1) .storeRef(beginCell().storeBit(1).endCell()) .endCell(); - const bits = (await contract.getSliceBits(slice)); - const refs = (await contract.getSliceRefs(slice)); - const empty = (await contract.getSliceEmpty(slice)); + const bits = await contract.getSliceBits(slice); + const refs = await contract.getSliceRefs(slice); + const empty = await contract.getSliceEmpty(slice); expect(bits).toBe(2n); expect(refs).toBe(1n); expect(empty).toBe(false); }); -}); \ No newline at end of file +}); diff --git a/src/types/createTLBType.ts b/src/types/createTLBType.ts index 6a32bb685..8cdd45473 100644 --- a/src/types/createTLBType.ts +++ b/src/types/createTLBType.ts @@ -1,101 +1,131 @@ import { ABIField, beginCell } from "@ton/core"; -import * as cs from 'change-case'; +import * as cs from "change-case"; import { sha256_sync } from "@ton/crypto"; -function createTypeFormat(type: string, format: string | number | boolean | null) { - if (type === 'int') { - if (typeof format === 'number') { +function createTypeFormat( + type: string, + format: string | number | boolean | null, +) { + if (type === "int") { + if (typeof format === "number") { return `int${format}`; } else if (format !== null) { - throw Error('Unsupported int format ' + format); + throw Error("Unsupported int format " + format); } return `int`; - } else if (type === 'uint') { - if (typeof format === 'number') { + } else if (type === "uint") { + if (typeof format === "number") { return `uint${format}`; - } else if (format === 'coins') { + } else if (format === "coins") { return `coins`; } else if (format !== null) { - throw Error('Unsupported uint format ' + format); + throw Error("Unsupported uint format " + format); } return `uint`; - } else if (type === 'bool') { + } else if (type === "bool") { if (format !== null) { - throw Error('Unsupported bool format ' + format); + throw Error("Unsupported bool format " + format); } - return 'bool'; - } else if (type === 'address') { + return "bool"; + } else if (type === "address") { if (format !== null) { - throw Error('Unsupported address format ' + format); + throw Error("Unsupported address format " + format); } - return 'address'; - } else if (type === 'cell') { - if (format === 'remainder') { - return 'remainder'; - } else if (format === 'ref') { - return '^cell'; - } if (format !== null) { - throw Error('Unsupported cell format ' + format); + return "address"; + } else if (type === "cell") { + if (format === "remainder") { + return "remainder"; + } else if (format === "ref") { + return "^cell"; } - return '^cell'; - } else if (type === 'slice') { - if (format === 'remainder') { - return 'remainder'; - } else if (format === 'ref') { - return '^slice'; + if (format !== null) { + throw Error("Unsupported cell format " + format); + } + return "^cell"; + } else if (type === "slice") { + if (format === "remainder") { + return "remainder"; + } else if (format === "ref") { + return "^slice"; } else if (format !== null) { - throw Error('Unsupported cell format ' + format); + throw Error("Unsupported cell format " + format); } - return '^slice'; - } else if (type === 'fixed-bytes') { - if (typeof format === 'number') { + return "^slice"; + } else if (type === "fixed-bytes") { + if (typeof format === "number") { return `fixed_bytes${format}`; } else if (format !== null) { - throw Error('Unsupported fixed-bytes format ' + format); + throw Error("Unsupported fixed-bytes format " + format); } - throw Error('Missing fixed-bytes format'); + throw Error("Missing fixed-bytes format"); } // Struct types - if (format === 'ref') { + if (format === "ref") { return `^${type}`; } else if (format !== null) { - throw Error('Unsupported struct format ' + format); + throw Error("Unsupported struct format " + format); } return type; } function createTLBField(src: ABIField) { - - if (src.type.kind === 'simple') { - let base = createTypeFormat(src.type.type, src.type.format ? src.type.format : null); + if (src.type.kind === "simple") { + let base = createTypeFormat( + src.type.type, + src.type.format ? src.type.format : null, + ); if (src.type.optional) { - base = 'Maybe ' + base; + base = "Maybe " + base; } - return src.name + ':' + base; + return src.name + ":" + base; } - if (src.type.kind === 'dict') { + if (src.type.kind === "dict") { if (src.type.format !== null && src.type.format !== undefined) { - throw Error('Unsupported map format ' + src.type.format); + throw Error("Unsupported map format " + src.type.format); } - const key = createTypeFormat(src.type.key, src.type.keyFormat ? src.type.keyFormat : null); - const value = createTypeFormat(src.type.value, src.type.valueFormat ? src.type.valueFormat : null); - return src.name + ':dict<' + key + ', ' + value + '>'; + const key = createTypeFormat( + src.type.key, + src.type.keyFormat ? src.type.keyFormat : null, + ); + const value = createTypeFormat( + src.type.value, + src.type.valueFormat ? src.type.valueFormat : null, + ); + return src.name + ":dict<" + key + ", " + value + ">"; } - throw Error('Unsupported ABI field'); + throw Error("Unsupported ABI field"); } -export function createTLBType(name: string, args: ABIField[], kind: 'struct' | 'message', knownHeader: number | null): { tlb: string, header: number | null } { - const fields = args.map(createTLBField).join(' '); - if (kind === 'struct') { - return { tlb: '_ ' + fields + ' = ' + name, header: null }; +export function createTLBType( + name: string, + args: ABIField[], + kind: "struct" | "message", + knownHeader: number | null, +): { tlb: string; header: number | null } { + const fields = args.map(createTLBField).join(" "); + if (kind === "struct") { + return { tlb: "_ " + fields + " = " + name, header: null }; } else { - const base = cs.snakeCase(name) + ' ' + fields + ' = ' + name; - const op = knownHeader !== null ? knownHeader : beginCell().storeBuffer(sha256_sync(base)).endCell().beginParse().loadUint(32); - const opText = beginCell().storeUint(op, 32).endCell().beginParse().loadBuffer(4).toString('hex'); - const res = cs.snakeCase(name) + '#' + opText + ' ' + fields + ' = ' + name; + const base = cs.snakeCase(name) + " " + fields + " = " + name; + const op = + knownHeader !== null + ? knownHeader + : beginCell() + .storeBuffer(sha256_sync(base)) + .endCell() + .beginParse() + .loadUint(32); + const opText = beginCell() + .storeUint(op, 32) + .endCell() + .beginParse() + .loadBuffer(4) + .toString("hex"); + const res = + cs.snakeCase(name) + "#" + opText + " " + fields + " = " + name; return { tlb: res, header: op }; } -} \ No newline at end of file +} diff --git a/src/types/getSupportedInterfaces.ts b/src/types/getSupportedInterfaces.ts index 726588925..90821af2c 100644 --- a/src/types/getSupportedInterfaces.ts +++ b/src/types/getSupportedInterfaces.ts @@ -2,20 +2,23 @@ import { enabledDebug, enabledMasterchain } from "../config/features"; import { CompilerContext } from "../context"; import { TypeDescription } from "./types"; -export function getSupportedInterfaces(type: TypeDescription, ctx: CompilerContext) { +export function getSupportedInterfaces( + type: TypeDescription, + ctx: CompilerContext, +) { const interfaces: string[] = []; - interfaces.push('org.ton.abi.ipfs.v0'); - interfaces.push('org.ton.deploy.lazy.v0'); + interfaces.push("org.ton.abi.ipfs.v0"); + interfaces.push("org.ton.deploy.lazy.v0"); if (enabledDebug(ctx)) { - interfaces.push('org.ton.debug.v0'); + interfaces.push("org.ton.debug.v0"); } if (!enabledMasterchain(ctx)) { - interfaces.push('org.ton.chain.workchain.v0'); + interfaces.push("org.ton.chain.workchain.v0"); } else { - interfaces.push('org.ton.chain.any.v0'); + interfaces.push("org.ton.chain.any.v0"); } for (let i = 0; i < type.interfaces.length; i++) { interfaces.push(type.interfaces[i]); } return interfaces; -} \ No newline at end of file +} diff --git a/src/types/isAssignable.ts b/src/types/isAssignable.ts index 4e2b6b2d2..6d2690050 100644 --- a/src/types/isAssignable.ts +++ b/src/types/isAssignable.ts @@ -1,10 +1,8 @@ import { TypeRef } from "./types"; export function isAssignable(src: TypeRef, to: TypeRef): boolean { - // If both are refs - if (src.kind === 'ref' && to.kind === 'ref') { - + if (src.kind === "ref" && to.kind === "ref") { // Can assign optional to non-optional if (!to.optional && src.optional) { return false; @@ -15,33 +13,38 @@ export function isAssignable(src: TypeRef, to: TypeRef): boolean { } // If both are maps - if (src.kind === 'map' && to.kind === 'map') { - return (src.key === to.key && src.value === to.value && src.keyAs === to.keyAs && src.valueAs === to.valueAs); + if (src.kind === "map" && to.kind === "map") { + return ( + src.key === to.key && + src.value === to.value && + src.keyAs === to.keyAs && + src.valueAs === to.valueAs + ); } // Bounced types - if (src.kind === 'ref_bounced' && to.kind === 'ref_bounced') { + if (src.kind === "ref_bounced" && to.kind === "ref_bounced") { return src.name === to.name; } // Allow assigning null to map - if (src.kind === 'null' && to.kind === 'map') { + if (src.kind === "null" && to.kind === "map") { return true; } // If either is void - if (src.kind === 'void' || to.kind === 'void') { + if (src.kind === "void" || to.kind === "void") { return false; // Void is not assignable } // Check null - if (src.kind === 'null' && to.kind === 'ref') { + if (src.kind === "null" && to.kind === "ref") { return to.optional; } - if (src.kind === 'null' && to.kind === 'null') { + if (src.kind === "null" && to.kind === "null") { return true; } // All other options are not assignable return false; -} \ No newline at end of file +} diff --git a/src/types/isRuntimeType.ts b/src/types/isRuntimeType.ts index d27e8ece4..fce53c651 100644 --- a/src/types/isRuntimeType.ts +++ b/src/types/isRuntimeType.ts @@ -1,11 +1,11 @@ import { TypeRef } from "./types"; export function isRuntimeType(src: TypeRef): boolean { - if (src.kind === 'null') { + if (src.kind === "null") { return true; } - if (src.kind === 'ref_bounced') { + if (src.kind === "ref_bounced") { return true; } return false; -} \ No newline at end of file +} diff --git a/src/types/resolveABITypeRef.ts b/src/types/resolveABITypeRef.ts index 7d9b4a83a..696cd74fd 100644 --- a/src/types/resolveABITypeRef.ts +++ b/src/types/resolveABITypeRef.ts @@ -2,129 +2,177 @@ import { ABITypeRef } from "@ton/core"; import { ASTField, ASTRef, throwError } from "../grammar/ast"; import { TypeRef } from "./types"; -type FormatDef = { [key: string]: { type: string, format: string | number } }; +type FormatDef = { [key: string]: { type: string; format: string | number } }; const intFormats: FormatDef = { - 'int8': { type: 'int', format: 8 }, - 'int16': { type: 'int', format: 16 }, - 'int32': { type: 'int', format: 32 }, - 'int64': { type: 'int', format: 64 }, - 'int128': { type: 'int', format: 128 }, - 'int256': { type: 'int', format: 256 }, + int8: { type: "int", format: 8 }, + int16: { type: "int", format: 16 }, + int32: { type: "int", format: 32 }, + int64: { type: "int", format: 64 }, + int128: { type: "int", format: 128 }, + int256: { type: "int", format: 256 }, - 'uint8': { type: 'uint', format: 8 }, - 'uint16': { type: 'uint', format: 16 }, - 'uint32': { type: 'uint', format: 32 }, - 'uint64': { type: 'uint', format: 64 }, - 'uint128': { type: 'uint', format: 128 }, - 'uint256': { type: 'uint', format: 256 }, + uint8: { type: "uint", format: 8 }, + uint16: { type: "uint", format: 16 }, + uint32: { type: "uint", format: 32 }, + uint64: { type: "uint", format: 64 }, + uint128: { type: "uint", format: 128 }, + uint256: { type: "uint", format: 256 }, - 'int257': { type: 'int', format: 257 }, - 'coins': { type: 'uint', format: 'coins' } + int257: { type: "int", format: 257 }, + coins: { type: "uint", format: "coins" }, }; const intMapFormats: FormatDef = { - 'int8': { type: 'int', format: 8 }, - 'int16': { type: 'int', format: 16 }, - 'int32': { type: 'int', format: 32 }, - 'int64': { type: 'int', format: 64 }, - 'int128': { type: 'int', format: 128 }, - 'int256': { type: 'int', format: 256 }, + int8: { type: "int", format: 8 }, + int16: { type: "int", format: 16 }, + int32: { type: "int", format: 32 }, + int64: { type: "int", format: 64 }, + int128: { type: "int", format: 128 }, + int256: { type: "int", format: 256 }, - 'uint8': { type: 'uint', format: 8 }, - 'uint16': { type: 'uint', format: 16 }, - 'uint32': { type: 'uint', format: 32 }, - 'uint64': { type: 'uint', format: 64 }, - 'uint128': { type: 'uint', format: 128 }, - 'uint256': { type: 'uint', format: 256 }, + uint8: { type: "uint", format: 8 }, + uint16: { type: "uint", format: 16 }, + uint32: { type: "uint", format: 32 }, + uint64: { type: "uint", format: 64 }, + uint128: { type: "uint", format: 128 }, + uint256: { type: "uint", format: 256 }, - 'int257': { type: 'int', format: 257 } + int257: { type: "int", format: 257 }, }; const cellFormats: FormatDef = { - 'remaining': { type: 'cell', format: 'remainder' } -} + remaining: { type: "cell", format: "remainder" }, +}; const sliceFormats: FormatDef = { - 'remaining': { type: 'slice', format: 'remainder' }, - 'bytes32': { type: 'fixed-bytes', format: 32 }, - 'bytes64': { type: 'fixed-bytes', format: 64 } -} + remaining: { type: "slice", format: "remainder" }, + bytes32: { type: "fixed-bytes", format: 32 }, + bytes64: { type: "fixed-bytes", format: 64 }, +}; const builderFormats: FormatDef = { - 'remaining': { type: 'builder', format: 'remainder' } -} + remaining: { type: "builder", format: "remainder" }, +}; export function resolveABIType(src: ASTField): ABITypeRef { - if (src.type.kind === 'type_ref_simple') { - + if (src.type.kind === "type_ref_simple") { // // Primitive types // - if (src.type.name === 'Int') { + if (src.type.name === "Int") { if (src.as) { const fmt = intFormats[src.as]; if (!fmt) { throwError(`Unsupported format ${src.as}`, src.ref); } - return { kind: 'simple', type: fmt.type, optional: src.type.optional, format: fmt.format }; + return { + kind: "simple", + type: fmt.type, + optional: src.type.optional, + format: fmt.format, + }; } - return { kind: 'simple', type: 'int', optional: src.type.optional, format: 257 }; // Default is maximumx size int + return { + kind: "simple", + type: "int", + optional: src.type.optional, + format: 257, + }; // Default is maximumx size int } - if (src.type.name === 'Bool') { + if (src.type.name === "Bool") { if (src.as) { throwError(`Unsupported format ${src.as}`, src.ref); } - return { kind: 'simple', type: 'bool', optional: src.type.optional }; + return { + kind: "simple", + type: "bool", + optional: src.type.optional, + }; } - if (src.type.name === 'Cell') { + if (src.type.name === "Cell") { if (src.as) { const fmt = cellFormats[src.as]; if (!fmt) { throwError(`Unsupported format ${src.as}`, src.ref); } - return { kind: 'simple', type: fmt.type, optional: src.type.optional, format: fmt.format }; + return { + kind: "simple", + type: fmt.type, + optional: src.type.optional, + format: fmt.format, + }; } - return { kind: 'simple', type: 'cell', optional: src.type.optional }; + return { + kind: "simple", + type: "cell", + optional: src.type.optional, + }; } - if (src.type.name === 'Slice') { + if (src.type.name === "Slice") { if (src.as) { if (src.as) { const fmt = sliceFormats[src.as]; if (!fmt) { throwError(`Unsupported format ${src.as}`, src.ref); } - return { kind: 'simple', type: fmt.type, optional: src.type.optional, format: fmt.format }; + return { + kind: "simple", + type: fmt.type, + optional: src.type.optional, + format: fmt.format, + }; } } - return { kind: 'simple', type: 'slice', optional: src.type.optional }; + return { + kind: "simple", + type: "slice", + optional: src.type.optional, + }; } - if (src.type.name === 'Builder') { + if (src.type.name === "Builder") { if (src.as) { if (src.as) { const fmt = builderFormats[src.as]; if (!fmt) { throwError(`Unsupported format ${src.as}`, src.ref); } - return { kind: 'simple', type: fmt.type, optional: src.type.optional, format: fmt.format }; + return { + kind: "simple", + type: fmt.type, + optional: src.type.optional, + format: fmt.format, + }; } } - return { kind: 'simple', type: 'builder', optional: src.type.optional }; + return { + kind: "simple", + type: "builder", + optional: src.type.optional, + }; } - if (src.type.name === 'Address') { + if (src.type.name === "Address") { if (src.as) { throwError(`Unsupported format ${src.as}`, src.ref); } - return { kind: 'simple', type: 'address', optional: src.type.optional }; + return { + kind: "simple", + type: "address", + optional: src.type.optional, + }; } - if (src.type.name === 'String') { + if (src.type.name === "String") { if (src.as) { throwError(`Unsupported format ${src.as}`, src.ref); } - return { kind: 'simple', type: 'string', optional: src.type.optional }; + return { + kind: "simple", + type: "string", + optional: src.type.optional, + }; } - if (src.type.name === 'StringBuilder') { + if (src.type.name === "StringBuilder") { throwError(`Unsupported type ${src.type.name}`, src.ref); } @@ -133,145 +181,187 @@ export function resolveABIType(src: ASTField): ABITypeRef { // if (src.as) { - if (src.as === 'reference') { - return { kind: 'simple', type: src.type.name, optional: src.type.optional, format: 'ref' }; + if (src.as === "reference") { + return { + kind: "simple", + type: src.type.name, + optional: src.type.optional, + format: "ref", + }; } else { throwError(`Unsupported format ${src.as}`, src.ref); } } - return { kind: 'simple', type: src.type.name, optional: src.type.optional }; + return { + kind: "simple", + type: src.type.name, + optional: src.type.optional, + }; } // // Map // - if (src.type.kind === 'type_ref_map') { + if (src.type.kind === "type_ref_map") { let key: string; let keyFormat: string | number | undefined = undefined; let value: string; let valueFormat: string | number | undefined = undefined; // Resolve key type - if (src.type.key === 'Int') { - key = 'int'; + if (src.type.key === "Int") { + key = "int"; if (src.type.keyAs) { const format = intMapFormats[src.type.keyAs]; if (!format) { - throwError(`Unsupported format ${src.type.keyAs} for map key`, src.ref); + throwError( + `Unsupported format ${src.type.keyAs} for map key`, + src.ref, + ); } key = format.type; keyFormat = format.format; } - } else if (src.type.key === 'Address') { - key = 'address'; + } else if (src.type.key === "Address") { + key = "address"; if (src.type.keyAs) { - throwError(`Unsupported format ${src.type.keyAs} for map key`, src.ref); + throwError( + `Unsupported format ${src.type.keyAs} for map key`, + src.ref, + ); } } else { throwError(`Unsupported map key type ${src.type.key}`, src.ref); } // Resolve value type - if (src.type.value === 'Int') { - value = 'int'; + if (src.type.value === "Int") { + value = "int"; if (src.type.valueAs) { const format = intMapFormats[src.type.valueAs]; if (!format) { - throwError(`Unsupported format ${src.type.valueAs} for map value`, src.ref); + throwError( + `Unsupported format ${src.type.valueAs} for map value`, + src.ref, + ); } value = format.type; valueFormat = format.format; } - } else if (src.type.value === 'Bool') { - value = 'bool'; + } else if (src.type.value === "Bool") { + value = "bool"; if (src.type.valueAs) { - throwError(`Unsupported format ${src.type.valueAs} for map value`, src.ref); + throwError( + `Unsupported format ${src.type.valueAs} for map value`, + src.ref, + ); } - } else if (src.type.value === 'Cell') { - value = 'cell'; - valueFormat = 'ref'; - if (src.type.valueAs && src.type.valueAs !== 'reference') { - throwError(`Unsupported format ${src.type.valueAs} for map value`, src.ref); + } else if (src.type.value === "Cell") { + value = "cell"; + valueFormat = "ref"; + if (src.type.valueAs && src.type.valueAs !== "reference") { + throwError( + `Unsupported format ${src.type.valueAs} for map value`, + src.ref, + ); } - } else if (src.type.value === 'Slice') { + } else if (src.type.value === "Slice") { throwError(`Unsupported map value type ${src.type.value}`, src.ref); - } else if (src.type.value === 'Address') { - value = 'address'; + } else if (src.type.value === "Address") { + value = "address"; if (src.type.valueAs) { - throwError(`Unsupported format ${src.type.valueAs} for map value`, src.ref); + throwError( + `Unsupported format ${src.type.valueAs} for map value`, + src.ref, + ); } - } else if (src.type.value === 'String') { + } else if (src.type.value === "String") { throwError(`Unsupported map value type ${src.type.value}`, src.ref); - } else if (src.type.value === 'StringBuilder' || src.type.value === 'Builder') { + } else if ( + src.type.value === "StringBuilder" || + src.type.value === "Builder" + ) { throwError(`Unsupported map value type ${src.type.value}`, src.ref); } else { value = src.type.value; - valueFormat = 'ref'; - if (src.type.valueAs && src.type.valueAs !== 'reference') { - throwError(`Unsupported format ${src.type.valueAs} for map value`, src.ref); + valueFormat = "ref"; + if (src.type.valueAs && src.type.valueAs !== "reference") { + throwError( + `Unsupported format ${src.type.valueAs} for map value`, + src.ref, + ); } } - return { kind: 'dict', key, keyFormat, value, valueFormat }; + return { kind: "dict", key, keyFormat, value, valueFormat }; } throwError(`Unsupported type`, src.ref); } -export function createABITypeRefFromTypeRef(src: TypeRef, ref: ASTRef): ABITypeRef { - - if (src.kind === 'ref') { - +export function createABITypeRefFromTypeRef( + src: TypeRef, + ref: ASTRef, +): ABITypeRef { + if (src.kind === "ref") { // Primitives - if (src.name === 'Int') { - return { kind: 'simple', type: 'int', optional: src.optional, format: 257 }; // Default is maximumx size int + if (src.name === "Int") { + return { + kind: "simple", + type: "int", + optional: src.optional, + format: 257, + }; // Default is maximumx size int } - if (src.name === 'Bool') { - return { kind: 'simple', type: 'bool', optional: src.optional }; + if (src.name === "Bool") { + return { kind: "simple", type: "bool", optional: src.optional }; } - if (src.name === 'Cell') { - return { kind: 'simple', type: 'cell', optional: src.optional }; + if (src.name === "Cell") { + return { kind: "simple", type: "cell", optional: src.optional }; } - if (src.name === 'Slice') { - return { kind: 'simple', type: 'slice', optional: src.optional }; + if (src.name === "Slice") { + return { kind: "simple", type: "slice", optional: src.optional }; } - if (src.name === 'Builder') { - return { kind: 'simple', type: 'builder', optional: src.optional }; + if (src.name === "Builder") { + return { kind: "simple", type: "builder", optional: src.optional }; } - if (src.name === 'Address') { - return { kind: 'simple', type: 'address', optional: src.optional }; + if (src.name === "Address") { + return { kind: "simple", type: "address", optional: src.optional }; } - if (src.name === 'String') { - return { kind: 'simple', type: 'string', optional: src.optional }; + if (src.name === "String") { + return { kind: "simple", type: "string", optional: src.optional }; } - if (src.name === 'StringBuilder') { + if (src.name === "StringBuilder") { throw Error(`Unsupported type ${src.name}`); } // Structs - return { kind: 'simple', type: src.name, optional: src.optional }; + return { kind: "simple", type: src.name, optional: src.optional }; } - if (src.kind === 'map') { + if (src.kind === "map") { let key: string; let keyFormat: string | number | undefined = undefined; let value: string; let valueFormat: string | number | undefined = undefined; // Resolve key type - if (src.key === 'Int') { - key = 'int'; + if (src.key === "Int") { + key = "int"; if (src.keyAs) { const format = intMapFormats[src.keyAs]; if (!format) { - throwError(`Unsupported format ${src.keyAs} for map key`, ref); + throwError( + `Unsupported format ${src.keyAs} for map key`, + ref, + ); } key = format.type; keyFormat = format.format; } - } else if (src.key === 'Address') { - key = 'address'; + } else if (src.key === "Address") { + key = "address"; if (src.keyAs) { throwError(`Unsupported format ${src.keyAs} for map key`, ref); } @@ -280,52 +370,67 @@ export function createABITypeRefFromTypeRef(src: TypeRef, ref: ASTRef): ABITypeR } // Resolve value type - if (src.value === 'Int') { - value = 'int'; + if (src.value === "Int") { + value = "int"; if (src.valueAs) { const format = intMapFormats[src.valueAs]; if (!format) { - throwError(`Unsupported format ${src.valueAs} for map value`, ref); + throwError( + `Unsupported format ${src.valueAs} for map value`, + ref, + ); } value = format.type; valueFormat = format.format; } - } else if (src.value === 'Bool') { - value = 'bool'; + } else if (src.value === "Bool") { + value = "bool"; if (src.valueAs) { - throwError(`Unsupported format ${src.valueAs} for map value`, ref); + throwError( + `Unsupported format ${src.valueAs} for map value`, + ref, + ); } - } else if (src.value === 'Cell') { - value = 'cell'; - valueFormat = 'ref'; - if (src.valueAs && src.valueAs !== 'reference') { - throwError(`Unsupported format ${src.valueAs} for map value`, ref); + } else if (src.value === "Cell") { + value = "cell"; + valueFormat = "ref"; + if (src.valueAs && src.valueAs !== "reference") { + throwError( + `Unsupported format ${src.valueAs} for map value`, + ref, + ); } - } else if (src.value === 'Slice') { + } else if (src.value === "Slice") { throw Error(`Unsupported map value type ${src.value}`); - } else if (src.value === 'Address') { - value = 'address'; + } else if (src.value === "Address") { + value = "address"; if (src.valueAs) { - throwError(`Unsupported format ${src.valueAs} for map value`, ref); + throwError( + `Unsupported format ${src.valueAs} for map value`, + ref, + ); } - } else if (src.value === 'String') { + } else if (src.value === "String") { throw Error(`Unsupported map value type ${src.value}`); - } else if (src.value === 'StringBuilder' || src.value === 'Builder') { + } else if (src.value === "StringBuilder" || src.value === "Builder") { throw Error(`Unsupported map value type ${src.value}`); } else { value = src.value; - valueFormat = 'ref'; - if (src.valueAs && src.valueAs !== 'reference') { - throwError(`Unsupported format ${src.valueAs} for map value`, ref); + valueFormat = "ref"; + if (src.valueAs && src.valueAs !== "reference") { + throwError( + `Unsupported format ${src.valueAs} for map value`, + ref, + ); } } - return { kind: 'dict', key, keyFormat, value, valueFormat }; + return { kind: "dict", key, keyFormat, value, valueFormat }; } - if (src.kind === 'ref_bounced') { + if (src.kind === "ref_bounced") { throw Error("Unexpected bounced reference"); } throw Error(`Unsupported type`); -} \ No newline at end of file +} diff --git a/src/types/resolveConstantValue.ts b/src/types/resolveConstantValue.ts index 19d0bca2a..6a4d3f118 100644 --- a/src/types/resolveConstantValue.ts +++ b/src/types/resolveConstantValue.ts @@ -6,102 +6,105 @@ import { printTypeRef, TypeRef } from "./types"; import { sha256_sync } from "@ton/crypto"; function reduceIntImpl(ast: ASTExpression): bigint { - if (ast.kind === 'number') { + if (ast.kind === "number") { return ast.value; - } else if (ast.kind === 'op_binary') { + } else if (ast.kind === "op_binary") { const l = reduceInt(ast.left); const r = reduceInt(ast.right); - if (ast.op === '+') { + if (ast.op === "+") { return l + r; - } else if (ast.op === '-') { + } else if (ast.op === "-") { return l - r; - } else if (ast.op === '*') { + } else if (ast.op === "*") { return l * r; - } else if (ast.op === '/') { + } else if (ast.op === "/") { return l / r; - } else if (ast.op === '%') { + } else if (ast.op === "%") { return l % r; - } else if (ast.op === '<<') { + } else if (ast.op === "<<") { return l << r; - } else if (ast.op === '>>') { + } else if (ast.op === ">>") { return l >> r; - } else if (ast.op === '&') { + } else if (ast.op === "&") { return l & r; - } else if (ast.op === '|') { + } else if (ast.op === "|") { return l | r; } - } else if (ast.kind === 'op_unary') { - if (ast.op === '-') { + } else if (ast.kind === "op_unary") { + if (ast.op === "-") { return -reduceInt(ast.right); - } else if (ast.op === '+') { + } else if (ast.op === "+") { return reduceInt(ast.right); } - } else if (ast.kind === 'op_static_call') { - if (ast.name === 'ton') { + } else if (ast.kind === "op_static_call") { + if (ast.name === "ton") { if (ast.args.length === 1) { return BigInt(toNano(reduceString(ast.args[0])).toString(10)); } } - if (ast.name === 'pow') { + if (ast.name === "pow") { if (ast.args.length === 2) { return reduceInt(ast.args[0]) ** reduceInt(ast.args[1]); } } - if (ast.name === 'sha256') { - if (ast.args.length === 1 && ast.args[0].kind === 'string') { + if (ast.name === "sha256") { + if (ast.args.length === 1 && ast.args[0].kind === "string") { const str = reduceString(ast.args[0]); if (Buffer.from(str).length <= 128) { - return BigInt('0x' + sha256_sync(str).toString('hex')); + return BigInt("0x" + sha256_sync(str).toString("hex")); } } } } - throwError('Cannot reduce expression to a constant integer', ast.ref); + throwError("Cannot reduce expression to a constant integer", ast.ref); } function reduceInt(ast: ASTExpression): bigint { - try { - return reduceIntImpl(ast) - } catch (error) { - if (error instanceof RangeError) { - throwError('Cannot evaluate constant expression due to integer overflow', ast.ref); - } else { - throw error; - } - } + try { + return reduceIntImpl(ast); + } catch (error) { + if (error instanceof RangeError) { + throwError( + "Cannot evaluate constant expression due to integer overflow", + ast.ref, + ); + } else { + throw error; + } + } } function reduceBool(ast: ASTExpression): boolean { - if (ast.kind === 'boolean') { + if (ast.kind === "boolean") { return ast.value; } - if (ast.kind === 'op_unary') { - if (ast.op === '!') { + if (ast.kind === "op_unary") { + if (ast.op === "!") { return !reduceBool(ast.right); } } - if (ast.kind === 'op_binary') { - if (ast.op === '&&') { + if (ast.kind === "op_binary") { + if (ast.op === "&&") { return reduceBool(ast.left) && reduceBool(ast.right); - } else if (ast.op === '||') { + } else if (ast.op === "||") { return reduceBool(ast.left) || reduceBool(ast.right); } // TODO: More cases } - throwError('Cannot reduce expression to a constant boolean', ast.ref); + throwError("Cannot reduce expression to a constant boolean", ast.ref); } function reduceString(ast: ASTExpression): string { - if (ast.kind === 'string') { + if (ast.kind === "string") { return ast.value; } - throwError('Cannot reduce expression to a constant string', ast.ref); + throwError("Cannot reduce expression to a constant string", ast.ref); } function reduceAddress(ast: ASTExpression, ctx: CompilerContext): Address { - if (ast.kind === 'op_static_call') { - if (ast.name === 'address') { + if (ast.kind === "op_static_call") { + if (ast.name === "address") { if (ast.args.length === 1) { const str = reduceString(ast.args[0]); const address = Address.parse(str); @@ -110,19 +113,22 @@ function reduceAddress(ast: ASTExpression, ctx: CompilerContext): Address { } if (!enabledMasterchain(ctx)) { if (address.workChain !== 0) { - throwError(`Address ${str} from masterchain are not enabled for this contract`, ast.ref); + throwError( + `Address ${str} from masterchain are not enabled for this contract`, + ast.ref, + ); } } return address; } } } - throwError('Cannot reduce expression to a constant Address', ast.ref); + throwError("Cannot reduce expression to a constant Address", ast.ref); } function reduceCell(ast: ASTExpression): Cell { - if (ast.kind === 'op_static_call') { - if (ast.name === 'cell') { + if (ast.kind === "op_static_call") { + if (ast.name === "cell") { if (ast.args.length === 1) { const str = reduceString(ast.args[0]); let c: Cell; @@ -135,49 +141,56 @@ function reduceCell(ast: ASTExpression): Cell { } } } - throwError('Cannot reduce expression to a constant Cell', ast.ref); + throwError("Cannot reduce expression to a constant Cell", ast.ref); } -export function resolveConstantValue(type: TypeRef, ast: ASTExpression | null, ctx: CompilerContext) { +export function resolveConstantValue( + type: TypeRef, + ast: ASTExpression | null, + ctx: CompilerContext, +) { if (ast === null) { return undefined; } - if (type.kind !== 'ref') { - throwError(`Expected constant value, got ${printTypeRef(type)}`, ast.ref); + if (type.kind !== "ref") { + throwError( + `Expected constant value, got ${printTypeRef(type)}`, + ast.ref, + ); } // Handle optional if (type.optional) { - if (ast.kind === 'null') { + if (ast.kind === "null") { return null; } } // Handle int - if (type.name === 'Int') { + if (type.name === "Int") { return reduceInt(ast); } // Handle bool - if (type.name === 'Bool') { + if (type.name === "Bool") { return reduceBool(ast); } // Handle string - if (type.name === 'String') { + if (type.name === "String") { return reduceString(ast); } // Handle Address - if (type.name === 'Address') { + if (type.name === "Address") { return reduceAddress(ast, ctx); } // Handle Cell - if (type.name === 'Cell') { + if (type.name === "Cell") { return reduceCell(ast); } throwError(`Expected constant value, got ${printTypeRef(type)}`, ast.ref); -} \ No newline at end of file +} diff --git a/src/types/resolveDescriptors.spec.ts b/src/types/resolveDescriptors.spec.ts index 8b974f151..a0581b4f9 100644 --- a/src/types/resolveDescriptors.spec.ts +++ b/src/types/resolveDescriptors.spec.ts @@ -1,22 +1,30 @@ import { CompilerContext } from "../context"; -import { getAllStaticFunctions, getAllTypes, resolveDescriptors } from "./resolveDescriptors"; -import { resolveSignatures } from './resolveSignatures'; +import { + getAllStaticFunctions, + getAllTypes, + resolveDescriptors, +} from "./resolveDescriptors"; +import { resolveSignatures } from "./resolveSignatures"; import { ASTRef, __DANGER_resetNodeId } from "../grammar/ast"; import { loadCases } from "../utils/loadCases"; import { openContext } from "../grammar/store"; expect.addSnapshotSerializer({ test: (src) => src instanceof ASTRef, - print: (src) => `${(src as ASTRef).contents}` + print: (src) => `${(src as ASTRef).contents}`, }); -describe('resolveDescriptors', () => { +describe("resolveDescriptors", () => { beforeEach(() => { __DANGER_resetNodeId(); }); for (const r of loadCases(__dirname + "/test/")) { - it('should resolve descriptors for ' + r.name, () => { - let ctx = openContext(new CompilerContext(), [{ code: r.code, path: '', origin: 'user' }], []); + it("should resolve descriptors for " + r.name, () => { + let ctx = openContext( + new CompilerContext(), + [{ code: r.code, path: "", origin: "user" }], + [], + ); ctx = resolveDescriptors(ctx); ctx = resolveSignatures(ctx); expect(getAllTypes(ctx)).toMatchSnapshot(); @@ -24,9 +32,15 @@ describe('resolveDescriptors', () => { }); } for (const r of loadCases(__dirname + "/test-failed/")) { - it('should fail descriptors for ' + r.name, () => { - const ctx = openContext(new CompilerContext(), [{ code: r.code, path: '', origin: 'user' }], []); - expect(() => resolveDescriptors(ctx)).toThrowErrorMatchingSnapshot(); + it("should fail descriptors for " + r.name, () => { + const ctx = openContext( + new CompilerContext(), + [{ code: r.code, path: "", origin: "user" }], + [], + ); + expect(() => + resolveDescriptors(ctx), + ).toThrowErrorMatchingSnapshot(); }); } -}); \ No newline at end of file +}); diff --git a/src/types/resolveDescriptors.ts b/src/types/resolveDescriptors.ts index 1c7eb53c8..a0567b111 100644 --- a/src/types/resolveDescriptors.ts +++ b/src/types/resolveDescriptors.ts @@ -1,6 +1,31 @@ -import { ASTConstant, ASTField, ASTFunction, ASTInitFunction, ASTNativeFunction, ASTNode, ASTRef, ASTTypeRef, createNode, throwError, traverse } from "../grammar/ast"; +import { + ASTConstant, + ASTField, + ASTFunction, + ASTInitFunction, + ASTNativeFunction, + ASTNode, + ASTRef, + ASTTypeRef, + createNode, + throwError, + traverse, +} from "../grammar/ast"; import { CompilerContext, createContextStore } from "../context"; -import { ConstantDescription, FieldDescription, FunctionArgument, FunctionDescription, InitArgument, InitDescription, printTypeRef, ReceiverSelector, TypeDescription, TypeOrigin, TypeRef, typeRefEquals } from "./types"; +import { + ConstantDescription, + FieldDescription, + FunctionArgument, + FunctionDescription, + InitArgument, + InitDescription, + printTypeRef, + ReceiverSelector, + TypeDescription, + TypeOrigin, + TypeRef, + typeRefEquals, +} from "./types"; import { getRawAST } from "../grammar/store"; import { cloneNode } from "../grammar/clone"; import { crc16 } from "../utils/crc16"; @@ -14,59 +39,69 @@ const store = createContextStore(); const staticFunctionsStore = createContextStore(); const staticConstantsStore = createContextStore(); -function verifyMapType(key: string, keyAs: string | null, value: string, valueAs: string | null, ref: ASTRef) { +function verifyMapType( + key: string, + keyAs: string | null, + value: string, + valueAs: string | null, + ref: ASTRef, +) { if (!keyAs && !valueAs) { return; } // keyAs if (keyAs) { - if (key === 'Int') { - if (![ - 'int8', - 'int16', - 'int32', - 'int64', - 'int128', - 'int256', - 'int257', - 'uint8', - 'uint16', - 'uint32', - 'uint64', - 'uint128', - 'uint256', - ].includes(keyAs)) { - throwError('Invalid key type for map', ref); + if (key === "Int") { + if ( + ![ + "int8", + "int16", + "int32", + "int64", + "int128", + "int256", + "int257", + "uint8", + "uint16", + "uint32", + "uint64", + "uint128", + "uint256", + ].includes(keyAs) + ) { + throwError("Invalid key type for map", ref); } } else { - throwError('Invalid key type for map', ref); + throwError("Invalid key type for map", ref); } } // valueAs if (valueAs) { - if (value === 'Int') { - if (![ - 'int8', - 'int16', - 'int32', - 'int64', - 'int128', - 'int256', - 'int257', - 'uint8', - 'uint16', - 'uint32', - 'uint64', - 'uint128', - 'uint256', - 'coins' - ].includes(valueAs)) { - throwError('Invalid value type for map', ref); + if (value === "Int") { + if ( + ![ + "int8", + "int16", + "int32", + "int64", + "int128", + "int256", + "int257", + "uint8", + "uint16", + "uint32", + "uint64", + "uint128", + "uint256", + "coins", + ].includes(valueAs) + ) { + throwError("Invalid value type for map", ref); } } else { - throwError('Invalid value type for map', ref); + throwError("Invalid value type for map", ref); } } } @@ -74,70 +109,73 @@ function verifyMapType(key: string, keyAs: string | null, value: string, valueAs export const toBounced = (type: string) => `${type}%%BOUNCED%%`; export function resolveTypeRef(ctx: CompilerContext, src: ASTTypeRef): TypeRef { - if (src.kind === 'type_ref_simple') { + if (src.kind === "type_ref_simple") { const t = getType(ctx, src.name); return { - kind: 'ref', + kind: "ref", name: t.name, - optional: src.optional + optional: src.optional, }; } - if (src.kind === 'type_ref_map') { + if (src.kind === "type_ref_map") { const k = getType(ctx, src.key).name; const v = getType(ctx, src.value).name; verifyMapType(k, src.keyAs, v, src.valueAs, src.ref); return { - kind: 'map', + kind: "map", key: k, keyAs: src.keyAs, value: v, - valueAs: src.valueAs + valueAs: src.valueAs, }; } - if (src.kind === 'type_ref_bounced') { + if (src.kind === "type_ref_bounced") { const t = getType(ctx, src.name); return { - kind: 'ref_bounced', + kind: "ref_bounced", name: t.name, }; } - throw Error('Invalid type ref'); + throw Error("Invalid type ref"); } -function buildTypeRef(src: ASTTypeRef, types: Map): TypeRef { - if (src.kind === 'type_ref_simple') { +function buildTypeRef( + src: ASTTypeRef, + types: Map, +): TypeRef { + if (src.kind === "type_ref_simple") { if (!types.has(src.name)) { - throwError('Type ' + src.name + ' not found', src.ref); + throwError("Type " + src.name + " not found", src.ref); } return { - kind: 'ref', + kind: "ref", name: src.name, - optional: src.optional + optional: src.optional, }; } - if (src.kind === 'type_ref_map') { + if (src.kind === "type_ref_map") { if (!types.has(src.key)) { - throwError('Type ' + src.key + ' not found', src.ref); + throwError("Type " + src.key + " not found", src.ref); } if (!types.has(src.value)) { - throwError('Type ' + src.value + ' not found', src.ref); + throwError("Type " + src.value + " not found", src.ref); } return { - kind: 'map', + kind: "map", key: src.key, keyAs: src.keyAs, value: src.value, - valueAs: src.valueAs + valueAs: src.valueAs, }; } - if (src.kind === 'type_ref_bounced') { + if (src.kind === "type_ref_bounced") { return { - kind: 'ref_bounced', + kind: "ref_bounced", name: src.name, }; } - throw Error('Unknown type ref'); + throw Error("Unknown type ref"); } function uidForName(name: string, types: Map) { @@ -166,9 +204,9 @@ export function resolveDescriptors(ctx: CompilerContext) { const uid = uidForName(a.name, types); - if (a.kind === 'primitive') { + if (a.kind === "primitive") { types.set(a.name, { - kind: 'primitive', + kind: "primitive", origin: a.origin, name: a.name, uid, @@ -184,11 +222,11 @@ export function resolveDescriptors(ctx: CompilerContext) { ast: a, interfaces: [], constants: [], - partialFieldCount: 0 + partialFieldCount: 0, }); - } else if (a.kind === 'def_contract') { + } else if (a.kind === "def_contract") { types.set(a.name, { - kind: 'contract', + kind: "contract", origin: a.origin, name: a.name, uid, @@ -202,13 +240,15 @@ export function resolveDescriptors(ctx: CompilerContext) { dependsOn: [], init: null, ast: a, - interfaces: a.attributes.filter((v) => v.type === 'interface').map((v) => v.name.value), + interfaces: a.attributes + .filter((v) => v.type === "interface") + .map((v) => v.name.value), constants: [], - partialFieldCount: 0 + partialFieldCount: 0, }); - } else if (a.kind === 'def_struct') { + } else if (a.kind === "def_struct") { types.set(a.name, { - kind: 'struct', + kind: "struct", origin: a.origin, name: a.name, uid, @@ -224,11 +264,11 @@ export function resolveDescriptors(ctx: CompilerContext) { ast: a, interfaces: [], constants: [], - partialFieldCount: 0 + partialFieldCount: 0, }); - } else if (a.kind === 'def_trait') { + } else if (a.kind === "def_trait") { types.set(a.name, { - kind: 'trait', + kind: "trait", origin: a.origin, name: a.name, uid, @@ -242,9 +282,11 @@ export function resolveDescriptors(ctx: CompilerContext) { dependsOn: [], init: null, ast: a, - interfaces: a.attributes.filter((v) => v.type === 'interface').map((v) => v.name.value), + interfaces: a.attributes + .filter((v) => v.type === "interface") + .map((v) => v.name.value), constants: [], - partialFieldCount: 0 + partialFieldCount: 0, }); } } @@ -253,16 +295,24 @@ export function resolveDescriptors(ctx: CompilerContext) { // Resolve fields // - function buildFieldDescription(src: ASTField, index: number): FieldDescription { + function buildFieldDescription( + src: ASTField, + index: number, + ): FieldDescription { const tr = buildTypeRef(src.type, types); // Check if field is runtime type if (isRuntimeType(tr)) { - throwError(printTypeRef(tr) + ' is a runtime only type and can\'t be used as field', src.ref); + throwError( + printTypeRef(tr) + + " is a runtime only type and can't be used as field", + src.ref, + ); } // Resolve default value - let d: bigint | boolean | string | null | Address | Cell | undefined = undefined; + let d: bigint | boolean | string | null | Address | Cell | undefined = + undefined; if (src.init) { d = resolveConstantValue(tr, src.init, ctx); } @@ -270,7 +320,16 @@ export function resolveDescriptors(ctx: CompilerContext) { // Resolve abi type const type = resolveABIType(src); - return { name: src.name, type: tr, index, as: src.as, default: d, ref: src.ref, ast: src, abi: { name: src.name, type } }; + return { + name: src.name, + type: tr, + index, + as: src.as, + default: d, + ref: src.ref, + ast: src, + abi: { name: src.name, type }, + }; } function buildConstantDescription(src: ASTConstant): ConstantDescription { @@ -280,72 +339,124 @@ export function resolveDescriptors(ctx: CompilerContext) { } for (const a of ast.types) { - // Contract - if (a.kind === 'def_contract') { + if (a.kind === "def_contract") { for (const f of a.declarations) { - if (f.kind === 'def_field') { - if (types.get(a.name)!.fields.find((v) => v.name === f.name)) { + if (f.kind === "def_field") { + if ( + types.get(a.name)!.fields.find((v) => v.name === f.name) + ) { throwError(`Field ${f.name} already exists`, f.ref); } - if (types.get(a.name)!.constants.find((v) => v.name === f.name)) { + if ( + types + .get(a.name)! + .constants.find((v) => v.name === f.name) + ) { throwError(`Constant ${f.name} already exists`, f.ref); } - types.get(a.name)!.fields.push(buildFieldDescription(f, types.get(a.name)!.fields.length)); - } else if (f.kind === 'def_constant') { - if (types.get(a.name)!.fields.find((v) => v.name === f.name)) { + types + .get(a.name)! + .fields.push( + buildFieldDescription( + f, + types.get(a.name)!.fields.length, + ), + ); + } else if (f.kind === "def_constant") { + if ( + types.get(a.name)!.fields.find((v) => v.name === f.name) + ) { throwError(`Field ${f.name} already exists`, f.ref); } - if (types.get(a.name)!.constants.find((v) => v.name === f.name)) { + if ( + types + .get(a.name)! + .constants.find((v) => v.name === f.name) + ) { throwError(`Constant ${f.name} already exists`, f.ref); } - if (f.attributes.find((v) => v.type !== 'overrides')) { + if (f.attributes.find((v) => v.type !== "overrides")) { throwError(`Constant can be only overridden`, f.ref); } - types.get(a.name)!.constants.push(buildConstantDescription(f)); + types + .get(a.name)! + .constants.push(buildConstantDescription(f)); } } } // Struct - if (a.kind === 'def_struct') { + if (a.kind === "def_struct") { for (const f of a.fields) { - if (types.get(a.name)!.fields.find((v) => v.name === f.name)) { throwError(`Field ${f.name} already exists`, f.ref); } - types.get(a.name)!.fields.push(buildFieldDescription(f, types.get(a.name)!.fields.length)); + types + .get(a.name)! + .fields.push( + buildFieldDescription( + f, + types.get(a.name)!.fields.length, + ), + ); } if (a.fields.length === 0 && !a.message) { - throwError(`Struct ${a.name} must have at least one field`, a.ref); + throwError( + `Struct ${a.name} must have at least one field`, + a.ref, + ); } } // Trait - if (a.kind === 'def_trait') { + if (a.kind === "def_trait") { for (const f of a.declarations) { - if (f.kind === 'def_field') { - if (types.get(a.name)!.fields.find((v) => v.name === f.name)) { + if (f.kind === "def_field") { + if ( + types.get(a.name)!.fields.find((v) => v.name === f.name) + ) { throwError(`Field ${f.name} already exists`, f.ref); } if (f.as) { - throwError(`Trait field cannot have serialization specifier`, f.ref); + throwError( + `Trait field cannot have serialization specifier`, + f.ref, + ); } - types.get(a.name)!.fields.push(buildFieldDescription(f, types.get(a.name)!.fields.length)); - } else if (f.kind === 'def_constant') { - if (types.get(a.name)!.fields.find((v) => v.name === f.name)) { + types + .get(a.name)! + .fields.push( + buildFieldDescription( + f, + types.get(a.name)!.fields.length, + ), + ); + } else if (f.kind === "def_constant") { + if ( + types.get(a.name)!.fields.find((v) => v.name === f.name) + ) { throwError(`Field ${f.name} already exists`, f.ref); } - if (types.get(a.name)!.constants.find((v) => v.name === f.name)) { + if ( + types + .get(a.name)! + .constants.find((v) => v.name === f.name) + ) { throwError(`Constant ${f.name} already exists`, f.ref); } - if (f.attributes.find((v) => v.type === 'overrides')) { - throwError(`Trait constant cannot be overridden`, f.ref); + if (f.attributes.find((v) => v.type === "overrides")) { + throwError( + `Trait constant cannot be overridden`, + f.ref, + ); } // if (f.attributes.find((v) => v.type === 'abstract')) { // continue; // Do not materialize abstract constants // } - types.get(a.name)!.constants.push(buildConstantDescription(f)); + types + .get(a.name)! + .constants.push(buildConstantDescription(f)); } } } @@ -363,12 +474,15 @@ export function resolveDescriptors(ctx: CompilerContext) { // Resolve contract functions // - function resolveFunctionDescriptor(sself: string | null, a: ASTFunction | ASTNativeFunction, origin: TypeOrigin): FunctionDescription { - + function resolveFunctionDescriptor( + sself: string | null, + a: ASTFunction | ASTNativeFunction, + origin: TypeOrigin, + ): FunctionDescription { let self = sself; // Resolve return - let returns: TypeRef = { kind: 'void' }; + let returns: TypeRef = { kind: "void" }; if (a.return) { returns = buildTypeRef(a.return, types); } @@ -379,121 +493,171 @@ export function resolveDescriptors(ctx: CompilerContext) { args.push({ name: r.name, type: buildTypeRef(r.type, types), - ref: r.ref + ref: r.ref, }); } // Resolve flags - const isGetter = a.attributes.find(a => a.type === 'get'); - const isMutating = a.attributes.find(a => a.type === 'mutates'); - const isExtends = a.attributes.find(a => a.type === 'extends'); - const isVirtual = a.attributes.find(a => a.type === 'virtual'); - const isOverrides = a.attributes.find(a => a.type === 'overrides'); - const isInline = a.attributes.find(a => a.type === 'inline'); - const isAbstract = a.attributes.find(a => a.type === 'abstract'); + const isGetter = a.attributes.find((a) => a.type === "get"); + const isMutating = a.attributes.find((a) => a.type === "mutates"); + const isExtends = a.attributes.find((a) => a.type === "extends"); + const isVirtual = a.attributes.find((a) => a.type === "virtual"); + const isOverrides = a.attributes.find((a) => a.type === "overrides"); + const isInline = a.attributes.find((a) => a.type === "inline"); + const isAbstract = a.attributes.find((a) => a.type === "abstract"); // Check for native - if (a.kind === 'def_native_function') { + if (a.kind === "def_native_function") { if (isGetter) { - throwError('Native functions cannot be getters', isGetter.ref); + throwError("Native functions cannot be getters", isGetter.ref); } if (self) { - throwError('Native functions cannot be delated within a contract', a.ref); + throwError( + "Native functions cannot be delated within a contract", + a.ref, + ); } if (isVirtual) { - throwError('Native functions cannot be virtual', isVirtual.ref); + throwError("Native functions cannot be virtual", isVirtual.ref); } if (isOverrides) { - throwError('Native functions cannot be overrides', isOverrides.ref); + throwError( + "Native functions cannot be overrides", + isOverrides.ref, + ); } } // Check virtual and overrides if (isVirtual && isExtends) { - throwError('Extend functions cannot be virtual', isVirtual.ref); + throwError("Extend functions cannot be virtual", isVirtual.ref); } if (isOverrides && isExtends) { - throwError('Extend functions cannot be overrides', isOverrides.ref); + throwError("Extend functions cannot be overrides", isOverrides.ref); } if (isAbstract && isExtends) { - throwError('Extend functions cannot be abstract', isAbstract.ref); + throwError("Extend functions cannot be abstract", isAbstract.ref); } if (!self && isVirtual) { - throwError('Virtual functions must be defined within a contract or a trait', isVirtual.ref); + throwError( + "Virtual functions must be defined within a contract or a trait", + isVirtual.ref, + ); } if (!self && isOverrides) { - throwError('Overrides functions must be defined within a contract or a trait', isOverrides.ref); + throwError( + "Overrides functions must be defined within a contract or a trait", + isOverrides.ref, + ); } if (!self && isAbstract) { - throwError('Abstract functions must be defined within a trait', isAbstract.ref); + throwError( + "Abstract functions must be defined within a trait", + isAbstract.ref, + ); } if (isVirtual && isAbstract) { - throwError('Abstract functions cannot be virtual', isAbstract.ref); + throwError("Abstract functions cannot be virtual", isAbstract.ref); } if (isVirtual && isOverrides) { - throwError('Overrides functions cannot be virtual', isOverrides.ref); + throwError( + "Overrides functions cannot be virtual", + isOverrides.ref, + ); } if (isAbstract && isOverrides) { - throwError('Overrides functions cannot be abstract', isOverrides.ref); + throwError( + "Overrides functions cannot be abstract", + isOverrides.ref, + ); } // Check virtual if (isVirtual) { const t = types.get(self!)!; - if (t.kind !== 'trait') { - throwError('Virtual functions must be defined within a trait', isVirtual.ref); + if (t.kind !== "trait") { + throwError( + "Virtual functions must be defined within a trait", + isVirtual.ref, + ); } } // Check abstract if (isAbstract) { const t = types.get(self!)!; - if (t.kind !== 'trait') { - throwError('Abstract functions must be defined within a trait', isAbstract.ref); + if (t.kind !== "trait") { + throwError( + "Abstract functions must be defined within a trait", + isAbstract.ref, + ); } } // Check overrides if (isOverrides) { const t = types.get(self!)!; - if (t.kind !== 'contract') { - throwError('Overrides functions must be defined within a contract', isOverrides.ref); + if (t.kind !== "contract") { + throwError( + "Overrides functions must be defined within a contract", + isOverrides.ref, + ); } } // Check for common - if (a.kind === 'def_function') { + if (a.kind === "def_function") { if (isGetter && !self) { - throwError('Getters must be defined within a contract', isGetter.ref); + throwError( + "Getters must be defined within a contract", + isGetter.ref, + ); } } // Check for getter if (isInline && isGetter) { - throwError('Getters cannot be inline', isInline.ref); + throwError("Getters cannot be inline", isInline.ref); } // Validate mutating if (isExtends) { - // Validate arguments if (self) { - throwError('Extend functions cannot be defined within a contract', isExtends.ref); + throwError( + "Extend functions cannot be defined within a contract", + isExtends.ref, + ); } if (args.length === 0) { - throwError('Extend functions must have at least one argument', isExtends.ref); + throwError( + "Extend functions must have at least one argument", + isExtends.ref, + ); } - if (args[0].name !== 'self') { - throwError('Extend function must have first argument named "self"', args[0].ref); + if (args[0].name !== "self") { + throwError( + 'Extend function must have first argument named "self"', + args[0].ref, + ); } - if (args[0].type.kind !== 'ref') { - throwError('Extend functions must have a reference type as the first argument', args[0].ref); + if (args[0].type.kind !== "ref") { + throwError( + "Extend functions must have a reference type as the first argument", + args[0].ref, + ); } if (args[0].type.optional) { - throwError('Extend functions must have a non-optional type as the first argument', args[0].ref); + throwError( + "Extend functions must have a non-optional type as the first argument", + args[0].ref, + ); } if (!types.has(args[0].type.name)) { - throwError('Type ' + args[0].type.name + ' not found', args[0].ref); + throwError( + "Type " + args[0].type.name + " not found", + args[0].ref, + ); } // Update self and remove first argument @@ -503,17 +667,23 @@ export function resolveDescriptors(ctx: CompilerContext) { // Check for mutating and extends if (isMutating && !isExtends) { - throwError('Mutating functions must be extend functions', isMutating.ref); + throwError( + "Mutating functions must be extend functions", + isMutating.ref, + ); } // Check argument names const exNames = new Set(); for (const arg of args) { - if (arg.name === 'self') { + if (arg.name === "self") { throwError('Argument name "self" is reserved', arg.ref); } if (exNames.has(arg.name)) { - throwError('Argument name "' + arg.name + '" is already used', arg.ref); + throwError( + 'Argument name "' + arg.name + '" is already used', + arg.ref, + ); } exNames.add(arg.name); } @@ -522,11 +692,19 @@ export function resolveDescriptors(ctx: CompilerContext) { if (isGetter) { for (const arg of args) { if (isRuntimeType(arg.type)) { - throwError(printTypeRef(arg.type) + ' is a runtime-only type and can\'t be used as a getter argument', arg.ref); + throwError( + printTypeRef(arg.type) + + " is a runtime-only type and can't be used as a getter argument", + arg.ref, + ); } } if (isRuntimeType(returns)) { - throwError(printTypeRef(returns) + ' is a runtime-only type and can\'t be used as getter return type', a.ref); + throwError( + printTypeRef(returns) + + " is a runtime-only type and can't be used as getter return type", + a.ref, + ); } } @@ -538,12 +716,12 @@ export function resolveDescriptors(ctx: CompilerContext) { args, returns, ast: a, - isMutating: !!isMutating || (!!sself /* && !isGetter */), // Mark all contract functions as mutating + isMutating: !!isMutating || !!sself /* && !isGetter */, // Mark all contract functions as mutating isGetter: !!isGetter, isVirtual: !!isVirtual, isOverrides: !!isOverrides, isInline: !!isInline, - isAbstract: !!isAbstract + isAbstract: !!isAbstract, }; } @@ -554,235 +732,397 @@ export function resolveDescriptors(ctx: CompilerContext) { name: r.name, type: buildTypeRef(r.type, types), as: null, - ref: r.ref + ref: r.ref, }); } // Check if runtime types are used for (const a of args) { if (isRuntimeType(a.type)) { - throwError(printTypeRef(a.type) + ' is a runtime-only type and can\'t be used as a init function argument', a.ref); + throwError( + printTypeRef(a.type) + + " is a runtime-only type and can't be used as a init function argument", + a.ref, + ); } } return { args, - ast + ast, }; } for (const a of ast.types) { - if (a.kind === 'def_contract' || a.kind === 'def_trait') { + if (a.kind === "def_contract" || a.kind === "def_trait") { const s = types.get(a.name)!; for (const d of a.declarations) { - if (d.kind === 'def_function') { + if (d.kind === "def_function") { const f = resolveFunctionDescriptor(s.name, d, s.origin); if (f.self !== s.name) { - throw Error('Function self must be ' + s.name); // Impossible + throw Error("Function self must be " + s.name); // Impossible } if (s.functions.has(f.name)) { - throwError(`Function ${f.name} already exists in type ${s.name}`, s.ast.ref); + throwError( + `Function ${f.name} already exists in type ${s.name}`, + s.ast.ref, + ); } s.functions.set(f.name, f); } - if (d.kind === 'def_init_function') { + if (d.kind === "def_init_function") { if (s.init) { - throwError('Init function already exists', d.ref); + throwError("Init function already exists", d.ref); } s.init = resolveInitFunction(d); } - if (d.kind === 'def_receive') { - + if (d.kind === "def_receive") { // Check if externals are enabled - if (d.selector.kind.startsWith('external-') && !enabledExternals(ctx)) { - throwError('External functions are not enabled', d.ref); + if ( + d.selector.kind.startsWith("external-") && + !enabledExternals(ctx) + ) { + throwError("External functions are not enabled", d.ref); } - if (d.selector.kind === 'internal-simple' || d.selector.kind === 'external-simple') { + if ( + d.selector.kind === "internal-simple" || + d.selector.kind === "external-simple" + ) { const arg = d.selector.arg; - const internal = d.selector.kind === 'internal-simple'; + const internal = d.selector.kind === "internal-simple"; // Check argument type - if (arg.type.kind !== 'type_ref_simple') { - throwError('Receive function can only accept message', d.ref); + if (arg.type.kind !== "type_ref_simple") { + throwError( + "Receive function can only accept message", + d.ref, + ); } if (arg.type.optional) { - throwError('Receive function cannot have optional argument', d.ref); + throwError( + "Receive function cannot have optional argument", + d.ref, + ); } // Check resolved argument type const t = types.get(arg.type.name); if (!t) { - throwError('Type ' + arg.type.name + ' not found', d.ref); + throwError( + "Type " + arg.type.name + " not found", + d.ref, + ); } // Raw receiver - if (t.kind === 'primitive') { - - if (t.name === 'Slice') { - + if (t.kind === "primitive") { + if (t.name === "Slice") { // Check for existing receiver - if (s.receivers.find((v) => v.selector.kind === (internal ? 'internal-fallback' : 'external-fallback'))) { - throwError(`Fallback receive function already exists`, d.ref); + if ( + s.receivers.find( + (v) => + v.selector.kind === + (internal + ? "internal-fallback" + : "external-fallback"), + ) + ) { + throwError( + `Fallback receive function already exists`, + d.ref, + ); } // Persist receiver s.receivers.push({ selector: { - kind: internal ? 'internal-fallback' : 'external-fallback', - name: arg.name + kind: internal + ? "internal-fallback" + : "external-fallback", + name: arg.name, }, - ast: d + ast: d, }); - - } else if (t.name === 'String') { - + } else if (t.name === "String") { // Check for existing receiver - if (s.receivers.find((v) => v.selector.kind === (internal ? 'internal-comment-fallback' : 'external-comment-fallback'))) { - throwError('Comment fallback receive function already exists', d.ref); + if ( + s.receivers.find( + (v) => + v.selector.kind === + (internal + ? "internal-comment-fallback" + : "external-comment-fallback"), + ) + ) { + throwError( + "Comment fallback receive function already exists", + d.ref, + ); } // Persist receiver s.receivers.push({ selector: { - kind: (internal ? 'internal-comment-fallback' : 'external-comment-fallback'), - name: arg.name + kind: internal + ? "internal-comment-fallback" + : "external-comment-fallback", + name: arg.name, }, - ast: d + ast: d, }); } else { - throwError('Receive function can only accept message, Slice or String', d.ref); + throwError( + "Receive function can only accept message, Slice or String", + d.ref, + ); } } else { - // Check type - if (t.kind !== 'struct') { - throwError('Receive function can only accept message', d.ref); + if (t.kind !== "struct") { + throwError( + "Receive function can only accept message", + d.ref, + ); } - if (t.ast.kind !== 'def_struct') { - throwError('Receive function can only accept message', d.ref); + if (t.ast.kind !== "def_struct") { + throwError( + "Receive function can only accept message", + d.ref, + ); } if (!t.ast.message) { - throwError('Receive function can only accept message', d.ref); + throwError( + "Receive function can only accept message", + d.ref, + ); } // Check for duplicate const n = arg.type.name; - if (s.receivers.find((v) => v.selector.kind === (internal ? 'internal-binary' : 'external-binary') && v.selector.name === n)) { - throwError(`Receive function for ${arg.type.name} already exists`, d.ref); + if ( + s.receivers.find( + (v) => + v.selector.kind === + (internal + ? "internal-binary" + : "external-binary") && + v.selector.name === n, + ) + ) { + throwError( + `Receive function for ${arg.type.name} already exists`, + d.ref, + ); } // Persist receiver s.receivers.push({ selector: { - kind: (internal ? 'internal-binary' : 'external-binary'), + kind: internal + ? "internal-binary" + : "external-binary", name: arg.name, type: arg.type.name, }, - ast: d + ast: d, }); } - } else if (d.selector.kind === 'internal-comment' || d.selector.kind === 'external-comment') { - const internal = d.selector.kind === 'internal-comment'; - if (d.selector.comment.value === '') { - throwError('To use empty comment receiver, just remove argument instead of passing empty string', d.ref); + } else if ( + d.selector.kind === "internal-comment" || + d.selector.kind === "external-comment" + ) { + const internal = d.selector.kind === "internal-comment"; + if (d.selector.comment.value === "") { + throwError( + "To use empty comment receiver, just remove argument instead of passing empty string", + d.ref, + ); } const c = d.selector.comment.value; - if (s.receivers.find((v) => v.selector.kind === (internal ? 'internal-comment' : 'external-comment') && v.selector.comment === c)) { - throwError(`Receive function for "${c}" already exists`, d.ref); + if ( + s.receivers.find( + (v) => + v.selector.kind === + (internal + ? "internal-comment" + : "external-comment") && + v.selector.comment === c, + ) + ) { + throwError( + `Receive function for "${c}" already exists`, + d.ref, + ); } s.receivers.push({ selector: { - kind: (internal ? 'internal-comment' : 'external-comment'), - comment: c + kind: internal + ? "internal-comment" + : "external-comment", + comment: c, }, - ast: d + ast: d, }); - } else if (d.selector.kind === 'internal-fallback') { - const internal = d.selector.kind === 'internal-fallback'; + } else if (d.selector.kind === "internal-fallback") { + const internal = + d.selector.kind === "internal-fallback"; // Handle empty - if (s.receivers.find((v) => v.selector.kind === (internal ? 'internal-empty' : 'external-empty'))) { - throwError('Empty receive function already exists', d.ref); + if ( + s.receivers.find( + (v) => + v.selector.kind === + (internal + ? "internal-empty" + : "external-empty"), + ) + ) { + throwError( + "Empty receive function already exists", + d.ref, + ); } s.receivers.push({ selector: { - kind: (internal ? 'internal-empty' : 'external-empty') + kind: internal + ? "internal-empty" + : "external-empty", }, - ast: d + ast: d, }); - } else if (d.selector.kind === 'bounce') { + } else if (d.selector.kind === "bounce") { const arg = d.selector.arg; // If argument is a direct reference if (arg.type.kind === "type_ref_simple") { - if (arg.type.optional) { - throwError('Bounce receive function cannot have optional argument', d.ref); + throwError( + "Bounce receive function cannot have optional argument", + d.ref, + ); } if (arg.type.name === "Slice") { - - if (s.receivers.find((v) => v.selector.kind === 'bounce-fallback')) { - throwError(`Fallback bounce receive function already exists`, d.ref); + if ( + s.receivers.find( + (v) => + v.selector.kind === + "bounce-fallback", + ) + ) { + throwError( + `Fallback bounce receive function already exists`, + d.ref, + ); } s.receivers.push({ - selector: { kind: 'bounce-fallback', name: arg.name }, - ast: d + selector: { + kind: "bounce-fallback", + name: arg.name, + }, + ast: d, }); } else { const type = types.get(arg.type.name)!; - if (type.ast.kind !== 'def_struct' || !type.ast.message) { - throwError('Bounce receive function can only accept bounced message, message or Slice', d.ref); + if ( + type.ast.kind !== "def_struct" || + !type.ast.message + ) { + throwError( + "Bounce receive function can only accept bounced message, message or Slice", + d.ref, + ); } - if (type.fields.length !== type.partialFieldCount) { - throwError('This message is too big for bounce receiver, you need to wrap it to a bounced<' + arg.type.name + '>.', d.ref); + if ( + type.fields.length !== + type.partialFieldCount + ) { + throwError( + "This message is too big for bounce receiver, you need to wrap it to a bounced<" + + arg.type.name + + ">.", + d.ref, + ); } - if (s.receivers.find((v) => v.selector.kind === 'bounce-binary' && v.selector.name === type.name)) { - throwError(`Bounce receive function for ${arg.type.name} already exists`, d.ref); + if ( + s.receivers.find( + (v) => + v.selector.kind === + "bounce-binary" && + v.selector.name === type.name, + ) + ) { + throwError( + `Bounce receive function for ${arg.type.name} already exists`, + d.ref, + ); } s.receivers.push({ selector: { - kind: 'bounce-binary', + kind: "bounce-binary", name: arg.name, type: arg.type.name, - bounced: false + bounced: false, }, - ast: d + ast: d, }); } - } else if (arg.type.kind === "type_ref_bounced") { const t = types.get(arg.type.name)!; - if (t.kind !== 'struct') { - throwError('Bounce receive function can only accept bounced struct types', d.ref); + if (t.kind !== "struct") { + throwError( + "Bounce receive function can only accept bounced struct types", + d.ref, + ); } - if (t.ast.kind !== 'def_struct') { - throwError('Bounce receive function can only accept bounced struct types', d.ref); + if (t.ast.kind !== "def_struct") { + throwError( + "Bounce receive function can only accept bounced struct types", + d.ref, + ); } if (!t.ast.message) { - throwError('Bounce receive function can only accept bounced message, message or Slice', d.ref); + throwError( + "Bounce receive function can only accept bounced message, message or Slice", + d.ref, + ); } - if (s.receivers.find((v) => v.selector.kind === 'bounce-binary' && v.selector.type === t.name)) { - throwError(`Bounce receive function for ${t.name} already exists`, d.ref); + if ( + s.receivers.find( + (v) => + v.selector.kind === "bounce-binary" && + v.selector.type === t.name, + ) + ) { + throwError( + `Bounce receive function for ${t.name} already exists`, + d.ref, + ); } if (t.fields.length === t.partialFieldCount) { - throwError('This message is small enough for bounce receiver, you need to remove bounced modifier.', d.ref); + throwError( + "This message is small enough for bounce receiver, you need to remove bounced modifier.", + d.ref, + ); } s.receivers.push({ selector: { - kind: 'bounce-binary', + kind: "bounce-binary", name: arg.name, type: arg.type.name, - bounced: true + bounced: true, }, - ast: d + ast: d, }); } else { - throwError('Bounce receive function can only accept bounced struct args or Slice', d.ref); + throwError( + "Bounce receive function can only accept bounced struct args or Slice", + d.ref, + ); } } else { - throwError('Invalid receive function selector', d.ref); + throwError("Invalid receive function selector", d.ref); } } } @@ -794,17 +1134,17 @@ export function resolveDescriptors(ctx: CompilerContext) { // for (const t of types.values()) { - if (t.kind === 'contract') { + if (t.kind === "contract") { if (!t.init) { t.init = { args: [], ast: createNode({ - kind: 'def_init_function', + kind: "def_init_function", args: [], statements: [], - ref: t.ast.ref - }) as ASTInitFunction - } + ref: t.ast.ref, + }) as ASTInitFunction, + }; } } } @@ -814,8 +1154,7 @@ export function resolveDescriptors(ctx: CompilerContext) { // for (const t of types.values()) { - if (t.ast.kind === 'def_trait' || t.ast.kind === 'def_contract') { - + if (t.ast.kind === "def_trait" || t.ast.kind === "def_contract") { // Flatten traits const traits: TypeDescription[] = []; const visited = new Set(); @@ -827,11 +1166,11 @@ export function resolveDescriptors(ctx: CompilerContext) { } const tt = types.get(name); if (!tt) { - throwError('Trait ' + name + ' not found', t.ast.ref) + throwError("Trait " + name + " not found", t.ast.ref); } visited.add(name); traits.push(tt); - if (tt.ast.kind === 'def_trait') { + if (tt.ast.kind === "def_trait") { for (const s of tt.ast.traits) { visit(s.value); } @@ -839,10 +1178,10 @@ export function resolveDescriptors(ctx: CompilerContext) { visit(f.name); } } else { - throwError('Type ' + name + ' is not a trait', t.ast.ref); + throwError("Type " + name + " is not a trait", t.ast.ref); } } - visit('BaseTrait'); + visit("BaseTrait"); for (const s of t.ast.traits) { visit(s.value); } @@ -858,28 +1197,32 @@ export function resolveDescriptors(ctx: CompilerContext) { for (const t of types.values()) { for (const tr of t.traits) { - // Check that trait is valid if (!types.has(tr.name)) { - throwError('Trait ' + tr.name + ' not found', t.ast.ref); + throwError("Trait " + tr.name + " not found", t.ast.ref); } - if (types.get(tr.name)!.kind !== 'trait') { - throwError('Type ' + tr.name + ' is not a trait', t.ast.ref); + if (types.get(tr.name)!.kind !== "trait") { + throwError("Type " + tr.name + " is not a trait", t.ast.ref); } // Check that trait has all required fields const ttr = types.get(tr.name)!; for (const f of ttr.fields) { - // Check if field exists const ex = t.fields.find((v) => v.name === f.name); if (!ex) { - throwError(`Trait ${tr.name} requires field ${f.name}`, t.ast.ref); + throwError( + `Trait ${tr.name} requires field ${f.name}`, + t.ast.ref, + ); } // Check type if (!typeRefEquals(f.type, ex.type)) { - throwError(`Trait ${tr.name} requires field ${f.name} of type ${printTypeRef(f.type)}`, t.ast.ref); + throwError( + `Trait ${tr.name} requires field ${f.name} of type ${printTypeRef(f.type)}`, + t.ast.ref, + ); } } } @@ -891,33 +1234,50 @@ export function resolveDescriptors(ctx: CompilerContext) { function copyTraits(t: TypeDescription) { for (const tr of t.traits) { - // Copy functions for (const f of tr.functions.values()) { const ex = t.functions.get(f.name); if (!ex && f.isAbstract) { - throwError(`Trait ${tr.name} requires function ${f.name}`, t.ast.ref); + throwError( + `Trait ${tr.name} requires function ${f.name}`, + t.ast.ref, + ); } // Check overrides if (ex && ex.isOverrides) { if (f.isGetter) { - throwError(`Overridden function ${f.name} can not be a getter`, ex.ast.ref); + throwError( + `Overridden function ${f.name} can not be a getter`, + ex.ast.ref, + ); } if (f.isMutating !== ex.isMutating) { - throwError(`Overridden function ${f.name} should have same mutability`, ex.ast.ref); + throwError( + `Overridden function ${f.name} should have same mutability`, + ex.ast.ref, + ); } if (!typeRefEquals(f.returns, ex.returns)) { - throwError(`Overridden function ${f.name} should have same return type`, ex.ast.ref); + throwError( + `Overridden function ${f.name} should have same return type`, + ex.ast.ref, + ); } if (f.args.length !== ex.args.length) { - throwError(`Overridden function ${f.name} should have same number of arguments`, ex.ast.ref); + throwError( + `Overridden function ${f.name} should have same number of arguments`, + ex.ast.ref, + ); } for (let i = 0; i < f.args.length; i++) { const a = ex.args[i]; const b = f.args[i]; if (!typeRefEquals(a.type, b.type)) { - throwError(`Overridden function ${f.name} should have same argument types`, ex.ast.ref); + throwError( + `Overridden function ${f.name} should have same argument types`, + ex.ast.ref, + ); } } continue; // Ignore overridden functions @@ -925,77 +1285,126 @@ export function resolveDescriptors(ctx: CompilerContext) { // Check duplicates if (ex) { - throwError(`Function ${f.name} already exist in ${t.name}`, t.ast.ref); + throwError( + `Function ${f.name} already exist in ${t.name}`, + t.ast.ref, + ); } // Register function t.functions.set(f.name, { ...f, self: t.name, - ast: cloneNode(f.ast) + ast: cloneNode(f.ast), }); } // Copy constants for (const f of tr.constants) { const ex = t.constants.find((v) => v.name === f.name); - if (!ex && f.ast.attributes.find((v) => v.type === 'abstract')) { - throwError(`Trait ${tr.name} requires constant ${f.name}`, t.ast.ref); + if ( + !ex && + f.ast.attributes.find((v) => v.type === "abstract") + ) { + throwError( + `Trait ${tr.name} requires constant ${f.name}`, + t.ast.ref, + ); } // Check overrides - if (ex && ex.ast.attributes.find((v) => v.type === 'overrides')) { + if ( + ex && + ex.ast.attributes.find((v) => v.type === "overrides") + ) { if (!typeRefEquals(f.type, ex.type)) { - throwError(`Overridden constant ${f.name} should have same type`, ex.ast.ref); + throwError( + `Overridden constant ${f.name} should have same type`, + ex.ast.ref, + ); } continue; } // Check duplicates if (ex) { - throwError(`Constant ${f.name} already exist in ${t.name}`, t.ast.ref); + throwError( + `Constant ${f.name} already exist in ${t.name}`, + t.ast.ref, + ); } // Register constant t.constants.push({ ...f, - ast: cloneNode(f.ast) + ast: cloneNode(f.ast), }); } // Copy receivers for (const f of tr.receivers) { // eslint-disable-next-line no-inner-declarations - function sameReceiver(a: ReceiverSelector, b: ReceiverSelector) { - if (a.kind === 'internal-comment' && b.kind === 'internal-comment') { + function sameReceiver( + a: ReceiverSelector, + b: ReceiverSelector, + ) { + if ( + a.kind === "internal-comment" && + b.kind === "internal-comment" + ) { return a.comment === b.comment; } - if (a.kind === 'internal-binary' && b.kind === 'internal-binary') { + if ( + a.kind === "internal-binary" && + b.kind === "internal-binary" + ) { return a.type === b.type; } - if (a.kind === 'bounce-fallback' && b.kind === 'bounce-fallback') { + if ( + a.kind === "bounce-fallback" && + b.kind === "bounce-fallback" + ) { return true; // Could be only one } - if (a.kind === 'bounce-binary' && b.kind === 'bounce-binary') { + if ( + a.kind === "bounce-binary" && + b.kind === "bounce-binary" + ) { return a.type === b.type; } - if (a.kind === 'internal-empty' && b.kind === 'internal-empty') { + if ( + a.kind === "internal-empty" && + b.kind === "internal-empty" + ) { return true; } - if (a.kind === 'internal-fallback' && b.kind === 'internal-fallback') { + if ( + a.kind === "internal-fallback" && + b.kind === "internal-fallback" + ) { return true; } - if (a.kind === 'internal-comment-fallback' && b.kind === 'internal-comment-fallback') { + if ( + a.kind === "internal-comment-fallback" && + b.kind === "internal-comment-fallback" + ) { return true; } return false; } - if (t.receivers.find((v) => sameReceiver(v.selector, f.selector))) { - throwError(`Receive function for "${f.selector}" already exists`, t.ast.ref); + if ( + t.receivers.find((v) => + sameReceiver(v.selector, f.selector), + ) + ) { + throwError( + `Receive function for "${f.selector}" already exists`, + t.ast.ref, + ); } t.receivers.push({ selector: f.selector, - ast: cloneNode(f.ast) + ast: cloneNode(f.ast), }); } @@ -1014,18 +1423,22 @@ export function resolveDescriptors(ctx: CompilerContext) { const processing = new Set(); function processType(name: string) { - // Check if processed if (processed.has(name)) { return; } if (processing.has(name)) { - throwError(`Circular dependency detected for type ${name}`, types.get(name)!.ast.ref); + throwError( + `Circular dependency detected for type ${name}`, + types.get(name)!.ast.ref, + ); } processing.has(name); // Process dependencies first - const dependencies = Array.from(types.values()).filter((v) => v.traits.find((v2) => v2.name === name)); + const dependencies = Array.from(types.values()).filter((v) => + v.traits.find((v2) => v2.name === name), + ); for (const d of dependencies) { processType(d.name); } @@ -1048,13 +1461,13 @@ export function resolveDescriptors(ctx: CompilerContext) { for (const [k, t] of types) { const dependsOn = new Set(); const handler = (src: ASTNode) => { - if (src.kind === 'init_of') { + if (src.kind === "init_of") { if (!types.has(src.name)) { throwError(`Type ${src.name} not found`, src.ref); } dependsOn.add(src.name); } - } + }; // Traverse functions for (const f of t.functions.values()) { @@ -1105,12 +1518,18 @@ export function resolveDescriptors(ctx: CompilerContext) { const r = resolveFunctionDescriptor(null, a, a.origin); if (r.self) { if (types.get(r.self)!.functions.has(r.name)) { - throwError(`Function ${r.name} already exists in type ${r.self}`, r.ast.ref); + throwError( + `Function ${r.name} already exists in type ${r.self}`, + r.ast.ref, + ); } types.get(r.self)!.functions.set(r.name, r); } else { if (staticFunctions.has(r.name)) { - throwError(`Static function ${r.name} already exists`, r.ast.ref); + throwError( + `Static function ${r.name} already exists`, + r.ast.ref, + ); } if (staticConstants.has(r.name)) { throwError(`Static constant ${r.name} already exists`, a.ref); @@ -1153,7 +1572,7 @@ export function resolveDescriptors(ctx: CompilerContext) { export function getType(ctx: CompilerContext, name: string): TypeDescription { const r = store.get(ctx, name); if (!r) { - throw Error('Type ' + name + ' not found'); + throw Error("Type " + name + " not found"); } return r; } @@ -1163,13 +1582,18 @@ export function getAllTypes(ctx: CompilerContext) { } export function getContracts(ctx: CompilerContext) { - return Object.values(getAllTypes(ctx)).filter((v) => v.kind === 'contract').map((v) => v.name); + return Object.values(getAllTypes(ctx)) + .filter((v) => v.kind === "contract") + .map((v) => v.name); } -export function getStaticFunction(ctx: CompilerContext, name: string): FunctionDescription { +export function getStaticFunction( + ctx: CompilerContext, + name: string, +): FunctionDescription { const r = staticFunctionsStore.get(ctx, name); if (!r) { - throw Error('Static function ' + name + ' not found'); + throw Error("Static function " + name + " not found"); } return r; } @@ -1178,10 +1602,13 @@ export function hasStaticFunction(ctx: CompilerContext, name: string) { return !!staticFunctionsStore.get(ctx, name); } -export function getStaticConstant(ctx: CompilerContext, name: string): ConstantDescription { +export function getStaticConstant( + ctx: CompilerContext, + name: string, +): ConstantDescription { const r = staticConstantsStore.get(ctx, name); if (!r) { - throw Error('Static constant ' + name + ' not found'); + throw Error("Static constant " + name + " not found"); } return r; } @@ -1198,8 +1625,11 @@ export function getAllStaticConstants(ctx: CompilerContext) { return staticConstantsStore.all(ctx); } -export function resolvePartialFields(ctx: CompilerContext, type: TypeDescription) { - if (type.kind !== 'struct') return 0; +export function resolvePartialFields( + ctx: CompilerContext, + type: TypeDescription, +) { + if (type.kind !== "struct") return 0; let partialFieldsCount = 0; @@ -1234,4 +1664,4 @@ export function resolvePartialFields(ctx: CompilerContext, type: TypeDescription } return partialFieldsCount; -} \ No newline at end of file +} diff --git a/src/types/resolveErrors.ts b/src/types/resolveErrors.ts index 7137885c8..f0f46c6df 100644 --- a/src/types/resolveErrors.ts +++ b/src/types/resolveErrors.ts @@ -2,39 +2,48 @@ import { sha256_sync } from "@ton/crypto"; import { CompilerContext, createContextStore } from "../context"; import { ASTNode, traverse } from "../grammar/ast"; import { resolveConstantValue } from "./resolveConstantValue"; -import { getAllStaticConstants, getAllStaticFunctions, getAllTypes } from "./resolveDescriptors"; +import { + getAllStaticConstants, + getAllStaticFunctions, + getAllTypes, +} from "./resolveDescriptors"; -const exceptions = createContextStore<{ value: string, id: number }>(); +const exceptions = createContextStore<{ value: string; id: number }>(); function stringId(src: string): number { return sha256_sync(src).readUInt32BE(0); } function exceptionId(src: string): number { - return stringId(src) % 63000 + 1000; + return (stringId(src) % 63000) + 1000; } function resolveStringsInAST(ast: ASTNode, ctx: CompilerContext) { traverse(ast, (node) => { - if (node.kind === 'op_static_call' && node.name === 'require') { + if (node.kind === "op_static_call" && node.name === "require") { if (node.args.length !== 2) { return; } - const resolved = resolveConstantValue({ kind: 'ref', name: 'String', optional: false }, node.args[1], ctx) as string; + const resolved = resolveConstantValue( + { kind: "ref", name: "String", optional: false }, + node.args[1], + ctx, + ) as string; if (!exceptions.get(ctx, resolved)) { const id = exceptionId(resolved); - if (Object.values(exceptions.all(ctx)).find((v) => v.id === id)) { + if ( + Object.values(exceptions.all(ctx)).find((v) => v.id === id) + ) { throw new Error(`Duplicate error id: ${resolved}`); } ctx = exceptions.set(ctx, resolved, { value: resolved, id }); } } - }) + }); return ctx; } export function resolveErrors(ctx: CompilerContext) { - // Process all static functions for (const f of Object.values(getAllStaticFunctions(ctx))) { ctx = resolveStringsInAST(f.ast, ctx); @@ -47,7 +56,6 @@ export function resolveErrors(ctx: CompilerContext) { // Process all types for (const t of Object.values(getAllTypes(ctx))) { - // Process fields for (const f of Object.values(t.fields)) { ctx = resolveStringsInAST(f.ast, ctx); @@ -87,4 +95,4 @@ export function getErrorId(value: string, ctx: CompilerContext) { throw new Error(`Error not found: ${value}`); } return ex.id; -} \ No newline at end of file +} diff --git a/src/types/resolveExpression.ts b/src/types/resolveExpression.ts index 871f97082..d57bcbd30 100644 --- a/src/types/resolveExpression.ts +++ b/src/types/resolveExpression.ts @@ -1,63 +1,127 @@ -import { ASTBoolean, ASTExpression, ASTInitOf, ASTLvalueRef, ASTNull, ASTNumber, ASTOpBinary, ASTOpCall, ASTOpCallStatic, ASTOpField, ASTOpNew, ASTOpUnary, ASTString, throwError, ASTConditional } from '../grammar/ast'; +import { + ASTBoolean, + ASTExpression, + ASTInitOf, + ASTLvalueRef, + ASTNull, + ASTNumber, + ASTOpBinary, + ASTOpCall, + ASTOpCallStatic, + ASTOpField, + ASTOpNew, + ASTOpUnary, + ASTString, + throwError, + ASTConditional, +} from "../grammar/ast"; import { CompilerContext, createContextStore } from "../context"; -import { getStaticConstant, getStaticFunction, getType, hasStaticConstant, hasStaticFunction } from "./resolveDescriptors"; -import { FieldDescription, printTypeRef, TypeRef, typeRefEquals } from "./types"; +import { + getStaticConstant, + getStaticFunction, + getType, + hasStaticConstant, + hasStaticFunction, +} from "./resolveDescriptors"; +import { + FieldDescription, + printTypeRef, + TypeRef, + typeRefEquals, +} from "./types"; import { StatementContext } from "./resolveStatements"; import { MapFunctions } from "../abi/map"; import { GlobalFunctions } from "../abi/global"; import { isAssignable } from "./isAssignable"; import { StructFunctions } from "../abi/struct"; -const store = createContextStore<{ ast: ASTExpression, description: TypeRef }>(); +const store = createContextStore<{ + ast: ASTExpression; + description: TypeRef; +}>(); export function getExpType(ctx: CompilerContext, exp: ASTExpression) { const t = store.get(ctx, exp.id); if (!t) { - throw Error('Expression ' + exp.id + ' not found'); + throw Error("Expression " + exp.id + " not found"); } return t.description; } -function registerExpType(ctx: CompilerContext, exp: ASTExpression, description: TypeRef): CompilerContext { +function registerExpType( + ctx: CompilerContext, + exp: ASTExpression, + description: TypeRef, +): CompilerContext { const ex = store.get(ctx, exp.id); if (ex) { if (typeRefEquals(ex.description, description)) { return ctx; } - throw Error('Expression ' + exp.id + ' already has a type'); + throw Error("Expression " + exp.id + " already has a type"); } return store.set(ctx, exp.id, { ast: exp, description }); } -function resolveBooleanLiteral(exp: ASTBoolean, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - return registerExpType(ctx, exp, { kind: 'ref', name: 'Bool', optional: false }); +function resolveBooleanLiteral( + exp: ASTBoolean, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { + return registerExpType(ctx, exp, { + kind: "ref", + name: "Bool", + optional: false, + }); } -function resolveIntLiteral(exp: ASTNumber, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - return registerExpType(ctx, exp, { kind: 'ref', name: 'Int', optional: false }); +function resolveIntLiteral( + exp: ASTNumber, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { + return registerExpType(ctx, exp, { + kind: "ref", + name: "Int", + optional: false, + }); } -function resolveNullLiteral(exp: ASTNull, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - return registerExpType(ctx, exp, { kind: 'null' }); +function resolveNullLiteral( + exp: ASTNull, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { + return registerExpType(ctx, exp, { kind: "null" }); } -function resolveStringLiteral(exp: ASTString, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - return registerExpType(ctx, exp, { kind: 'ref', name: 'String', optional: false }); +function resolveStringLiteral( + exp: ASTString, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { + return registerExpType(ctx, exp, { + kind: "ref", + name: "String", + optional: false, + }); } -function resolveStructNew(exp: ASTOpNew, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - +function resolveStructNew( + exp: ASTOpNew, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { // Get type const tp = getType(ctx, exp.type); - if (tp.kind !== 'struct') { + if (tp.kind !== "struct") { throwError(`Invalid type "${exp.type}" for construction`, exp.ref); } // Process fields const processed = new Set(); for (const e of exp.args) { - // Check duplicates if (processed.has(e.name)) { throwError(`Duplicate fields "${e.name}"`, e.ref); @@ -76,23 +140,36 @@ function resolveStructNew(exp: ASTOpNew, sctx: StatementContext, ctx: CompilerCo // Check expression type const expressionType = getExpType(ctx, e.exp); if (!isAssignable(expressionType, f.type)) { - throwError(`Invalid type "${printTypeRef(expressionType)}" for fields "${e.name}" with type ${printTypeRef(f.type)} in type ${tp.name}`, e.ref); + throwError( + `Invalid type "${printTypeRef(expressionType)}" for fields "${e.name}" with type ${printTypeRef(f.type)} in type ${tp.name}`, + e.ref, + ); } } // Check missing fields for (const f of tp.fields) { if (f.default === undefined && !processed.has(f.name)) { - throwError(`Missing fields "${f.name}" in type ${tp.name}`, exp.ref); + throwError( + `Missing fields "${f.name}" in type ${tp.name}`, + exp.ref, + ); } } // Register result - return registerExpType(ctx, exp, { kind: 'ref', name: tp.name, optional: false }); + return registerExpType(ctx, exp, { + kind: "ref", + name: tp.name, + optional: false, + }); } -function resolveBinaryOp(exp: ASTOpBinary, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - +function resolveBinaryOp( + exp: ASTOpBinary, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { // Resolve left and right expressions ctx = resolveExpression(exp.left, sctx, ctx); ctx = resolveExpression(exp.right, sctx, ctx); @@ -101,108 +178,204 @@ function resolveBinaryOp(exp: ASTOpBinary, sctx: StatementContext, ctx: Compiler // Check operands let resolved: TypeRef; - if (exp.op === '-' || exp.op === '+' || exp.op === '*' || exp.op === '/' || exp.op === '%' || exp.op === '>>' || exp.op === '<<' || exp.op === '&' || exp.op === '|') { - if (le.kind !== 'ref' || le.optional || le.name !== 'Int') { - throwError(`Invalid type "${printTypeRef(le)}" for binary operator "${exp.op}"`, exp.ref); + if ( + exp.op === "-" || + exp.op === "+" || + exp.op === "*" || + exp.op === "/" || + exp.op === "%" || + exp.op === ">>" || + exp.op === "<<" || + exp.op === "&" || + exp.op === "|" + ) { + if (le.kind !== "ref" || le.optional || le.name !== "Int") { + throwError( + `Invalid type "${printTypeRef(le)}" for binary operator "${exp.op}"`, + exp.ref, + ); } - if (re.kind !== 'ref' || re.optional || re.name !== 'Int') { - throwError(`Invalid type "${printTypeRef(re)}" for binary operator "${exp.op}"`, exp.ref); + if (re.kind !== "ref" || re.optional || re.name !== "Int") { + throwError( + `Invalid type "${printTypeRef(re)}" for binary operator "${exp.op}"`, + exp.ref, + ); } - resolved = { kind: 'ref', name: 'Int', optional: false }; - } else if (exp.op === '<' || exp.op === '<=' || exp.op === '>' || exp.op === '>=') { - if (le.kind !== 'ref' || le.optional || le.name !== 'Int') { - throwError(`Invalid type "${printTypeRef(le)}" for binary operator "${exp.op}"`, exp.ref); + resolved = { kind: "ref", name: "Int", optional: false }; + } else if ( + exp.op === "<" || + exp.op === "<=" || + exp.op === ">" || + exp.op === ">=" + ) { + if (le.kind !== "ref" || le.optional || le.name !== "Int") { + throwError( + `Invalid type "${printTypeRef(le)}" for binary operator "${exp.op}"`, + exp.ref, + ); } - if (re.kind !== 'ref' || re.optional || re.name !== 'Int') { - throwError(`Invalid type "${printTypeRef(re)}" for binary operator "${exp.op}"`, exp.ref); + if (re.kind !== "ref" || re.optional || re.name !== "Int") { + throwError( + `Invalid type "${printTypeRef(re)}" for binary operator "${exp.op}"`, + exp.ref, + ); } - resolved = { kind: 'ref', name: 'Bool', optional: false }; - } else if (exp.op === '==' || exp.op === '!=') { - + resolved = { kind: "ref", name: "Bool", optional: false }; + } else if (exp.op === "==" || exp.op === "!=") { // Check if types are compatible - if (le.kind !== 'null' && re.kind !== 'null') { + if (le.kind !== "null" && re.kind !== "null") { const l = le; const r = re; - if (l.kind === 'map' && r.kind === 'map') { - if (l.key !== r.key || l.value !== r.value || l.keyAs !== r.keyAs || l.valueAs !== r.valueAs) { - throwError(`Incompatible types "${printTypeRef(le)}" and "${printTypeRef(re)}" for binary operator "${exp.op}"`, exp.ref); + if (l.kind === "map" && r.kind === "map") { + if ( + l.key !== r.key || + l.value !== r.value || + l.keyAs !== r.keyAs || + l.valueAs !== r.valueAs + ) { + throwError( + `Incompatible types "${printTypeRef(le)}" and "${printTypeRef(re)}" for binary operator "${exp.op}"`, + exp.ref, + ); } } else { - if (l.kind === 'ref_bounced' || r.kind === 'ref_bounced') { - throwError("Bounced types are not supported in binary operators", exp.ref); + if (l.kind === "ref_bounced" || r.kind === "ref_bounced") { + throwError( + "Bounced types are not supported in binary operators", + exp.ref, + ); } - if (l.kind !== 'ref' || r.kind !== 'ref') { - throwError(`Incompatible types "${printTypeRef(le)}" and "${printTypeRef(re)}" for binary operator "${exp.op}"`, exp.ref); + if (l.kind !== "ref" || r.kind !== "ref") { + throwError( + `Incompatible types "${printTypeRef(le)}" and "${printTypeRef(re)}" for binary operator "${exp.op}"`, + exp.ref, + ); } if (l.name !== r.name) { - throwError(`Incompatible types "${printTypeRef(le)}" and "${printTypeRef(re)}" for binary operator "${exp.op}"`, exp.ref); + throwError( + `Incompatible types "${printTypeRef(le)}" and "${printTypeRef(re)}" for binary operator "${exp.op}"`, + exp.ref, + ); } - if (r.name !== 'Int' && r.name !== 'Bool' && r.name !== 'Address' && r.name !== 'Cell' && r.name !== 'Slice' && r.name !== 'String') { - throwError(`Invalid type "${r.name}" for binary operator "${exp.op}"`, exp.ref); + if ( + r.name !== "Int" && + r.name !== "Bool" && + r.name !== "Address" && + r.name !== "Cell" && + r.name !== "Slice" && + r.name !== "String" + ) { + throwError( + `Invalid type "${r.name}" for binary operator "${exp.op}"`, + exp.ref, + ); } } } - resolved = { kind: 'ref', name: 'Bool', optional: false }; - } else if (exp.op === '&&' || exp.op === '||') { - if (le.kind !== 'ref' || le.optional || le.name !== 'Bool') { - throwError(`Invalid type "${printTypeRef(le)}" for binary operator "${exp.op}"`, exp.ref); + resolved = { kind: "ref", name: "Bool", optional: false }; + } else if (exp.op === "&&" || exp.op === "||") { + if (le.kind !== "ref" || le.optional || le.name !== "Bool") { + throwError( + `Invalid type "${printTypeRef(le)}" for binary operator "${exp.op}"`, + exp.ref, + ); } - if (re.kind !== 'ref' || re.optional || re.name !== 'Bool') { - throwError(`Invalid type "${printTypeRef(re)}" for binary operator "${exp.op}"`, exp.ref); + if (re.kind !== "ref" || re.optional || re.name !== "Bool") { + throwError( + `Invalid type "${printTypeRef(re)}" for binary operator "${exp.op}"`, + exp.ref, + ); } - resolved = { kind: 'ref', name: 'Bool', optional: false }; + resolved = { kind: "ref", name: "Bool", optional: false }; } else { - throw Error('Unsupported operator: ' + exp.op); + throw Error("Unsupported operator: " + exp.op); } // Register result return registerExpType(ctx, exp, resolved); } -function resolveUnaryOp(exp: ASTOpUnary, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - +function resolveUnaryOp( + exp: ASTOpUnary, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { // Resolve right side ctx = resolveExpression(exp.right, sctx, ctx); // Check right type dependent on operator let resolvedType = getExpType(ctx, exp.right); - if (exp.op === '-' || exp.op === '+') { - if (resolvedType.kind !== 'ref' || resolvedType.optional || resolvedType.name !== 'Int') { - throwError(`Invalid type "${printTypeRef(resolvedType)}" for unary operator "${exp.op}"`, exp.ref); + if (exp.op === "-" || exp.op === "+") { + if ( + resolvedType.kind !== "ref" || + resolvedType.optional || + resolvedType.name !== "Int" + ) { + throwError( + `Invalid type "${printTypeRef(resolvedType)}" for unary operator "${exp.op}"`, + exp.ref, + ); } - } else if (exp.op === '!') { - if (resolvedType.kind !== 'ref' || resolvedType.optional || resolvedType.name !== 'Bool') { - throwError(`Invalid type "${printTypeRef(resolvedType)}" for unary operator "${exp.op}"`, exp.ref); + } else if (exp.op === "!") { + if ( + resolvedType.kind !== "ref" || + resolvedType.optional || + resolvedType.name !== "Bool" + ) { + throwError( + `Invalid type "${printTypeRef(resolvedType)}" for unary operator "${exp.op}"`, + exp.ref, + ); } - } else if (exp.op === '!!') { - if (resolvedType.kind !== 'ref' || !resolvedType.optional) { - throwError(`Type "${printTypeRef(resolvedType)}" is not optional`, exp.ref); + } else if (exp.op === "!!") { + if (resolvedType.kind !== "ref" || !resolvedType.optional) { + throwError( + `Type "${printTypeRef(resolvedType)}" is not optional`, + exp.ref, + ); } - resolvedType = { kind: 'ref', name: resolvedType.name, optional: false }; + resolvedType = { + kind: "ref", + name: resolvedType.name, + optional: false, + }; } else { - throwError('Unknown operator ' + exp.op, exp.ref); + throwError("Unknown operator " + exp.op, exp.ref); } // Register result return registerExpType(ctx, exp, resolvedType); } -function resolveField(exp: ASTOpField, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - +function resolveField( + exp: ASTOpField, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { // Resolve expression ctx = resolveExpression(exp.src, sctx, ctx); // Find target type and check for type const src = getExpType(ctx, exp.src); - if (src === null || ((src.kind !== 'ref' || src.optional) && (src.kind !== 'ref_bounced'))) { - throwError(`Invalid type "${printTypeRef(src)}" for field access`, exp.ref); + if ( + src === null || + ((src.kind !== "ref" || src.optional) && src.kind !== "ref_bounced") + ) { + throwError( + `Invalid type "${printTypeRef(src)}" for field access`, + exp.ref, + ); } // Check if field initialized - if (sctx.requiredFields.length > 0 && exp.src.kind === 'id' && exp.src.value === 'self') { + if ( + sctx.requiredFields.length > 0 && + exp.src.kind === "id" && + exp.src.value === "self" + ) { if (sctx.requiredFields.find((v) => v === exp.name)) { throwError(`Field "${exp.name}" is not initialized`, exp.ref); } @@ -214,17 +387,23 @@ function resolveField(exp: ASTOpField, sctx: StatementContext, ctx: CompilerCont const srcT = getType(ctx, src.name); fields = srcT.fields; - if (src.kind === 'ref_bounced') { + if (src.kind === "ref_bounced") { fields = fields.slice(0, srcT.partialFieldCount); } const field = fields.find((v) => v.name === exp.name); const cst = srcT.constants.find((v) => v.name === exp.name); if (!field && !cst) { - if (src.kind === 'ref_bounced') { - throwError(`Type bounced<"${src.name}"> does not have a field named "${exp.name}"`, exp.ref); + if (src.kind === "ref_bounced") { + throwError( + `Type bounced<"${src.name}"> does not have a field named "${exp.name}"`, + exp.ref, + ); } else { - throwError(`Type "${src.name}" does not have a field named "${exp.name}"`, exp.ref); + throwError( + `Type "${src.name}" does not have a field named "${exp.name}"`, + exp.ref, + ); } } @@ -236,8 +415,11 @@ function resolveField(exp: ASTOpField, sctx: StatementContext, ctx: CompilerCont } } -function resolveStaticCall(exp: ASTOpCallStatic, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - +function resolveStaticCall( + exp: ASTOpCallStatic, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { // Check if abi global function if (GlobalFunctions.has(exp.name)) { const f = GlobalFunctions.get(exp.name)!; @@ -248,7 +430,11 @@ function resolveStaticCall(exp: ASTOpCallStatic, sctx: StatementContext, ctx: Co } // Resolve return type - const resolved = f.resolve(ctx, exp.args.map((v) => getExpType(ctx, v)), exp.ref); + const resolved = f.resolve( + ctx, + exp.args.map((v) => getExpType(ctx, v)), + exp.ref, + ); // Register return type return registerExpType(ctx, exp, resolved); @@ -269,14 +455,20 @@ function resolveStaticCall(exp: ASTOpCallStatic, sctx: StatementContext, ctx: Co // Check arguments if (f.args.length !== exp.args.length) { - throwError(`Function "${exp.name}" expects ${f.args.length} arguments, got ${exp.args.length}`, exp.ref); + throwError( + `Function "${exp.name}" expects ${f.args.length} arguments, got ${exp.args.length}`, + exp.ref, + ); } for (let i = 0; i < f.args.length; i++) { const a = f.args[i]; const e = exp.args[i]; const t = getExpType(ctx, e); if (!isAssignable(t, a.type)) { - throwError(`Invalid type "${printTypeRef(t)}" for argument "${a.name}"`, e.ref); + throwError( + `Invalid type "${printTypeRef(t)}" for argument "${a.name}"`, + e.ref, + ); } } @@ -284,14 +476,21 @@ function resolveStaticCall(exp: ASTOpCallStatic, sctx: StatementContext, ctx: Co return registerExpType(ctx, exp, f.returns); } -function resolveCall(exp: ASTOpCall, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - +function resolveCall( + exp: ASTOpCall, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { // Resolve expression ctx = resolveExpression(exp.src, sctx, ctx); // Check if self is initialized - if (exp.src.kind === 'id' && exp.src.value === 'self' && (sctx.requiredFields.length > 0)) { - throwError('Cannot access self before init', exp.ref); + if ( + exp.src.kind === "id" && + exp.src.value === "self" && + sctx.requiredFields.length > 0 + ) { + throwError("Cannot access self before init", exp.ref); } // Resolve args @@ -302,43 +501,61 @@ function resolveCall(exp: ASTOpCall, sctx: StatementContext, ctx: CompilerContex // Resolve return value const src = getExpType(ctx, exp.src); if (src === null) { - throwError(`Invalid type "${printTypeRef(src)}" for function call`, exp.ref); + throwError( + `Invalid type "${printTypeRef(src)}" for function call`, + exp.ref, + ); } // Handle ref - if (src.kind === 'ref') { - + if (src.kind === "ref") { if (src.optional) { - throwError(`Invalid type "${printTypeRef(src)}" for function call`, exp.ref); + throwError( + `Invalid type "${printTypeRef(src)}" for function call`, + exp.ref, + ); } // Register return type const srcT = getType(ctx, src.name); // Check struct ABI - if (srcT.kind === 'struct') { + if (srcT.kind === "struct") { if (StructFunctions.has(exp.name)) { const abi = StructFunctions.get(exp.name)!; - const resolved = abi.resolve(ctx, [src, ...exp.args.map((v) => getExpType(ctx, v))], exp.ref); + const resolved = abi.resolve( + ctx, + [src, ...exp.args.map((v) => getExpType(ctx, v))], + exp.ref, + ); return registerExpType(ctx, exp, resolved); } } const f = srcT.functions.get(exp.name)!; if (!f) { - throwError(`Type "${src.name}" does not have a function named "${exp.name}"`, exp.ref); + throwError( + `Type "${src.name}" does not have a function named "${exp.name}"`, + exp.ref, + ); } // Check arguments if (f.args.length !== exp.args.length) { - throwError(`Function "${exp.name}" expects ${f.args.length} arguments, got ${exp.args.length}`, exp.ref); + throwError( + `Function "${exp.name}" expects ${f.args.length} arguments, got ${exp.args.length}`, + exp.ref, + ); } for (let i = 0; i < f.args.length; i++) { const a = f.args[i]; const e = exp.args[i]; const t = getExpType(ctx, e); if (!isAssignable(t, a.type)) { - throwError(`Invalid type "${printTypeRef(t)}" for argument "${a.name}"`, e.ref); + throwError( + `Invalid type "${printTypeRef(t)}" for argument "${a.name}"`, + e.ref, + ); } } @@ -346,31 +563,44 @@ function resolveCall(exp: ASTOpCall, sctx: StatementContext, ctx: CompilerContex } // Handle map - if (src.kind === 'map') { + if (src.kind === "map") { if (!MapFunctions.has(exp.name)) { throwError(`Map function "${exp.name}" not found`, exp.ref); } const abf = MapFunctions.get(exp.name)!; - const resolved = abf.resolve(ctx, [src, ...exp.args.map((v) => getExpType(ctx, v))], exp.ref); + const resolved = abf.resolve( + ctx, + [src, ...exp.args.map((v) => getExpType(ctx, v))], + exp.ref, + ); return registerExpType(ctx, exp, resolved); } - if (src.kind === 'ref_bounced') { + if (src.kind === "ref_bounced") { throwError(`Cannot call function on bounced value`, exp.ref); } - throwError(`Invalid type "${printTypeRef(src)}" for function call`, exp.ref); + throwError( + `Invalid type "${printTypeRef(src)}" for function call`, + exp.ref, + ); } -export function resolveInitOf(ast: ASTInitOf, sctx: StatementContext, ctx: CompilerContext): CompilerContext { - +export function resolveInitOf( + ast: ASTInitOf, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { // Resolve type const type = getType(ctx, ast.name); - if (type.kind !== 'contract') { + if (type.kind !== "contract") { throwError(`Type "${ast.name}" is not a contract`, ast.ref); } if (!type.init) { - throwError(`Contract "${ast.name}" does not have an init function`, ast.ref); + throwError( + `Contract "${ast.name}" does not have an init function`, + ast.ref, + ); } // Resolve args @@ -380,27 +610,48 @@ export function resolveInitOf(ast: ASTInitOf, sctx: StatementContext, ctx: Compi // Check arguments if (type.init.args.length !== ast.args.length) { - throwError(`Init function of "${type.name}" expects ${type.init.args.length} arguments, got ${ast.args.length}`, ast.ref); + throwError( + `Init function of "${type.name}" expects ${type.init.args.length} arguments, got ${ast.args.length}`, + ast.ref, + ); } for (let i = 0; i < type.init.args.length; i++) { const a = type.init.args[i]; const e = ast.args[i]; const t = getExpType(ctx, e); if (!isAssignable(t, a.type)) { - throwError(`Invalid type "${printTypeRef(t)}" for argument "${a.name}"`, e.ref); + throwError( + `Invalid type "${printTypeRef(t)}" for argument "${a.name}"`, + e.ref, + ); } } // Register return type - return registerExpType(ctx, ast, { kind: 'ref', name: 'StateInit', optional: false }); + return registerExpType(ctx, ast, { + kind: "ref", + name: "StateInit", + optional: false, + }); } -export function resolveConditional(ast: ASTConditional, sctx: StatementContext, ctx: CompilerContext): CompilerContext { +export function resolveConditional( + ast: ASTConditional, + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { // Resolve condition ctx = resolveExpression(ast.condition, sctx, ctx); const conditionType = getExpType(ctx, ast.condition); - if (conditionType.kind !== 'ref' || conditionType.optional || conditionType.name !== 'Bool') { - throwError(`Invalid type "${printTypeRef(conditionType)}" for ternary condition`, ast.condition.ref); + if ( + conditionType.kind !== "ref" || + conditionType.optional || + conditionType.name !== "Bool" + ) { + throwError( + `Invalid type "${printTypeRef(conditionType)}" for ternary condition`, + ast.condition.ref, + ); } // Resolve then and else branches @@ -409,14 +660,21 @@ export function resolveConditional(ast: ASTConditional, sctx: StatementContext, const thenType = getExpType(ctx, ast.thenBranch); const elseType = getExpType(ctx, ast.elseBranch); if (!typeRefEquals(thenType, elseType)) { - throwError(`Non-matching types "${printTypeRef(thenType)}" and "${printTypeRef(elseType)}" for ternary branches`, ast.elseBranch.ref); + throwError( + `Non-matching types "${printTypeRef(thenType)}" and "${printTypeRef(elseType)}" for ternary branches`, + ast.elseBranch.ref, + ); } // Register result return registerExpType(ctx, ast, thenType); } -export function resolveLValueRef(path: ASTLvalueRef[], sctx: StatementContext, ctx: CompilerContext): CompilerContext { +export function resolveLValueRef( + path: ASTLvalueRef[], + sctx: StatementContext, + ctx: CompilerContext, +): CompilerContext { const paths: ASTLvalueRef[] = path; let t = sctx.vars.get(paths[0].name); if (!t) { @@ -426,13 +684,19 @@ export function resolveLValueRef(path: ASTLvalueRef[], sctx: StatementContext, c // Paths for (let i = 1; i < paths.length; i++) { - if (t.kind !== 'ref' || t.optional) { - throwError(`Invalid type "${printTypeRef(t)}" for field access`, path[i].ref); + if (t.kind !== "ref" || t.optional) { + throwError( + `Invalid type "${printTypeRef(t)}" for field access`, + path[i].ref, + ); } const srcT = getType(ctx, t.name); const ex = srcT.fields.find((v) => v.name === paths[i].name); if (!ex) { - throwError('Field ' + paths[i].name + ' not found in type ' + srcT.name, path[i].ref); + throwError( + "Field " + paths[i].name + " not found in type " + srcT.name, + path[i].ref, + ); } ctx = registerExpType(ctx, paths[i], ex.type); t = ex.type; @@ -441,22 +705,25 @@ export function resolveLValueRef(path: ASTLvalueRef[], sctx: StatementContext, c return ctx; } -export function resolveExpression(exp: ASTExpression, sctx: StatementContext, ctx: CompilerContext) { - +export function resolveExpression( + exp: ASTExpression, + sctx: StatementContext, + ctx: CompilerContext, +) { // // Literals // - if (exp.kind === 'boolean') { + if (exp.kind === "boolean") { return resolveBooleanLiteral(exp, sctx, ctx); } - if (exp.kind === 'number') { + if (exp.kind === "number") { return resolveIntLiteral(exp, sctx, ctx); } - if (exp.kind === 'null') { + if (exp.kind === "null") { return resolveNullLiteral(exp, sctx, ctx); } - if (exp.kind === 'string') { + if (exp.kind === "string") { return resolveStringLiteral(exp, sctx, ctx); } @@ -464,7 +731,7 @@ export function resolveExpression(exp: ASTExpression, sctx: StatementContext, ct // Constructors // - if (exp.kind === 'op_new') { + if (exp.kind === "op_new") { return resolveStructNew(exp, sctx, ctx); } @@ -472,11 +739,11 @@ export function resolveExpression(exp: ASTExpression, sctx: StatementContext, ct // Binary, unary and suffix operations // - if (exp.kind === 'op_binary') { + if (exp.kind === "op_binary") { return resolveBinaryOp(exp, sctx, ctx); } - if (exp.kind === 'op_unary') { + if (exp.kind === "op_unary") { return resolveUnaryOp(exp, sctx, ctx); } @@ -484,13 +751,12 @@ export function resolveExpression(exp: ASTExpression, sctx: StatementContext, ct // References // - if (exp.kind === 'id') { - + if (exp.kind === "id") { // Find variable const v = sctx.vars.get(exp.value); if (!v) { if (!hasStaticConstant(ctx, exp.value)) { - throwError('Unable to resolve id ' + exp.value, exp.ref); + throwError("Unable to resolve id " + exp.value, exp.ref); } else { const cc = getStaticConstant(ctx, exp.value); return registerExpType(ctx, exp, cc.type); @@ -500,7 +766,7 @@ export function resolveExpression(exp: ASTExpression, sctx: StatementContext, ct return registerExpType(ctx, exp, v); } - if (exp.kind === 'op_field') { + if (exp.kind === "op_field") { return resolveField(exp, sctx, ctx); } @@ -508,23 +774,23 @@ export function resolveExpression(exp: ASTExpression, sctx: StatementContext, ct // Function calls // - if (exp.kind === 'op_static_call') { + if (exp.kind === "op_static_call") { return resolveStaticCall(exp, sctx, ctx); } - if (exp.kind === 'op_call') { + if (exp.kind === "op_call") { return resolveCall(exp, sctx, ctx); } - if (exp.kind === 'init_of') { + if (exp.kind === "init_of") { return resolveInitOf(exp, sctx, ctx); } - if (exp.kind === 'conditional') { + if (exp.kind === "conditional") { return resolveConditional(exp, sctx, ctx); } - throw Error('Unknown expression'); // Unreachable + throw Error("Unknown expression"); // Unreachable } export function getAllExpressionTypes(ctx: CompilerContext) { @@ -534,4 +800,4 @@ export function getAllExpressionTypes(ctx: CompilerContext) { res.push([a[e].ast.ref.contents, printTypeRef(a[e].description)]); } return res; -} \ No newline at end of file +} diff --git a/src/types/resolveSignatures.ts b/src/types/resolveSignatures.ts index 37b9c1383..68b9d07ea 100644 --- a/src/types/resolveSignatures.ts +++ b/src/types/resolveSignatures.ts @@ -1,132 +1,150 @@ -import * as changeCase from 'change-case'; +import * as changeCase from "change-case"; import { ABIField } from "@ton/core"; import { CompilerContext } from "../context"; -import { idToHex } from '../utils/idToHex'; +import { idToHex } from "../utils/idToHex"; import { newMessageId } from "../utils/newMessageId"; import { getAllTypes, getType } from "./resolveDescriptors"; export function resolveSignatures(ctx: CompilerContext) { - const types = getAllTypes(ctx); - const signatures = new Map(); - function createTypeFormat(type: string, format: string | number | boolean | null) { - if (type === 'int') { - if (typeof format === 'number') { + const signatures = new Map< + string, + { signature: string; tlb: string; id: number | null } + >(); + function createTypeFormat( + type: string, + format: string | number | boolean | null, + ) { + if (type === "int") { + if (typeof format === "number") { return `int${format}`; } else if (format !== null) { - throw Error('Unsupported int format ' + format); + throw Error("Unsupported int format " + format); } return `int`; - } else if (type === 'uint') { - if (typeof format === 'number') { + } else if (type === "uint") { + if (typeof format === "number") { return `uint${format}`; - } else if (format === 'coins') { + } else if (format === "coins") { return `coins`; } else if (format !== null) { - throw Error('Unsupported uint format ' + format); + throw Error("Unsupported uint format " + format); } return `uint`; - } else if (type === 'bool') { + } else if (type === "bool") { if (format !== null) { - throw Error('Unsupported bool format ' + format); + throw Error("Unsupported bool format " + format); } - return 'bool'; - } else if (type === 'address') { + return "bool"; + } else if (type === "address") { if (format !== null) { - throw Error('Unsupported address format ' + format); + throw Error("Unsupported address format " + format); + } + return "address"; + } else if (type === "cell") { + if (format === "remainder") { + return "remainder"; + } else if (format === "ref") { + return "^cell"; } - return 'address'; - } else if (type === 'cell') { - if (format === 'remainder') { - return 'remainder'; - } else if (format === 'ref') { - return '^cell'; - } if (format !== null) { - throw Error('Unsupported cell format ' + format); + if (format !== null) { + throw Error("Unsupported cell format " + format); } - return '^cell'; - } else if (type === 'slice') { - if (format === 'remainder') { - return 'remainder'; - } else if (format === 'ref') { - return '^slice'; + return "^cell"; + } else if (type === "slice") { + if (format === "remainder") { + return "remainder"; + } else if (format === "ref") { + return "^slice"; } else if (format !== null) { - throw Error('Unsupported slice format ' + format); + throw Error("Unsupported slice format " + format); } - return '^slice'; - } else if (type === 'builder') { - if (format === 'remainder') { - return 'remainder'; - } else if (format === 'ref') { - return '^slice'; + return "^slice"; + } else if (type === "builder") { + if (format === "remainder") { + return "remainder"; + } else if (format === "ref") { + return "^slice"; } else if (format !== null) { - throw Error('Unsupported builder format ' + format); + throw Error("Unsupported builder format " + format); } - return '^builder'; - } else if (type === 'string') { + return "^builder"; + } else if (type === "string") { if (format !== null) { - throw Error('Unsupported builder format ' + format); + throw Error("Unsupported builder format " + format); } - return '^string'; - } else if (type === 'fixed-bytes') { - if (typeof format === 'number') { + return "^string"; + } else if (type === "fixed-bytes") { + if (typeof format === "number") { return `fixed_bytes${format}`; } else if (format !== null) { - throw Error('Unsupported fixed-bytes format ' + format); + throw Error("Unsupported fixed-bytes format " + format); } - throw Error('Missing fixed-bytes format'); + throw Error("Missing fixed-bytes format"); } // Struct types const t = getType(ctx, type); - if (t.kind !== 'struct') { - throw Error('Unsupported type ' + type); + if (t.kind !== "struct") { + throw Error("Unsupported type " + type); } const s = createTupeSignature(type); - if (format === 'ref') { + if (format === "ref") { return `^${s.signature}`; } else if (format !== null) { - throw Error('Unsupported struct format ' + format); + throw Error("Unsupported struct format " + format); } return `${s.signature}`; } function createTLBField(src: ABIField) { - - if (src.type.kind === 'simple') { - let base = createTypeFormat(src.type.type, src.type.format ? src.type.format : null); + if (src.type.kind === "simple") { + let base = createTypeFormat( + src.type.type, + src.type.format ? src.type.format : null, + ); if (src.type.optional) { - base = 'Maybe ' + base; + base = "Maybe " + base; } - return src.name + ':' + base; + return src.name + ":" + base; } - if (src.type.kind === 'dict') { + if (src.type.kind === "dict") { if (src.type.format !== null && src.type.format !== undefined) { - throw Error('Unsupported map format ' + src.type.format); + throw Error("Unsupported map format " + src.type.format); } - const key = createTypeFormat(src.type.key, src.type.keyFormat ? src.type.keyFormat : null); - const value = createTypeFormat(src.type.value, src.type.valueFormat ? src.type.valueFormat : null); - return src.name + ':dict<' + key + ', ' + value + '>'; + const key = createTypeFormat( + src.type.key, + src.type.keyFormat ? src.type.keyFormat : null, + ); + const value = createTypeFormat( + src.type.value, + src.type.valueFormat ? src.type.valueFormat : null, + ); + return src.name + ":dict<" + key + ", " + value + ">"; } - throw Error('Unsupported ABI field'); + throw Error("Unsupported ABI field"); } - function createTupeSignature(name: string): { signature: string, tlb: string, id: number | null } { + function createTupeSignature(name: string): { + signature: string; + tlb: string; + id: number | null; + } { if (signatures.has(name)) { - return signatures.get(name)! + return signatures.get(name)!; } const t = getType(ctx, name); - if (t.kind !== 'struct') { - throw Error('Unsupported type ' + name); + if (t.kind !== "struct") { + throw Error("Unsupported type " + name); } const fields = t.fields.map((v) => createTLBField(v.abi)); // Calculate signature and method id - const signature = name + '{' + fields.join(',') + '}'; + const signature = name + "{" + fields.join(",") + "}"; let id: number | null = null; - if (t.ast.kind === 'def_struct' && t.ast.message) { + if (t.ast.kind === "def_struct" && t.ast.message) { if (t.ast.prefix !== null) { id = t.ast.prefix; } else { @@ -135,8 +153,9 @@ export function resolveSignatures(ctx: CompilerContext) { } // Calculate TLB - const tlbHeader = (id !== null ? changeCase.snakeCase(name) + '#' + idToHex(id) : '_'); - const tlb = tlbHeader + ' ' + fields.join(' ') + ' = ' + name; + const tlbHeader = + id !== null ? changeCase.snakeCase(name) + "#" + idToHex(id) : "_"; + const tlb = tlbHeader + " " + fields.join(" ") + " = " + name; signatures.set(name, { signature, id, tlb }); return { signature, id, tlb }; @@ -144,7 +163,7 @@ export function resolveSignatures(ctx: CompilerContext) { for (const k in types) { const t = types[k]; - if (t.kind === 'struct') { + if (t.kind === "struct") { const r = createTupeSignature(t.name); t.tlb = r.tlb; t.signature = r.signature; @@ -153,4 +172,4 @@ export function resolveSignatures(ctx: CompilerContext) { } return ctx; -} \ No newline at end of file +} diff --git a/src/types/resolveStatements.spec.ts b/src/types/resolveStatements.spec.ts index 922684180..85266076a 100644 --- a/src/types/resolveStatements.spec.ts +++ b/src/types/resolveStatements.spec.ts @@ -6,23 +6,31 @@ import { openContext } from "../grammar/store"; import { resolveStatements } from "./resolveStatements"; import { CompilerContext } from "../context"; -describe('resolveStatements', () => { +describe("resolveStatements", () => { beforeEach(() => { __DANGER_resetNodeId(); }); for (const r of loadCases(__dirname + "/stmts/")) { - it('should resolve statements for ' + r.name, () => { - let ctx = openContext(new CompilerContext(), [{ code: r.code, path: '', origin: 'user' }], []); + it("should resolve statements for " + r.name, () => { + let ctx = openContext( + new CompilerContext(), + [{ code: r.code, path: "", origin: "user" }], + [], + ); ctx = resolveDescriptors(ctx); ctx = resolveStatements(ctx); expect(getAllExpressionTypes(ctx)).toMatchSnapshot(); }); } for (const r of loadCases(__dirname + "/stmts-failed/")) { - it('should fail statements for ' + r.name, () => { - let ctx = openContext(new CompilerContext(), [{ code: r.code, path: '', origin: 'user' }], []); + it("should fail statements for " + r.name, () => { + let ctx = openContext( + new CompilerContext(), + [{ code: r.code, path: "", origin: "user" }], + [], + ); ctx = resolveDescriptors(ctx); expect(() => resolveStatements(ctx)).toThrowErrorMatchingSnapshot(); }); } -}); \ No newline at end of file +}); diff --git a/src/types/resolveStatements.ts b/src/types/resolveStatements.ts index 0f80759ca..f5c8548a6 100644 --- a/src/types/resolveStatements.ts +++ b/src/types/resolveStatements.ts @@ -1,13 +1,21 @@ import { CompilerContext } from "../context"; import { ASTCondition, ASTRef, ASTStatement, throwError } from "../grammar/ast"; import { isAssignable } from "./isAssignable"; -import { getAllStaticFunctions, getAllTypes, resolveTypeRef } from "./resolveDescriptors"; -import { getExpType, resolveExpression, resolveLValueRef } from "./resolveExpression"; +import { + getAllStaticFunctions, + getAllTypes, + resolveTypeRef, +} from "./resolveDescriptors"; +import { + getExpType, + resolveExpression, + resolveLValueRef, +} from "./resolveExpression"; import { printTypeRef, TypeRef } from "./types"; export type StatementContext = { - root: ASTRef, - returns: TypeRef, + root: ASTRef; + returns: TypeRef; vars: Map; requiredFields: string[]; }; @@ -17,46 +25,56 @@ function emptyContext(root: ASTRef, returns: TypeRef): StatementContext { root, returns, vars: new Map(), - requiredFields: [] + requiredFields: [], }; } -function addRequiredVariables(name: string, src: StatementContext): StatementContext { +function addRequiredVariables( + name: string, + src: StatementContext, +): StatementContext { if (src.requiredFields.find((v) => v === name)) { - throw Error('Variable already exists: ' + name); // Should happen earlier + throw Error("Variable already exists: " + name); // Should happen earlier } return { ...src, - requiredFields: [ - ...src.requiredFields, - name - ] + requiredFields: [...src.requiredFields, name], }; } -function removeRequiredVariable(name: string, src: StatementContext): StatementContext { +function removeRequiredVariable( + name: string, + src: StatementContext, +): StatementContext { if (!src.requiredFields.find((v) => v === name)) { - throw Error('Variable is not required: ' + name); // Should happen earlier + throw Error("Variable is not required: " + name); // Should happen earlier } const filtered = src.requiredFields.filter((v) => v !== name); return { ...src, - requiredFields: filtered + requiredFields: filtered, }; } -function addVariable(name: string, ref: TypeRef, src: StatementContext): StatementContext { +function addVariable( + name: string, + ref: TypeRef, + src: StatementContext, +): StatementContext { if (src.vars.has(name)) { - throw Error('Variable already exists: ' + name); // Should happen earlier + throw Error("Variable already exists: " + name); // Should happen earlier } return { ...src, - vars: new Map(src.vars).set(name, ref) - } + vars: new Map(src.vars).set(name, ref), + }; } -function processCondition(condition: ASTCondition, sctx: StatementContext, ctx: CompilerContext): { ctx: CompilerContext, sctx: StatementContext } { - +function processCondition( + condition: ASTCondition, + sctx: StatementContext, + ctx: CompilerContext, +): { ctx: CompilerContext; sctx: StatementContext } { // Process expression ctx = resolveExpression(condition.expression, sctx, ctx); let initialCtx = sctx; @@ -82,13 +100,16 @@ function processCondition(condition: ASTCondition, sctx: StatementContext, ctx: const r = processStatements(condition.falseStatements, initialCtx, ctx); ctx = r.ctx; processedCtx.push(r.sctx); - } else if (condition.falseStatements === null && condition.elseif !== null) { + } else if ( + condition.falseStatements === null && + condition.elseif !== null + ) { // if-else if const r = processCondition(condition.elseif, initialCtx, ctx); ctx = r.ctx; processedCtx.push(r.sctx); } else { - throw Error('Impossible'); + throw Error("Impossible"); } // Merge statement contexts @@ -112,21 +133,22 @@ function processCondition(condition: ASTCondition, sctx: StatementContext, ctx: return { ctx, sctx: initialCtx }; } -function processStatements(statements: ASTStatement[], sctx: StatementContext, ctx: CompilerContext): { ctx: CompilerContext, sctx: StatementContext } { - +function processStatements( + statements: ASTStatement[], + sctx: StatementContext, + ctx: CompilerContext, +): { ctx: CompilerContext; sctx: StatementContext } { // Process statements let exited = false; for (const s of statements) { - // Check for unreachable if (exited) { - throwError('Unreachable statement', s.ref); + throwError("Unreachable statement", s.ref); } // Process statement - if (s.kind === 'statement_let') { - + if (s.kind === "statement_let") { // Process expression ctx = resolveExpression(s.expression, sctx, ctx); @@ -134,7 +156,10 @@ function processStatements(statements: ASTStatement[], sctx: StatementContext, c const expressionType = getExpType(ctx, s.expression); const variableType = resolveTypeRef(ctx, s.type); if (!isAssignable(expressionType, variableType)) { - throwError(`Type mismatch: ${printTypeRef(expressionType)} is not assignable to ${printTypeRef(variableType)}`, s.ref); + throwError( + `Type mismatch: ${printTypeRef(expressionType)} is not assignable to ${printTypeRef(variableType)}`, + s.ref, + ); } // Add variable to statement context @@ -142,9 +167,7 @@ function processStatements(statements: ASTStatement[], sctx: StatementContext, c throwError(`Variable already exists: ${s.name}`, s.ref); } sctx = addVariable(s.name, variableType, sctx); - - } else if (s.kind === 'statement_assign') { - + } else if (s.kind === "statement_assign") { // Process lvalue ctx = resolveLValueRef(s.path, sctx, ctx); @@ -155,19 +178,20 @@ function processStatements(statements: ASTStatement[], sctx: StatementContext, c const expressionType = getExpType(ctx, s.expression); const tailType = getExpType(ctx, s.path[s.path.length - 1]); if (!isAssignable(expressionType, tailType)) { - throwError(`Type mismatch: ${printTypeRef(expressionType)} is not assignable to ${printTypeRef(tailType)}`, s.ref); + throwError( + `Type mismatch: ${printTypeRef(expressionType)} is not assignable to ${printTypeRef(tailType)}`, + s.ref, + ); } // Mark as assigned - if (s.path.length === 2 && s.path[0].name === 'self') { + if (s.path.length === 2 && s.path[0].name === "self") { const field = s.path[1].name; if (sctx.requiredFields.findIndex((v) => v === field) >= 0) { sctx = removeRequiredVariable(field, sctx); } } - - } else if (s.kind == 'statement_augmentedassign') { - + } else if (s.kind == "statement_augmentedassign") { // Process lvalue ctx = resolveLValueRef(s.path, sctx, ctx); @@ -178,24 +202,23 @@ function processStatements(statements: ASTStatement[], sctx: StatementContext, c const expressionType = getExpType(ctx, s.expression); const tailType = getExpType(ctx, s.path[s.path.length - 1]); if (!isAssignable(expressionType, tailType)) { - throwError(`Type mismatch: ${printTypeRef(expressionType)} is not assignable to ${printTypeRef(tailType)}`, s.ref); + throwError( + `Type mismatch: ${printTypeRef(expressionType)} is not assignable to ${printTypeRef(tailType)}`, + s.ref, + ); } // Mark as assigned - if (s.path.length === 2 && s.path[0].name === 'self') { + if (s.path.length === 2 && s.path[0].name === "self") { const field = s.path[1].name; if (sctx.requiredFields.findIndex((v) => v === field) >= 0) { sctx = removeRequiredVariable(field, sctx); } } - - } else if (s.kind === 'statement_expression') { - + } else if (s.kind === "statement_expression") { // Process expression ctx = resolveExpression(s.expression, sctx, ctx); - - } else if (s.kind === 'statement_condition') { - + } else if (s.kind === "statement_condition") { // Process condition (expression resolved inside) const r = processCondition(s, sctx, ctx); ctx = r.ctx; @@ -203,105 +226,145 @@ function processStatements(statements: ASTStatement[], sctx: StatementContext, c // Check type const expressionType = getExpType(ctx, s.expression); - if (expressionType.kind !== 'ref' || expressionType.name !== 'Bool' || expressionType.optional) { - throwError(`Type mismatch: ${printTypeRef(expressionType)} is not assignable to Bool`, s.ref); + if ( + expressionType.kind !== "ref" || + expressionType.name !== "Bool" || + expressionType.optional + ) { + throwError( + `Type mismatch: ${printTypeRef(expressionType)} is not assignable to Bool`, + s.ref, + ); } - - } else if (s.kind === 'statement_return') { - + } else if (s.kind === "statement_return") { if (s.expression) { - // Process expression ctx = resolveExpression(s.expression, sctx, ctx); // Check type const expressionType = getExpType(ctx, s.expression); if (!isAssignable(expressionType, sctx.returns)) { - throwError(`Type mismatch: ${printTypeRef(expressionType)} is not assignable to ${printTypeRef(sctx.returns)}`, s.ref); + throwError( + `Type mismatch: ${printTypeRef(expressionType)} is not assignable to ${printTypeRef(sctx.returns)}`, + s.ref, + ); } } else { - if (sctx.returns.kind !== 'void') { - throwError(`Type mismatch: void is not assignable to ${printTypeRef(sctx.returns)}`, s.ref); + if (sctx.returns.kind !== "void") { + throwError( + `Type mismatch: void is not assignable to ${printTypeRef(sctx.returns)}`, + s.ref, + ); } } // Check if all required variables are assigned if (sctx.requiredFields.length > 0) { if (sctx.requiredFields.length === 1) { - throwError(`Field ${sctx.requiredFields[0]} is not set`, sctx.root); + throwError( + `Field ${sctx.requiredFields[0]} is not set`, + sctx.root, + ); } else { - throwError(`Fields ${sctx.requiredFields.join(', ')} are not set`, sctx.root); + throwError( + `Fields ${sctx.requiredFields.join(", ")} are not set`, + sctx.root, + ); } } // Mark as ended exited = true; - - } else if (s.kind === 'statement_repeat') { - + } else if (s.kind === "statement_repeat") { // Process expression ctx = resolveExpression(s.condition, sctx, ctx); // Check type const expressionType = getExpType(ctx, s.condition); - if (expressionType.kind !== 'ref' || expressionType.name !== 'Int' || expressionType.optional) { - throwError(`Type mismatch: ${printTypeRef(expressionType)} is not assignable to Int`, s.ref); + if ( + expressionType.kind !== "ref" || + expressionType.name !== "Int" || + expressionType.optional + ) { + throwError( + `Type mismatch: ${printTypeRef(expressionType)} is not assignable to Int`, + s.ref, + ); } // Process inner statements const r = processStatements(s.statements, sctx, ctx); ctx = r.ctx; sctx = r.sctx; - - } else if (s.kind === 'statement_until') { - + } else if (s.kind === "statement_until") { // Process expression ctx = resolveExpression(s.condition, sctx, ctx); // Check type const expressionType = getExpType(ctx, s.condition); - if (expressionType.kind !== 'ref' || expressionType.name !== 'Bool' || expressionType.optional) { - throwError(`Type mismatch: ${printTypeRef(expressionType)} is not assignable to bool`, s.ref); + if ( + expressionType.kind !== "ref" || + expressionType.name !== "Bool" || + expressionType.optional + ) { + throwError( + `Type mismatch: ${printTypeRef(expressionType)} is not assignable to bool`, + s.ref, + ); } // Process inner statements const r = processStatements(s.statements, sctx, ctx); ctx = r.ctx; sctx = r.sctx; - - } else if (s.kind === 'statement_while') { - + } else if (s.kind === "statement_while") { // Process expression ctx = resolveExpression(s.condition, sctx, ctx); // Check type const expressionType = getExpType(ctx, s.condition); - if (expressionType.kind !== 'ref' || expressionType.name !== 'Bool' || expressionType.optional) { - throwError(`Type mismatch: ${printTypeRef(expressionType)} is not assignable to bool`, s.ref); + if ( + expressionType.kind !== "ref" || + expressionType.name !== "Bool" || + expressionType.optional + ) { + throwError( + `Type mismatch: ${printTypeRef(expressionType)} is not assignable to bool`, + s.ref, + ); } // Process inner statements const r = processStatements(s.statements, sctx, ctx); ctx = r.ctx; sctx = r.sctx; - } else { - throw Error('Unknown statement'); + throw Error("Unknown statement"); } } return { ctx, sctx }; } -function processFunctionBody(statements: ASTStatement[], sctx: StatementContext, ctx: CompilerContext) { +function processFunctionBody( + statements: ASTStatement[], + sctx: StatementContext, + ctx: CompilerContext, +) { const res = processStatements(statements, sctx, ctx); // Check if all required variables are assigned if (res.sctx.requiredFields.length > 0) { if (res.sctx.requiredFields.length === 1) { - throwError(`Field ${res.sctx.requiredFields[0]} is not set`, res.sctx.root); + throwError( + `Field ${res.sctx.requiredFields[0]} is not set`, + res.sctx.root, + ); } else { - throwError(`Fields ${res.sctx.requiredFields.join(', ')} are not set`, res.sctx.root); + throwError( + `Fields ${res.sctx.requiredFields.join(", ")} are not set`, + res.sctx.root, + ); } } @@ -309,11 +372,9 @@ function processFunctionBody(statements: ASTStatement[], sctx: StatementContext, } export function resolveStatements(ctx: CompilerContext) { - // Process all static functions for (const f of Object.values(getAllStaticFunctions(ctx))) { - if (f.ast.kind === 'def_function') { - + if (f.ast.kind === "def_function") { // Build statement context let sctx = emptyContext(f.ast.ref, f.returns); for (const p of f.args) { @@ -329,22 +390,25 @@ export function resolveStatements(ctx: CompilerContext) { // Process all types for (const t of Object.values(getAllTypes(ctx))) { - // Process init if (t.init) { - // Build statement context - let sctx = emptyContext(t.init.ast.ref, { kind: 'void' }); + let sctx = emptyContext(t.init.ast.ref, { kind: "void" }); // Self - sctx = addVariable('self', { kind: 'ref', name: t.name, optional: false }, sctx); + sctx = addVariable( + "self", + { kind: "ref", name: t.name, optional: false }, + sctx, + ); // Required variables for (const f of t.fields) { - if (f.default !== undefined) { // NOTE: undefined is important here + if (f.default !== undefined) { + // NOTE: undefined is important here continue; } - if (isAssignable({ kind: 'null' }, f.type)) { + if (isAssignable({ kind: "null" }, f.type)) { continue; } sctx = addRequiredVariables(f.name, sctx); @@ -361,24 +425,67 @@ export function resolveStatements(ctx: CompilerContext) { // Process receivers for (const f of Object.values(t.receivers)) { - // Build statement context - let sctx = emptyContext(f.ast.ref, { kind: 'void' }); - sctx = addVariable('self', { kind: 'ref', name: t.name, optional: false }, sctx); - if (f.selector.kind === 'internal-binary' || f.selector.kind === 'external-binary') { - sctx = addVariable(f.selector.name, { kind: 'ref', name: f.selector.type, optional: false }, sctx); - } else if (f.selector.kind === 'internal-empty' || f.selector.kind === 'external-empty' || f.selector.kind === 'external-comment' || f.selector.kind === 'internal-comment') { + let sctx = emptyContext(f.ast.ref, { kind: "void" }); + sctx = addVariable( + "self", + { kind: "ref", name: t.name, optional: false }, + sctx, + ); + if ( + f.selector.kind === "internal-binary" || + f.selector.kind === "external-binary" + ) { + sctx = addVariable( + f.selector.name, + { kind: "ref", name: f.selector.type, optional: false }, + sctx, + ); + } else if ( + f.selector.kind === "internal-empty" || + f.selector.kind === "external-empty" || + f.selector.kind === "external-comment" || + f.selector.kind === "internal-comment" + ) { // Nothing to add to context - } else if (f.selector.kind === 'internal-comment-fallback' || f.selector.kind === 'external-comment-fallback') { - sctx = addVariable(f.selector.name, { kind: 'ref', name: 'String', optional: false }, sctx); - } else if (f.selector.kind === 'internal-fallback' || f.selector.kind === 'external-fallback') { - sctx = addVariable(f.selector.name, { kind: 'ref', name: 'Slice', optional: false }, sctx); - } else if (f.selector.kind === 'bounce-fallback') { - sctx = addVariable(f.selector.name, { kind: 'ref', name: 'Slice', optional: false }, sctx); - } else if (f.selector.kind === 'bounce-binary') { - sctx = addVariable(f.selector.name, f.selector.bounced ? { kind: 'ref_bounced', name: f.selector.type } : { kind: 'ref', name: f.selector.type, optional: false }, sctx); + } else if ( + f.selector.kind === "internal-comment-fallback" || + f.selector.kind === "external-comment-fallback" + ) { + sctx = addVariable( + f.selector.name, + { kind: "ref", name: "String", optional: false }, + sctx, + ); + } else if ( + f.selector.kind === "internal-fallback" || + f.selector.kind === "external-fallback" + ) { + sctx = addVariable( + f.selector.name, + { kind: "ref", name: "Slice", optional: false }, + sctx, + ); + } else if (f.selector.kind === "bounce-fallback") { + sctx = addVariable( + f.selector.name, + { kind: "ref", name: "Slice", optional: false }, + sctx, + ); + } else if (f.selector.kind === "bounce-binary") { + sctx = addVariable( + f.selector.name, + f.selector.bounced + ? { kind: "ref_bounced", name: f.selector.type } + : { + kind: "ref", + name: f.selector.type, + optional: false, + }, + sctx, + ); } else { - throw Error('Unknown selector'); + throw Error("Unknown selector"); } // Process @@ -387,11 +494,14 @@ export function resolveStatements(ctx: CompilerContext) { // Process functions for (const f of t.functions.values()) { - if (f.ast.kind !== 'def_native_function') { - + if (f.ast.kind !== "def_native_function") { // Build statement context let sctx = emptyContext(f.ast.ref, f.returns); - sctx = addVariable('self', { kind: 'ref', name: t.name, optional: false }, sctx); + sctx = addVariable( + "self", + { kind: "ref", name: t.name, optional: false }, + sctx, + ); for (const a of f.args) { sctx = addVariable(a.name, a.type, sctx); } @@ -405,4 +515,4 @@ export function resolveStatements(ctx: CompilerContext) { } return ctx; -} \ No newline at end of file +} diff --git a/src/types/types.ts b/src/types/types.ts index 58bd235be..f44bc278e 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,10 +1,20 @@ import { ABIField, Address, Cell } from "@ton/core"; -import { ASTConstant, ASTFunction, ASTInitFunction, ASTNativeFunction, ASTNode, ASTReceive, ASTRef, ASTStatement, ASTType } from "../grammar/ast"; +import { + ASTConstant, + ASTFunction, + ASTInitFunction, + ASTNativeFunction, + ASTNode, + ASTReceive, + ASTRef, + ASTStatement, + ASTType, +} from "../grammar/ast"; -export type TypeOrigin = 'stdlib' | 'user'; +export type TypeOrigin = "stdlib" | "user"; export type TypeDescription = { - kind: 'struct' | 'primitive' | 'contract' | 'trait'; + kind: "struct" | "primitive" | "contract" | "trait"; origin: TypeOrigin; name: string; uid: number; @@ -21,144 +31,163 @@ export type TypeDescription = { dependsOn: TypeDescription[]; interfaces: string[]; constants: ConstantDescription[]; -} - -export type TypeRef = { - kind: 'ref', - name: string, - optional: boolean -} | { - kind: 'map', - key: string, - keyAs: string | null, - value: string, - valueAs: string | null, -} | { - kind: 'ref_bounced', - name: string -} | { - kind: 'void' -} | { - kind: 'null' }; +export type TypeRef = + | { + kind: "ref"; + name: string; + optional: boolean; + } + | { + kind: "map"; + key: string; + keyAs: string | null; + value: string; + valueAs: string | null; + } + | { + kind: "ref_bounced"; + name: string; + } + | { + kind: "void"; + } + | { + kind: "null"; + }; + export type FieldDescription = { - name: string, - index: number, - type: TypeRef, - as: string | null, - default: bigint | boolean | string | null | Address | Cell | undefined, - ref: ASTRef, - ast: ASTNode, - abi: ABIField -} + name: string; + index: number; + type: TypeRef; + as: string | null; + default: bigint | boolean | string | null | Address | Cell | undefined; + ref: ASTRef; + ast: ASTNode; + abi: ABIField; +}; export type ConstantDescription = { name: string; type: TypeRef; value: bigint | boolean | string | Address | Cell | null | undefined; - ref: ASTRef, - ast: ASTConstant -} + ref: ASTRef; + ast: ASTConstant; +}; export type FunctionArgument = { - name: string, - type: TypeRef, - ref: ASTRef -} + name: string; + type: TypeRef; + ref: ASTRef; +}; export type InitArgument = { - name: string, - type: TypeRef, - as: string | null, - ref: ASTRef -} + name: string; + type: TypeRef; + as: string | null; + ref: ASTRef; +}; export type FunctionDescription = { - name: string, - origin: TypeOrigin, - isGetter: boolean, - isMutating: boolean, - isOverrides: boolean, - isVirtual: boolean, - isAbstract: boolean, - isInline: boolean, - self: string | null, - returns: TypeRef, - args: FunctionArgument[], - ast: ASTFunction | ASTNativeFunction -} + name: string; + origin: TypeOrigin; + isGetter: boolean; + isMutating: boolean; + isOverrides: boolean; + isVirtual: boolean; + isAbstract: boolean; + isInline: boolean; + self: string | null; + returns: TypeRef; + args: FunctionArgument[]; + ast: ASTFunction | ASTNativeFunction; +}; -export type StatementDescription = { - kind: 'native', - src: ASTStatement -} | { - kind: 'intrinsic' -} +export type StatementDescription = + | { + kind: "native"; + src: ASTStatement; + } + | { + kind: "intrinsic"; + }; -export type ReceiverSelector = { - kind: 'internal-binary', - type: string, - name: string, -} | { - kind: 'internal-empty' -} | { - kind: 'internal-comment', - comment: string -} | { - kind: 'internal-comment-fallback', - name: string -} | { - kind: 'internal-fallback', - name: string -} | { - kind: 'bounce-fallback', - name: string, -} | { - kind: 'bounce-binary', - name: string, - type: string, - bounced: boolean, -} | { - kind: 'external-binary', - type: string, - name: string, -} | { - kind: 'external-empty' -} | { - kind: 'external-comment', - comment: string -} | { - kind: 'external-comment-fallback', - name: string -} | { - kind: 'external-fallback', - name: string -} +export type ReceiverSelector = + | { + kind: "internal-binary"; + type: string; + name: string; + } + | { + kind: "internal-empty"; + } + | { + kind: "internal-comment"; + comment: string; + } + | { + kind: "internal-comment-fallback"; + name: string; + } + | { + kind: "internal-fallback"; + name: string; + } + | { + kind: "bounce-fallback"; + name: string; + } + | { + kind: "bounce-binary"; + name: string; + type: string; + bounced: boolean; + } + | { + kind: "external-binary"; + type: string; + name: string; + } + | { + kind: "external-empty"; + } + | { + kind: "external-comment"; + comment: string; + } + | { + kind: "external-comment-fallback"; + name: string; + } + | { + kind: "external-fallback"; + name: string; + }; export type ReceiverDescription = { - selector: ReceiverSelector, - ast: ASTReceive -} + selector: ReceiverSelector; + ast: ASTReceive; +}; export type InitDescription = { - args: InitArgument[], - ast: ASTInitFunction -} + args: InitArgument[]; + ast: ASTInitFunction; +}; export function printTypeRef(src: TypeRef): string { - if (src.kind === 'ref') { - return src.name + (src.optional ? '?' : ''); - } else if (src.kind === 'map') { - return `map<${src.key + (src.keyAs ? ' as ' + src.keyAs : '')}, ${src.value + (src.valueAs ? ' as ' + src.valueAs : '')}>`; - } else if (src.kind === 'void') { - return ''; - } else if (src.kind === 'null') { - return ''; - } else if (src.kind === 'ref_bounced') { + if (src.kind === "ref") { + return src.name + (src.optional ? "?" : ""); + } else if (src.kind === "map") { + return `map<${src.key + (src.keyAs ? " as " + src.keyAs : "")}, ${src.value + (src.valueAs ? " as " + src.valueAs : "")}>`; + } else if (src.kind === "void") { + return ""; + } else if (src.kind === "null") { + return ""; + } else if (src.kind === "ref_bounced") { return `bounced<${src.name}>`; } else { - throw Error('Invalid type ref'); + throw Error("Invalid type ref"); } } @@ -166,20 +195,20 @@ export function typeRefEquals(a: TypeRef, b: TypeRef) { if (a.kind !== b.kind) { return false; } - if (a.kind === 'ref' && b.kind === 'ref') { + if (a.kind === "ref" && b.kind === "ref") { return a.name === b.name && a.optional === b.optional; } - if (a.kind === 'map' && b.kind === 'map') { + if (a.kind === "map" && b.kind === "map") { return a.key === b.key && a.value === b.value; } - if (a.kind === 'ref_bounced' && b.kind === 'ref_bounced') { + if (a.kind === "ref_bounced" && b.kind === "ref_bounced") { return a.name === b.name; } - if (a.kind === 'null' && b.kind === 'null') { + if (a.kind === "null" && b.kind === "null") { return true; } - if (a.kind === 'void' && b.kind === 'void') { + if (a.kind === "void" && b.kind === "void") { return true; } return false; -} \ No newline at end of file +} diff --git a/src/utils/Writer.ts b/src/utils/Writer.ts index 704fd73d9..892535a7c 100644 --- a/src/utils/Writer.ts +++ b/src/utils/Writer.ts @@ -10,18 +10,18 @@ export class Writer { this.indent--; }; - append(src: string = '') { - this.lines.push(' '.repeat(this.indent * 4) + src); + append(src: string = "") { + this.lines.push(" ".repeat(this.indent * 4) + src); } write(src: string) { - const lines = trimIndent(src).split('\n'); + const lines = trimIndent(src).split("\n"); for (const l of lines) { this.append(l); } } end() { - return this.lines.join('\n'); + return this.lines.join("\n"); } -} \ No newline at end of file +} diff --git a/src/utils/calculateIPFSlink.ts b/src/utils/calculateIPFSlink.ts index 858a62a73..379151b9b 100644 --- a/src/utils/calculateIPFSlink.ts +++ b/src/utils/calculateIPFSlink.ts @@ -1,18 +1,21 @@ -import { importer } from 'ipfs-unixfs-importer'; -import { MemoryBlockstore } from 'blockstore-core/memory'; +import { importer } from "ipfs-unixfs-importer"; +import { MemoryBlockstore } from "blockstore-core/memory"; export async function calculateIPFSlink(data: Buffer) { const blockstore = new MemoryBlockstore(); const cid = await new Promise((resolve, reject) => { (async () => { try { - for await (const entry of importer({ content: data }, blockstore)) { + for await (const entry of importer( + { content: data }, + blockstore, + )) { resolve(entry.cid.toString()); } } catch (e) { reject(e); } - })() + })(); }); - return 'ipfs://' + cid; + return "ipfs://" + cid; } diff --git a/src/utils/crc16.ts b/src/utils/crc16.ts index 861c1d631..e907f5b73 100644 --- a/src/utils/crc16.ts +++ b/src/utils/crc16.ts @@ -1,49 +1,46 @@ const TABLE = new Int16Array([ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -]) + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, + 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, + 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, + 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, + 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, + 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, + 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, + 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, + 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, + 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, + 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, + 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, + 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, + 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, + 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, + 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, + 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, + 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, + 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, + 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, + 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, + 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, + 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, + 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, + 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, + 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, +]); export function crc16(data: string | Buffer) { if (!(data instanceof Buffer)) { - data = Buffer.from(data) + data = Buffer.from(data); } - let crc = 0 + let crc = 0; for (let index = 0; index < data.length; index++) { - const byte = data[index] - crc = (TABLE[((crc >> 8) ^ byte) & 0xff] ^ (crc << 8)) & 0xffff + const byte = data[index]; + crc = (TABLE[((crc >> 8) ^ byte) & 0xff] ^ (crc << 8)) & 0xffff; } - return crc -} \ No newline at end of file + return crc; +} diff --git a/src/utils/crc32.ts b/src/utils/crc32.ts index edbc7c384..6ef03e942 100644 --- a/src/utils/crc32.ts +++ b/src/utils/crc32.ts @@ -2,7 +2,7 @@ const POLYNOMIAL = -306674912; let crc32_table: Int32Array | undefined = undefined; -export function crc32(bytes: Uint8Array, crc = 0xFFFFFFFF) { +export function crc32(bytes: Uint8Array, crc = 0xffffffff) { if (crc32_table === undefined) { calcTable(); } @@ -16,7 +16,7 @@ function calcTable() { for (let i = 0; i < 256; i++) { let r = i; for (let bit = 8; bit > 0; --bit) - r = ((r & 1) ? ((r >>> 1) ^ POLYNOMIAL) : (r >>> 1)); + r = r & 1 ? (r >>> 1) ^ POLYNOMIAL : r >>> 1; crc32_table[i] = r; } -} \ No newline at end of file +} diff --git a/src/utils/errorToString.ts b/src/utils/errorToString.ts index 4844851e9..3b47d2b72 100644 --- a/src/utils/errorToString.ts +++ b/src/utils/errorToString.ts @@ -2,6 +2,6 @@ export function errorToString(src: unknown): string { if (src instanceof Error) { return src.stack || src.message; } else { - return '' + src; + return "" + src; } -} \ No newline at end of file +} diff --git a/src/utils/filePath.ts b/src/utils/filePath.ts index 7a5708417..3cbebdc17 100644 --- a/src/utils/filePath.ts +++ b/src/utils/filePath.ts @@ -1,7 +1,12 @@ -import pathModule from 'node:path'; +import pathModule from "node:path"; export function posixNormalize(path: string): string { - if (typeof global === 'object' && typeof global.process === 'object' && typeof global.process.versions === 'object' && global.process.versions.node) { + if ( + typeof global === "object" && + typeof global.process === "object" && + typeof global.process.versions === "object" && + global.process.versions.node + ) { return path.split(pathModule.sep).join(pathModule.posix.sep); } return path; diff --git a/src/utils/idToHex.ts b/src/utils/idToHex.ts index 59a0ab9eb..54bbbe4fe 100644 --- a/src/utils/idToHex.ts +++ b/src/utils/idToHex.ts @@ -1,5 +1,10 @@ import { beginCell } from "@ton/core"; export function idToHex(id: number) { - return beginCell().storeUint(id, 32).endCell().beginParse().loadBuffer(4).toString('hex') -} \ No newline at end of file + return beginCell() + .storeUint(id, 32) + .endCell() + .beginParse() + .loadBuffer(4) + .toString("hex"); +} diff --git a/src/utils/loadCases.ts b/src/utils/loadCases.ts index 055f1f807..afe948bb8 100644 --- a/src/utils/loadCases.ts +++ b/src/utils/loadCases.ts @@ -1,13 +1,16 @@ -import fs from 'fs'; +import fs from "fs"; export function loadCases(src: string) { const recs = fs.readdirSync(src); - const res: { name: string, code: string }[] = []; + const res: { name: string; code: string }[] = []; for (const r of recs) { - if (r.endsWith('.tact')) { - res.push({ name: r.slice(0, r.length - '.tact'.length), code: fs.readFileSync(src + r, 'utf8') }); + if (r.endsWith(".tact")) { + res.push({ + name: r.slice(0, r.length - ".tact".length), + code: fs.readFileSync(src + r, "utf8"), + }); } } res.sort((a, b) => a.name.localeCompare(b.name)); return res; -} \ No newline at end of file +} diff --git a/src/utils/newMessageId.ts b/src/utils/newMessageId.ts index afedbd8d1..a06ef2e5a 100644 --- a/src/utils/newMessageId.ts +++ b/src/utils/newMessageId.ts @@ -2,5 +2,9 @@ import { beginCell } from "@ton/core"; import { sha256_sync } from "@ton/crypto"; export function newMessageId(signature: string) { - return beginCell().storeBuffer(sha256_sync(signature)).endCell().beginParse().loadUint(32); -} \ No newline at end of file + return beginCell() + .storeBuffer(sha256_sync(signature)) + .endCell() + .beginParse() + .loadUint(32); +} diff --git a/src/utils/text.spec.ts b/src/utils/text.spec.ts index fc4fcdf92..b6345d583 100644 --- a/src/utils/text.spec.ts +++ b/src/utils/text.spec.ts @@ -1,12 +1,12 @@ -import { isBlank, trimIndent } from './text'; -describe('text', () => { - it('should detect blank lines', () => { - expect(isBlank('')).toBe(true); - expect(isBlank(' ')).toBe(true); - expect(isBlank('\t')).toBe(true); - expect(isBlank('a')).toBe(false); +import { isBlank, trimIndent } from "./text"; +describe("text", () => { + it("should detect blank lines", () => { + expect(isBlank("")).toBe(true); + expect(isBlank(" ")).toBe(true); + expect(isBlank("\t")).toBe(true); + expect(isBlank("a")).toBe(false); }); - it('should trim indent', () => { + it("should trim indent", () => { const res = trimIndent(` hello world 123123 123123 @@ -15,4 +15,4 @@ describe('text', () => { `); expect(res).toBe(`hello world\n123123 123123\n 12312312\n12312312`); }); -}); \ No newline at end of file +}); diff --git a/src/utils/text.ts b/src/utils/text.ts index 2bcb2f69a..895ba2be9 100644 --- a/src/utils/text.ts +++ b/src/utils/text.ts @@ -1,4 +1,3 @@ - export function isBlank(src: string) { return src.trim().length === 0; } @@ -13,11 +12,10 @@ export function indentWidth(src: string) { } export function trimIndent(src: string) { - // Prase lines - let lines = src.split('\n'); + let lines = src.split("\n"); if (lines.length === 0) { - return ''; + return ""; } if (lines.length === 1) { return lines[0].trim(); @@ -31,7 +29,7 @@ export function trimIndent(src: string) { lines = lines.slice(0, lines.length - 1); } if (lines.length === 0) { - return ''; + return ""; } // Find minimal indent @@ -39,5 +37,5 @@ export function trimIndent(src: string) { const minimal = indents.length > 0 ? Math.min(...indents) : 0; // Trim indent - return lines.map((v) => isBlank(v) ? '' : v.slice(minimal)).join('\n'); -} \ No newline at end of file + return lines.map((v) => (isBlank(v) ? "" : v.slice(minimal))).join("\n"); +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts index c8fa53094..f693fecbf 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -6,7 +6,7 @@ export function topologicalSort(src: T[], references: (src: T) => T[]) { const visiting = new Set(); const visit = (src: T) => { if (visiting.has(src)) { - throw Error('Cycle detected'); + throw Error("Cycle detected"); } if (!visited.has(src)) { visiting.add(src); @@ -17,7 +17,7 @@ export function topologicalSort(src: T[], references: (src: T) => T[]) { visited.add(src); result.push(src); } - } + }; for (const s of src) { visit(s); } @@ -38,4 +38,4 @@ export function deepFreeze(obj: T) { export function getMethodId(name: string) { return (crc16(name) & 0xffff) | 0x10000; -} \ No newline at end of file +} diff --git a/src/verify.ts b/src/verify.ts index 272df81c8..db52fffc5 100644 --- a/src/verify.ts +++ b/src/verify.ts @@ -6,16 +6,26 @@ import { PackageFileFormat, run, TactLogger } from "./main"; import { fileFormat } from "./packaging/fileFormat"; import { getCompilerVersion } from "./pipeline/version"; -export type VerifyResult = { - ok: true, - package: PackageFileFormat, - files: { [key: string]: string } -} | { - ok: false, - error: 'invalid-package-format' | 'invalid-compiler' | 'invalid-compiler-version' | 'compilation-failed' | 'verification-failed' -}; +export type VerifyResult = + | { + ok: true; + package: PackageFileFormat; + files: { [key: string]: string }; + } + | { + ok: false; + error: + | "invalid-package-format" + | "invalid-compiler" + | "invalid-compiler-version" + | "compilation-failed" + | "verification-failed"; + }; -export async function verify(args: { pkg: string, logger?: TactLogger | null | undefined }): Promise { +export async function verify(args: { + pkg: string; + logger?: TactLogger | null | undefined; +}): Promise { const logger = args.logger || consoleLogger; // Loading package @@ -24,62 +34,65 @@ export async function verify(args: { pkg: string, logger?: TactLogger | null | u const data = JSON.parse(args.pkg); unpacked = fileFormat.parse(data); } catch (e) { - return { ok: false, error: 'invalid-package-format' }; + return { ok: false, error: "invalid-package-format" }; } // Check compier and version - if (unpacked.compiler.name !== 'tact') { - return { ok: false, error: 'invalid-compiler' }; + if (unpacked.compiler.name !== "tact") { + return { ok: false, error: "invalid-compiler" }; } if (unpacked.compiler.version !== getCompilerVersion()) { - return { ok: false, error: 'invalid-compiler-version' }; + return { ok: false, error: "invalid-compiler-version" }; } // Create a options if (!unpacked.compiler.parameters) { - return { ok: false, error: 'invalid-package-format' } + return { ok: false, error: "invalid-package-format" }; } const params = JSON.parse(unpacked.compiler.parameters); - if (typeof params.entrypoint !== 'string') { - return { ok: false, error: 'invalid-package-format' } + if (typeof params.entrypoint !== "string") { + return { ok: false, error: "invalid-package-format" }; } const options: Options = params.options || {}; const entrypoint: string = params.entrypoint; // Create config const config: Config = { - projects: [{ - name: 'verifier', - path: normalize('./contract/' + entrypoint), - output: './output', - options - }] - } + projects: [ + { + name: "verifier", + path: normalize("./contract/" + entrypoint), + output: "./output", + options, + }, + ], + }; // Build - const files: { [key: string]: string } = {} + const files: { [key: string]: string } = {}; for (const s in unpacked.sources) { - files['contract/' + s] = unpacked.sources[s]; + files["contract/" + s] = unpacked.sources[s]; } const result = await run({ config, files, logger }); if (!result) { - return { ok: false, error: 'compilation-failed' }; + return { ok: false, error: "compilation-failed" }; } // Read output - const compiledCell = files['output/verifier_' + unpacked.name + '.code.boc']; + const compiledCell = + files["output/verifier_" + unpacked.name + ".code.boc"]; if (!compiledCell) { - return { ok: false, error: 'verification-failed' }; + return { ok: false, error: "verification-failed" }; } // Check output const a = Cell.fromBase64(compiledCell); const b = Cell.fromBase64(unpacked.code); if (!a.equals(b)) { - return { ok: false, error: 'verification-failed' }; + return { ok: false, error: "verification-failed" }; } // Return return { ok: true, package: unpacked, files }; -} \ No newline at end of file +} diff --git a/src/vfs/VirtualFileSystem.ts b/src/vfs/VirtualFileSystem.ts index b63eac46e..1c7d0fd08 100644 --- a/src/vfs/VirtualFileSystem.ts +++ b/src/vfs/VirtualFileSystem.ts @@ -4,4 +4,4 @@ export type VirtualFileSystem = { exists(path: string): boolean; readFile(path: string): Buffer; writeFile(path: string, content: Buffer | string): void; -} \ No newline at end of file +}; diff --git a/src/vfs/createNodeFileSystem.spec.ts b/src/vfs/createNodeFileSystem.spec.ts index c25855f17..1da40052a 100644 --- a/src/vfs/createNodeFileSystem.spec.ts +++ b/src/vfs/createNodeFileSystem.spec.ts @@ -1,43 +1,50 @@ -import path from 'path'; -import fs from 'fs'; -import rimraf from 'rimraf'; -import { createNodeFileSystem } from './createNodeFileSystem'; +import path from "path"; +import fs from "fs"; +import rimraf from "rimraf"; +import { createNodeFileSystem } from "./createNodeFileSystem"; -describe('createNodeFileSystem', () => { - it('should open file system', () => { - const vfs = createNodeFileSystem(path.resolve(__dirname, './__testdata/')); - expect(vfs.root).toBe(path.normalize(__dirname + '/__testdata/')); +describe("createNodeFileSystem", () => { + it("should open file system", () => { + const vfs = createNodeFileSystem( + path.resolve(__dirname, "./__testdata/"), + ); + expect(vfs.root).toBe(path.normalize(__dirname + "/__testdata/")); }); - it('should write and read files', () => { - const vfs = createNodeFileSystem(path.resolve(__dirname, './__testdata'), false); + it("should write and read files", () => { + const vfs = createNodeFileSystem( + path.resolve(__dirname, "./__testdata"), + false, + ); // Create a single file - const filename = 'tmp-' + Math.random() + '.txt'; + const filename = "tmp-" + Math.random() + ".txt"; const realPath = vfs.resolve(filename); try { expect(vfs.exists(realPath)).toBe(false); - vfs.writeFile(realPath, 'Hello world'); + vfs.writeFile(realPath, "Hello world"); expect(vfs.exists(realPath)).toBe(true); - expect(vfs.readFile(realPath).toString('utf8')).toBe('Hello world'); - expect(fs.readFileSync(realPath, 'utf8')).toBe('Hello world'); + expect(vfs.readFile(realPath).toString("utf8")).toBe("Hello world"); + expect(fs.readFileSync(realPath, "utf8")).toBe("Hello world"); } finally { fs.unlinkSync(realPath); } // Automatically create directories - const dir = 'dir-' + Math.random(); - const fileName2 = dir + '/' + Math.random() + '.txt'; + const dir = "dir-" + Math.random(); + const fileName2 = dir + "/" + Math.random() + ".txt"; const realPath2 = vfs.resolve(fileName2); const realPaathDir2 = vfs.resolve(dir); try { expect(vfs.exists(realPath2)).toBe(false); - vfs.writeFile(realPath2, 'Hello world'); + vfs.writeFile(realPath2, "Hello world"); expect(vfs.exists(realPath2)).toBe(true); - expect(vfs.readFile(realPath2).toString('utf8')).toBe('Hello world'); - expect(fs.readFileSync(realPath2, 'utf8')).toBe('Hello world'); + expect(vfs.readFile(realPath2).toString("utf8")).toBe( + "Hello world", + ); + expect(fs.readFileSync(realPath2, "utf8")).toBe("Hello world"); } finally { rimraf.sync(realPaathDir2); } }); -}); \ No newline at end of file +}); diff --git a/src/vfs/createNodeFileSystem.ts b/src/vfs/createNodeFileSystem.ts index 91ea47a42..c5d019081 100644 --- a/src/vfs/createNodeFileSystem.ts +++ b/src/vfs/createNodeFileSystem.ts @@ -1,9 +1,12 @@ import { VirtualFileSystem } from "./VirtualFileSystem"; -import fs from 'fs'; +import fs from "fs"; import path from "path"; import mkdirp from "mkdirp"; -export function createNodeFileSystem(root: string, readonly: boolean = true): VirtualFileSystem { +export function createNodeFileSystem( + root: string, + readonly: boolean = true, +): VirtualFileSystem { let normalizedRoot = path.normalize(root); if (!normalizedRoot.endsWith(path.sep)) { normalizedRoot += path.sep; @@ -12,7 +15,9 @@ export function createNodeFileSystem(root: string, readonly: boolean = true): Vi root: normalizedRoot, exists(filePath: string): boolean { if (!filePath.startsWith(normalizedRoot)) { - throw new Error(`Path '${filePath}' is outside of the root directory '${normalizedRoot}'`); + throw new Error( + `Path '${filePath}' is outside of the root directory '${normalizedRoot}'`, + ); } return fs.existsSync(filePath); }, @@ -21,20 +26,24 @@ export function createNodeFileSystem(root: string, readonly: boolean = true): Vi }, readFile(filePath) { if (!filePath.startsWith(normalizedRoot)) { - throw new Error(`Path '${filePath}' is outside of the root directory '${normalizedRoot}'`); + throw new Error( + `Path '${filePath}' is outside of the root directory '${normalizedRoot}'`, + ); } return fs.readFileSync(filePath); }, writeFile(filePath, content) { if (readonly) { - throw new Error('File system is readonly'); + throw new Error("File system is readonly"); } if (!filePath.startsWith(normalizedRoot)) { - throw new Error(`Path '${filePath}' is outside of the root directory '${normalizedRoot}'`); + throw new Error( + `Path '${filePath}' is outside of the root directory '${normalizedRoot}'`, + ); } mkdirp.sync(path.dirname(filePath)); fs.writeFileSync(filePath, content); - } - } -} \ No newline at end of file + }, + }; +} diff --git a/src/vfs/createVirtualFileSystem.spec.ts b/src/vfs/createVirtualFileSystem.spec.ts index cbc0fdb5e..eddeea9d5 100644 --- a/src/vfs/createVirtualFileSystem.spec.ts +++ b/src/vfs/createVirtualFileSystem.spec.ts @@ -1,30 +1,30 @@ -import { createVirtualFileSystem } from './createVirtualFileSystem'; +import { createVirtualFileSystem } from "./createVirtualFileSystem"; -describe('createVirtualFileSystem', () => { - it('should create a virtual file system', () => { - let vfs = createVirtualFileSystem('/', {}); - expect(vfs.root).toBe('/'); - vfs = createVirtualFileSystem('//', {}); - expect(vfs.root).toBe('/'); - vfs = createVirtualFileSystem('//./', {}); - expect(vfs.root).toBe('/'); - vfs = createVirtualFileSystem('@stdlib', {}); - expect(vfs.root).toBe('@stdlib/'); +describe("createVirtualFileSystem", () => { + it("should create a virtual file system", () => { + let vfs = createVirtualFileSystem("/", {}); + expect(vfs.root).toBe("/"); + vfs = createVirtualFileSystem("//", {}); + expect(vfs.root).toBe("/"); + vfs = createVirtualFileSystem("//./", {}); + expect(vfs.root).toBe("/"); + vfs = createVirtualFileSystem("@stdlib", {}); + expect(vfs.root).toBe("@stdlib/"); }); - it('should read from virtual file system', () => { + it("should read from virtual file system", () => { const fs: { [key: string]: string } = { - ['file.txt']: Buffer.from('Hello World').toString('base64'), - ['empty.txt']: Buffer.from([]).toString('base64'), + ["file.txt"]: Buffer.from("Hello World").toString("base64"), + ["empty.txt"]: Buffer.from([]).toString("base64"), }; - const vfs = createVirtualFileSystem('@stdlib', fs); - let realPath = vfs.resolve('./', './', 'file.txt'); - expect(realPath).toBe('@stdlib/file.txt'); + const vfs = createVirtualFileSystem("@stdlib", fs); + let realPath = vfs.resolve("./", "./", "file.txt"); + expect(realPath).toBe("@stdlib/file.txt"); expect(vfs.exists(realPath)).toBe(true); - expect(vfs.readFile(realPath).toString()).toBe('Hello World'); - realPath = vfs.resolve('./', './', 'empty.txt'); - expect(realPath).toBe('@stdlib/empty.txt'); + expect(vfs.readFile(realPath).toString()).toBe("Hello World"); + realPath = vfs.resolve("./", "./", "empty.txt"); + expect(realPath).toBe("@stdlib/empty.txt"); expect(vfs.exists(realPath)).toBe(true); - expect(vfs.readFile(realPath).toString()).toBe(''); + expect(vfs.readFile(realPath).toString()).toBe(""); }); -}); \ No newline at end of file +}); diff --git a/src/vfs/createVirtualFileSystem.ts b/src/vfs/createVirtualFileSystem.ts index ddfd01fe4..7129c4edc 100644 --- a/src/vfs/createVirtualFileSystem.ts +++ b/src/vfs/createVirtualFileSystem.ts @@ -1,44 +1,57 @@ import normalize from "path-normalize"; import { VirtualFileSystem } from "./VirtualFileSystem"; -export function createVirtualFileSystem(root: string, fs: { [key: string]: string }, readonly: boolean = true): VirtualFileSystem { +export function createVirtualFileSystem( + root: string, + fs: { [key: string]: string }, + readonly: boolean = true, +): VirtualFileSystem { let normalizedRoot = normalize(root); - if (!normalizedRoot.endsWith('/')) { - normalizedRoot += '/'; + if (!normalizedRoot.endsWith("/")) { + normalizedRoot += "/"; } return { root: normalizedRoot, exists(filePath: string): boolean { if (!filePath.startsWith(normalizedRoot)) { - throw new Error(`Path '${filePath}' is outside of the root directory '${normalizedRoot}'`); + throw new Error( + `Path '${filePath}' is outside of the root directory '${normalizedRoot}'`, + ); } const name = filePath.slice(normalizedRoot.length); - return typeof fs[name] === 'string'; + return typeof fs[name] === "string"; }, resolve(...filePath) { - return normalize([normalizedRoot, ...filePath].join('/')); + return normalize([normalizedRoot, ...filePath].join("/")); }, readFile(filePath) { if (!filePath.startsWith(normalizedRoot)) { - throw new Error(`Path '${filePath}' is outside of the root directory '${normalizedRoot}'`); + throw new Error( + `Path '${filePath}' is outside of the root directory '${normalizedRoot}'`, + ); } const name = filePath.slice(normalizedRoot.length); const content = fs[name]; - if (typeof content !== 'string') { + if (typeof content !== "string") { throw Error(`File ${name} not found at ${filePath}`); } else { - return Buffer.from(content, 'base64'); + return Buffer.from(content, "base64"); } }, writeFile(filePath, content) { if (readonly) { - throw new Error('File system is readonly'); + throw new Error("File system is readonly"); } if (!filePath.startsWith(normalizedRoot)) { - throw new Error(`Path '${filePath}' is outside of the root directory '${normalizedRoot}'`); + throw new Error( + `Path '${filePath}' is outside of the root directory '${normalizedRoot}'`, + ); } const name = filePath.slice(normalizedRoot.length); - fs[name] = typeof content === 'string' ? Buffer.from(content).toString('base64') : content.toString('base64'); - } - } -} \ No newline at end of file + fs[name] = + typeof content === "string" + ? Buffer.from(content).toString("base64") + : content.toString("base64"); + }, + }; +} diff --git a/tact.config.json b/tact.config.json index d1d49aca2..c970e44cc 100644 --- a/tact.config.json +++ b/tact.config.json @@ -150,13 +150,13 @@ } }, { - "name": "integer-literals", - "path": "./src/test/features/integer-literals.tact", - "output": "./src/test/features/output", - "options": { - "debug": true - } - }, + "name": "integer-literals", + "path": "./src/test/features/integer-literals.tact", + "output": "./src/test/features/output", + "options": { + "debug": true + } + }, { "name": "random", "path": "./src/test/features/random.tact", diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 2ce0f9563..9fcaa7d7b 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -3,27 +3,27 @@ /* Visit https://aka.ms/tsconfig.json to read more about this file */ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": false, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */, // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ @@ -46,8 +46,8 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ /* Source Map Options */ @@ -59,18 +59,10 @@ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ + "skipLibCheck": true /* Skip type checking of declaration files. */, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "resolveJsonModule": true }, - "include": [ - "src/", - "examples/", - "scripts/", - "./jest.config.js" - ], - "exclude": [ - "**/*.bind.ts", - "src/test/features/output/**/*", - ] -} \ No newline at end of file + "include": ["src/", "examples/", "scripts/", "./jest.config.js"], + "exclude": ["**/*.bind.ts", "src/test/features/output/**/*"] +} diff --git a/tsconfig.json b/tsconfig.json index 3972de9e8..757a55252 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,27 +3,27 @@ /* Visit https://aka.ms/tsconfig.json to read more about this file */ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, // "lib": [], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ - "declaration": true, /* Generates corresponding '.d.ts' file. */ + "declaration": true /* Generates corresponding '.d.ts' file. */, // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": false, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */, // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ @@ -46,8 +46,8 @@ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ - "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ /* Source Map Options */ @@ -59,16 +59,10 @@ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ /* Advanced Options */ - "skipLibCheck": true, /* Skip type checking of declaration files. */ + "skipLibCheck": true /* Skip type checking of declaration files. */, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, "resolveJsonModule": true }, - "include": [ - "src/**/*" - ], - "exclude": [ - "**/**.spec.ts", - "**/**.bind.ts", - "src/test/features/output/**/*" - ] -} \ No newline at end of file + "include": ["src/**/*"], + "exclude": ["**/**.spec.ts", "**/**.bind.ts", "src/test/features/output/**/*"] +} diff --git a/yarn.lock b/yarn.lock index c423b79f5..3b5ab8079 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4918,6 +4918,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== +prettier@^3.2.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" + integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== + pretty-format@^29.0.0, pretty-format@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.3.1.tgz#1841cac822b02b4da8971dacb03e8a871b4722da"