Skip to main content

Overview

Tables are the primary data structure in Lua, serving as both arrays and dictionaries. SolarSharp’s Table class provides a powerful C# interface for working with Lua tables.

Creating Tables

From C#

using SolarSharp.Interpreter.DataTypes;

Script script = new Script();

// Empty table
Table table = new Table(script);

// With size hints for performance
Table optimized = new Table(script, 
    arraySizeHint: 100,      // Expected array elements
    associativeSizeHint: 50  // Expected key-value pairs
);

// Initialize with array values
Table array = new Table(script,
    LuaValue.NewNumber(1),
    LuaValue.NewNumber(2),
    LuaValue.NewNumber(3)
);

From Lua

LuaValue result = script.DoString(@"
    return {
        name = 'John',
        age = 30,
        scores = {95, 87, 92}
    }
");

Table table = result.Table;

Accessing Values

Using Indexers

The Table class provides convenient indexer access:
Table table = new Table(script);

// Set values using indexers
table["name"] = "Alice";
table["age"] = 25;
table[1] = "first";
table[2] = "second";

// Get values (auto-converts to C# types)
string name = (string)table["name"];  // "Alice"
int age = (int)table["age"];          // 25

Using Get/Set Methods

For more control, use the Get and Set methods:
// Set with LuaValue keys and values
table.Set("key", LuaValue.NewString("value"));
table.Set(LuaValue.NewNumber(42), LuaValue.NewBoolean(true));

// Get returns LuaValue
LuaValue value = table.Get("key");
Console.WriteLine(value.String); // "value"

// Integer keys (array access)
LuaValue first = table.Get(1);

// Returns nil for missing keys
LuaValue missing = table.Get("nonexistent");
Console.WriteLine(missing.IsNil()); // true

Nested Access

Access nested tables using multiple keys:
LuaValue result = script.DoString(@"
    return {
        player = {
            stats = {
                health = 100,
                mana = 50
            }
        }
    }
");

Table root = result.Table;

// Access nested values
LuaValue health = root.Get("player", "stats", "health");
Console.WriteLine(health.Number); // 100

// Set nested values
root.Set(new object[] { "player", "stats", "mana" }, LuaValue.NewNumber(75));

Array Operations

Length

Get the length of the array part:
Table array = new Table(script,
    LuaValue.NewNumber(10),
    LuaValue.NewNumber(20),
    LuaValue.NewNumber(30)
);

int length = array.Length;
Console.WriteLine(length); // 3
Lua arrays are 1-indexed. The Length property returns the size of the contiguous array part starting from index 1.

Append

Add elements to the end of the array:
Table array = new Table(script);

array.Append(LuaValue.NewNumber(1));
array.Append(LuaValue.NewNumber(2));
array.Append(LuaValue.NewNumber(3));

Console.WriteLine(array.Length); // 3
Console.WriteLine(array.Get(1).Number); // 1

Insert

Insert elements at specific positions:
Table array = new Table(script,
    LuaValue.NewNumber(1),
    LuaValue.NewNumber(3)
);

// Insert at index 2
array.Insert(2, LuaValue.NewNumber(2));

// Array is now: 1, 2, 3
Console.WriteLine(array.Get(2).Number); // 2

Remove

Remove elements from arrays:
Table array = new Table(script,
    LuaValue.NewNumber(1),
    LuaValue.NewNumber(2),
    LuaValue.NewNumber(3)
);

// Remove element at index 2
LuaValue removed = array.ArrayRemoveAt(2);
Console.WriteLine(removed.Number); // 2

// Array is now: 1, 3
Console.WriteLine(array.Length); // 2

Sort

Sort the array segment:
using System.Collections.Generic;

Table array = new Table(script,
    LuaValue.NewNumber(3),
    LuaValue.NewNumber(1),
    LuaValue.NewNumber(2)
);

// Sort with custom comparer
array.Sort(Comparer<LuaValue>.Create((a, b) => 
    a.Number.CompareTo(b.Number)
));

// Array is now: 1, 2, 3

Iteration

Enumerating Key-Value Pairs

Table table = script.DoString(@"
    return {
        name = 'Alice',
        age = 30,
        city = 'New York'
    }
").Table;

// Iterate over all pairs
foreach (var pair in table)
{
    Console.WriteLine($"{pair.Key.ToPrintString()} = {pair.Value.ToPrintString()}");
}

Enumerating Values Only

foreach (LuaValue value in table.Values)
{
    Console.WriteLine(value.ToPrintString());
}

Using Next (Lua-style)

Implement Lua’s next() function behavior:
LuaValue key = LuaValue.Nil;

while (true)
{
    LuaValue result = table.GetNextFromIt(key);
    
    if (result.IsNil())
        break;
        
    // Result is a tuple of (key, value)
    LuaValue nextKey = result.Tuple[0];
    LuaValue value = result.Tuple[1];
    
    Console.WriteLine($"{nextKey.ToPrintString()} = {value.ToPrintString()}");
    
    key = nextKey;
}

Clearing Tables

Remove all entries:
Table table = new Table(script);
table["key"] = "value";
table[1] = 42;

table.Clear();

Console.WriteLine(table.Length); // 0
Console.WriteLine(table.Get("key").IsNil()); // true

Metatables

Metatables enable powerful customization of table behavior:

Setting Metatables

Table table = new Table(script);
Table metatable = new Table(script);

// Define __index metamethod
metatable["__index"] = LuaValue.NewCallback((ctx, args) =>
{
    Console.WriteLine("Accessing missing key!");
    return LuaValue.NewString("default");
});

table.MetaTable = metatable;

// Access missing key triggers __index
LuaValue result = script.DoString(@"
    t = ...
    return t.missingKey
", table);

Console.WriteLine(result.String); // "default"

Common Metamethods

Script script = new Script();

script.DoString(@"
    Vector = {}
    Vector.__index = Vector
    
    function Vector:new(x, y)
        local v = setmetatable({}, Vector)
        v.x = x
        v.y = y
        return v
    end
    
    function Vector:__add(other)
        return Vector:new(self.x + other.x, self.y + other.y)
    end
    
    function Vector:__tostring()
        return '(' .. self.x .. ', ' .. self.y .. ')'
    end
    
    v1 = Vector:new(1, 2)
    v2 = Vector:new(3, 4)
    v3 = v1 + v2  -- Uses __add
    
    return v3
");

Available Metamethods

  • __index: Called when accessing a missing key
  • __newindex: Called when setting a new key
  • __add, __sub, __mul, __div: Arithmetic operations
  • __eq, __lt, __le: Comparison operations
  • __tostring: String conversion
  • __call: Make table callable like a function
  • __len: Custom length operator
  • And more…

Global Table Access

Access the script’s global table:
Script script = new Script();

// Get global table
Table globals = script.Globals;

// Set global variables
globals["myVar"] = 42;
globals["myFunc"] = (Func<int, int>)(x => x * 2);

// Access from Lua
script.DoString(@"
    print(myVar)      -- 42
    print(myFunc(5))  -- 10
");

Registry Table

Use the registry for private storage:
Script script = new Script();

// Store data in registry (not accessible from Lua)
Table registry = script.Registry;
registry["my-private-data"] = myObject;

// Retrieve later
var retrieved = registry.Get("my-private-data").ToObject();
The registry is useful for storing CLR objects that shouldn’t be directly accessible from Lua code.

Type Metatables

Set metatables for primitive types:
Table stringMeta = new Table(script);
stringMeta["__add"] = LuaValue.NewCallback((ctx, args) =>
{
    string a = args[0].String;
    string b = args[1].String;
    return LuaValue.NewString(a + b);
});

script.SetTypeMetatable(DataType.String, stringMeta);

// Now strings support + operator
LuaValue result = script.DoString("return 'Hello' + ' World'");
Console.WriteLine(result.String); // "Hello World"

Performance Tips

// Efficient - pre-allocates space
Table large = new Table(script, 
    arraySizeHint: 1000,
    associativeSizeHint: 500
);

// Less efficient - multiple reallocations
Table large = new Table(script);
for (int i = 0; i < 1000; i++)
    large[i] = i;
// Efficient - uses array segment
table.Set(1, value);
table.Set(2, value);

// Less efficient - uses hash map
table.Set("1", value);
table.Set("2", value);
// Fastest for integer indices
LuaValue val = table.Get(1);

// Slower - requires key conversion
LuaValue val = table.Get(LuaValue.NewNumber(1));

Common Patterns

Dictionary-like Usage

Table config = new Table(script);
config["host"] = "localhost";
config["port"] = 8080;
config["debug"] = true;

string host = (string)config["host"];
int port = (int)config["port"];

Array-like Usage

Table items = new Table(script);
items.Append(LuaValue.NewString("sword"));
items.Append(LuaValue.NewString("shield"));
items.Append(LuaValue.NewString("potion"));

for (int i = 1; i <= items.Length; i++)
{
    Console.WriteLine(items.Get(i).String);
}

Building Complex Structures

Table player = new Table(script);
player["name"] = "Hero";
player["level"] = 5;

Table inventory = new Table(script);
inventory.Append(LuaValue.NewString("sword"));
inventory.Append(LuaValue.NewString("shield"));

player["inventory"] = inventory;

// Pass to Lua
script.Globals["player"] = player;
script.DoString(@"
    print(player.name)              -- Hero
    print(player.inventory[1])      -- sword
");

See Also

Lua Values

Understanding the LuaValue type system

Script Execution

Executing scripts and passing tables

Functions

Tables as function arguments

Interop

Converting CLR objects to tables

Build docs developers (and LLMs) love