Lua is one of the most popular embeddable language (other than Javascript that already embedded in browser), there's a lot of products that embed lua as it's scripting language (probably because the language and the C-API is simple, VM size is small, and there's a product called LuaJIT that is currently the fastest JIT implementation for scripting language. Current version of Lua is 5.4, but LuaJIT only support 5.1 and 5.2 partially. There's a lot of products that are built having Lua embedded inside:
- Tarantool -- fastest in-memory database that supports SQL, but can store 100x more than RAM size
- Aerospike -- the fastest multi-master in-memory database
- Redis -- most popular key-value in-memory database
- Cheat Engine -- memory editor for cheating game
- Conky -- linux monitoring app
- game engines: Defold, Core (UnrealEngine-based), Love2D, CryEngine, Leadwerks
- games (to create mods mostly): Factorio, Garry'sMod, WarFrame, Roblox, WorldOfWarcraft, Payday2, PSO2, DOTA2, Crysis
- Darktable -- photo editing software
- video composition/editing: DavinciResolve, BlackmagicFusion
- audio composition/editing: Reaper, Renoise
- animation creation: Moho,
- mpv -- video player
- NeoVIM, Vim 7.3+ -- text editor
- web servers, reverse proxy/load balancer: Nginx/OpenResty, Lighttpd, HAProxy, Varnish, WinGate
- PowerDNS -- DNS server
- NSBase -- RAD tool
- MySQLWorkbench -- database GUI for MySQL
- chat server/client: Prosody XMPP IM, WeeChat IRC, TeamSpeak
- network diagnostic tool: WireShark, NMAP
Variables, Data Types, Comments, basic stdlib Functions
types in Lua: boolean, number, string
table, function, nil, thread, userdata
]]
-- this is single line comment, just like SQL
-- create variable
local foo = 'test' -- create a string, can escape \ same as with ""
local bar = 1 -- create a number
-- random value
math.randomseed(os.time()) -- set random table
math.random() -- get random float
math.random(10) -- get random int 0-9
-- print, concat
print(#foo) -- get length of string, same as string.len(foo)
foo .. 123 -- concat string, will convert non-string to string
print(1 + 2 ^ 3 * 4, 5) -- print 33 5 separated with tab
-- type and conversion
type(bar) -- get type (number)
tostring(1) -- convert to string
tonumber("123") -- convert to number
-- multiline string
print([[foo
bar]]) -- print a string, but preserve newlines, will not escape \
-- string functions
string.upper('abc') -- return uppercased string
string.sub('abcde', 2,4) -- get 2nd to 4th char (bcd)
string.char(65) -- return ascii of a byte
string.byte('A') -- return byte of a character
string.rep('x', 5, ' ') -- repeat x 5 times separated with space
string.format('%.2f %d %i', math.pi, 1, 4) -- smae as C's sprintf
start, end = string.find('haystack', 'st') -- find index start-end of need on haystack, nil if not found, end is optional
string.gsub('abc','b','d') -- global substitution
Decision and Loop
There's one syntax for decision (if), and 3 syntax for loop (for, while-do, repeat-until) in Lua:
-- decision
if false == true then
print(1)
elseif 15 then -- truthy
print( 15 ~= 0 ) -- not equal, in another language: !=
else
print('aaa')
end
-- can be on single line
if (x > 0) or (x < 0) then print(not true) end
-- counter loop
for i = 1, 5 do print(i) end -- 1 2 3 4 5
for i = 1, 4, 2 do print(i) end -- 1 3
for i = 4, 1, -2 do print(i) end -- 4 2
-- iterating array
local arr = {1, 4, 9} -- table
for i = 1, #arr do print(arr[i]) end
-- top-checked loop
while true do
break -- break loop
end
-- bottom-checked loop
repeat
until false
IO, File, and OS
IO is for input output, OS is for operating system functions:
-- IO
local in = io.read() -- read until newline
io.write('foo') -- like print() but without newline
io.output('bla.txt') -- set bla.txt as stdout
io.close() -- make sure stdout is closed
io.input('foo.txt') -- set foo.txt as stdin
io.read(4) -- read 4 characters from stdin
io.read('*number') -- read as number
io.read('*line') -- read until newline
io.read('*all') -- read everything
local f = io.open('file.txt', 'w') -- create for write, a=append, r=read
f:write('bla') -- write to file
f:close() -- flush and close file
-- OS
os.time({year = 2023, month = 2, day = 4, hour = 12, min = 23,sec = 34})
os.getenv('PATH') -- get environtment variables value
os.rename('a.txt','b.txt') -- rename a.txt to b.txt
os.rename('a.txt') -- erase file
os.execute('echo 1') -- execute shell command
os.clock() -- get current second as float
os.exit() -- exit lua script
Table
Table is combination of list/dictionary/set/record/object, it can store anything, including a function.
-- as list
local arr = {1, true, "yes"} -- arr[4] is nil, #arr is 3
-- mutation functions
table.sort(arr) -- error, cannot sort if elements on different types
table.insert(arr, 2, 3.4) -- insert on 2nd position, shifting the rest
table.remove(arr, 3) -- remove 3rd element, shifting remaining left
-- non mutating
table.concat(arr, ' ') -- return string with spaces
-- nested
local mat = {
{1,2,3},
{4,5,6}, -- can end with comma
}
-- table as dictionary and record
local user = {
name = 'Yui',
age = 23,
}
user['salutations'] = 'Ms.' -- or user.salutations
Function
Function is block of code that contains logic.
-- function can receive parameter
local function bla(x)
x = x or 'the default value'
local y = 1 -- local scope variable
return x
end
-- function can receive multivalue
local function foo()
return 1,2
end
local x,y = foo()
local x = foo() -- only receive first one
-- function with internal state
local function lmd()
local counter = 0
return function()
counter = counter + 1
return counter
end
end
local x = lmd()
print(x()) -- 1
print(x()) -- 2
-- variadic arguments
local function vargs(...)
for k, v in pairs({...}) do print(k, v) end
end
vargs('a','b') -- 1 a, 2 b
Coroutine
Coroutine is resumable function
local rot1 = coroutine.create(function()
for i = 1, 10 do
print(i)
if i == 5 then coroutine.yield() end -- suspend
end
end)
if coroutine.status(rot1) == 'suspended' then
-- running, [suspended], normal, dead
coroutine.resume(rot1) -- will resume after yield
end
Module
To organize a code, we can use module:
-- module, eg. mod1.lua
_G.mod1 = {}
function mod1.add(x,y)
return x + y
end
return mod1
-- import a module
local mod1 = require('mod1')
local x = mod1.add(1,2)
Basic OOP
To simulate OOP-like features we can do things like in Javascript, just that to call a method of object we can use colon instead of dot (so self parameter will be passed):
-- constructor
local function NewUser(name, age)
return {
name = name,
age = age,
show = function(self) -- method example
print(self.name, self.age)
end
}
end
local me = NewUser('Tzuyu', 21)
me:show() -- short version of: me.show(me)
-- inheritance, overriding
local function NewAdmin(name, age)
local usr = NewUser(name, age)
usr.isAdmin = true
usr.setPerm = function(self, perms)
self.perms = perms
end
local parent_show = usr.show
usr.show = function(self)
-- override parent method
-- parent_show(self) to call parent
end
return usr
end
local adm = NewAdmin('Kis', 37)
adm:setPerm({'canErase','canAddNewAdmin'})
adm:show() -- does nothing
-- override operator
local obj = {whatever = 1}
setmetatable(obj, {
__add = function(x,y) -- overrides + operator
return x.whatever + tonumber(y)
end,
-- __sub, __mul, __div, __mod, __pow,
-- __concat, __len, __eq, __lt, __le, __gt, __ge
})
print(obj + "123")
For more information about modules you can visit https://luarocks.org/
For more information about the syntax you can check their doc: https://devdocs.io/lua~5.2/ or https://learnxinyminutes.com/docs/lua/ or this 220 minutes video
But as usual, don't use dynamic typed language for large project, it would be pain to maintain in the long run, especially if the IDE cannot jump properly to correct method or cannot suggest correct type hints/methods to call