Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

feat: Expressions page #136

Merged
merged 3 commits into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pages/book/_meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default {
types: 'Type system overview',
integers: 'Integers',
operators: 'Operators',
expressions: 'Expressions',
statements: 'Statements',
constants: 'Constants',
functions: 'Functions',
Expand Down
2 changes: 1 addition & 1 deletion pages/book/composite-types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Callout } from 'nextra/components'

Tact supports a number of [primitive data types](/book/types#primitive-types) that are tailored for smart contract use. However, using individual means of storage often becomes cumbersome, so there are two main ways to combine multiple primitives together: [Structs](#structs) and [Messages](#messages).

Note, while Traits and Contracts are also considered a part of the Tacts type system, one can't pass them around like [Structs](#structs) or [Messages](#messages). Instead, one can obtain the initial state of the given Contract by using the [initOf](/book/statements#initof) statement described later in the Book.
Note, while Traits and Contracts are also considered a part of the Tacts type system, one can't pass them around like [Structs](#structs) or [Messages](#messages). Instead, one can obtain the initial state of the given Contract by using the [`initOf{:tact}`](/book/expressions#initof) expression.

<Callout type="warning" emoji="⚠️">

Expand Down
193 changes: 193 additions & 0 deletions pages/book/expressions.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Expressions

import { Callout } from 'nextra/components'

Every operator in Tact forms an expression, but there's much more to uncover as Tact offers a wide range of expressive options to choose from.

## Literals

Literals represent values in Tact. These are fixed values—not variables—that you _literally_ provide in your code. All literals in Tact are expressions themselves.

You can also call [extension functions](/book/functions#extension-function) defined on certain [primitive types][p] corresponding to literals right on the literal values:

```tact
// Calling toString() defined for Int on a integer literal:
42.toString();

// Calling asComment() defined for String on a string literal:
"Tact is awesome!".asComment();
```

### Integer literals

Integer literals can be written in [decimal](/book/integers#decimal) (base $10$), [hexadecimal](/book/integers#hexadecimal) (base $16$), [octal](/book/integers#octal) (base $8$) and [binary](/book/integers#binary) (base $2$) notations:

* A [_decimal_ integer](/book/integers#decimal) literal is a sequence of digits ($\mathrm{0 - 9}$).

* A leading $\mathrm{0x}$ (or $\mathrm{0X}$) indicates a [hexadecimal integer](/book/integers#hexadecimal) literal. They can include digits ($\mathrm{0 - 9}$) and the letters $\mathrm{a - f}$ and $\mathrm{A - F}$. Note, that the case of a character does not change its value. Therefore: $\mathrm{0xa}$ = $\mathrm{0xA}$ = $10$ and $\mathrm{0xf}$ = $\mathrm{0xF}$ = $15$.

* A leading $\mathrm{0o}$ (or $\mathrm{0O}$) indicates a [octal integer](/book/integers#octal) literals. They can include only the digits $\mathrm{0 - 7}$.

* A leading $\mathrm{0b}$ (or $\mathrm{0B}$) indicates a [binary integer](/book/integers#binary) literal. THey can only include the digits $0$ and $1$.

<Callout type="warning" emoji="⚠️">
Be wary that in Tact integer literals with a leading $0$ are still considered decimals, unlike in JavaScript/TypeScript where leading $0$ indicates an octal!
</Callout>

Some examples of integer literals:

```tact
// decimal, base 10:
0, 42, 1_000, 020

// hexadecimal, base 16:
0xABC, 0xF, 0x0011

// octal, base 8:
0o777, 0o001

// binary, base 2:
0b01111001_01101111_01110101_00100000_01100001_01110010_01100101_00100000_01100001_01110111_01100101_01110011_01101111_01101101_01100101
```

Read more about integers and [`Int{:tact}`](/book/integers) type on the dedicated page: [Integers](/book/integers).

### Boolean literals

The [`Bool{:tact}`](/book/types#booleans) type has only two literal values: `true{:tact}` and `false{:tact}`.

```tact
true == true;
true != false;
```

Read more about booleans and [`Bool{:tact}`](/book/types#booleans) type in the dedicated chapter: [Booleans](/book/types#booleans).

### String literals

A string literal is zero or more characters enclosed in double (`"`) quotation marks. All string literals are objects of [`String{:tact}`][p] type.

```tact
"foo"
"1234"

// Note, that at the moment Tact strings can't have escape characters in them:
"line \n another"; // SYNTAX ERROR!, see: https://github.com/tact-lang/tact/issues/25

// This means, that double quotes inside strings are also prohibited:
"this \"can't be!\""; // SYNTAX ERROR!
```

Read more about strings and [`String{:tact}`][p] type there: [Primitive types][p].

### `null` literal

The `null{:tact}` value is written with a `null{:tact}` literal. It's **not** an [identifier](#identifiers) and doesn't refer to any object. It's also **not** an instance of a [primitive type][p]. Instead, `null{:tact}` represents a lack of identification and the intentional absence of any value.

```tact
let var: Int? = null; // variable, which can hold null value
var = 42;
if (var != null) {
var!! + var!!;
}
```

Read more about working with `null{:tact}` in the dedicated chapter: [Optionals](/book/composite-types#optionals).

## Identifiers

An identifier is a sequence of characters in the code that _identifies_ a [variable](/book/statements#let), [constant](/book/constants), [map](/book/types#maps) and a [function](/book/functions), as well as a [Struct][s], [Message][m], [contract](/book/types#contract), [trait](/book/types#trait), or their fields and methods. Identifiers are case-sensitive and not quoted.

In Tact, identifiers can contain latin lowercase letters (`a-z`), latin uppercase letters (`A-Z`), underscores (`_`) and digits ($\mathrm{0 - 9}$), but may not start with a digit. An identifier differs from a [string](#string-literals) in that a string is data, while an identifier is part of the code.

Note, that when identifiers for [primitive types][p] start with an uppercase letter. Used-defined [composite types](/book/composite-types), such as [Structs][s] and [Messages][m] also must be capitalized.

## Instantiation

You can create instances of the following types:

* [Structs][s]
* [Messages][m]

```tact
struct StExample {
fieldInit: Int = 1;
fieldUninit: Int;
}

fun example() {
StExample{ fieldUninit: 2 }; // instance with default value of fieldInit
StExample{ fieldInit: 0, fieldUninit: 2 }; // instance with both fields set
}
```

## Field access

You can directly access fields of the following types:

* [Structs][s]
* [Messages][m]

```tact
struct StExample {
fieldInit: Int = 1;
fieldUninit: Int;
}

fun example(): Int {
let struct: StExample = StExample{ fieldUninit: 2 }; // instantiation

struct.fieldInit; // access a field
return struct.fieldUninit; // return field value from the function
}
```

## Extension function call

[Extension functions](/book/functions#extension-function) are defined only on specific types. They can be called similar to method calls in many other languages:

```tact
42.toString(); // toString() is a stdlib function that is defined on Int type
```

## Static function call

Anywhere in the function body, a global [static function](/book/functions#global-static-functions) or an internal function of a contract can be called:

```tact
contract ExampleContract {
init() {}
receive() {
now(); // now() is a static function of stdlib
let expiration: Int = now() + 1000; // operation and variable declaration
expiration = self.answerQuestion(); // internal function
}
fun answerQuestion(): Int {
return 42;
}
}
```

## `initOf`

Expression `initOf{:tact}` computes initial state (`StateInit{:tact}`) of a [contract](/book/types#contracts):

```tact
// argument values for the init() function of the contract
// ↓ ↓
initOf ExampleContract(42, 100); // returns a Struct StateInit{}
// ---------------
// ↑
// name of the contract
```

Where `StateInit{:tact}` is a built-in [Struct][s], that consists of:

Field | Type | Description
:----- | :----------------- | :----------
`code` | [`Cell{:tact}`][p] | initial code of the contract (the compiled bytecode)
`data` | [`Cell{:tact}`][p] | initial data of the contract (arguments of `init(){:tact}` function of the contract)

[p]: /book/types#primitive-types
[s]: /book/composite-types#structs
[m]: /book/composite-types#messages
2 changes: 1 addition & 1 deletion pages/book/integers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ It's capable of storing integers between $-2^{256}$ and $2^{256} - 1.$

## Notation

Tact supports various ways of writing primitive values of `Int{:tact}` (integer literals).
Tact supports various ways of writing primitive values of `Int{:tact}` as [integer literals](/book/expressions#integer-literals).

Most of the notations allow adding underscores (`_`) in-between digits, except for:
* Representations in strings, as seen in [nano-tons](#nano-tons) case.
Expand Down
4 changes: 2 additions & 2 deletions pages/book/send.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ send(SendParameters{

## Deploy contract

To deploy a contract you need to calculate its address and init package, then send it with an initial message. You can always send an init package with a message and it would be ignored, but would cost more than the message without an init package.
To deploy a contract you need to calculate its address and initial state with [`initOf{:tact}`](/book/expressions#initof), then send them in the initialization message:

```tact
let init: StateInit = initOf SecondContract(arg1, arg2);
Expand All @@ -70,4 +70,4 @@ send(SendParameters{
data: init.data,
body: "Hello, World!".asComment()
});
```
```
2 changes: 2 additions & 0 deletions pages/book/types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ Learn more about them on a dedicated page about [composite types](/book/composit

Contracts are the main entry of a smart contract on the TON blockchain. It holds all [functions](/book/functions), [getters](/book/functions#getter-functions), and [receivers](/book/functions#receiver-functions) of a TON contract.

Contracts and [traits](#traits) have a built-in [identifier](/book/expressions#identifiers) `self{:tact}`, which is used for referring to their fields (persistent state variables) and methods (internal functions).

```tact
// Basic example of a counter contract:
contract HelloWorld {
Expand Down
Loading