diff --git a/cspell.json b/cspell.json index 047ade6457..be1ac66069 100644 --- a/cspell.json +++ b/cspell.json @@ -103,6 +103,7 @@ "Threadripper", "Threadripper-based", "Timeouted", + "Tolk", "Toncoin", "Toncoins", "Underload", @@ -175,6 +176,7 @@ "hmac", "howto", "HOWTO", + "idict", "inclusivity", "inplace", "int", @@ -196,12 +198,14 @@ "liteservers", "logname", "long", + "lowlevel", "masterchain", "mathrm", "mintless", "micropayment", "micropayments", "mintable", + "moddiv", "multichain", "multisignature", "mystore", @@ -217,6 +221,7 @@ "nonfinal", "penalising", "penalised", + "pfxdict", "precomplied", "preimage", "prer", @@ -259,6 +264,7 @@ "sunt", "superserver", "tick-tock", + "tilda", "timeouted", "tock", "tokenomics", @@ -266,6 +272,7 @@ "udict", "uint", "unbounceable", + "uncons", "underload", "underloaded", "unixtime", diff --git a/docs/v3/documentation/smart-contracts/tolk/changelog.md b/docs/v3/documentation/smart-contracts/tolk/changelog.md new file mode 100644 index 0000000000..bdb362fae0 --- /dev/null +++ b/docs/v3/documentation/smart-contracts/tolk/changelog.md @@ -0,0 +1,32 @@ +# History of Tolk + +When new versions of Tolk are released, they will be mentioned here. + + +## v0.6 + +The first public release. Here are some notes about its origin: + + +## How Tolk was born + +In June 2024, I created a pull request [FunC v0.5.0](https://github.com/ton-blockchain/ton/pull/1026). +Besides this PR, I've written a roadmap — what can be enhanced in FunC, syntactically and semantically. + +All in all, instead of merging v0.5.0 and continuing developing FunC, we decided to **fork** it. +To leave FunC untouched, as it is. As it always was. And to develop a new language, driven by a fresh and new name. + +For several months, I have worked on Tolk privately. I have implemented a giant list of changes. +And it's not only about the syntax. For instance, Tolk has an internal AST representation, completely missed in FunC. + +On TON Gateway, on 1-2 November in Dubai, I had a speech presenting Tolk to the public, and we released it the same day. +Once the video is available, I'll attach it here. + +Here is the very first pull request: ["Tolk Language: next-generation FunC"](https://github.com/ton-blockchain/ton/pull/1345). + +The first version of the Tolk Language is v0.6, a metaphor of FunC v0.5 that missed a chance to occur. + + +## Meaning of the name "Tolk" + +I'll update this section after announcing Tolk on TON Gateway. diff --git a/docs/v3/documentation/smart-contracts/tolk/overview.mdx b/docs/v3/documentation/smart-contracts/tolk/overview.mdx new file mode 100644 index 0000000000..3189d24670 --- /dev/null +++ b/docs/v3/documentation/smart-contracts/tolk/overview.mdx @@ -0,0 +1,158 @@ +--- +title: Overview +--- + +import Button from '@site/src/components/button' + +# Tolk Language: overview + +**Tolk** is a new language for writing smart contracts in TON. Think of Tolk as the "**next‑generation FunC**". +Tolk compiler is literally a fork of FunC compiler, introducing familiar syntax similar to TypeScript, +but leaving all low-level optimizations untouched. + +```tolk +import "storage.tolk" + +fun loadData() { + ctxCounter = getContractData().beginParse().loadUint(32); +} + +fun onInternalMessage(msgValue: int, msgFull: cell, msgBody: slice) { + var cs = msgFull.beginParse(); + var flags = cs.loadMessageFlags(); + if (isMessageBounced(flags)) { + return; + } + ... +} + +get currentCounter(): int { + loadData(); // fills global variables + return ctxCounter; +} +``` + +
+ See same logic implemented with FunC + +```func +#include "storage.fc"; + +() load_data() impure { + slice cs = get_data().begin_parse(); + ctx_counter = cs~load_uint(32); +} + +() recv_internal(int msg_value, cell msg_full, slice msg_body) impure { + slice cs = msg_full.begin_parse(); + int flags = cs.load_uint(4); + if (flags & 1) { + return (); + } + ... +} + +int currentCounter() method_id { + load_data(); ;; fills global variables + return ctx_counter; +} +``` +
+ + + +
+ + +## Motivation behind Tolk + +FunC is awesome. +It is really low-level and encourages a programmer to think about compiler internals. +It gives full control over TVM assembler, allowing a programmer to make his contract as effective as possible. +If you get used to it, you love it. + +But there is a problem. +FunC is "functional C", and it's for ninja. +If you are keen on Lisp and Haskell, you'll be happy. +But if you are a JavaScript / Go / Kotlin developer, its syntax is peculiar for you, leading to occasional mistakes. +A struggle with syntax may decrease your motivation for digging into TON. + +Imagine, what if there was a language, also smart, also low-level, but not functional and not like C? +Leaving all beauty and complexity inside, what if it would be more similar to popular languages at first glance? + +That's what Tolk is about. + + +## Migrating from FunC to Tolk + +If you know FunC and want to try a new syntax, your way is: +1. Read [Tolk vs FunC: in short](/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-short). +2. With blueprint, create a new Tolk contract (for example, a counter) and experiment around. Remember, that almost all stdlib functions are renamed to ~~verbose~~ clear names. Here is [a mapping](/v3/documentation/smart-contracts/tolk/tolk-vs-func/stdlib). +3. Try a [converter](https://github.com/ton-blockchain/convert-func-to-tolk) for your existing contracts or one from [FunC Contracts](/v3/documentation/smart-contracts/contracts-specs/examples). Keep in mind that contracts written in Tolk from scratch would definitely look nicer than being auto-converted. For instance, using logical operators instead of bitwise tremendously increases code readability. + +## How to try Tolk if you don't know FunC + +:::tip Currently, this documentation assumes that you know FunC +The documentation describes "Tolk vs FunC" differences. +Later it will be adapted to land newcomers. Moreover, FunC will eventually become deprecated, +and all code snippets throughout the whole documentation will be rewritten to Tolk. +::: + +If you are new to TON, your way is: +1. Dig into [this documentation](/v3/documentation/smart-contracts/overview) to acquire basic on development in TON. No matter what language you'll use, you need to be aware of cells, slices, TON asynchronous nature after all. +2. Facing FunC snippets, you can still use FunC, or try to express the same in Tolk. If FunC syntax is peculiar for you, don't worry: the goal of Tolk is exactly to fix this issue. +3. Once you gain some understanding of what's going on, try using Tolk with [blueprint](https://github.com/ton-org/blueprint). + + +## Tooling around Tolk Language + +Sources of the Tolk compiler are a part of the `ton-blockchain` [repo](https://github.com/ton-blockchain/ton). +Besides the compiler, we have: + +1. [tolk-js](https://github.com/ton-blockchain/tolk-js) — a WASM wrapper for Tolk compiler. +2. [JetBrains IDE plugin](https://github.com/ton-blockchain/intellij-ton) supports Tolk besides FunC, Fift, TL/B, and Tact. +3. [VS Code Extension](https://github.com/ton-blockchain/tolk-vscode) enabling Tolk Language support. +4. [Converter from FunC to Tolk](https://github.com/ton-blockchain/convert-func-to-tolk) — convert a `.fc` file to a `.tolk` file with a single `npx` command. +5. Tolk Language is available in [blueprint](https://github.com/ton-org/blueprint). + + +## Is Tolk production-ready? + +The Tolk compiler, a fork of the FunC compiler, is deemed production-ready, albeit somewhat experimental at the moment. + +Undiscovered bugs may exist, potentially inherited from FunC or attributable to TVM characteristics. +Anyway, no matter what language you use, you should cover your contracts with tests to reach high reliability. + + +## Roadmap + +The first released version of Tolk is v0.6, emphasizing [missing](/v3/documentation/smart-contracts/tolk/changelog#how-tolk-was-born) FunC v0.5. + +Here are some (yet not all and not ordered in any way) points to be investigated: +- type system improvements: boolean type, nullability, dictionaries +- structures, with auto-packing to/from cells, probably integrated with message handlers +- structures with methods, probably generalized to cover built-in types +- some integrations with TL scheme, either syntactical or via code generation +- human-readable compiler errors +- easier messages sending +- better experience for common use-cases (jettons, nft, etc.) +- gas and stack optimizations, AST inlining +- extending and maintaining stdlib +- think about some kind of ABI (how explorers "see" bytecode) +- think about gas and fee management in general + +Note, that most of the points above are a challenge to implement. +At first, FunC kernel must be fully refactored to "interbreed" with abilities it was not designed for. + +Also, I see Tolk evolution partially guided by community needs. +It would be nice to talk to developers who have created interconnected FunC contracts, +to absorb their pain points and discuss how things could be done differently. + + +## Issues and Contacts + +If you face issue, connect to developer society on [TON Dev Chats](https://t.me/addlist/1r5Vcb8eljk5Yzcy) or create GitHub issues. diff --git a/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-detail.mdx b/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-detail.mdx new file mode 100644 index 0000000000..111958bf4d --- /dev/null +++ b/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-detail.mdx @@ -0,0 +1,849 @@ +# Tolk vs FunC: in detail + +A very huge list below. Will anyone have enough patience to read it up to the end?.. + +:::tip There is a compact version +Here: [Tolk vs FunC: in short](/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-short) +::: + + +

+ ✅ Traditional comments :) +

+ + + + + + + + + + + + + + + + + + +
FunCTolk
{';; comment'}{'// comment'}
{'{- multiline comment -}'}{'/* multiline comment */'}
+ + +

+ ✅ `2+2` is 4, not an identifier. Identifiers can only be alpha-numeric +

+ +In FunC, almost any character can be a part of identifier. +For example, `2+2` (without a space) is an identifier. +You can even declare a variable with such a name. + +In Tolk, spaces are not mandatory. `2+2` is 4, as expected. `3+~x` is `3 + (~ x)`, and so on. + + + + + + + + + + + + + + +
FunCTolk
{'return 2+2; ;; undefined function `2+2`'}{'return 2+2; // 4'}
+ +More precisely, an identifier can start from {'[a-zA-Z$_]'} +and be continued with {'[a-zA-Z0-9$_]'}. Note, that `?`, `:`, and others are not valid symbols, `found?` and `op::increase` are not valid identifiers. + +You can use backticks to surround an identifier, and then it can contain any symbols (similar to Kotlin and some other langs). Its potential usage is to allow keywords be used as identifiers, in case of code generation by a scheme, for example. + + + + + + + + + + + + + + + + + + +
FunCTolk
{'const op::increase = 0x1234;'}{'const OP_INCREASE = 0x1234;'}
int 2+2 = 5;'}}>var \`2+2\` = 5;'}}>
+ + +

+ ✅ Impure by default, compiler won't drop user function calls +

+ +FunC has an `impure` function specifier. When absent, a function is treated as pure. If its result is unused, its call was deleted by the compiler. + +Though this behavior is documented, it is very unexpected to newcomers. +For instance, various functions that don't return anything (throw an exception on mismatch, for example), +are silently deleted. This situation is spoilt by the fact that FunC doesn't check and validate function body, +allowing impure operations inside pure functions. + +In Tolk, all functions are impure by default. You can mark a function pure with annotation, +and then impure operations are forbidden in its body (exceptions, globals modification, calling non-pure functions, etc.). + + +

+ ✅ New functions syntax: `fun` keyword, `@` attributes, types on the right (like in TypeScript, Kotlin, Python, etc.) +

+ + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
{'cell parse_data(slice cs) { }'}{'fun parse_data(cs: slice): cell { }'}
{'(cell, int) load_storage() { }'}{'fun load_storage(): (cell, int) { }'}
{'() main() { ... }'}{'fun main() { ... }'}
+ +Types of variables — also to the right: + + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
{'slice cs = ...;'}{'var cs: slice = ...;'}
{'(cell c, int n) = parse_data(cs);'}{'var (c: cell, n: int) = parse_data(cs);'}
{'global int stake_at;'}{'global stake_at: int;'}
+ +Modifiers `inline` and others — with annotations: + + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
int f(cell s) inline {'}}>fun f(s: cell): int {'}}>
() load_data() impure inline_ref {'}}>fun load_data() {'}}>
{'global int stake_at;'}{'global stake_at: int;'}
+ +`forall` — this way: + + + + + + + + + + + + + + +
FunCTolk
{'forall X -> tuple cons(X head, tuple tail)'}
+ +`asm` implementation — like in FunC, but being properly aligned, it looks nicer: +```tolk +@pure +fun third(t: tuple): X + asm "THIRD"; + +@pure +fun iDictDeleteGet(dict: cell, keyLen: int, index: int): (cell, slice, int) + asm(index dict keyLen) "DICTIDELGET NULLSWAPIFNOT"; + +@pure +fun mulDivFloor(x: int, y: int, z: int): int + builtin; +``` + +There is also a `@deprecated` attribute, not affecting compilation, but for a human and IDE. + + +

+ ✅ `get` instead of `method_id` +

+ +In FunC, `method_id` (without arguments) actually declared a get method. In Tolk, you use a straightforward syntax: + + + + + + + + + + + + + + +
FunCTolk
{'int seqno() method_id { ... }'}{'get seqno(): int { ... }'}
+ +Both `get methodName()` and `get fun methodName()` are acceptable. + +For `method_id(xxx)` (uncommon in practice, but valid), there is an attribute: + + + + + + + + + + + + + + +
FunCTolk
() after_code_upgrade(cont old_code) impure method_id(1666)'}}>fun afterCodeUpgrade(oldCode: continuation)'}}>
+ + +

+ ✅ It's essential to declare types of parameters (though optional for locals) +

