Goodbye snake, I go to the moon
Lua in the sky
NASA is not the only one going to the moon — Lua (pronounced LOO-ah) meaning "Moon" in Portuguese, is a multiple-paradigm scripting language. Created in 1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes, members of the Computer Graphics Technology Group (Tecgraf) at the Pontifical Catholic University of Rio de Janeiro in Brazil.
Initially overlooked by me as "just another scripting language", it caught my eye when I was looking for an alternative to Python. Growing frustrated with the Snake for different reasons, I wanted a small and simple-to-use language. One of my criteria was that I could embed it into a project's build system, making setup and management easier, sparing the need for specific version installations, or handling local installed language environments with one of too many tools. Speed was another factor that was neglected far too often with script languages.
Lua ticked many boxes for me and grew on to become a favorite of mine, from small scripts to large tools; a language as a general toolset for my daily tasks, running really everywhere. In this article, I will give an overview of how and with what I personally use Lua; this is not an exhaustive guide. Nevertheless, I will publish a multipart series on Lua, going in-depth on many parts of the language; more on this at the end.
You may already have Lua installed on your system! You can check by running
lua -v in a terminal.
If this is not the case, there are different ways to get Lua; one way on macOS is using brew.
Having Lua installed, you can start a
LuaRocks can be seen as the de facto standard package manager for Lua. If not already installed on macOS, you can use brew to install LuaRocks.
Similar to NPM, I want to keep packages
local and inside the project rather than global or in my user
directory. A global installation is the default; using
--local will install packages in a folder in the user's home
I will install all packages inside a
lua_modules folder in the
root of a project, using the
--tree option of the LuaRocks
CLI. As an example, installing the
For Lua to find any local package on
require, the search paths
need to be set. This can be done by defining a setup module (that can have any
name) that will be loaded when executing a script through the
-l option of the Lua
Creating a script file …
… and running it with the
-l option will find the
inspect package from the local folder and execute the script.
This will now work for any package installed inside the local project via
luarocks install --tree=lua_modules <package>.
Setting up a project would not be complete without testing, lint tooling, and code formatting.
For unit tests, I use LuaUnit, installed via LuaRocks.
Tests are just Lua files importing LuaUnit. I define test files with the
.test.lua extension and use
TAP (Test Anything Protocol)
as the output format, an amazing output format I also use in
The test can then be executed via Lua, printing the result on the terminal.
To lint, I use LuaCheck, again installed via LuaRocks.
Using the default options, run it on a whole folder, for example, the
Auto formatting Lua code happens via LuaFormatter.
For the checker, I will use a small set of custom options, defined in a
.lua-format file (the format is YAML), in the
Running it on all files in a folder and formatting them.
C++, CMake, and Lua
Having established a base working with Lua, how about embedding it into other software? Lua is also known as an "extensible extension language", referring to it being created with the idea of having a scripting language that can extend and customize applications.
I will use
Sol2, a header-only C++ library providing Lua bindings, in combination with my
C++ base template
I created in my
basic C++ setup with CMake article. Having that set up, I first need to add Lua to the project, creating a new
vendor/lua/CMakeLists.txt to build Lua as it does not
come with CMake support.
This will set up Lua as a C project and create a library under the name
lua. Next, extending the dependency list in
Sol2 can be added as a dependency by creating a new
file, which is rather short as it comes with CMake support.
Again, adding it to the dependency list in
That's it for getting Lua and Sol2 into the project. Next up is adding the libraries to the application by linking them to the target.
Now that everything is ready, trying a small test program in
This will indeed print
Some value is now: 1. A different example,
a Lua script file with a simple function for demonstration.
With CMake, any non-build files need to be copied to the build output
directory. Using the
file function, this can be done on the CMake
The script file can then be loaded, extracting the defined Lua function
hello to call it from C++.
Running this will print
Result: Hello, Mr. Anderson.
This example project is also available on GitHub as embedding-lua-in-cpp-example repository. How to inject new functions and values into Lua, as well as many other things, can be found in the official Sol2 documentation.
What comes next
This just showed some basics of Lua and parts of how I use Lua personally, only scratching the surface. To extend this and shine an even brighter light on the Moon, I will create a series of articles covering many topics of Lua more in depth. Some of those will be:
- In-depth Project setup using LuaRocks
- All about dependencies and creating your own modules
- Debugging, and profiling
- CLI applications with Lua
- Scaling Lua applications and large Lua projects
- Building and extending C++ applications with embedded Lua
And maybe more, if I find more things to cover. Until then 👋🏻