Introduction to Lua

Lua is a scripting language and is used in the WoW User Interface and addons.
== Getting started ==
* It's recommended to use a simple text editor like VS Code or Notepad++
* You will need an AddOn to test your code in, or the WowLua addon.
You can also use the Lua Demo or get Lua on your machine. Note that World of Warcraft uses Lua 5.1.
This guide will follow the Learn X in Y minutes format.
== Variables ==
:Reference: PIL: Types and Values, Reference Manual
-- Lua is dynamically typed. variables do not have types; only values
apple = 4 -- number
apple = true -- boolean
apple = nil
banana = "yellow" -- string
cherry = 'red' -- single quotes are also fine
print(banana) -- prints "yellow"
print(type(banana)) -- prints "string"
d, e, f = "hi", false, 123 -- multiple assignment
i = 10 -- global variable
local j = 1 -- local variable
local k = 42; -- semicolons are mostly optional
--[[
	This is a
	multi-line comment
--]]
-- variable scope https://www.lua.org/pil/4.2.html
if true then
	local x = 4
	print(x) -- 4
end
print(x) -- nil (out of scope)
local y = 10
local z
-- a do-end block can be useful for explicitly scoping variables
do
	print(y) -- 10
	local y = 3
	print(y) -- 3
	z = true
end
print(y) -- 10
print(z) -- true
== Boolean Logic ==
:Reference: PIL: Relational Operators, Logical Operators
-- relational operators 
local apple, berry = 3, 5
print(apple == berry) -- false
print(apple ~= berry) -- true
print(apple <= berry) -- true
-- be careful when comparing values with different types
print(0 == "0") -- false
print(2 == tonumber("2")) -- true
print("2" == tostring(2)) -- true
-- only nil and false are falsy, anything else is truthy
local a = 123
if a then
	print("This is truthy") -- prints this
else
	print("This is falsy")