+ +```tolk +// not allowed +fun do_smth(c, n) +// types are mandatory +fun do_smth(c: cell, n: int) +``` + +There is an `auto` type, so `fun f(a: auto)` is valid, though not recommended. + +If parameter types are mandatory, return type is not (it's often obvious of verbose). If omitted, it means `auto`: +```tolk +fun x() { ... } // auto infer return +``` + +For local variables, types are also optional: +```tolk +var i = 10; // ok, int +var b = beginCell(); // ok, builder +var (i, b) = (10, beginCell()); // ok, two variables, int and builder + +// types can be specified manually, of course: +var b: builder = beginCell(); +var (i: int, b: builder) = (10, beginCell()); +``` + + +

+ ✅ Variables are not allowed to be redeclared in the same scope +

+ +```tolk +var a = 10; +... +var a = 20; // error, correct is just `a = 20` +if (1) { + var a = 30; // it's okay, it's another scope +} +``` + +As a consequence, partial reassignment is not allowed: +```tolk +var a = 10; +... +var (a, b) = (20, 30); // error, releclaration of a +``` + +Note, that it's not a problem for `loadUint()` and other methods. In FunC, they returned a modified object, so a pattern `var (cs, int value) = cs.load_int(32)` was quite common. In Tolk, such methods mutate an object: `var value = cs.loadInt(32)`, so redeclaration is unlikely to be needed. + +```tolk +fun send(msg: cell) { + var msg = ...; // error, redeclaration of msg + + // solution 1: intruduce a new variable + var msgWrapped = ...; + // solution 2: use `redef`, though not recommended + var msg redef = ...; +``` + + +

+ ✅ Changes in the type system +

+ +Type system in the first Tolk release is the same as in FunC, with the following modifications: +- `void` is effectively an empty tensor (more canonical to be named `unit`, but `void` is more reliable); btw, `return` (without expression) is actually `return ()`, a convenient way to return from void functions +```tolk +fun setContractData(c: cell): void + asm "c4 POP"; +``` +- `auto` mean "auto infer"; in FunC, `_` was used for that purpose; note, that if a function doesn't specify return type, it's `auto`, not `void` +- `self`, to make chainable methods, described below; actually it's not a type, it can only occur instead of return type of a function +- `cont` renamed to `continuation` + + +

+ ✅ Another naming for recv_internal / recv_external +

+ +```tolk +fun onInternalMessage +fun onExternalMessage +fun onTickTock +fun onSplitPrepare +fun onSplitInstall +``` + +All parameter types and their order rename the same, only naming is changed. `fun main` is also available. + + +

+ ✅ #include → import. Strict imports +

+ + + + + + + + + + + + + + +
FunCTolk
{'#include "another.fc";'}{'import "another.tolk"'}
+ +In Tolk, you can not used a symbol from `a.tolk` without importing this file. In other words, "import what you use". + +All stdlib functions are available out of the box, downloading stdlib and `#include "stdlib.fc"` is not needed. See below about embedded stdlib. + +There is still a global scope of naming. If `f` is declared in two different files, it's an error. We "import" a whole file, no per-file visibility and `export` keyword is now supported, but probably will be in the future. + + +

+ ✅ #pragma → compiler options +

+ +In FunC, "experimental" features like `allow-post-modifications` were turned on by a pragma in .fc files (leading to problems when some files contain it, some don't). Indeed, it's not a pragma for a file, it's a compilation option. + +In Tolk, all pragmas were removed. `allow-post-modification` and `compute-asm-ltr` were merged into Tolk sources (as if they were always on in FunC). Instead of pragmas, there is now an ability to pass experimental options. + +As for now, there is one experimental option introduced — `remove-unused-functions`, which doesn't include unused symbols to Fift output. + +`#pragma version xxx` was replaced by `tolk xxx` (no >=, just a strict version). It's good practice to annotate compiler version you are using. If it doesn't match, Tolk will show a warning. +```tolk +tolk 0.6 +``` + + +

+ ✅ Late symbols resolving. AST representation +

+ +In FunC (like in С) you can not access a function declared below: +```func +int b() { a(); } ;; error +int a() { ... } ;; since it's declared below +``` + +To avoid an error, a programmer should create a forward declaration at first. The reason is that symbols resolving is performed right at the time of parsing. + +Tolk compiler separates these two steps. At first it does parsing, and then it does symbol resolving. Hence, a snippet above would not be erroneous. + +Sounds simple, but internally, it's a very huge job. To make this available, I've introduced an intermediate AST representation, completely missed in FunC. That's an essential point of future modifications and performing semantic code analisys. + + +

+ ✅ `null` keyword +

+ +Creating null values and checking variables on null looks very pretty now. + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
{'a = null()'}{'a = null'}
{'if (null?(a))'}{'if (a == null)'}
{'if (~ null?(b))'}{'if (b != null)'}
{'if (~ cell_null?(c))'}{'if (c != null)'}
+ +Note, that it does NOT mean that Tolk language has nullability. No, you can still assign `null` to an integer variable — like in FunC, just syntactically pleasant. A true nullability will be available someday, after hard work on the type system. + + +

+ ✅ `throw` and `assert` keywords +

+ +Tolk greatly simplifies working with exceptions. + +If FunC has `throw()`, `throw_if()`, `throw_arg_if()`, and the same for unless, Tolk has only two primitives: `throw` and `assert`. + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
{'throw(excNo)'}{'throw excNo'}
{'throw_arg(arg, excNo)'}{'throw (excNo, arg)'}
{'throw_unless(excNo, condition)'}{'assert(condition, excNo)'}
{'throw_if(excNo, condition)'}{'assert(!condition, excNo)'}
+ +Note, that `!condition` is possible since logical NOT is available, see below. + +There is a long (verbose) syntax of `assert(condition, excNo)`: +```tolk +assert(condition) throw excNo; +// with possibility to include arg to throw +``` + +Also, Tolk swaps `catch` arguments: it's `catch (excNo, arg)`, both optional (since arg is most likely empty). + + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
{'try { } catch (_, _) { }'}{'try { } catch { }'}
{'try { } catch (_, excNo) { }'}{'try { } catch(excNo) { }'}
{'try { } catch (arg, excNo) { }'}{'try { } catch(excNo, arg) { }'}
+ + +

+ ✅ `do ... until` → `do ... while` +

+ + + + + + + + + + + + + + + + + + +
FunCTolk
{'do { ... } until (~ condition);'}{'do { ... } while (condition);'}
{'do { ... } until (condition);'}{'do { ... } while (!condition);'}
+ +Note, that `!condition` is possible since logical NOT is available, see below. + + +

+ ✅ Operator precedence became identical to C++ / JavaScript +

+ +In FunC, such code `if (slices_equal() & status == 1)` is parsed as `if( (slices_equal()&status) == 1 )`. This is a reason of various errors in real-world contracts. + +In Tolk, `&` has lower priority, identical to C++ and JavaScript. + +Moreover, Tolk fires errors on potentially wrong operators usage to completely eliminate such errors: +```tolk +if (flags & 0xFF != 0) +``` + +will lead to a compilation error (similar to gcc/clang): +``` +& has lower precedence than ==, probably this code won't work as you expected. Use parenthesis: either (... & ...) to evaluate it first, or (... == ...) to suppress this error. +``` + +Hence, the code should be rewritten: +```tolk +// either to evaluate it first (our case) +if ((flags & 0xFF) != 0) +// or to emphasize the behavior (not our case here) +if (flags & (0xFF != 0)) +``` + +I've also added a diagnostic for a common mistake in bitshift operators: `a << 8 + 1` is equivalent to `a << 9`, probably unexpected. + +``` +int result = a << 8 + low_mask; + +error: << has lower precedence than +, probably this code won't work as you expected. Use parenthesis: either (... << ...) to evaluate it first, or (... + ...) to suppress this error. +``` + +Operators `~% ^% /% ~/= ^/= ~%= ^%= ~>>= ^>>=` no longer exist. + + +

+ ✅ Immutable variables, declared via `val` +

+ +Like in Kotlin: `var` for mutable, `val` for immutable, optionally followed by a type. FunC has no analogue of `val`. +```tolk +val flags = msgBody.loadMessageFlags(); +flags &= 1; // error, modifying an immutable variable + +val cs: slice = c.beginParse(); +cs.loadInt(32); // error, since loadInt() mutates an object +cs.preloadInt(32); // ok, it's a read-only method +``` + +Parameters of a function are mutable, but since they are copied by value, called arguments aren't changed. Exactly like in FunC, just to clarify. +```tolk +fun some(x: int) { + x += 1; +} + +val origX = 0; +some(origX); // origX remains 0 + +fun processOpIncrease(msgBody: slice) { + val flags = msgBody.loadInt(32); + ... +} + +processOpIncrease(msgBody); // by value, not modified +``` + +In Tolk, a function can declare `mutate` parameters. It's a generalization of FunC `~` tilda functions, read below. + + +

+ ✅ Deprecated command-line options removed +

+ +Command-line flags `-A`, `-P`, and others, were removed. Default behavior +``` +/path/to/tolk {inputFile} +``` +is more than enough. Use `-v` to print version and exit. Use `-h` for all available command-line flags. + +Only one input file can be passed, others should be `import`'ed. + + +

+ ✅ stdlib functions renamed to ~~verbose~~ clear names, camelCase style +

+ +All naming in standard library was reconsidered. Now, functions are named using longer, but clear names. + + + + + + + + + + + + + + +
FunCTolk
car(l)
get_balance().pair_first()
raw_reserve(count)
dict~idict_add?(...)
dict~udict::delete_get_max()
t~tpush(triple(x, y, z))
s.slice_bits()
~dump(x)
...'}}>
listGetHead(l)
getMyOriginalBalance()
reserveToncoinsOnBalance(count)
dict.iDictSetIfNotExists(...)
dict.uDictDeleteLastAndGet()
t.tuplePush([x, y, z])
s.getRemainingBitsCount()
debugPrint(x)
...'}}>
+ +A former "stdlib.fc" was split into multiple files: common.tolk, tvm-dicts.tolk, and others. + +Continue here: [Tolk vs FunC: standard library](/v3/documentation/smart-contracts/tolk/tolk-vs-func/stdlib). + + +

+ ✅ stdlib is now embedded, not downloaded from GitHub +

+ + + + + + + + + + + + + + +
FunCTolk
+
    +
  1. Download stdlib.fc from GitHub
  2. +
  3. Save into your project
  4. +
  5. `#include "stdlib.fc";`
  6. +
  7. Use standard functions
  8. +
+
+
    +
  1. Use standard functions
  2. +
+
+ +In Tolk, stdlib a part of distribution. Standard library is inseparable, since keeping a triple "language, compiler, stdlib" together is the only correct way to maintain release cycle. + +It works in such a way. Tolk compiler knows how to locate a standard library. If a user has installed an apt package, stdlib sources were also downloaded and exist on a hard disk, so the compiler locates them by system paths. If a user uses a WASM wrapper, they are provided by tolk-js. And so on. + +Standard library is split into multiple files: `common.tolk` (most common functions), `gas-payments.tolk` (calculating gas fees), `tvm-dicts.tolk`, and others. Functions from `common.tolk` are available always (a compiler implicitly imports it). Other files are needed to be explicitly imported: +```tolk +import "@stdlib/tvm-dicts" // ".tolk" optional + +... +var dict = createEmptyDict(); +dict.iDictSet(...); +``` + +Mind the rule "import what you use", it's applied to `@stdlib/...` files also (with the only exception of "common.tolk"). + +JetBrains IDE plugin automatically discovers stdlib folder and inserts necessary imports as you type. + + +

+ ✅ Logical operators `&& ||`, logical not `!` +

+ +In FunC, there are only bitwise operators `~ & | ^`. Developers making first steps, thinking "okay, no logical, I'll use bitwise in the same manner", often do errors, since operator behavior is completely different: + + + + + + + + + + + + + + + + + + + + + + + + +
`a & b``a && b`
sometimes, identical:
{'0 & X = 0'}{'0 & X = 0'}
{'-1 & X = -1'}{'-1 & X = -1'}
but generally, not:
{'1 & 2 = 0'}{'1 && 2 = -1 (true)'}
+ + + + + + + + + + + + + + + + + + + + + + + + +
`~ found``!found`
sometimes, identical:
{'true (-1) → false (0)'}{'-1 → 0'}
{'false (0) → true (-1)'}{'0 → -1'}
but generally, not:
{'1 → -2'}{'1 → 0 (false)'}
+ + + + + + + + + + + + + + +
condition & f()condition && f()
f() is called alwaysf() is called only if condition
+ + + + + + + + + + + + + + +
condition | f()condition || f()
f() is called alwaysf() is called only if condition is false
+ +Tolk supports logical operators. They behave exactly as you get used to (right column). For now, `&&` and `||` sometimes produce not optimal Fift code, but in the future, Tolk compiler will become smarter in this case. It's negligible, just use them like in other languages. + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
{'if (~ found?)'}{'if (!found)'}
    if (cs~load_int(32) == 0) {
        ...
    }
}'}}>
    ...
}'}}>
{'ifnot (cell_null?(signatures))'}{'if (signatures != null)'}
{'elseifnot (eq_checksum)'}{'else if (!eqChecksum)'}
+ +Keywords `ifnot` and `elseifnot` were removed, since now we have logical not (for optimization, Tolk compiler generates `IFNOTJMP`, btw). Keyword `elseif` was replaced by traditional `else if`. + +Note, that it does NOT mean that Tolk language has `bool` type. No, comparison operators still return an integer. A `bool` type support will be available someday, after hard work on the type system. + +Remember, that `true` is -1, not 1. Both in FunC and Tolk. It's a TVM representation. + + +

+ ✅ No tilda `~` methods, `mutate` keyword instead +

+ +This change is so huge that it's described on a separate page: [Tolk mutability](/v3/documentation/smart-contracts/tolk/tolk-vs-func/mutability). + + +
+ +

Tolk vs FunC gas consumption

+ +:::caution TLDR +Tolk gas consumption could be a bit higher, because it fixes unexpected arguments shuffling in FunC. It's negligible in practice. +In the future, Tolk compiler will become smart enough to reorder arguments targeting less stack manipulations, +but still avoiding a shuffling problem. +::: + +FunC compiler could unexpectedly shuffle arguments when calling an assembly function: +``` +some_asm_function(f1(), f2()); +``` + +Sometimes, `f2()` could be called before `f1()`, and it's unexpected. +To fix this behavior, one could specify `#pragma compute-asm-ltr`, forcing arguments to be always evaluated in ltr-order. +This was experimental, and therefore turned off by default. + +This pragma reorders arguments on a stack, often leading to more stack manipulations than without it. +In other words, in fixes unexpected behavior, but increases gas consumption. + +Tolk puts arguments onto a stack exactly the same as if this pragma turned on. +So, its gas consumption is sometimes higher than in FunC if you didn't use this pragma. +Of course, there is no shuffling problem in Tolk. + +In the future, Tolk compiler will become smart enough to reorder arguments targeting less stack manipulations, +but still avoiding a shuffling problem. diff --git a/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-short.md b/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-short.md new file mode 100644 index 0000000000..36642c4c3e --- /dev/null +++ b/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-short.md @@ -0,0 +1,56 @@ +# Tolk vs FunC: in short + +Tolk is much more similar to TypeScript and Kotlin than to C and Lisp. +But it still gives you full control over TVM assembler, since it has a FunC kernel inside. + +1. Functions are declared via `fun`, get methods via `get`, variables via `var` (and `val` for immutable), putting types on the right; parameter types are mandatory; return type can be omitted (auto inferred), as well as for locals; specifiers `inline` and others are `@` attributes +```tolk +global storedV: int; + +fun parseData(cs: slice): cell { + var flags: int = cs.loadMessageFlags(); + ... +} + +@inline +fun sum(a: int, b: int) { // auto inferred int + val both = a + b; // same + return both; +} + +get currentCounter(): int { ... } +``` +2. No `impure`, it's by default, compiler won't drop user function calls +3. Not `recv_internal` and `recv_external`, but `onInternalMessage` and `onExternalMessage` +4. `2+2` is 4, not an identifier; identifiers are alpha-numeric; use naming `const OP_INCREASE` instead of `const op::increase` +5. Logical operators AND `&&`, OR `||`, NOT `!` are supported +6. Syntax improvements: + - `;; comment` → `// comment` + - `{- comment -}` → `/* comment */` + - `#include` → `import`, with a strict rule "import what you use" + - `~ found` → `!found` (for true/false only, obviously) (true is -1, like in FunC) + - `v = null()` → `v = null` + - `null?(v)` → `v == null`, same for `builder_null?` and others + - `~ null?(v)` → `c != null` + - `throw(excNo)` → `throw excNo` + - `catch(_, _)` → `catch` + - `catch(_, excNo)` → `catch(excNo)` + - `throw_unless(excNo, cond)` → `assert(cond, excNo)` + - `throw_if(excNo, cond)` → `assert(!cond, excNo)` + - `return ()` → `return` + - `do ... until (cond)` → `do ... while (!cond)` + - `elseif` → `else if` + - `ifnot (cond)` → `if (!cond)` +7. A function can be called even if declared below; forward declarations not needed; the compiler at first does parsing, and then it does symbol resolving; there is now an AST representation of source code +8. stdlib functions renamed to ~~verbose~~ clear names, camelCase style; it's now embedded, not downloaded from GitHub; it's split into several files; common functions available always, more specific available with `import "@stdlib/tvm-dicts"`, IDE will suggest you; here is [a mapping](/v3/documentation/smart-contracts/tolk/tolk-vs-func/stdlib) +9. No `~` tilda methods; `cs.loadInt(32)` modifies a slice and returns an integer; `b.storeInt(x, 32)` modifies a builder; `b = b.storeInt()` also works, since it not only modifies, but returns; chained methods work identically to JS, they return `self`; everything works exactly as expected, similar to JS; no runtime overhead, exactly same Fift instructions; custom methods are created with ease; tilda `~` does not exist in Tolk at all; [more details here](/v3/documentation/smart-contracts/tolk/tolk-vs-func/mutability) + +#### Tooling around +- JetBrains plugin exists +- VS Code extension [exists](https://github.com/ton-blockchain/tolk-vscode) +- WASM wrapper for blueprint [exists](https://github.com/ton-blockchain/tolk-js) +- And even a converter from FunC to Tolk [exists](https://github.com/ton-blockchain/convert-func-to-tolk) + +#### Where to go next + +[Tolk vs FunC: in detail](/v3/documentation/smart-contracts/tolk/tolk-vs-func/in-detail) diff --git a/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/mutability.mdx b/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/mutability.mdx new file mode 100644 index 0000000000..06bfc9aba8 --- /dev/null +++ b/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/mutability.mdx @@ -0,0 +1,321 @@ +--- +title: "Tolk vs FunC: mutability" +--- + +# Mutability in Tolk vs tilda functions in FunC + +:::tip TLDR +- no `~` tilda methods +- `cs.loadInt(32)` modifies a slice and returns an integer +- `b.storeInt(x, 32)` modifies a builder +- `b = b.storeInt()` also works, since it not only modifies, but returns +- chained methods work identically to JS, they return `self` +- everything works exactly as expected, similar to JS +- no runtime overhead, exactly same Fift instructions +- custom methods are created with ease +- tilda `~` does not exist in Tolk at all +::: + +This is a drastic change. If FunC has `.methods()` and `~methods()`, Tolk has only dot, one and only way to call a `.method()`. A method may *mutate* an object, or may not. Unlike the list "in short", it's a behavioral and semantic difference from FunC. + +The goal is to have calls identical to JS and other languages: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
{'int flags = cs~load_uint(32);'}{'var flags = cs.loadUint(32);'}
{'(cs, int flags) = cs.load_uint(32);'}{'var flags = cs.loadUint(32);'}
{'(slice cs2, int flags) = cs.load_uint(32);'}var flags = cs2.loadUint(32);'}}>
             .begin_parse();
