Lua is a very minimalistic language. This doesn't mean that it lacks syntactic sugar though; plenty of constructs native to other languages are "emulated" using more powerful constructs in Lua and thus need sugar for Lua to look & feel nice - Lua has more syntactic sugar than you may realize!

Table Indexing

Lua's one-in-all datastructure is the table, which is array and hash map in one.

Since tables are used to implement data structures including "instances" of "classes", accessing "fields" must be convenient. Lua thus provides the . indexing sugar:

field = table.name

is equivalent to

field = table["name"]

where name is a valid Lua identifier; similarly,

table = {name = value}

is equivalent to

table = {["name"] = value}

Environmental Variables

All variables that are not declared local are "environmental" (typically global) by default.

This means name_a = name_b is just syntactic sugar for _ENV.name_a = _ENV.name_b.

Functions

Declarations

The typical "named" function declaration

function name(...) end

is just shorthand for the assignment

name = function(...) end

this works for locally declared variables just as well:

local name
function name(...) end

is still equivalent to

local name
name = function(...) end

or in short (assuming the function doesn't recurse):

local name = function(...) end

More sugar is provided to allow local functions to recurse. Consider

local f = function() return f() end

as the right-hand side is evaluated first, f inside the function body refers to the environmental variable _ENV.f rather than the local variable f.

The solution is to split declaration & assignment such that f becomes an upvalue of the function that is later assigned to it:

local f
f = function() return f() end

since recursive local functions are frequently required, Lua provides local function sugar for this:

local function f() return f() end

Functions being table fields (where the table possibly is the environment _ENV) don't require such a sugar - they only need access to the table containing them, which is usually provided as an upvalue (often _ENV).

All functions in Lua are anonymous; Lua actually figures out the function name to show in error messages by looking at from where (local / global variable / table field) the function was accessed.

Additionally, names support the dot indexing sugar:

function x.y.z(...) end

is shorthand for

x.y.z = function(...) end

arbitrary expressions (including [x] indexing) are however not allowed; finally, Lua supports adding a trailing :method

function x.y.z:method(...) end

which is equivalent to .method plus an implicit self parameter:

function x.y.z.method(self, ...) end

to easen metatable-based "OOP" for instance methods, which can then be registered as function class:method(...) end.

Calls

Implicit Self ("OOP")

Consistent with the : sugar for adding an implicit self parameter when declaring a function, Lua also offers the : sugar to add an implicit self argument when calling a function obtained through indexing:

prefixexp:name(...)

is roughly equivalent to

(function(self) return self.name(self, ...) end)(prefixexp)

a particularly cursed example of this (which helps illustrate that this really just syntactic sugar) is _G:pairs(), which will loop over the global variables.

Single String or Table Argument

If the only argument is a string or table, Lua allows omitting the call parentheses; parentheses-less calls can be chained:

f"double-quoted string"
f'single-quoted string'
f[[long string]]
f{table = true}

f{"chaining"}"works""just"'fine'[[yay]]

equivalent to

f("double-quoted string")
f('single-quoted string')
f([[long string]])
f({table = true})

f({"chaining"})("works")("just")('fine')([[yay]])

The omission of parentheses around "table calls" in particular makes Lua very suitable for implementing DSLs.

Side note: Chaining may be (ab?)used to implement ropes without requiring a separator (like , or ; in tables). This enables a neat hack to implement indented multiline strings:

local lines = {__call = function(self, str)
	if not str then return table.concat(self, "\n") end
	table.insert(self, str)
	return self
end}
local function L(str)
	return setmetatable({}, lines)
end

Usage is straightforward:

local multiline = L
	"a"
	"couple"
	"lines"
()

Metatables

Metatables allow you to change how Lua's built-in operators work, effectively making operators syntactic sugar for function calls.

This is very flexible; besides the use of the __index metamethod to emulate OOP, metatables are probably most commonly used to implement the arithmetic operators e.g. for vectors or custom number types (fractions, big integers), Lua itself sets a metatable using __index on strings to enable the OOP-style str:method(...) syntactic sugar.

Even more, using debug.setmetatable you can largely alter operations on builtin Lua types such as nil, booleans, functions, threads, so long as they aren't Lua-defined already.

Conclusion

Lua has just the right syntactic sugar it needs to give you the most bang for the buck, enabling it to be very minimalistic while remaining very readable and rather easy to parse.