end
print(not nil) -- true
print(not false) -- true
print(not true) -- false
print(not 0) -- false
print(not "") -- false
-- logical operators
-- and operator: if the first operand is truthy, then the second operand will be returned, otherwise returns the first operand
-- or operator: if the first operand is truthy, then the first operand will be returned, otherwise returns the second operand
print(true and 7) -- first op is truthy -> returns the second op: 7
print(4 and 5) -- first op is truthy -> returns the second op: 5
print(false and 13) -- first op is falsy -> returns the first op: false
print(nil and 9) -- first op is falsy -> returns the first op: nil
print(true or 7) -- first op is truthy -> returns the first op: true
print(4 or 5) -- first op is truthy -> returns the first op: 4
print(false or 13) -- first op is falsy -> returns the second op: 13
print(nil or 9) -- first op is falsy -> returns the second op: 9
print(true and true and false) -- false (not all operands are true)
print(false or false or true) -- true (at least a single operand is true)
-- both "and" and "or" use short-circuit evaluation, that is, they evaluate their second operand only when necessary
local function greet() print("hello") end
local a = true and greet() -- hello
local b = false and greet() -- prints nothing
local c = true or greet() -- prints nothing
local d = false or greet() -- hello
-- toggles between true/false
local x = true
x = not x
print(x) -- false
x = not x
print(x) -- true
-- combining the "and" and "or" operators is similar to the ternary operator
-- http://lua-users.org/wiki/TernaryOperator
local b = true and "yes" or "no"
local c = false and "yes" or "no"
print(b) -- yes
print(c) -- no
== Math ==
:Reference: MathLibraryTutorial, Reference Manual
-- the normal order of operations is followed
print(2 + 3 * 4) -- 14
print((2 + 3) * 4) -- 20
-- Lua can automatically convert between strings and numbers (coercion)
-- https://www.lua.org/manual/5.1/manual.html#2.2.1
local a = 3
local b = "2"
print(a + b) -- 5
print(math.max(23, 17)) -- 23
print(math.floor(2.9)) -- 2
print(math.cos(2 * math.pi)) -- 1
print(math.pow(2, 10)) -- 1024
print(2^10) -- 1024
print(math.fmod(14, 3)) -- 2
print(14%3) -- 2
-- WoW includes a bitwise library
-- https://wow.gamepedia.com/Lua_functions#Bit_Functions
print(bit.band(0x10A48, 0x800)) -- 2048 (0x800)
print(bit.bor(1, 2)) -- 3
print(bit.lshift(1, 10)) -- 1024
== Strings ==
:Reference: String Manipulation, String Library Tutorial
-- string concatenation https://www.lua.org/pil/3.4.html
print("Storm".."wind") -- Stormwind
print(40 .." yards") -- 40 yards
local a, b = "Where", "wife"
print(a.." is Mankrik's "..b.."?") -- Where is Mankrik's wife?
-- string length https://www.lua.org/manual/5.1/manual.html#2.5.5
print(#a) -- 5
-- format strings https://www.lua.org/manual/5.1/manual.html#pdf-string.format
print(string.format("Hello %s", "Bob")) -- Hello Bob
-- object-oriented style
local fs = "WTS %d Shiny %s"
print(fs:format(6, "Red Apples")) -- WTS 6 Shiny Red Apples
local s = "Hello my friend"
print(string.sub(s, 7)) -- my friend
print(string.gsub(s, "l", "w")) -- Hewwo my friend, 2
print( s:upper() ) -- HELLO MY FRIEND
Patterns
:Reference: PatternsTutorial, PIL: Patterns, Reference Manual
-- Lua uses "patterns" instead of regular expressions
print(string.match("abcdefg", "b..")) -- bcd (. matches all characters)
print(string.find("abcdefg", "b..")) -- 2, 4
print(string.match("foo 123 bar", "%d%d%d")) -- 123 (%d matches a digit)
print(string.match("hello World", "%u")) -- W (%u matches an uppercase letter)
-- a pattern can contain sub-patterns enclosed in parentheses, also known as captures
local m, d, y = string.match("04/19/64", "(%d+)/(%d+)/(%d+)")
print(m, d, y) -- 04, 19, 64
== Control Flow ==
:Reference: PIL: Control Structures
-- if-then block
local x = math.random(1, 6)
if x == 3 then
	print(x, "is three")
elseif x < 3 then
	print(x, "is less than three")
else
	print(x, "is greater than three")
end
-- for loop
local sum = 0
for i = 1, 10 do
	sum = sum + i
end
print(sum) -- 55
-- while loop
local sum, i = 0, 0
while true do
	i = i + 1
	sum = sum + i
	if i == 10 then
		break
	end
end
print(sum) -- 55
== Functions ==
:Reference: PIL: Functions
function square(v)
	return v * v
end
print(square(4)) -- 16
-- varargs can hold a variable number of arguments
-- https://www.lua.org/pil/5.2.html
local function add(...)
	print(...) -- 3, 5, 4
	local a, b, c = ...
	return a + b + c
end
-- returns the sum of 3 values
print(add(3, 5, 4)) -- 12
-- the select() function returns all parameters after the nth index
-- https://www.lua.org/manual/5.1/manual.html#pdf-select
local function foo(...)
	print(select(2, ...)) -- green, blue
end
foo("red", "green", "blue")
local function bar()
	return "hello", "world", "bye"
end
print(select(2, bar())) -- world, bye
print(select(2, "a", "b", "c", "d")) -- b, c, d
print(select(3, "a", "b", "c", "d")) -- c, d
print(select(4, "a", "b", "c", "d")) -- d
-- an extra pair of brackets captures the first value
print((select(2, "a", "b", "c", "d"))) -- b
Syntactic sugar
:References: WowAce coding tips, Difference between . and : in Lua
Alpha = function() end
-- is the same as
function Alpha() end
local Object = {}
Object.Bravo = function(Object) print(Object) end
-- is the same as
function Object.Bravo(Object) print(Object) end
-- is the same as
function Object:Bravo() print(self) end
-- and for calling
Object.Bravo(Object)
-- is the same as
Object:Bravo()
== Tables ==
:Reference: PIL: Tables, Tables Tutorial, Arrays
-- table constructors can contain a comma separated list of objects to create an "array"
-- table indices start at one instead of zero
local t = {"a", "b", "c"}
print(t[1]) -- a
print(t[3]) -- c
-- this is syntactic sugar for
local t = {[1]="a", [2]="b", [3]="c"}
print(t[1]) -- a
print(t[3]) -- c
-- tables can be nested
local t = {"test", {17, 43}}
print(t[2][1]) -- 17
print(t[2][2]) -- 43
-- table with key-value pairs
local t = {["fruit"] = "peach", foo = true}
print(t.fruit) -- peach
print(t["foo"]) -- true
print(t.foo) -- true (syntactic sugar)
-- table used as a list / sequentially ordered array
local t = {8, 5, 7}
print(t[1]) -- 8
t[2] = 14 -- changes the value at index 2
table.insert(t, "banana")
-- iterates through the table with a for loop
for i = 1, #t do
	print(i, t[i])
end
-- 1, 8
-- 2, 14
-- 3, 7
-- 4, banana
-- note this is table.unpack() in Lua 5.2
print(unpack(t)) -- 8, 14, 7, banana
-- table used as a dictionary / associative array
local t = {
	foo = 2,
	bar = 5,
	baz = 7,
}
print(t.bar) -- 5
t.fruit = "banana"
-- iterates through the table with a generic for loop (random order)
-- https://www.lua.org/pil/4.3.5.html
for k, v in pairs(t) do
	print(k, v)
end
-- baz, 7
-- bar, 5
-- fruit, banana
-- foo, 2
-- tables are passed as a reference https://stackoverflow.com/a/6128322
local a = {"strawberry", 12, true}
print(a) -- table: 0000022C0973C2A0
local b = a -- points b to the table at a
print(b == a) -- true
-- changing a key/value in b also changes it in a since they point to the same table
b[1] = "banana"
print(a[1]) -- banana
a = nil -- a no longer points to the table
print(a) -- nil
print(b) -- table: 0000022C0973C2A0 (b still points to the table)
local c = {}
for key, value in pairs(b) do -- shallow copies a table
	c[key] = value
end
-- the contents of c equals b but they both are different tables in memory
print(c == b) -- false
-- tip: you can e.g. concatenate variables when indexing a table
local names = {
	party1 = "Bob",
	party2 = "Alice",
	party3 = "Elroy",
	party4 = "Flintlocke",
}
for i = 1, 4 do
	print(names["party"..i])
end
-- Bob
-- Alice
-- Elroy
-- Flintlocke
-- tip: use a lookup table if you want to check if a (really big) table contains a specific value 
local mounts = {352, 435, 464, 522}
local function contains(t, id)
	-- we use the _ single underscore as a dummy variable when we're not interested in it
	-- https://stackoverflow.com/questions/5893163
	for _, v in pairs(t) do
		if v == id then
			return true
		end
	end
end
print(contains(mounts, 464))
:
-- this is much faster
local mounts = {
	[352] = true,
	[435] = true,
	[464] = true,
	[522] = true,
}
print(mounts[464])
Sorting
-- sequential tables can be sorted
-- http://lua-users.org/wiki/TableLibraryTutorial
local t = {2, 14, 5, 9}
table.sort(t)
print(unpack(t)) -- 2, 5, 9, 14
local mounts = {
	{id = 352, name = "Big Love Rocket"},
	{id = 435, name = "Mountain Horse"},
	{id = 464, name = "Azure Cloud Serpent"},
	{id = 522, name = "Sky Golem"},
}
-- sorts alphabetically by the name field
table.sort(mounts, function(a, b)
	return a.name < b.name
end)
for _, tbl in pairs(mounts) do
	print(tbl.id, tbl.name)
end
-- 464, Azure Cloud Serpent
-- 352, Big Love Rocket
-- 435, Mountain Horse
-- 522, Sky Golem
== Details ==
Global Environment
:References: PIL: The Environment, Reference Manual
-- _G holds the global environment
-- you can explicitly access global variables from there
apple = "ripe"
local apple = "fresh"
print(apple) -- fresh
print(_G.apple) -- ripe
-- prints all global functions in the environment
for k, v in pairs(_G) do
	if type(v) == "function" then
		print(k, v)
	end
end
ipairs vs pairs
:References: For Tutorial, Difference between pairs() and ipairs() in Lua, PIL: Stateless Iterators
-- ipairs() only iterates over index-value pairs sequentially
local t = {
	[1] = "a",
	[2] = "b",
	[3] = "c",
	[27] = 123,
	fruit = "strawberry",
	[33] = "hello",
}
for i, v in ipairs(t) do
	print(i, v)
end
-- 1, a
-- 2, b
-- 3, c
for k, v in pairs(t) do
	print(k, v)
end
-- 1, a
-- 2, b
-- 3, c
-- fruit, strawberry
-- 33, hello
-- 27, 123
: Follow-up: Create a WoW AddOn in 15 Minutes
== Resources ==
API Reference
* Lua functions - The Lua 5.1 API as implemented in WoW
* https://www.lua.org/manual/5.1/ - Reference Manual (5.1)
Tutorials
* https://www.mmo-champion.com/threads/817817-Creating-Your-Own-WoW-Addon - MMO-Champion tutorial
* https://www.lua.org/start.html - Getting started with Lua
* https://www.lua.org/pil/contents.html - Programming in Lua (online version)
* https://en.wikibooks.org/wiki/Lua_Programming - Wikibooks tutorial
* https://wiki.facepunch.com/gmod/Beginner_Tutorial_Intro - Garry's Mod tutorial
* https://developer.roblox.com/en-us/learn-roblox/coding-scripts - Roblox tutorial
Style Guides
These may be helpful for seeing what coding conventions other projects follow. See also Lua Coding Tips for best practices.
* https://github.com/Olivine-Labs/lua-style-guide/
* https://github.com/luarocks/lua-style-guide
* http://lua-users.org/wiki/LuaStyleGuide