int flag = data~load_uint(32);'}}>
           .beginParse()
           .loadUint(32);'}}>
{'dict~udict_set(...);'}{'dict.uDictSet(...);'}
{'b~store_uint(x, 32);'}{'b.storeInt(x, 32);'}
{'b = b.store_int(x, 32);'}
// also works
b = b.storeUint(32);'}}>
     .store_int(y, 32);'}}> .storeInt(y, 32);

// b = ...; also works'}}>
+ +In order to make this available, Tolk offers a mutability conception, which is a generalization of what a tilda means in FunC. + +

+ By default, all arguments are copied by value (identical to FunC) +

+ +```tolk +fun someFn(x: int) { + x += 1; +} + +var origX = 0; +someFn(origX); // origX remains 0 +someFn(10); // ok, just int +origX.someFn(); // still allowed (but not recommended), origX remains 0 +``` + +Same goes for cells, slices, whatever: +```tolk +fun readFlags(cs: slice) { + return cs.loadInt(32); +} + +var flags = readFlags(msgBody); // msgBody is not modified +// msgBody.loadInt(32) will read the same flags +``` + +It means, that when you call a function, you are sure that original data is not modified. + +

+ `mutate` keyword and mutating functions +

+ +But if you add `mutate` keyword to a parameter, a passed argument will be mutated. To avoid unexpected mutations, you must specify `mutate` when calling it, also: +```tolk +fun increment(mutate x: int) { + x += 1; +} + +// it's correct, simple and straightforward +var origX = 0; +increment(mutate origX); // origX becomes 1 + +// these are compiler errors +increment(origX); // error, unexpected mutation +increment(10); // error, not lvalue +origX.increment(); // error, not a method, unexpected mutation +val constX = getSome(); +increment(mutate constX); // error, it's immutable, since `val` +``` + +Same for slices and any other types: +```tolk +fun readFlags(mutate cs: slice) { + return cs.loadInt(32); +} + +val flags = readFlags(mutate msgBody); +// msgBody.loadInt(32) will read the next integer +``` + +It's a generalization. A function may have several mutate parameters: +```tolk +fun incrementXY(mutate x: int, mutate y: int, byValue: int) { + x += byValue; + y += byValue; +} + +incrementXY(mutate origX, mutate origY, 10); // both += 10 +``` + +*You may ask — is it just passing by reference? It effectively is, but since "ref" is an overloaded term in TON (cells and slices have refs), a keyword `mutate` was chosen.* + +

+ `self` parameter turning a function into a method +

+ +When a first parameter is named `self`, it emphasizes that a function (still a global one) is a method and should be called via dot. +```tolk +fun assertNotEq(self: int, throwIfEq: int) { + if (self == throwIfEq) { + throw 100; + } +} + +someN.assertNotEq(10); +10.assertNotEq(10); // also ok, since self is not mutating +assertNotEq(someN, 10); // still allowed (but not recommended) +``` + +`self`, without `mutate`, is **immutable** (unlike all other parameters). Think of it like "read-only method". +```tolk +fun readFlags(self: slice) { + return self.loadInt(32); // error, modifying immutable variable +} + +fun preloadInt32(self: slice) { + return self.preloadInt(32); // ok, it's a read-only method +} +``` + +Combining `mutate` and `self`, we get mutating methods. + +

+ `mutate self` is a method, called via dot, mutating an object +

+ +As follows: +```tolk +fun readFlags(mutate self: slice) { + return self.loadInt(32); +} + +val flags = msgBody.readFlags(); // pretty obvious + +fun increment(mutate self: int) { + self += 1; +} + +var origX = 10; +origX.increment(); // 11 +10.increment(); // error, not lvalue + +// even this is possible +fun incrementWithY(mutate self: int, mutate y: int, byValue: int) { + self += byValue; + y += byValue; +} + +origX.incrementWithY(mutate origY, 10); // both += 10 +``` + +If you take a look into stdlib, you'll notice, that lots of functions are actually `mutate self`, meaning they are methods, modifying an object. Tuples, dictionaries, and so on. In FunC, they were usually called via tilda. +```tolk +@pure +fun tuplePush(mutate self: tuple, value: X): void + asm "TPUSH"; + +t.tuplePush(1); +``` + +

+ `return self` makes a method chainable +

+ +Exactly like `return self` in Python or `return this` in JavaScript. That's what makes methods like `storeInt()` and others chainable. +```tolk +fun storeInt32(mutate self: builder, x: int): self { + self.storeInt(x, 32); + return self; + + // this would also work as expected (the same Fift code) + // return self.storeInt(x, 32); +} + +var b = beginCell().storeInt(1, 32).storeInt32(2).storeInt(3, 32); +b.storeInt32(4); // works without assignment, since mutates b +b = b.storeInt32(5); // and works with assignment, since also returns +``` + +Pay attention to the return type, it's `self`. Currently, you should specify it. Being left empty, compilation will fail. Probably, in the future it would be correct. + +

+ `mutate self` and asm functions +

