Skip to main content

Tables

Tables are Lua's only data structure. Nebra supports all Lua table syntax with optional type annotations.

Table Constructors

Array-Style

local arr = {1, 2, 3, 4, 5}
local names: string[] = {"Alice", "Bob", "Charlie"}

Record-Style

local point = {x = 10, y = 20}
local config: {[string]: any} = {host = "localhost", port = 8080}

Bracket Keys

local map = {
["key with spaces"] = 1,
[42] = "number key",
[true] = "bool key"
}

Mixed

local mixed = {
1, 2, 3, -- positional
name = "test", -- named
[10] = "explicit" -- bracket
}

Named Functions

A field may be written as a named function. It is shorthand for the name = function ... end form and produces the same string key:

local ops = {
function add(a: number, b: number): number
return a + b
end,
function negate(v: number): number
return -v
end,
}

-- exactly equivalent to
local ops = {
add = function(a: number, b: number): number
return a + b
end,
negate = function(v: number): number
return -v
end,
}

async works the same way:

local io = {
async function read(path: string): string
return await fs.readFile(path)
end,
}

The shorthand needs an identifier after function. A function followed by ( is still an anonymous function and still takes an integer key, so both forms mix freely:

local handlers = {
function onStart() print "start" end, -- key "onStart"
function() print "anonymous" end, -- key 1
label = "tail",
}

The separator between fields is required, exactly as for any other field, and a trailing one is allowed. Integer and computed keys still need the bracket form ([1] = function() end).

There is no function t:method() end form inside a table constructor. Write the self parameter out when you want the field to be callable with ::

local counter = {
total = 0,
function bump(self: any, by: number): number
self.total = self.total + by
return self.total
end,
}

counter:bump(3)

Calling a table field with : when it has no self parameter is a warning, because Lua passes the receiver anyway and every declared parameter ends up shifted by one:

local t = { function greet(name: string) print(name) end }
t:greet("world") -- warning: 'greet' declares no 'self' parameter
t.greet("world") -- fine

Empty Table

local empty = {}
local typed: number[] = {}
local map: {[string]: number} = {}

Field Separators

Both , and ; are valid separators. Trailing separator is allowed:

local a = {1, 2, 3,}
local b = {x = 1; y = 2; z = 3;}

Type Annotations on Tables

-- Typed as array
local nums: number[] = {1, 2, 3}

-- Typed as map
local scores: {[string]: number} = {alice = 100, bob = 85}

-- Typed as struct
local user: {name: string, age: number} = {name = "Alice", age = 30}

Index Base

With index_base = 0 in config, array indices are adjusted from 0-based to Lua's 1-based:

local arr = {10, 20, 30}
print(arr[0]) -- compiles to arr[1], prints 10
print(arr[1]) -- compiles to arr[2], prints 20