Skip to content

marvinb16/MiniLua

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

MiniLua for C#

A self-contained Lua interpreter written in C# with zero external dependencies. I built this for a Timberborn mod i'm working on. I originally tried using MoonSharp but kept running into DLL loading issues at runtime, so i just wrote my own.

This project is inspired by Expression 2, a lua scripting intrepertor for the Valve Source Game Engine. Famously found in the Wiremod mod for Garrysmod.

Super simple, (hopefully) drops straight into any C# project.


Why

I needed to embed a Lua runtime inside a Unity game mod. every DLL-based solution (MoonSharp etc) caused runtime errors. so instead of fighting the DLL loader i just wrote a basic one from scratch — lexer → parser → AST → evaluator.

if you're in a similar situation (Unity mod, game plugin, embedded scripting in a restricted environment) this might save you the same headache.


What it supports

  • variables — local and global
  • if / elseif / else
  • while, repeat / until
  • numeric for for i = 1, 10, 2 do
  • generic for pairs and ipairs
  • functions and closures
  • tables (array and hash style)
  • multiple return values
  • string concatenation with ..
  • varargs ...

stdlib included:

  • print, tostring, tonumber, type, pairs, ipairs, unpack, select, error, assert
  • math — floor, ceil, abs, sqrt, sin, cos, exp, log, max, min, pow, fmod, random, pi
  • string — len, sub, upper, lower, rep, reverse, find, format
  • table — insert, remove, concat, sort

Usage

var lua = new MiniLua(msg => Console.WriteLine(msg));
string output = lua.Execute(@"
    for i = 1, 5 do
        print('hello ' .. i)
    end
");

Timberborn script example:

-- List all linked buildings
print("Linked: " .. chip.count())
for i, alias in ipairs(chip.list()) do
    local b = chip.get(alias)
    if b ~= nil then
        print(i .. ". [" .. alias .. "] " .. b.name())
    end
end

-- Check water storage contents
local storage = chip.get("water storage")
if storage == nil then
    print("No building linked as 'water storage'")
else
    local contents = storage.getContents()
    if contents == nil then
        print("Could not read contents")
    else
        local water = contents["Water"] or 0
        print("Water: " .. water)
        if water > 50 then
            print("Status: GOOD")
        else
            print("Status: BAD")
        end
    end
end

Injecting globals (C# → Lua)

you can expose C# functions or values to Lua before running a script:

var lua = new MiniLua();

// inject a simple value
lua.SetGlobal("playerName", LuaValue.Of("Marvin"));

// inject a callable function
lua.SetGlobal("add", new LuaValue((LuaValue[] args) => {
    double a = args[0].NumVal;
    double b = args[1].NumVal;
    return new[] { LuaValue.Of(a + b) };
}));

// inject a table
var api = new LuaTable();
api.Set("greet", new LuaValue((LuaValue[] args) => {
    Console.WriteLine("hi " + args[0].StrVal);
    return new LuaValue[0];
}));
lua.SetGlobal("myApi", new LuaValue(api));

lua.Execute(@"
    print(playerName)
    print(add(3, 4))
    myApi.greet('world')
");

Error handling

try {
    lua.Execute(scriptCode);
} catch (LuaError ex) {
    Console.WriteLine("Lua error: " + ex.Message);
}

How it works


Lexer - tokenizes the source into a flat token stream. handles strings, numbers, operators, keywords, comments.

Parser - recursive descent parser. produces a typed AST. Statements (IfStmt, WhileStmt, ForStmt, AssignStmt, LocalStmt, FuncDefStmt, ReturnStmt etc); Expressions (BinOpExpr, UnOpExpr, FieldExpr, IndexExpr, CallExpr, MethodCallExpr etc).

Interpreter - 'Walking the tree' evaluator. ExecBlock walks statements, Eval evaluates expressions. control flow (return, break) uses exception signals (ReturnSignal, BreakSignal) to unwind the call stack cleanly.

LuaTable - backed by a List<LuaValue> for sequential integer keys (array part) and a Dictionary<string, LuaValue> for string keys (hash part). standard # length semantics.


Limitations

This is not a full Lua implementation. There are many things that are missing or not fully supported... For an in-game scripting interpretor it's more than enough. if you need the full language, I'd reccomending using MoonSharp as its probably much easier...


— marvinb16

About

Custom C# Mini Lua interpretor for a unity game mod (Timberborn).

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages