Datatypes

LSL integer

Used as a boolean value, with contants TRUE and FALSE is an SLua type boolean, with values true and false.

Used as an integer is an SLua type number (which has integer and float values all in the same datatype).

// integers (LSL)

integer isOk = TRUE;
integer count = 0;
-- integers (SLua)

local isOk = true
local count = 0

LSL float

It is an SLua type number (floats and integers all go in the datatype number). SLua numbers are stored in 64 bits.

// floats (LSL)

float height = 1.65;
-- floats (SLua)

local height = 1.65

LSL string

It is an SLua type string.

// strings (LSL)

string message = "hello";
-- strings (SLua)

local message = "hello"

More about strings here: Strings.

LSL key

SLua adds the type uuid, which is the same than the LSL key.The change of name is to avoid confusion with the key of a table.

  • We get a uuid using myId = uuid("0f16c0e1-384e-4b5f-b7ce-886dda3bce41").
// keys (LSL)

key myId = "0f16c0e1-384e-4b5f-b7ce-886dda3bce41";
-- uuids (SLua)

local myId = uuid("0f16c0e1-384e-4b5f-b7ce-886dda3bce41")

LSL vector

It is an SLua type vector. It uses the Luau library vector.

  • We get a vector using myVec = vector(50, 50, 0).
  • It’s not possible to assign a value to a component. We need to create a new vector.
  • We can get a component from the return value of a function, not only from a variable.
  • Components are stored in 32 bits (same as LSL).
// vectors (LSL)

vector myVec = <50, 50, 0>;

myVec.z = 20;

vector pos = llGetPos();  // we need a variable before reading the component
float posZ = pos.z;
-- vectors (SLua)

local myVec = vector(50, 50, 0)

myVec = vector(myVec.x, myVec.y, 20)  -- we can't assign a value to a component


local posZ = ll.GetPos().z 

LSL rotation

SLua adds the types rotation and quaternion. They are synonims, internally they are the same datatype quaternion.

  • We get a rotation using myRot = rotation(1, 1, 1, 0) or myRot = quaternion(1, 1, 1, 0).
  • It’s not possible to assign a value to a component. We need to create a new rotation.
  • We can get a component from the return value of a function, not only from a variable.
  • Components are stored in 32 bits (same as LSL).
  • Vectors can use uppercase components (X, Y, Z) but rotations can’t. Let’s use always lowercase components.
// rotations (LSL)

rotation myRot = <1, 1, 1, 0>;
quaternion myRot2 = <2, 2, 2, 0>;

myRot.s = -myRot.s;

rotation rot = llGetRot();  // we need a variable before reading the component
float rotS = rot.s;
-- rotations (SLua)

local myRot = rotation(1, 1, 1, 0)
local myRot2 = quaternion(2, 2, 2, 0)

myRot = vector(myRot.x, myRot.y, myRot.z, -myRot.s)  -- we can't assign a value to a component


local rotS = ll.GetRot().s

LSL list

Lists are tables.

// lists (LSL)

list fruits = ["apple", "banana", "orange"];

//
-- tables (SLua)

local fruits = {"apple", "banana", "orange"}

-- tables are enclosed in { and }, instead of [ and ]

More about tables here: Tables.

The SLua datatype integer

SLua adds the type integer (32 bits). It exists only for compatibility reasons with a few LL functions.

It’s not to be used as an LSL integer, the LSL integer is an SLua number (which has integer and float values all in the same datatype).

The SLua type integer is only for a few uses that requires it. We can’t make operations with a number and an integer, they are different types. Integers must be casted to numbers to operate with numbers.

-- type integer (SLua)

	local myInt = integer(42)    -- don't use this type unless there is a good reason

The uses of “integer” are:

  • Typecasting in LSL-style
    • integer("123abc") -- > 123 or integer("aaa") -- > 0
      • tonumber() returns nil in both cases.
      • same as string.match( "123abc", "^%s*([-+]?%d+)" ) or 0
    • integer(myBool) -- > 1 or 0
      • same as if myBool then 1 else 0
  • ll.List2Integer() returns type integer.
  • ll.DumpList2String() and ll.List2CSV() print type number always with six decimals and type integer without decimals
  • the bit32 library functions return type integer when all the parameters have type integer.

