Lua is a scripting language most famously known for powering Blizzard’s World of Warcraft but was designed to be a general-purpose means of building robust, efficient, and lightweight solutions. Lua was created in 1993 (30 years ago!) and has since been popular among developers across all ecosystems. Lua is an excellent choice for folks because it is fast, portable, embeddable, small, and Free under an MIT license.

If you’ve ever dreamed about building extensibility into your applications, Lua is an excellent choice, as you can allow consumers to build scripts that work within your .NET host application. In this post, we’ll walk through the straightforward steps to add Lua support to your .NET applications, how you might invoke Lua scripts, and how you might let Lua scripts call your .NET methods.

Getting Started with MoonSharp

In a .NET console application, start by installing the MoonSharp NuGet package.

dotnet add package MoonSharp

You can also add the following ItemGroup to your .NET project file.

<ItemGroup>  
  <PackageReference Include="MoonSharp" Version="2.0.0" />  
</ItemGroup>

After that, you should be ready to start writing your first Lua script.

As a bonus, if you’re using JetBrains Rider you’ll get Lua syntax highlighting with the language injections feature.

In your Program.cs file, add the following code.

using MoonSharp.Interpreter;  
  
// language=lua  
var lua =  
  """  
  print("Hello, Lua")  
  """;  
  
Script.DefaultOptions.DebugPrint = Console.WriteLine;  
Script.RunString(lua);

You should see the following output in your console when you run your Console application.

Hello, Lua

Setting the DebugPrint property is necessary to allow MoonSharp to redirect print output somewhere. We chose the console in this case, but it could be directed to a string, Stream, or whatever.

Invoke a Lua Function from C#

Doing a print call is fun, but we can be more practical. Let’s step it up by invoking a Lua add method that takes two integer arguments.

using MoonSharp.Interpreter;  
  
Script.DefaultOptions.DebugPrint = Console.WriteLine;  
  
// language=lua  
var lua =  
  """  
  function add(x, y)    
      return x + y  
  end  
  """;  
  
var script = new Script();  
// loads and executes script  
script.DoString(lua);  
// call our global method  
DynValue value = script.Call(script.Globals["add"], 1, 1);  
  
Console.WriteLine($"1 + 1 is {value.Number}");

Here, we load a new function of add that takes two number arguments. Remember, Lua is dynamically typed, so you won’t see any type declarations. We create a script variable to reuse the context of our script, which now holds a global add function. We can use the script variable from here to invoke our global function and the necessary arguments. The DynValue is a dynamic result, but there are helper properties you can use to type the result to a known .NET type. In this case, we’ll use the Number property to get an integer value. Executing our application, we get the console output.

1 + 1 is 2

What if we want a Lua script to call our .NET application?

Invoking a C# Method from a Lua Script

MoonSharp allows you to inject elements into the global script context. In this section, we’ll provide our Lua script with an add method and do the addition in the scope of a C# code block.

using MoonSharp.Interpreter;  
  
Script.DefaultOptions.DebugPrint = Console.WriteLine;  
  
// language=lua  
var lua =  
  """  
  local result = add(1,1)  
  local output = string.format("1 + 1 is %s", result)  
  print(output)  
  """;  
  
var script = new Script();  
  
int add(int x, int y)  
{  
  Console.WriteLine("Yep, I'm .NET baby!");  
  return x + y;  
}  
  
// load our C# method into global  
script.Globals[nameof(add)] = add;   
// loads and executes script  
script.DoString(lua);

We pass the C# local function of add as a Func<int,int,int> to Lua. This allows Lua scripts to invoke the global method. Running the code above returns the following result.

Yep, I'm .NET baby!
1 + 1 is 2

Awesome!

Conclusion

We’ve only scratched the surface of what MoonSharp has to offer with Lua, but with these three demonstrations, you can see that the interoperability possibilities are through the roof. Leveraging MoonSharp you can build extensibility into your applications using a battle-hardened scripting language.

If you want to learn more about MoonSharp, visit the official site to see more samples and to read the official documentation.

Thanks for reading and sharing my posts with friends and colleagues.