+ +While it's obvious for user-defined functions, one could be interested, how to make an `asm` function with such behavior? To answer this question, we should look under the hood, how mutation works inside the compiler. + +When a function has `mutate` parameters, it actually implicitly returns them, and they are implicitly assigned to arguments. It's better by example: +```tolk +// actually returns (int, void) +fun increment(mutate x: int): void { ... } + +// actually does: (x', _) = increment(x); x = x' +increment(mutate x); + +// actually returns (int, int, (slice, cell)) +fun f2(mutate x: int, mutate y: int): (slice, cell) { ... } + +// actually does: (x', y', r) = f2(x, y); x = x'; y = y'; someF(r) +someF(f2(mutate x, mutate y)); + +// when `self`, it's exactly the same +// actually does: (cs', r) = loadInt(cs, 32); cs = cs'; flags = r +flags = cs.loadInt(32); +``` + +So, an `asm` function should place `self'` onto a stack before its return value: +```tolk +// "TPUSH" pops (tuple) and pushes (tuple') +// so, self' = tuple', and return an empty tensor +// `void` is a synonym for an empty tensor +fun tuplePush(mutate self: tuple, value: X): void + asm "TPUSH"; + +// "LDU" pops (slice) and pushes (int, slice') +// with asm(-> 1 0), we make it (slice', int) +// so, self' = slice', and return int +fun loadMessageFlags(mutate self: slice): int + asm(-> 1 0) "4 LDU"; +``` + +Note, that to return self, you don't have to do anything special, just specify a return type. Compiler will do the rest. +```tolk +// "STU" pops (int, builder) and pushes (builder') +// with asm(op self), we put arguments to correct order +// so, self' = builder', and return an empty tensor +// but to make it chainable, `self` instead of `void` +fun storeMessageOp(mutate self: builder, op: int): self + asm(op self) "32 STU"; +``` + +It's very unlikely you'll have to do such tricks. Most likely, you'll just write wrappers around existing functions: +```tolk +// just do like this, without asm, it's the same effective + +@inline +fun myLoadMessageFlags(mutate self: slice): int { + return self.loadUint(4); +} + +@inline +fun myStoreMessageOp(mutate self: builder, flags: int): self { + return self.storeUint(32, flags); +} +``` + +

+ Do I need `@inline` for simple functions/methods? +

+ +For now, better do it, yes. In most examples above, `@inline` was omitted for clarity. Currently, without `@inline`, it will be a separate TVM continuation with jumps in/out. With `@inline`, a function will be generated, but inlined by Fift (like `inline` specifer in FunC). + +In the future, Tolk will automatically detect simple functions and perform a true inlining by itself, on AST level. Such functions won't be even codegenerated to Fift. The compiler would decide, better than a human, whether to inline, to make a ref, etc. But it will take some time for Tolk to become so smart :) For now, please specify the `@inline` attribute. + +

+ But `self` is not a method, it's still a function! I feel like I've been cheated +

