Back to Luau

Luau syntax by example

syntax.md

latest21.2 KB
Original Source

Luau syntax by example

Luau uses the baseline syntax of Lua 5.1. For detailed documentation, please refer to the Lua manual, this is an example:

Embedded content

Note that future versions of Lua extend the Lua 5.1 syntax with more features; Luau does support string literal extensions but does not support other 5.x additions; for details please refer to compatibility section.

The rest of this document documents additional syntax used in Luau.

String literals

Section titled “String literals”

Luau implements support for hexadecimal (\x), Unicode (\u) and \z escapes for string literals. This syntax follows Lua 5.3 syntax:

  • \xAB inserts a character with the code 0xAB into the string
  • \u{ABC} inserts a UTF8 byte sequence that encodes U+0ABC character into the string (note that braces are mandatory)
  • \z at the end of the line inside a string literal ignores all following whitespace including newlines, which can be helpful for breaking long literals into multiple lines.

Number literals

Section titled “Number literals”

In addition to basic integer and floating-point decimal numbers, Luau supports:

  • Hexadecimal integer literals, 0xABC or 0XABC
  • Binary integer literals, 0b01010101 or 0B01010101
  • Decimal separators in all integer literals, using _ for readability: 1_048_576, 0xFFFF_FFFF, 0b_0101_0101

Note that Luau only has a single number type, a 64-bit IEEE754 double precision number (which can represent integers up to 2^53 exactly), and larger integer literals are stored with precision loss.

Continue statement

Section titled “Continue statement”

In addition to break in all loops, Luau supports continue statement. Similar to break, continue must be the last statement in the block.

Note that unlike break, continue is not a keyword. This is required to preserve backwards compatibility with existing code; so this is a continue statement:

Embedded content

Whereas this is a function call:

Embedded content

When used in repeat..until loops, continue can not skip the declaration of a local variable if that local variable is used in the loop condition; code like this is invalid and won’t compile:

Embedded content

Compound assignments

Section titled “Compound assignments”

Luau supports compound assignments with the following operators: +=, -=, *=, /=, //=, %=, ^=, ..=. Just like regular assignments, compound assignments are statements, not expressions:

Embedded content

Compound assignments only support a single value on the left and right hand side; additionally, the function calls on the left hand side are only evaluated once:

Embedded content

Compound assignments call the arithmetic metamethods (__add et al) and table indexing metamethods (__index and __newindex) as needed - for custom types no extra effort is necessary to support them.

Type annotations

Section titled “Type annotations”

To support gradual typing, Luau supports optional type annotations for variables and functions, as well as declaring type aliases.

Types can be declared for local variables, function arguments and function return types using : as a separator:

Embedded content

In addition, the type of any expression can be overridden using a type cast :::

Embedded content

There are several simple builtin types: any (represents inability of the type checker to reason about the type), nil, boolean, number, string and thread.

Function types are specified using the arguments and return types, separated with ->:

Embedded content

To return no values or more than one, you need to wrap the return type position with parentheses, and then list your types there.

Embedded content

Note that function types are specified without the argument names in the examples above, but it’s also possible to specify the names (that are not semantically significant but can show up in documentation and autocomplete):

Embedded content

Table types are specified using the table literal syntax, using : to separate keys from values:

Embedded content

When the table consists of values keyed by numbers, it’s called an array-like table and has a special short-hand syntax, {T} (e.g. {string}).

Additionally, the type syntax supports type intersections (((number) -> string) & ((boolean) -> string)) and unions ((number | boolean) -> string). An intersection represents a type with values that conform to both sides at the same time, which is useful for overloaded functions; a union represents a type that can store values of either type - any is technically a union of all possible types.

It’s common in Lua for function arguments or other values to store either a value of a given type or nil; this is represented as a union (number | nil), but can be specified using ? as a shorthand syntax (number?).

In addition to declaring types for a given value, Luau supports declaring type aliases via type syntax:

Embedded content

The right hand side of the type alias can be a type definition or a typeof expression; typeof expression doesn’t evaluate its argument at runtime.

By default type aliases are local to the file they are declared in. To be able to use type aliases in other modules using require, they need to be exported:

Embedded content

An exported type can be used in another module by prefixing its name with the require alias that you used to import the module.

Embedded content

For more information please refer to typechecking documentation.

Const bindings

Section titled “Const bindings”

Luau supports const declarations for local variable bindings. A const binding works like local, but prevents reassignment after initialization, making the binding itself immutable.

Embedded content

