As explained before, expressions are pieces of code that have a value and that can be evaluated. They cannot be executed directly (with the exception of function calls), and thus, a script that would contain only the following code, which consists of an expression, would be erroneous:
3 + 5 -- The code above is erroneous because all it contains is an expression. -- The computer cannot execute '3 + 5', since that does not make sense.
Code must be comprised of a sequence of statements. These statements can contain expressions which will be values the statement has to manipulate or use to execute the instruction.
Some code examples in this chapter do not constitute valid code, because they consist of only expressions. In the next chapter, statements will be covered and it will be possible to start writing valid code.
To evaluate an expression is to compute it to find its value. The value a given expression evaluates to might be different from one context to another, since it can depend on the environment and stack level. This value will sometimes be a number, sometimes text and the other times any of many other data types, which is why it is said to have a type.
In Lua, and in programming in general, expressions will usually consist of one or more values with zero or more operators. Some operators can only be used with some types (it would be illogical, for example, to try to divide text, while it makes sense to divide numbers). There are two kinds of operators: unary operators and binary operators. Unary operators are operators that only take one value. For example, the unary - operator only takes one number as a parameter: -5, -3, -6, etc. It takes one number as a parameter and negates that number. The binary - operator, however, which is not the same operator, takes two values and subtracts the second from the first: 5 - 3, 8 - 6, 4 - 9, etc.
It is possible to obtain a number's type as a string with the
print(type(32425)) --> number
Numbers generally represent quantities, but they can be used for many other things. The number type in Lua works mostly in the same way as real numbers. Numbers can be constructed as integers, decimal numbers, decimal exponents or even in hexadecimal. Here are some valid numbers:
The operators for numbers in Lua are the following:
|Arithmetic negation||-a||Changes the sign of a and returns the value||-3.14159|
|Addition||a + b||Returns the sum of a and b||5.2 + 3.6||8.8|
|Subtraction||a - b||Subtracts b from a and returns the result||6.7 - 1.2||5.5|
|Multiplication||a * b||Returns the product of a and b||3.2 * 1.5||4.8|
|Exponentiation||a ^ b||Returns a to the power b, or the exponentiation of a by b||5 ^ 2||25|
|Division||a / b||Divides a by b and returns the result||6.4 / 2||3.2|
|Modulo operation||a % b||Returns the remainder of the division of a by b||5 % 3||2|
|Floor division||a // b||Divides a by b and returns the integer part of the result||6.4 // 2||3|
You probably already know all of these operators (they are the same as basic mathematical operators) except the last. The last is called the modulo operator, and simply calculates the remainder of the division of one number by another. 5 % 3, for example, would give 2 as a result because 2 is the remainder of the division of 5 by 3. The modulo operator is less common than the other operators, but it has multiple uses.
A new subtype of numbers, integers, was added in Lua 5.3. Numbers can be either integers or floats. Floats are similar to numbers as described above, while integers are numbers with no decimal part. Float division (
/) and exponentiation always convert their operands to floats, while all other operators give integers if their two operands were integers. In other cases, with the exception of the floor division operator (
//), the result is a float.
Nil is the type of the value nil, whose main property is to be different from any other value; it usually represents the absence of a useful value. Some examples of things that have the value nil:
- value of variables that you access before assigning them a value
- value you get when trying to access a variable outside of its scope
- value for any key in a table that hasn't been assigned
- value returned by
tonumberif it can't convert a string to a number
On a more advanced note, purposefully assigning a nil value removes the reference to a variable or table, and allows the garbage collector to reclaim its memory.
A boolean value can be either true or false, but nothing else. This is written in Lua as
false, which are reserved keywords. Important to note is that
nil is a different data type as stated earlier.
not() are usually associated with boolean values, but can be used with any datatype in Lua.
|Boolean negation||not a||If a is false or nil, returns true. Otherwise, returns false.|
|Logical conjunction||a and b||Returns the first argument if it is false or nil. Otherwise, returns the second argument.|
|Logical disjunction||a or b||Returns the first argument if it is neither false nor nil. Otherwise, returns the second argument.|
not operator just negates the boolean value (makes it false if it is true and makes it true if it is false), the
and operator returns true if both are true and false if not and the
or operator returns true if either of arguments is true and false otherwise. This is however not exactly how they work, as the exact way they work is explained in the table above. In Lua, the values false and nil are both considered to be false in logical expressions, while everything else is considered as true (even 0 and empty strings).
The relational operators introduced in the next chapter (
==) do not necessarily take boolean values as operands, but will always give a boolean value as a result.
This can be difficult to reconcile. For added clarity, here are some truth tables or expression-result pairs. Here
x can be
This somewhat counterintuitively means that
Strings are sequences of characters that can be used to represent text. They can be written in Lua by being contained in double quotes, single quotes or long brackets, which were covered before in the section about comments (it should be noted that comments and strings have nothing in common other than the fact they can both be delimited by long brackets, preceded by two hyphens in the case of comments). Strings that aren't contained in long brackets will only continue for one line. Because of this, the only way to make a string that contains many lines without using long brackets is to use escape sequences. This is also the only way to insert single or double quotes in certain cases. Escape sequences consist of two things: an escape character, which will always be a backslash ('\') in Lua, and an identifier that identifies the character to be escaped.
|\n||A new line|
|\"||A double quote|
|\'||A single quote (or apostrophe)|
|\t||A horizontal tab|
|\###||### must be a number from 0 to 255. The result will be the corresponding ASCII character.|
Escape sequences are used when putting the character directly in the string would cause a problem. For example, if you have a string of text that is enclosed in double quotes and must contain double quotes, then you need to enclose the string in different characters or to escape the double quotes. Escaping characters in strings delimited by long brackets is not necessary, and this is true for all characters. All characters in a string delimited with long brackets will be taken as-is. The
% character is used in string patterns to escape magic characters, but the term escaping is then used in another context.
"This is a valid string." 'This is also a valid string.' "This is a valid \" string 'that contains unescaped single quotes and escaped double quotes." [[ This is a line that can continue on more than one line. It can contain single quotes, double quotes and everything else (-- including comments). It ignores everything (including escape characters) except closing long brackets of the same level as the opening long bracket. ]] "This is a valid string that contains tabs \t, double quotes \" and backlashes \\" "This is " not a valid string because there is an unescaped double quote in the middle of it."
For convenience, if an opening long string bracket is immediately followed by a new line, that new line will be ignored. Therefore, the two following strings are equivalent:
[[This is a string that can continue on many lines.]] [[ This is a string that can continue on many lines.]] -- Since the opening long bracket of the second string is immediately followed by a new line, that new line is ignored.
It is possible to get the length of a string, as a number, by using the unary length operator ('#'):
print(#("This is a string")) --> 16
In formal language theory and computer programming, string concatenation is the operation of joining two character strings end-to-end. For example, the concatenation of "snow" and "ball" is "snowball".—Wikipedia, Concatenation
The string concatenation operator in Lua is denoted by two dots ('..'). Here is an example of concatenation that concatenates "snow" and "ball" and prints the result:
print("snow" .. "ball") --> snowball
This code will concatenate "snow" and "ball" and will print the result.
The four basic types in Lua (numbers, booleans, nil and strings) have been described in the previous sections, but four types are missing: functions, tables, userdata and threads. Functions are pieces of code that can be called, receive values and return values back. Tables are data structures that can be used for data manipulation. Userdata are used internally by applications Lua is embedded in to allow Lua to communicate with that program through objects controlled by the application. Finally, threads are used by coroutines, which allow many functions to run at the same time. These will all be described later, so you only need to keep in mind that there are other data types.
Literals are notations for representing fixed values in source code. All values can be represented as literals in Lua except threads and userdata. String literals (literals that evaluate to strings), for example, consist of the text that the string must represent enclosed into single quotes, double quotes or long brackets. Number literals, on the other hand, consist the number they represent expressed using decimal notation (ex:
12.43), scientific notation (ex:
0.31416E1) or hexadecimal notation (ex:
Coercion is the conversion of a value of one data type to a value of another data type. Lua provides automatic coercion between string and number values. Any arithmetic operation applied to a string will attempt to convert this string to a number. Conversely, whenever a string is expected and a number is used instead, the number will be converted to a string. This applies both to Lua operators and to default functions (functions that are provided with the language).
print("122" + 1) --> 123 print("The number is " .. 5 .. ".") --> The number is 5.
Coercion of numbers to strings and strings to numbers can also be done manually with the
tonumber functions. The former accepts a number as an argument and converts it to a string, while the second accepts a string as an argument and converts it to a number (a different base than the default decimal one can optionally be given in the second argument).
Since Lua 5.3, bitwise operators are provided to operate on binary numerals (bit patterns). These operators are not used as frequently as the others, so you may skip this section if you do not need them.
The bitwise operators in Lua always operate on integers, converting their operands if this is necessary. They also give integers.
The bitwise AND operation (with operator
&) performs logical conjunction on each pair of bits of two binary representations of equal length. For example,
5 & 3 evaluates to 1. We can explain this by looking at the binary representation of these numbers (the subscripts are used to denote the base):
If the bits in a given position in the binary representation of both 5 and 3 are 1 (as is the case for the last bit), then the bit at that position will be 1 in the result; in all other cases, it will be 0.
The bitwise OR operation (with operator
|) works in the same way as the bitwise AND, performing logical disjunction instead where it performs logical conjunction. Thus,
5 | 3 will evaluate to 7:
Here, we can see that the bit in each position in the final result was 0 only when the binary representations of the two operands had a 0-bit at that position.
The bitwise XOR operation (with operator
~) works like two others, but at a given position, the final bit is only 1 if one, and not both, of the bits in the operands are 1.
This is the same as the previous example, but we can see that the last bit in the result is 0 instead of 1, since the last bit of both operands was 1.
The bitwise NOT operation (with operator
~) performs logical negation on each bit of its unique operand, which means that each 0 becomes 1 and that each 1 becomes 0. Thusly,
~7 will evaluate to -8:
Here, the first bit became 1 in the result because it was 0 in the operand, and the other bits became 0 because they were all 1.
In addition to these bitwise operators, Lua 5.3 also supports arithmetic bit shifts. The left shift, with operator
<< and illustrated on left, consists in shifting all bits to the left, by a number of bits that corresponds to the second operand. The right shift, denoted by operator
>> and illustrated on right, does the same but in the opposite direction.
Operator precedence works the same way in Lua as it typically does in mathematics. Certain operators will be evaluated before others, and parentheses can be used to arbitrarily change the order in which operations should be executed. The priority in which operators are evaluated is in the list below, from higher to lower priority. Some of these operators were not discussed yet, but they will all be covered at some point in this book.
- Unary operators:
- Level 2 mathematical operators:
- Level 1 mathematical operators:
- Bit shifts:
- Bitwise AND:
- Bitwise XOR:
- Bitwise OR:
- Relational operators:
- Boolean and:
- Boolean or:
There are some questions you can answer to verify that you have understood the material in this chapter. Note that finding the answer to some of those questions could require having knowledge that is not presented in this chapter. This is normal: the quizzes are part of the learning experience, and they can introduce information that is not available elsewhere in the book.