Operators
Division
There are two division operators: / and //.
The / is the decimal division. The // is the integer division.
If in LSL we are dividing two integers, in Slua we use //, otherwise we use /.
|
|
Exponentiation
In Lua, the ^ is the exponentiation operator.
We can use ^ instead of the function ll.Pow().
|
|
Not equal
The unequality operator is ~= instead of !=.
|
|
Increment / decrement
In Lua ++ and – don’t exist.
We need to add or subtract one.
|
|
Concatenation
The concatenation operator is .. instead of +.
|
|
And / Or / Not
The logical operators are “and”, “or”, “not” instead of &&, ||, !.
In SLua “and” has higher precedence than “or”. In LSL, && and || have the same precendence.
|
|
In LSL both operators are always evaluated.
In SLua sometimes only the left operator is evaluated:
- with “and”, if the left operator is false, the result is false, and the right operator is not evaluated
- with “or”, if the left operator is true, the result is true, and the right operator is not evaluated
We can do:
if people > 0 and total / people > 10 then- If people is 0 the right operator ( total / people ) is not evaluated, and we don’t divide by zero
We can use them as value selectors:
result = optionA and optionB- same as
result = if optionA then optionB else option A
- same as
result = optionA or optionB- same as
result = if optionA then optionA else option B
- same as
To set a default value in case that a variable is nil:
total = total or 0- except with boolean variables with a default value of true, in this case it must be:
total = total ~= nil or true
As a ternary operator:
text = count == 1 and "1 item" or count .. " items"- same as
text = if count == 1 then "1 item" else count .. " items"
- same as
To call a function on a condition:
isReady and start()- same as
if isReady then start() end
- same as
Bitwise operations
The bitwise operators &, |, ~, ^, «, » don’t exist in SLua.
We have the library bit32, with functions for bitwise operations:
- & : bit32.band()
- | : bit32.bor()
- ~ : bit32.bnot()
- ^ : bit32.bxor()
- « : bit32.lshift()
- >> : bit32.arshift()
band, bor and bnot can take any quantity of operators.
For instance, in the event changed:
- In LSL:
if (change & (CHANGED_OWNER | CHANGED_INVENTORY )) { - In SLua:
if bit32.band(change, bit32.bor(CHANGED_OWNER, CHANGED_INVENTORY)) ~= 0 then
Or better with bit32.btest() that does a bitwise-and, returning true if the resulting value is not 0, or false if it is 0.
- In SLua:
if bit32.btest(change, bit32.bor(CHANGED_OWNER, CHANGED_INVENTORY)) then
Another example, checking for -1:
- In LSL:
if (~llListFindList(myList, [item])) { - In SLua:
if bit32.bnot(ll.ListFindList(myList, {item})) ~= 0 then
The library bit32 works with 32 bits and SLua numbers are 64 bits. The library uses the low 32 bits of the number and the return value is an unsigned number:
- in LSL:
integer val = 0; val = ~val; llOwnerSay((string)val); // --> -1 - in SLua:
local val = 0 val = bit32.bnot(val) print(val) -- > 4294967295
To get signed results we can use the SLua type integer, which is a 32-bit signed integer, and cast the result to number:
- in SLua:
local val = 0 val = tonumber(bit32.bnot(integer(val))) print(val) -- > -1
when all the parameters are SLua integers the returned value is also an SLua integer.
The previous example, checking for -1, works because -1 is stored with all bits to 1, so no matter how many bits are used it is still -1.
Another example, to get a negative channel:
// LSL
integer gChannel = 0x80000000 | (integer)("0x" + (string)llGetKey());
llOwnerSay((string)gChannel); // --> -1261093815
-- SLua
local gChannel = bit32.bor(0x80000000, integer("0x" .. tostring(ll.GetKey())))
print(gChannel) -- > 3033873481
which is a way to get a channel number that can’t be used in LSL and neither used typing it in the viewer.
With all the parameters as SLua integers:
-- SLua
local gChannel = tonumber(bit32.bor(integer(0x80000000), integer("0x" .. tostring(ll.GetKey()))))
print(gChannel) -- > -1261093815
Comparing string and uuid
In SLua, equality checks use strict equality: two variables are considered equal only if they have the same type and the same value.
Variables of different types are always different, no matter their contents.
In LSL, we can compare strings and keys as if they were the same type.
In LSL this (in an object with blank textures) works:
if ( llGetTexture(0) == TEXTURE_BLANK ) { -- true
but in SLua:if ll.GetTexture(0) == TEXTURE_BLANK then -- false
Because the LL functions that return a texture can return the name of the texture or its UUID, but they can only have one return type. So they always return a string. And LL constants that contain a uuid have type uuid.
In SLua a variable of type uuid and a variable of type string are always different, even if they have the same text.
In SLua it must be:
if uuid( ll.GetTexture(0) ) == TEXTURE_BLANK then -- false
Vectors
The SLua datatype vector internally uses a Luau datatype vector and inherits some functions from Luau. We can’t use the :method notation, we have to call them on vector:
- vector.magnitude( myVec ) : returns a number, same as ll.VecMag().
- vector.normalize( myVec ) : returns a vector, same as ll.VecNorm().
- vector.dot( myVec1, myVec2 ) : the dot product, returns a number, same as myVec1 * myVec2 in LSL (but not in SLua).
- vector.cross( myVec1, myVec2 ) : the cross product, returns a vector, same as myVec1 % myVec2 in LSL and SLua.
- vector.angle( myVec1, myVec2 ) : returns a number, the angle between the two vectors.
myVec1 * myVec2
In LSL is the dot product, in SLua multiplies the components:
print( vector( 3, 4, 5) * vector ( 10, 10, 10 ) ) -- > < 30, 40, 50 >
In LSL this:
float dotProduct = myVec1 * myVec2; // LSL
is this in SLua:local dotProduct = vector.dot(myVec1, myVec2) -- SLua
SLua has also added the division, that divides the components, and doesn’t exist in LSL:
print( vector( 12, 6, 3) / vector ( 3, 2, 1 ) ) -- > < 4, 3, 3 >
Infinity and NaN
Lua has some “extreme” values in the datatype number.
Infinity
Lua has “inf” and “-inf” to represent positive and negative infinity, and the constant math.huge:
print( math.huge ) --> infprint( -math.huge ) --> -inf
We can get an infinite number with math.huge:
local big = math.huge
print( big ) --> inf
Or dividing by 0:
print( 1 / 0 ) --> infprint( -1 / 0 ) --> -inf
Or casting from a string:
local big = tonumber( "inf" )
We test for infinity with:
if x == math.huge then print("x is infinity!") endif y == -math.huge then print("y is negative infinity!") end
We can do operations with infinity, in the way that mathematical infinities work.
We can use math.huge for initial values instead of 999999999:
local minimum = math.huge
if quantity < minimum then
minimum = quantity
end
Division by zero doesn’t crash the script in Lua. It gives “inf”, “-inf”, or “nan”.
NaN
“nan” stands for “Not a Number”. It’s a special value that represents an undefined or unrepresentable result in math operations.
We can get “nan” when math goes wrong in a way that doesn’t crash the script, but also doesn’t produce a meaningful number:
print(0/0) --> nan (undefined division)print( math.huge - math.huge ) --> nan (infinity minus infinity)print( math.sqrt( -1 ) ) --> nan (square root of a negative number)
Arithmetic operations including a “nan” always result in “nan”. If any operand is “nan”, the whole expression becomes “nan”.
“nan” propagates through math operations, because once a value is undefined, any further calculation remains undefined.
Comparing “nan” with any number by >, < or == always returns false.
“nan” is not considered greater, smaller, or equal to any number, it’s “outside” the usual numeric order.
It’s false even compared to itself:
x = 0 / 0
print( x == x ) --> false
This is the only case that a variable is not equal to itself.
So we can check for “nan” with:
function isNaN(x)
return x ~= x
end
In logical operations, “nan” behaves like any other non-boolean value. Lua treats all non-nil, non-false values as truthy, so “nan” counts as truthy.
“inf”, “-inf” and “nan” avoid crashes when trying impossible math operations.
We can check for these extreme numbers with:
if x ~= x or x == math.huge or x == -math.huge then
-- do something with this extreme number
end
Integer division and modulo
LSL and SLua behaves different in the integer division and the modulo division when one, and only one, of the operands is negative.
Integer divison
The integer division in LSL:
llOwnerSay((string)(-7 / 4)); // --> -1llOwnerSay((string)(7 / -4)); // --> -1
And in SLua:
print(-7 // 4) -- > -2print(7 // -4) -- > -2
In LSL, integer division always truncates toward zero. This means that the result of dividing two integers is the whole number part of the result, with any fractional portion discarded, and the sign of the result follows the sign of the numerator.
In SLua, integer division using the // operator rounds down toward negative infinity. This is known as floor division. It always returns the largest integer less than or equal to the result of the division.
To simulate LSL behaviour we can use this:
function divLSL(a, b)
return if a * b < 0 then math.ceil(a / b) else math.floor(a / b)
end
Modulo
The modulo division in LSL:
llOwnerSay((string)(-7 % 4)); // --> -3llOwnerSay((string)(7 % -4)); // --> 3
And in SLua:
print(-7 % 4) -- > 1print(7 % -4) -- > -1
In LSL, the modulo operator (%) returns the remainder after division, and its result always carries the same sign as the numerator. This is known as truncating remainder, and it matches the way LSL performs integer division, both operations are consistent in that they truncate toward zero.
In SLua, the % operator performs true modulo, which means the result always has the same sign as the divisor. Lua defines this operation mathematically as the difference between the dividend and the product of the divisor and the floor of the division result. This ensures that the result is always non-negative if the divisor is positive, and always non-positive if the divisor is negative.
To simulate LSL behaviour we can use this:
function modLSL(a, b)
return a % b - if a * b < 0 and a % b ~= 0 then b else 0
end