Luawe the Wayfarer Engine's scripting language All pages

Lists and tables

A table keeps many values together. It is the only way Luawe has to do that, and it does two jobs: a list of values in order, and a set of values by name.

Lists

local colours = { "red", "green", "blue" }

print(colours[1]) -- places are counted from 1
print(#colours)   -- # is how many there are

colours[2] = "yellow"
colours[#colours + 1] = "pink" -- a new one at the end
print(table.concat(colours, ", "))

It prints

red
3
red, yellow, blue, pink

Asking for a place that has nothing in it gives nil; it is not an error.

The table library does the everyday work:

local queue = { "b", "c" }

table.insert(queue, "d")    -- at the end
table.insert(queue, 1, "a") -- at place 1: the others move up
print(table.concat(queue, " "))

local last = table.remove(queue)     -- takes the last one out
local first = table.remove(queue, 1) -- takes the first: the others move down
print(first, last, #queue)

print(table.find(queue, "c")) -- where is it?

It prints

a b c d
a d 2
2

table.sort puts a list in order, numbers by size and strings by the alphabet. With a function it sorts any way you like: the function gets two values and says whether the first belongs before the second.

local names = { "saw", "block", "spike" }
table.sort(names)
print(table.concat(names, " "))

table.sort(names, function(a, b)
    return #a < #b -- the shortest first
end)
print(table.concat(names, " "))

It prints

block saw spike
saw block spike

Values by name

A table can keep values under names, called keys:

local door = { name = "gate", open = false, width = 3 }

print(door.name)
door.open = true -- change one
door.colour = "blue" -- add one
door.width = nil -- take one away

for key, value in door do
    print(key, value)
end

It prints

gate
name gate
open true
colour blue

door.name is short for door["name"]. The long form is needed when the key is not a plain word, or is in a variable:

local stock = { ["red key"] = 1, gems = 12 }
local wanted = "gems"
print(stock["red key"], stock[wanted])

It prints

1 12

Any value except nil can be a key: numbers, strings, even things of the level.

local hits = {}
for _, thing in things.all("target") do
    hits[thing] = 0 -- a count for each target, kept under the thing itself
end

Tables in tables

A table's values can be tables. That is how a grid or a list of records is made:

local grid = {
    { 1, 0, 1 },
    { 0, 1, 0 },
}
print(grid[2][2])

local team = {
    { name = "Sky", score = 40 },
    { name = "Moon", score = 90 },
}
table.sort(team, function(a, b)
    return a.score > b.score
end)
print(team[1].name)

It prints

1
Moon

One table, many names

A variable does not hold a table; it points to one. Giving the table to another variable gives another pointer to the same table:

local a = { 1, 2, 3 }
local b = a
b[1] = 99
print(a[1])

local copy = table.clone(a) -- a table of its own
copy[1] = 0
print(a[1])

It prints

99
99

The same holds for a table handed to a function: the function can change it. table.clone copies a table one level deep (tables inside it are still shared).

Two tables are equal only if they are the very same table:

print({} == {})

It prints

false

How many

#list counts a list's places from 1 up. Use it for lists that have no gaps. For a table with names as keys it gives 0; table.count counts every value a table has, under any key:

local door = { name = "gate", open = false }
print(#door, table.count(door))

It prints

0 2

Tables that behave your way

A table can be given a second table, its metatable, which says what should happen when the first is used in ways tables normally cannot be: added, compared, called, or asked for a key it does not have. This is an advanced tool, and most scripts never need it.

The most used entry is __index: where to look when a key is missing. It is what makes the "one function, many tables" pattern from functions work.

local Vector = {}
Vector.__index = Vector -- a key that a vector lacks is looked up in Vector

function Vector.new(x, y)
    return setmetatable({ x = x, y = y }, Vector)
end

function Vector:length()
    return math.sqrt(self.x ^ 2 + self.y ^ 2)
end

-- What + does for two vectors, and how a vector is shown.
Vector.__add = function(a, b)
    return Vector.new(a.x + b.x, a.y + b.y)
end
Vector.__tostring = function(v)
    return `({v.x}, {v.y})`
end

local step = Vector.new(3, 4)
print(step:length())
print(tostring(step + step))

It prints

5
(6, 8)

The entries a metatable can have:

EntryDecides
__indexReading a key the table lacks (a table to look in, or a function)
__newindexSetting a key the table lacks
__callCalling the table like a function
__tostringWhat tostring and print show
__lenWhat # gives
__eq __lt __le==, < and <= (and with them ~=, > and >=)
__add __sub __mul __div __mod __pow __idiv __unmThe arithmetic signs (__idiv is //, __unm is the minus before a value)
__concat..

rawget, rawset, rawequal and rawlen do the plain thing and never ask the metatable.

Next: working with text.