Expression statements
The most common type of statement is the expression statement—an expression followed by;
. As a rule, all sub-expressions are evaluated from left to right,
except in cases where asm stack rearrangement explicitly defines the order.
Variable declaration
Local variables must be initialized at the time of declaration. Here are some examples:int x
is not a new declaration but a compile-time check ensuring that x
has type int
. The third line is equivalent to x = 3;
.
Variable redeclaration in nested scopes
In nested scopes, a new variable with the same name can be declared, just like in C:
int x = 2;
are valid expressions.
For instance:
int y = (int x = 3) + 1;
Here, x
is declared and assigned 3
, and y
is assigned 4
.
Underscore
The underscore_
is used when a value is not needed.
For example, if foo
is a function of type int -> (int, int, int)
,
you can retrieve only the first return value while ignoring the rest:
Function application
A function call in FunC follows a conventional syntax: the function name is followed by its arguments, separated by commas.foo
is a function that takes one tuple argument of type (int, int, int)
.
Function composition
To illustrate how function arguments work in FunC, consider a function bar
of type int -> (int, int, int)
. Since foo
expects a single tuple argument, you can pass the entire result of bar(42)
directly into foo
:
Lambda expressions
Lambda expressions are not yet supported in FunC.Methods calls
Non-modifying methods
In FunC, a function with at least one argument can be called a non-modifying method using the dot.
syntax.
For example, the function store_uint
has the type (builder, int, int) → builder
, where:
- The first argument is a builder object.
- The second argument is the value to store.
- The third argument is the bit length.
begin_cell()
creates a new builder.
These two ways of calling store_uint
are equivalent:
.
syntax allows the first argument of a function to be placed before the function name,
simplifying the code further:
Modifying methods
If a function’s first argument is of typeA
and its return value follows the structure (A, B)
,
where B
is an arbitrary type, the function can be used as a modifying method.
A modifying method modifies its first argument by assigning the first component of the returned value to the original variable. These methods may take additional arguments and return extra values, but their primary purpose is to update the first argument.
For example, consider a cell slice cs
and the function load_uint
, which has the type: load_uint(slice, int) → (slice, int)
.
This function takes a cell slice and a number of bits to load, returning the remaining slice and the loaded value. The following three calls are equivalent:
inc
of type int -> int
. It should be redefined as a function of type int -> (int, ())
to use it as a modifying method:
x
:
.
and ~
in function names
Suppose we want to use inc
as a non-modifying method. We can write:
inc
as a modifying method:
- If a function is called with
.
(e.g.,x.foo()
), the compiler looks for a.foo
definition. - If a function is called with
~
(e.g.,x~foo()
), the compiler looks for a~foo
definition. - If neither
.foo
nor~foo
is defined, the compiler falls back to the regularfoo
definition.
Operators
Note that all the unary and binary operators are currently integer operators. Logical operators are bitwise integer operators (cf. absence of boolean type).Unary operators
FunC supports two unary operators:~
is bitwise not (priority 75)-
is integer negation (priority 20)
- x
- Negates x.-x
- Interpreted as a single identifier, not an operation.
Binary operators
With priority 30 (left-associative):*
is integer multiplication/
is integer division (floor)~/
is integer division (round)^/
is integer division (ceil)%
is integer reduction by modulo (floor)~%
is integer reduction by modulo (round)^%
is integer reduction by modulo (ceil)/%
returns the quotient and the remainder&
is bitwise AND
+
is integer addition-
is integer subtraction|
is bitwise OR^
is bitwise XOR
<<
is bitwise left shift>>
is bitwise right shift~>>
is bitwise right shift (round)^>>
is bitwise right shift (ceil)
==
is integer equality check!=
is integer inequality check<
is integer comparison<=
is integer comparison>
is integer comparison>=
is integer comparison<=>
is integer comparison (returns -1, 0 or 1)
x + y
- Proper spacing between operands.x+y
- Interpreted as a single identifier, not an operation.
Conditional operator
FunC supports the standard conditional (ternary) operator with the following syntax:Assignments
Priority 10. Supports simple assignment=
and compound assignment operators: +=
, -=
, *=
, /=
, ~/=
, ^/=
, %=
, ~%=
, ^%=
, <<=
, >>=
, ~>>=
, ^>>=
, &=
, |=
, ^=
.
Loops
FunC supportsrepeat
, while
, and do { ... } until
loops. The for
loop is not supported.
Repeat loop
Therepeat
loop uses the repeat
keyword followed by an int
expression. It executes the code a specified number of times.
Examples:
-2³¹
or greater than 2³¹ - 1
.
While loop
Thewhile
loop follows standard syntax:
x < 100
is of type int
(cf. absence of boolean type).
Until loop
Thedo { ... } until
loop has the following syntax:
If statements
Examples Standardif
statement:
if
(~flag
):
If-else
statement:
{}
are required for if
statements. The following code will not compile:
Try-catch statements
Available in FunC since v0.4.0 Thetry
block executes a section of code.
If an error occurs, all changes made within the try
block are completely rolled back, and the catch
block is executed instead. The catch
block receives two arguments:
x
: the exception parameter, which can be of any typen
: the error code, an integer
try
block. These modifications include updates to local and global variables and changes to storage registers (c4
for storage, c5
for action/messages, c7
for context, etc.).
Any contract storage updates and outgoing messages are also reverted.
However, certain TVM state parameters are not rolled back, such as:
- Codepage settings
- Gas counters
As a result, all gas consumed within the
try
block is still accounted for, and any operations that modify gas limits (e.g.,accept_message
orset_gas_limit
) will remain in effect.
try-catch
usage:
x
is incremented inside the try
block, the modification is rolled back due to the exception, so x
remains 0
.
Block statements
Block statements are supported as well, creating a new nested scope:builder
variable named x
, which exists only within that scope.
The outer x
remains unchanged and can be used after the block ends.