This restriction applies to all assignment forms, including compound assignment, and when nested inside other scopes:

Embedded content

Type annotations are supported, just like with local:

Embedded content

Multi-assignment and function declarations work too:

Embedded content

Binding immutability vs. value immutability

Section titled “Binding immutability vs. value immutability”

const makes the binding immutable, but it does not make the value immutable. For instance, a const variable referring to a table will still allow the table’s contents to be mutated:

Embedded content

To prevent mutation of the table’s contents as well, use table.freeze:

Embedded content

Backwards compatibility

Section titled “Backwards compatibility”

const is a contextual keyword, and so is only treated as a keyword in positions where local is valid. Existing code that uses const as a variable name works:

Embedded content

If-then-else expressions

Section titled “If-then-else expressions”

In addition to supporting standard if statements, Luau adds support for if expressions. Syntactically, if-then-else expressions look very similar to if statements. However instead of conditionally executing blocks of code, if expressions conditionally evaluate expressions and return the value produced as a result. Also, unlike if statements, if expressions do not terminate with the end keyword.

Here is a simple example of an if-then-else expression:

Embedded content

if-then-else expressions may occur in any place a regular expression is used. The if-then-else expression must match if <expr> then <expr> else <expr>; it can also contain an arbitrary number of elseif clauses, like if <expr> then <expr> elseif <expr> then <expr> else <expr>. Note that in either case, else is mandatory.

Here’s is an example demonstrating elseif:

Embedded content

Note: In Luau, the if-then-else expression is preferred vs the standard Lua idiom of writing a and b or c (which roughly simulates a ternary operator). However, the Lua idiom may return an unexpected result if b evaluates to false. The if-then-else expression will behave as expected in all situations.

Generalized iteration

Section titled “Generalized iteration”

Luau uses the standard Lua syntax for iterating through containers, for vars in values, but extends the semantics with support for generalized iteration. In Lua, to iterate over a table you need to use an iterator like next or a function that returns one like pairs or ipairs. In Luau, you can simply iterate over a table:

Embedded content

Further, iteration can be extended for tables or userdata by implementing the __iter metamethod which is called before the iteration begins, and should return an iterator function like next (or a custom one):

Embedded content

The default iteration order for tables is specified to be consecutive for elements 1..#t and unordered after that, visiting every element; similarly to iteration using pairs, modifying the table entries for keys other than the current one results in unspecified behavior. As an exception for dense (gap-free) arrays, non-nil elements inserted at position #t+1 during iteration are guaranteed to be visited in order within the same loop.

String interpolation

Section titled “String interpolation”

Luau adds an additional way to define string values that allows you to place runtime expressions directly inside specific spots of the literal.

This is a more ergonomic alternative over using string.format or ("literal"):format.

To use string interpolation, use a backtick string literal:

Embedded content

Any expression can be used inside {}:

Embedded content

Inside backtick string literal, \ is used to escape ```, {, \ itself and a newline:

Embedded content

Restrictions and limitations

Section titled “Restrictions and limitations”

The sequence of two opening braces {{“{{”}} is rejected with a parse error. This restriction is made to prevent developers using other programming languages with a similar feature from trying to attempt that as a way to escape a single { and getting unexpected results in Luau.

Luau currently does not support backtick string literals in type annotations, therefore type Foo = Foo`` is invalid syntax.

Unlike single and double-quoted string literals, backtick string literals must always be wrapped in parentheses for function calls:

Embedded content

Floor division (//)

Section titled “Floor division (//)”

Luau supports floor division, including its operator (//), its compound assignment operator (//=), and overloading metamethod (__idiv), as an ergonomic alternative to math.floor.

For numbers, a // b is equal to math.floor(a / b):

Embedded content

Note that it’s possible to get inf, -inf, or NaN with floor division; when b is 0, a // b results in positive or negative infinity, and when both a and b are 0, a // b results in NaN.

For native vectors, c // d applies math.floor to each component of the vector c. Therefore c // d is equivalent to vector.create(math.floor(c.x / d), math.floor(c.y / b), math.floor(c.z / b)).

Floor division syntax and semantics follow from Lua 5.3 where applicable.

Attributes

Section titled “Attributes”

Luau supports attributes on function declarations. An attribute is a @name annotation placed before a function that adjusts the behavior of the compiler, analyzer, or runtime. Attributes are built into the language and cannot be defined by users.

Embedded content

Attributes work on all function declaration forms:

Embedded content

Multiple attributes can be placed on the same line:

Embedded content

For the full list of available attributes and their parameters, see the Attributes reference.