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.foodefinition. - If a function is called with
~(e.g.,x~foo()), the compiler looks for a~foodefinition. - If neither
.foonor~foois defined, the compiler falls back to the regularfoodefinition.
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, c5for 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
tryblock is still accounted for, and any operations that modify gas limits (e.g.,accept_messageorset_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.