+ +Absolutely. Like FunC, Tolk has only global functions (as of v0.6). There are no classes / structures with methods. There are no methods `hash()` for `slice` and `hash()` for `cell`. Instead, there are functions `sliceHash()` and `cellHash()`, which can be called either like functions or by dot (preferred): +```tolk +fun f(s: slice, c: cell) { + // not like this + s.hash(); + c.hash(); + // but like this + s.sliceHash(); + c.cellHash(); + // since it's the same as + sliceHash(s); + cellHash(s); +} +``` + +In the future, after a giant work on the type system, having fully refactored FunC kernel inside, Tolk might have an ability of declaring structures with real methods, generalized enough for covering built-in types. But it will take a long journey to follow. + diff --git a/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/stdlib.md b/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/stdlib.md new file mode 100644 index 0000000000..70e37faf41 --- /dev/null +++ b/docs/v3/documentation/smart-contracts/tolk/tolk-vs-func/stdlib.md @@ -0,0 +1,274 @@ +# Tolk vs FunC: standard library + +FunC has a rich [standard library](/v3/documentation/smart-contracts/func/docs/stdlib), +known as *"stdlib.fc"* file. It's quite low-level and contains lots of `asm` functions +named very closely to TVM commands. + +Tolk also has a standard library based on a FunC one. Three main differences: +1. It's split in multiple files: `common.tolk`, `tvm-dicts.tolk`, and others. Functions from `common.tolk` are available always. Functions from other files are available after import: +```tolk +import "@stdlib/tvm-dicts" + +beginCell() // available always +createEmptyDict() // available due to import +``` +2. You don't need to download it from GitHub, it's a part of Tolk distribution. +3. Almost all FunC functions were renamed to ~~verbose~~ clear names. So that when you write contracts or read example, you better understand what's going on. + + +## A list of renamed functions + +If "Required import" column is empty, a function is available without any imports. + +Note, that some of the functions were deleted, because they either can be expressed syntactically, +or they were very uncommon in practice. + +| FunC name | Tolk name | Required import | +|--------------------------|-----------------------------------------|-----------------| +| empty_tuple | createEmptyTuple | | +| tpush | tuplePush | | +| first | tupleFirst | | +| at | tupleAt | | +| touch | stackMoveToTop | tvm-lowlevel | +| impure_touch | *(deleted)* | | +| single | *(deleted)* | | +| unsingle | *(deleted)* | | +| pair | *(deleted)* | | +| unpair | *(deleted)* | | +| triple | *(deleted)* | | +| untriple | *(deleted)* | | +| tuple4 | *(deleted)* | | +| untuple4 | *(deleted)* | | +| second | *(deleted)* | | +| third | *(deleted)* | | +| fourth | *(deleted)* | | +| pair_first | *(deleted)* | | +| pair_second | *(deleted)* | | +| triple_first | *(deleted)* | | +| triple_second | *(deleted)* | | +| triple_third | *(deleted)* | | +| minmax | minMax | | +| my_address | getMyAddress | | +| get_balance | getMyOriginalBalanceWithExtraCurrencies | | +| cur_lt | getLogicalTime | | +| block_lt | getCurrentBlockLogicalTime | | +| cell_hash | cellHash | | +| slice_hash | sliceHash | | +| string_hash | stringHash | | +| check_signature | isSignatureValid | | +| check_data_signature | isSliceSignatureValid | | +| compute_data_size | calculateCellSizeStrict | | +| slice_compute_data_size | calculateSliceSizeStrict | | +| compute_data_size? | calculateCellSize | | +| slice_compute_data_size? | calculateSliceSize | | +| ~dump | debugPrint | | +| ~strdump | debugPrintString | | +| dump_stack | debugDumpStack | | +| get_data | getContractData | | +| set_data | setContractData | | +| get_c3 | getTvmRegisterC3 | tvm-lowlevel | +| set_c3 | setTvmRegisterC3 | tvm-lowlevel | +| bless | transformSliceToContinuation | tvm-lowlevel | +| accept_message | acceptExternalMessage | | +| set_gas_limit | setGasLimit | | +| buy_gas | *(deleted)* | | +| commit | commitContractDataAndActions | | +| divmod | divMod | | +| moddiv | modDiv | | +| muldiv | mulDivFloor | | +| muldivr | mulDivRound | | +| muldivc | mulDivCeil | | +| muldivmod | mulDivMod | | +| begin_parse | beginParse | | +| end_parse | assertEndOfSlice | | +| load_ref | loadRef | | +| preload_ref | preloadRef | | +| load_int | loadInt | | +| load_uint | loadUint | | +| preload_int | preloadInt | | +| preload_uint | preloadUint | | +| load_bits | loadBits | | +| preload_bits | preloadBits | | +| load_grams | loadCoins | | +| load_coins | loadCoins | | +| skip_bits | skipBits | | +| first_bits | getFirstBits | | +| skip_last_bits | removeLastBits | | +| slice_last | getLastBits | | +| load_dict | loadDict | | +| preload_dict | preloadDict | | +| skip_dict | skipDict | | +| load_maybe_ref | loadMaybeRef | | +| preload_maybe_ref | preloadMaybeRef | | +| cell_depth | getCellDepth | | +| slice_refs | getRemainingRefsCount | | +| slice_bits | getRemainingBitsCount | | +| slice_bits_refs | getRemainingBitsAndRefsCount | | +| slice_empty? | isEndOfSlice | | +| slice_data_empty? | isEndOfSliceBits | | +| slice_refs_empty? | isEndOfSliceRefs | | +| slice_depth | getSliceDepth | | +| equal_slice_bits | isSliceBitsEqual | | +| builder_refs | getBuilderRefsCount | | +| builder_bits | getBuilderBitsCount | | +| builder_depth | getBuilderDepth | | +| begin_cell | beginCell | | +| end_cell | endCell | | +| store_ref | storeRef | | +| store_uint | storeUint | | +| store_int | storeInt | | +| store_slice | storeSlice | | +| store_grams | storeCoins | | +| store_coins | storeCoins | | +| store_dict | storeDict | | +| store_maybe_ref | storeMaybeRef | | +| store_builder | storeBuilder | | +| load_msg_addr | loadAddress | | +| parse_addr | parseAddress | | +| parse_std_addr | parseStandardAddress | | +| parse_var_addr | *(deleted)* | | +| config_param | getBlockchainConfigParam | | +| raw_reserve | reserveToncoinsOnBalance | | +| raw_reserve_extra | reserveExtraCurrenciesOnBalance | | +| send_raw_message | sendRawMessage | | +| set_code | setContractCodePostponed | | +| rand | randomRange | | +| get_seed | randomGetSeed | | +| set_seed | randomSetSeed | | +| randomize | randomizeBy | | +| randomize_lt | randomizeByLogicalTime | | +| dump | debugPrint | | +| strdump | debugPrintString | | +| dump_stk | debugDumpStack | | +| empty_list | createEmptyList | lisp-lists | +| cons | listPrepend | lisp-lists | +| uncons | listSplit | lisp-lists | +| list_next | listNext | lisp-lists | +| car | listGetHead | lisp-lists | +| cdr | listGetTail | lisp-lists | +| new_dict | createEmptyDict | tvm-dicts | +| dict_empty? | dictIsEmpty | tvm-dicts | +| idict_set_ref | iDictSetRef | tvm-dicts | +| udict_set_ref | uDictSetRef | tvm-dicts | +| idict_get_ref | iDictGetRefOrNull | tvm-dicts | +| idict_get_ref? | iDictGetRef | tvm-dicts | +| udict_get_ref? | uDictGetRef | tvm-dicts | +| idict_set_get_ref | iDictSetAndGetRefOrNull | tvm-dicts | +| udict_set_get_ref | iDictSetAndGetRefOrNull | tvm-dicts | +| idict_delete? | iDictDelete | tvm-dicts | +| udict_delete? | uDictDelete | tvm-dicts | +| idict_get? | iDictGet | tvm-dicts | +| udict_get? | uDictGet | tvm-dicts | +| idict_delete_get? | iDictDeleteAndGet | tvm-dicts | +| udict_delete_get? | uDictDeleteAndGet | tvm-dicts | +| udict_set | uDictSet | tvm-dicts | +| idict_set | iDictSet | tvm-dicts | +| dict_set | sDictSet | tvm-dicts | +| udict_add? | uDictSetIfNotExists | tvm-dicts | +| udict_replace? | uDictSetIfExists | tvm-dicts | +| idict_add? | iDictSetIfNotExists | tvm-dicts | +| idict_replace? | iDictSetIfExists | tvm-dicts | +| udict_set_builder | uDictSetBuilder | tvm-dicts | +| idict_set_builder | iDictSetBuilder | tvm-dicts | +| dict_set_builder | sDictSetBuilder | tvm-dicts | +| udict_add_builder? | uDictSetBuilderIfNotExists | tvm-dicts | +| udict_replace_builder? | uDictSetBuilderIfExists | tvm-dicts | +| idict_add_builder? | iDictSetBuilderIfNotExists | tvm-dicts | +| idict_replace_builder? | iDictSetBuilderIfExists | tvm-dicts | +| udict_delete_get_min | uDictDeleteFirstAndGet | tvm-dicts | +| idict_delete_get_min | iDictDeleteFirstAndGet | tvm-dicts | +| dict_delete_get_min | sDictDeleteFirstAndGet | tvm-dicts | +| udict_delete_get_max | uDictDeleteLastAndGet | tvm-dicts | +| idict_delete_get_max | iDictDeleteLastAndGet | tvm-dicts | +| dict_delete_get_max | sDictDeleteLastAndGet | tvm-dicts | +| udict_get_min? | uDictGetFirst | tvm-dicts | +| udict_get_max? | uDictGetLast | tvm-dicts | +| udict_get_min_ref? | uDictGetFirstAsRef | tvm-dicts | +| udict_get_max_ref? | uDictGetLastAsRef | tvm-dicts | +| idict_get_min? | iDictGetFirst | tvm-dicts | +| idict_get_max? | iDictGetLast | tvm-dicts | +| idict_get_min_ref? | iDictGetFirstAsRef | tvm-dicts | +| idict_get_max_ref? | iDictGetLastAsRef | tvm-dicts | +| udict_get_next? | uDictGetNext | tvm-dicts | +| udict_get_nexteq? | uDictGetNextOrEqual | tvm-dicts | +| udict_get_prev? | uDictGetPrev | tvm-dicts | +| udict_get_preveq? | uDictGetPrevOrEqual | tvm-dicts | +| idict_get_next? | iDictGetNext | tvm-dicts | +| idict_get_nexteq? | iDictGetNextOrEqual | tvm-dicts | +| idict_get_prev? | iDictGetPrev | tvm-dicts | +| idict_get_preveq? | iDictGetPrevOrEqual | tvm-dicts | +| udict::delete_get_min | uDictDeleteFirstAndGet | tvm-dicts | +| idict::delete_get_min | iDictDeleteFirstAndGet | tvm-dicts | +| dict::delete_get_min | sDictDeleteFirstAndGet | tvm-dicts | +| udict::delete_get_max | uDictDeleteLastAndGet | tvm-dicts | +| idict::delete_get_max | iDictDeleteLastAndGet | tvm-dicts | +| dict::delete_get_max | sDictDeleteLastAndGet | tvm-dicts | +| pfxdict_get? | prefixDictGet | tvm-dicts | +| pfxdict_set? | prefixDictSet | tvm-dicts | +| pfxdict_delete? | prefixDictDelete | tvm-dicts | + + +## A list of added functions + +Tolk standard library has some functions that were missing in FunC, but are quite common for everyday tasks. + +Since Tolk is actively developed, and its standard library changes, better consider `tolk-stdlib/` folder +in sources [here](https://github.com/ton-blockchain/ton/tree/master/crypto/smartcont/tolk-stdlib). +Besides functions, there some constants were added: `SEND_MODE_*`, `RESERVE_MODE_*`, etc. + +When FunC becomes deprecated, the documentation about Tolk stdlib will be completely rewritten, anyway. + +And remember, that all the functions above are actually wrappers over TVM assembler. If something is missing, +you can easily wrap any TVM instruction yourself. + + +## Some functions became mutating, not returning a copy + + + + + + + + + + + + + + + + + + + + + + +
FunCTolk
{'int flags = cs~load_uint(32);'}{'var flags = cs.loadUint(32);'}
{'dict~udict_set(...);'}{'dict.uDictSet(...);'}
......
+ +Most FunC functions, that were used with `~` tilda in practice, now mutate the object, see examples above. + +For example, if you used `dict~udict_set(…)`, just use `dict.uDictSet(…)`, and everything is fine. +But if you used `dict.udict_set(…)` to obtain a copy, you'll need to express it some other way. + +[Read about mutability](/v3/documentation/smart-contracts/tolk/tolk-vs-func/mutability). + + +## How does embedded stdlib work under the hood + +As told above, all standard functions are available out of the box. +Yeah, for you need `import` for non-common functions (it's intentionally), but still, no external downloads. + +It works the following way. + +The first thing Tolk compiler does on start is locating stdlib folder by searching in predefined paths relative to an executable binary. +For example, if you launch Tolk compiler from a package installed (e.g. `/usr/bin/tolk`), locate stdlib in `/usr/share/ton/smartcont`. +If you have non-standard installation, you may pass `TOLK_STDLIB` env variable. It's standard practice for compilers. + +A WASM wrapper [tolk-js](https://github.com/ton-blockchain/tolk-js) also contains stdlib. +So, when you take tolk-js or blueprint, all stdlib functions are still available out of the box. + +IDE plugins (both JetBrains and VS Code) also auto-locate stdlib to provide auto-completion. +If you use blueprint, it automatically installs tolk-js, and therefore, folder `node_modules/@ton/tolk-js/` exists in your project file structure. +Inside, there are `common.tolk`, `tvm-dicts.tolk`, and others. diff --git a/navbar.js b/navbar.js index 681b946771..b7a48851a9 100644 --- a/navbar.js +++ b/navbar.js @@ -133,12 +133,16 @@ module.exports = { type: 'dropdown', to: 'v3/documentation/smart-contracts/overview', position: 'left', - label: 'Program Languages', + label: 'Languages', items: [ { to: 'v3/documentation/smart-contracts/func/overview', label: 'FunC', }, + { + to: 'v3/documentation/smart-contracts/tolk/overview', + label: 'Tolk', + }, { to: '/v3/documentation/tvm/instructions', label: 'TVM Instruction', diff --git a/prism-theme.js b/prism-theme.js index b20dab3673..4b03ecd1cb 100644 --- a/prism-theme.js +++ b/prism-theme.js @@ -26,6 +26,19 @@ const theme = { color: "rgb(247, 140, 108)", }, }, + { + types: ["self"], + style: { + color: "rgb(191, 199, 213)", + fontWeight: 'bold', + }, + }, + { + types: ["type-hint"], + style: { + color: "rgb(243,63,148)", + }, + }, { types: ["builtin", "function"], style: { diff --git a/redirects/redirects.json b/redirects/redirects.json index a129da7f56..c240b5c9f5 100644 --- a/redirects/redirects.json +++ b/redirects/redirects.json @@ -423,6 +423,10 @@ "from": "/develop/func/overview", "to": "/v3/documentation/smart-contracts/func/overview" }, + { + "from": "/develop/tolk/overview", + "to": "/v3/documentation/smart-contracts/tolk/overview" + }, { "from": "/develop/func/cookbook", "to": "/v3/documentation/smart-contracts/func/cookbook" diff --git a/sidebars/documentation.js b/sidebars/documentation.js index d344342e7f..19c081c76d 100644 --- a/sidebars/documentation.js +++ b/sidebars/documentation.js @@ -70,7 +70,7 @@ module.exports = [ }, { type: 'category', - label: 'FunC language', + label: 'FunC Language', items: [ { type: 'doc', @@ -102,7 +102,19 @@ module.exports = [ }, { type: 'category', - label: 'Fift language', + label: 'Tolk Language', + items: [ + 'v3/documentation/smart-contracts/tolk/overview', + 'v3/documentation/smart-contracts/tolk/tolk-vs-func/in-short', + 'v3/documentation/smart-contracts/tolk/tolk-vs-func/in-detail', + 'v3/documentation/smart-contracts/tolk/tolk-vs-func/mutability', + 'v3/documentation/smart-contracts/tolk/tolk-vs-func/stdlib', + 'v3/documentation/smart-contracts/tolk/changelog', + ], + }, + { + type: 'category', + label: 'Fift Language', items: [ 'v3/documentation/smart-contracts/fift/overview', 'v3/documentation/smart-contracts/fift/fift-and-tvm-assembly', diff --git a/src/css/custom.css b/src/css/custom.css index 27f2416a2c..76b1d7b7fc 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -1331,6 +1331,56 @@ strong { color: #003cb2 } +h3.cmp-func-tolk-header { + display: block; + font-weight: bold; + font-size: 120%; + margin: 3em 0 1em; + color: #0000CC; +} + +h3.cmp-func-tolk-header code { + font-size: 100% !important; +} + +[data-theme=dark] h3.cmp-func-tolk-header { + color: #d1fff7; +} + +table.cmp-func-tolk-table thead tr { + --ifm-table-background: #e9e9e9; + --ifm-table-border-color: #c0c0c0; +} + +table.cmp-func-tolk-table tbody tr { + --ifm-table-background: #f9f9f9; + --ifm-table-stripe-background: #f9f9f9; + --ifm-table-border-color: #b0b0b0; + vertical-align: top; +} + +[data-theme=dark] table.cmp-func-tolk-table thead tr { + --ifm-table-background: transparent; + --ifm-table-border-color: #606060; +} + +[data-theme=dark] table.cmp-func-tolk-table tbody tr { + --ifm-table-background: #0f0f0f; + --ifm-table-stripe-background: #0f0f0f; + --ifm-table-border-color: #606060; +} + +table.cmp-func-tolk-table:not(.different-col-widths) th { + width: 50%; +} + +table.cmp-func-tolk-table code:not(.inline) { + display: block; + padding: 0 3px; + background: transparent; + border: transparent; +} + .markdown a { font-weight: 500 } diff --git a/src/theme/prism-include-languages.js b/src/theme/prism-include-languages.js index 9be31a30a4..75ff487e9c 100644 --- a/src/theme/prism-include-languages.js +++ b/src/theme/prism-include-languages.js @@ -17,6 +17,7 @@ export default function prismIncludeLanguages(PrismObject) { require('./prism/prism-fift'); require('./prism/prism-func'); + require('./prism/prism-tolk'); require('./prism/prism-tlb'); require('./prism/prism-tact'); diff --git a/src/theme/prism/prism-tolk.js b/src/theme/prism/prism-tolk.js new file mode 100644 index 0000000000..819f4c6e8c --- /dev/null +++ b/src/theme/prism/prism-tolk.js @@ -0,0 +1,56 @@ +/** + * @file Prism.js definition for Tolk (next-generation FunC) + * @version 0.6.0 + * @author Aleksandr Kirsanov (https://github.com/tolk-vm) + * @license MIT + */ +(function(Prism) { + Prism.languages.tolk = { + 'comment': [ + { + pattern: /\/\/.*/, + greedy: true, + }, + { + // http://stackoverflow.com/questions/13014947/regex-to-match-a-c-style-multiline-comment/36328890#36328890 + pattern: /\/\*[^*]*\*+([^/*][^*]*\*+)*\//, + greedy: true, + } + ], + + 'type-hint': /\b(type|enum|int|cell|void|bool|auto|slice|tuple|builder|continuation)\b/, + + 'boolean': /\b(false|true|null)\b/, + + 'keyword': /\b(do|if|try|else|while|break|throw|catch|return|assert|repeat|continue|asm|builtin|import|export|true|false|null|redef|mutate|tolk|global|const|var|val|fun|get|struct)\b/, + + 'self': /\b(self)\b/, + + 'function': /[a-zA-Z$_][a-zA-Z0-9$_]*(?=\s*[(<])/, + + 'number': new RegExp("(-?([\\d]+|0x[\\da-fA-F]+))\\b"), + + 'string': [ + { + pattern: /"""[\s\S]*?"""/, + greedy: true, + }, + { + pattern: /"[^\n"]*"\w?/, + greedy: true, + }, + ], + + 'operator': new RegExp("\\+|-|\\*|/|%|\\?|:|=|<|>|!|&|\\||\\^|==|!=|<=|>=|<<|>>|&&|\\|\\||~/|\\^/|\\+=|-=|\\*=|/=|%=|&=|\\|=|\\^=|->|<=>|~>>|\\^>>|<<=|>>="), + + 'punctuation': /[.,;(){}\[\]]/, + + 'attr-name': /@[a-zA-Z0-9]+/, + + 'variable': [ + { pattern: /`[^`]+`/ }, + { pattern: /\b[a-zA-Z$_][a-zA-Z0-9$_]*\b/ }, + ] + }; + +}(Prism));