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

Commit

Permalink
feat: Expressions page (#136)
Browse files Browse the repository at this point in the history
Plus:
* initOf got cross-linked correctly
* various smaller fixes related to literals
  • Loading branch information
novusnota authored Mar 21, 2024
1 parent fe3d47c commit f908d55
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 4 deletions.
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

0 comments on commit f908d55

Please sign in to comment.