Cosmos.Executable.Lua is a Lua 5.5 interpreter, based on UniLua, made in C# for the Cosmos operating system construction kit.
Add the package to your kernel .csproj:
<ItemGroup>
<PackageReference Include="Cosmos.Executable.Lua" Version="4.0.1" />
</ItemGroup>using System;
using Cosmos.Executable.Lua;
LuaInterpreter lua = new()
{
WorkingDirectory = "/mnt", // where dofile, require and io.open start relative paths from
};
try
{
lua.DoString("print('Hello from ' .. _VERSION)");
lua.DoFile("script.lua", "first argument"); // as `lua script.lua first argument`
lua.RunPrompt(); // the interactive prompt, until os.exit()
}
catch (LuaException e)
{
// A syntax error, or a runtime error no pcall caught
Console.WriteLine(e.Message);
Console.WriteLine(e.LuaStackTrace);
}
catch (LuaExitException e)
{
// The script called os.exit(e.ExitCode)
}A C# function raises a Lua error with state.L_Error(...), or by throwing: a .NET exception becomes a Lua error that pcall catches.
As in C Lua, a Lua string is a sequence of bytes, not characters. Text is stored as UTF-8, so #"é" is 2, and the utf8 library works as expected.
Most of the time you don't need to care: LuaInterpreter converts the code, arguments and error messages for you, and so do the console, file names and os.getenv. Files are read and written byte for byte, in text and binary mode.
You only see the bytes in your own C# functions. On ILuaState, a Lua string is a .NET string with one character (\0 to \xFF) per byte. Use LuaText to convert:
state.PushString(LuaText.Encode("héllo")); // the 6 bytes of "héllo"
string text = LuaText.Decode(state.ToString(-1)); // "héllo" againThe interpreter behaves like the reference build of Lua 5.5 and passes the official Lua 5.5 test suite (lua-5.5.1-tests), unmodified.
What is different:
io.popenis not supported.- The garbage collector always runs full cycles. Weak tables,
__gcandcollectgarbage("count")work as usual, but the incremental and generational modes andcollectgarbage("param")only change how often a cycle runs. - On Cosmos, the local time is UTC,
os.getenvreturns nil, andos.tmpnamefails because the kernel has no/tmpyet.
Note that files are buffered as in C: a write reaches the file only when the buffer is full, on flush, or when the file is closed.
Contributions, issues and feature requests are welcome!
Feel free to check issues page.
Copyright © 2026 CosmosOS.
This project is BSD Clause 3 licensed. It includes UniLua, Copyright © 2013 Sheng Lunan, and code ported from Lua 5.3, Lua 5.4 and Lua 5.5, Copyright © 1994–2026 Lua.org, PUC-Rio, both under the MIT license: see THIRD-PARTY-NOTICES.txt.