Mastering Lua

A Comprehensive Guide to Coding in Lua

Lua is a lightweight, embeddable scripting language known for its simplicity, performance, and flexibility. Lua is free and open source and may be used for any purpose, including commercial purposes, at absolutely no cost. You can read up on Lua at https://www.lua.org 

While itโ€™s easy to pick up the basics, Lua also offers advanced features that make it a powerful tool for scripting, game development, and embedded systems. This guide is designed to take you from beginner to proficient Lua programmer, with enough depth to serve as a reference for your future projects.

Installation and Setup

To install lua you have two options. You can download and install it yourself from https://www.lua.org/download.html or you can use a package manager:

# install on windows using choco package manager
choco install lua

# install on Ubuntu/Debian
sudo apt install lua5.4 

# install on CentOS/Fedora
sudo yum install lua

# install on MacOS
brew install lua

Note: As you go through the following examples there are several Lua-specific operators and syntax elements you should be aware of. You will see many of these used in the examples in this article

-- Lua concatenates using two dots - ..
local fullName = "John" .. " " .. "Doe" -- "John Doe"

-- The pound sign is used to get Length - #
-- length of a string
local length = #"hello"  -- 5
-- length of a table
local tableLength = #{ 1, 2, 3, 4 }  -- 4

-- You can assign Multiple variables in the same statement
-- Easily swap or assign multiple variables
local a, b = 1, 2 -- a=1, b=2
a, b = b, a  -- Now a=2, b=1

-- Logical Operators are and, or, not instead of &&, ||, !
local result = (x > 0 and y < 10)
local value = nil or "default"  -- "default"

-- a table in lua is like an array in PHP
-- Different ways to create and access tables
local t = {x = 10, y = 20}
local value = t.x  -- Dot notation
local value = t["x"]  -- Bracket notation


-- Colons can also be used for  Method Call Syntax
local str = "hello"
local upstr1 = string.upper(str)   -- Using standard library function
local upstr2 = str:upper()         -- Using method call syntax

Ok so lets look at comments.  There are two different kinds of comments in Lua - single-line comments and multi-line comments.

-- single line comments

--[[
multi-line comments
can span multiple lines
]]

-- auto-document your code by using comments as shown below:


-- begin function encodeHtml
---@describe Encodes HTML characters that would cause issues in a browser
---@param str string The string to encode
---@return string Encoded HTML string
function encodeHtml(str)
    -- If string is empty, just return it
    if str == nil or #str == 0 then
        return str
    end
    
    -- Replace special characters. gsub is part of the standard string module (global substitution)
    str = str:gsub('\?', '[[!Q!]]')
    str = str:gsub('<', '<')
    str = str:gsub('>', '>')
    str = str:gsub('"', '"')
    str = str:gsub('\?', ' ')
    str = str:gsub('[[!Q!]]', '\?')
    
    return str
end

Code Blocks -In Lua, code blocks are typically defined using keywords that mark the beginning and end of a block of code. Code blocks begin with a keyword like function, if, for, while, repeat, or do. Code blocks are closed with end. Indentation is not mandatory but is strongly recommended for readability. Variables declared with local inside a block are only accessible within that block.

-- functional code block
function functionName(parameters)
    -- code block starts here
    local x = 10
    return x
end

-- Conditional Blocks (if-else)
if condition then
    -- code block for true condition
    print("Condition is true")
elseif another_condition then
    -- optional alternative code block
    print("Another condition is true")
else
    -- optional default code block
    print("No conditions were true")
end

-- For Loops
for i = 1, 10 do
    print(i)
end

-- While loops
local x=0
while x < 10 do
    -- code block runs while condition is true
    -- must have a way to eventually exit the loop
    x = x + 1
end

-- Repeat-Until Loops
repeat
    -- code block runs at least once
    -- continues until condition is true
until condition

-- Do-End Blocks (for local scoping)
do
    -- create a new local scope
    local x = 100
    print(x)
end  -- x is no longer accessible outside this block

Variables and Data Types. Lua is dynamically typed, meaning you do not have to declare variable types. It handles it for you. However the Basic data types are numbers, strings, Booleans, and Nil. Lua also has a concept of tables. Lua tables are the most fundamental and versatile data structure in Lua. They're essentially associative arrays that can be used as: arrays, dictionaries/hash maps, objects. namespaces, etc.