Typecasting

  • to boolean
    • from number or integer: myBool = (myNum ~= 0)
  • to number
    • from string:
      • if the string is fully numeric:
        • myNum = tonumber("123") -- > 123 or myNum = tonumber("1.75") -- > 1.75
        • but tonumber("123abc") --> nil
      • if the string starts with an integer:
        • myNum = integer("123abc") -- > 123 or integer("abc") -- > 0
        • but integer("1.75abc") -- > 1
    • from integer: myNum = tonumber(integer(42)) -- > 42
  • to string
    • from any type: myStr = tostring(myVar)
  • to uuid
    • from string: myUuid = uuid("0f16c0e1-384e-4b5f-b7ce-886dda3bce41")
  • to vector
    • from string: myVec = tovector("<50, 50, 20>")
  • to rotation/quaternion
    • from string:
      • myRot = torotation("<1, 1, 1, 0>")
      • myRot = toquaternion("<1, 1, 1, 0>")
  • to integer
    • from boolean: myInt = integer(isOk) -- > myInt will be 1 or 0
    • from number: myInt = integer(1.75)
    • from string: myInt = integer("123abc")

The types integer and uuid haven’t got a “to” function because they already use or can use a string to create the value.

type() and typeof()

type( myVar ) returns the Lua base type of the variable. All the types added by SLua return “userdata”, which is an internal datatype used to define new types in the language itself. We can’t use “userdata”.

typeof( myVar ) returns the type of the variable, including the new types:

  • typeof( vector( 1, 2, 3 ) ) -- > vector
  • typeof( uuid( "0f16c0e1-384e-4b5f-b7ce-886dda3bce41" ) ) -- > uuid
  • typeof ( rotation ( 1, 2, 3, 4) ) -- > quaternion

We have the datatypes rotation and quaternion and the functions torotation() and toquaternion() to cast from string, but internally only exists the type quaternion, rotation is just an alias.

-- typeof() (SLua)

if typeof(myVar) == "quaternion" then  -- NOT "rotation", it would never happen!
	-- do rotations stuff
end

typeof( ZERO_ROTATION ) -- > quaternion. There is no constant ZERO_QUATERNION.

Types in LL constants and functions

In SLua, LL constants, function return values, and the elements of lists returned by LL functions have the type number if their LSL type is integer or float.

  • The only exception is ll.List2Integer(), which returns an SLua integer.

For LL function parameters that are integer or float in LSL, SLua accepts both number and integer types.

  • If a number with decimals is passed to a parameter expecting an integer, the decimal part is truncated (not rounded).
  • Many, but not all, of these functions also accept a boolean type, which is internally cast to an integer.

In SLua, LL constants that contain a uuid have type uuid. In LSL they have type string.

Use of memory

Every variable or literal value, of any type, is stored as a 16 bytes tagged value (TValue) that includes the type identifier.

  • Primitive types (boolean, number, vector and nil) have their value in the TValue.
  • Reference types (string, table, function, thread, buffer and userdata) have a pointer to the heap.

SLua vector is derived from Luau vector and is a primitive type.
SLua quaternion, uuid and integer are derived from userdata and are reference types.

The format of the TValue is:

  • 8 bytes : value (for primitive types) or pointer (for reference types)
  • 4 bytes : extra (used for Luau vectors to store their 3rd component)
  • 4 bytes : type identifier

When used as a key in a table, it changes to a Tkey, with this format:

  • 8 bytes : value
  • 4 bytes : extra
  • 4 bits : type identifier
  • 28 bits : link to the next node in the table

Each node in a table has a TKey and a TValue.

Reference types have their data stored in the heap (pointed by the TValue) with a header with internal metadata:

  • string has its length (in bytes) and data for string interning.
  • table has the length of the array part, a pointer to its metatable, the read-only parameter and data to optimize search.
  • userdata has the length and a pointer to its internal metatable.

Strings and uuids are stored as UTF-8. The characters ASCII 0-127 use 1 byte (instead of 2 bytes in LSL):

Bytes Unicode Range Character Types
1 U+0000 to U+007F ASCII characters (basic English letters, digits, etc.)
2 U+0080 to U+07FF Extended Latin, Greek, Cyrillic, Hebrew, Arabic
3 U+0800 to U+FFFF Chinese, Japanese, Korean, symbols, most emojis
4 U+10000 to U+10FFFF Supplementary characters, rare scripts, more emojis