-- Variables and Basic Types
x = 10        -- integer
y = 9.45      -- number (Lua doesn't distinguish between float/double)
name = "Alice" -- string
is_student = true  -- boolean (note lowercase in Lua)

-- Tables (Lua's primary data structure, more flexible than PHP arrays)
-- Empty table
numbers = {}  

-- Table with initial values
numbers = {12, 33, 44, 1}

-- Adding elements
table.insert(numbers, 63)  -- Adds to end
table.insert(numbers, 1, 5)  -- Adds to beginning (1-based index)

-- Removing elements
last = table.remove(numbers)  -- Removes and returns last item
first = table.remove(numbers, 1)  -- Removes and returns first item

-- Constants (Lua doesn't have built-in constants, use convention)
local MAX_USERS = 100  -- Use local to prevent modification
print(MAX_USERS)  -- Output: 100

-- Associative Tables (similar to PHP associative arrays)
d = {name = "Bob", age = 32}
-- Alternative syntax
d = {}
d["name"] = "Bob"
d["age"] = 32

-- Nested tables (similar to nested arrays/dictionaries)
recs = {
    {name = "bob", age = 22},
    {name = "sam", age = 19}
}

-- Accessing table elements
print(d.name)        -- Outputs: Bob
print(d["name"])     -- Also outputs: Bob
print(recs[1].name)  -- Outputs: bob

Lua functions for manipulating strings

-- Native Lua String Manipulation Functions

local text = "Hello, World!"

-- Length
print(#text)  -- Returns string length

-- Case Conversion
print(text:upper())    -- HELLO, WORLD!
print(text:lower())    -- hello, world!

-- Substring
print(text:sub(1, 5))  -- "Hello"
print(text:sub(-6))    -- "World!"

-- Find (returns start and end index)
local start, end_pos = text:find("World")
print(start, end_pos)  -- 8, 12

-- Byte/Char Conversion
print(text:byte(1))    -- ASCII value of first character
print(string.char(65)) -- Character for ASCII value (A)

-- Pattern Matching Find
local pattern_start = text:find("%w+")  -- Finds first word

-- Global Substitution
local replaced = text:gsub("World", "Lua")  -- Replaces all occurrences

-- Formatting
print(string.format("Name: %s, Number: %d", "Alice", 42))

Loops - Lua supports for, while, and repeat-until loops

-- For loop example
for i = 1, 5 do
    print("Iteration: " .. i)
end

-- while loop
local count = 1
while count <= 5 do
    print("Count: " .. count)
    count = count + 1
end

-- repeat-until example
local x = 1
repeat
    print("x: " .. x)
    x = x + 1
until x > 5

There are two ways to define functions in Lua - declaration style and expression style

-- Function declaration style
function greet(name)
    return "Hello, " .. name .. "!"
end

-- Function expression style
local greet = function(name)
    return "Hello, " .. name .. "!"
end

function calculate(a, b)
    return a + b, a * b  -- Multiple return values
end

local sum, product = calculate(5, 3)
print(sum)      -- Outputs: 8
print(product)  -- Outputs: 15

-- optional parameters
function greet(name, greeting)
    greeting = greeting or "Hello"  -- Default value
    return greeting .. ", " .. name .. "!"
end

print(greet("Alice"))          -- Uses default "Hello"
print(greet("Bob", "Hi"))      -- Uses custom greeting

-- Variadic Functions (Variable Number of Arguments)
function sum(...)
    local total = 0
    local args = {...}  -- Converts variable arguments to a table
    for _, value in ipairs(args) do
        total = total + value
    end
    return total
end

print(sum(1, 2, 3, 4))  -- Outputs: 10

-- Functions can also take another function as an argument
function apply(func, x, y)
    return func(x, y)
end

local function add(a, b) return a + b end
local function multiply(a, b) return a * b end

print(apply(add, 5, 3))       -- Outputs: 8
print(apply(multiply, 5, 3))  -- Outputs: 15

File reading/writing is done via the Lua io module (included by default)

-- reading a file
local file = io.open("example.txt", "r")
if file then
    local content = file:read("*a")  -- Read entire file
    print(content)
    file:close()
else
    print("Failed to open file")
end

-- writing to a file
local file = io.open("output.txt", "w")
if file then
    file:write("Hello, Lua!")
    file:close()
else
    print("Failed to open file")
end

Lua does not have built-in try/catch error handling like many other programming languages. However it does have a function called pcall (protected call) that you can wrap your function calls in to catch errors.

-- This function will actually execute
local function risky_function()
    print("Starting risky function")
    error("Intentional error")  -- This stops the function's execution
    print("This line will never run")
end

-- pcall runs the entire function, not just testing it
local success, error_message = pcall(risky_function)

-- If an error occurs:
-- 1. The function runs until the error
-- 2. The program continues instead of crashing
-- 3. 'success' will be false
-- 4. 'error_message' contains the error description

if not success then
    print("Caught an error: " .. error_message)
end

---------- Second Example of using pcall ----------

local function run_database_query(connection, query)
    local result = connection:execute(query)
    return result
end

-- Using pcall with the database query
local success, result_or_error = pcall(run_database_query, database_connection, "SELECT * FROM users")

if success then
    -- Query ran successfully
    process_results(result_or_error)
else
    -- Handle the error
    log_error("Database query failed: " .. result_or_error)
end

-- If successful, success is true and result_or_error contains the query results
-- If an error occurs, success is false and result_or_error contains the error message

Last is includes. Lua includes some modules by default (shown below). Here are a few other really useful modules.

-- Use require to load modules that are not built in.
local json = require("luajson")

--[[
Basic libraries automatically available
	string   -- String manipulation
	math     -- Mathematical operations
	table    -- Table manipulation
	io       -- Input/Output operations
	os       -- Operating system interactions
	debug    -- Debugging facilities
	coroutine -- Cooperative multitasking

Useful Third-Party Libraries

Database Connections
	luasql: MySQL, PostgreSQL, SQLite connections
	luadbi: Database-agnostic interface
JSON Handling
	dkjson
	luajson
HTTP/Networking
	luasocket: Network programming
	luasec: SSL support
	lua-requests: HTTP requests (similar to Python)
Web Frameworks
	Lapis: Web framework
	OpenResty: Web platform integrating Nginx and Lua
Advanced Utilities
	luafilesystem: File system operations
	serpent: Advanced serialization
	lualogging: Logging library
]]

Congratulations on making it to the end of this article! Whew! That was a quite a bit to digest. If you're feeling a overwhelmed, I recommend re-reading it a couple more times over the next few days. Repetition will help immensely. When you are ready, try writing some Lua code yourself. Looking forward to hearing what you come up with!

 Sponsor: Need some extra storage room in your garage? I have installed two of these and love them! Heavy Duty Metal Garage Ceiling Storage Racks that support 600 lbs! https://www.amazon.com/dp/B0194RLK4Y?tag=fingerpointfo-20

Thanks again for reading. Please like and SHARE. ๐Ÿ™‚