# Problem There are two parts to this homework assignment. The first part asks your team to begin developing a simple website for the programming language of your choice. The second part asks your team to continue developing its website, by adding a larger demonstration program, and sharing its content with the class, in an oral presentation. A webspace for your team has been established at: ``` csweb.boisestate.edu:/home/JBuffenb/public_html/classes/354/teams/team ``` ... The purpose of your team's space is to describe and demonstrate your team's programming language, to a beginning or intermediate programmer. Choose a language that no one on your team already knows well. Rather, choose a language that you and your teammates would like to learn about. For example, any of the homework languages would have been acceptable: they are important languages, which you may have heard of, but they were (hopefully) new to you. Consider choosing a language from the `sum` examples, in our `pub` directory. Choose a language for which you can obtain a translator. It does not have to run on the `onyx` cluster, but you will need to develop and demonstrate example programs. I can help build/install translators on the `onyx` cluster. Develop your own examples. Do not just copy examples from the Internet. You are expected to learn the language. Each team member should develop several original examples. ## (a) Meet with your team and choose a couple of candidate programming languages. ## (b) Send me an email with your proposed language. I will reply with an approval email, unless too many teams have already chosen that language. Repeat this step, until we have decided on a language. ## (c) Plan the structure of your site: For example: * Description and history. * Links to specification, documentation, manuals, and tutorials. * Available translators and installation instructions. * Introductory programs (e.g. hello world), with build / run instructions. * More complex example programs, with build / run instructions. * Tabular comparison of characteristics and features, with respect to other (representative) languages. ## (d) Build your site. # Process ... # Answer ## (a) ... ## (b) ... ## (c) ... ## (d) ### Home Lua is an open-source, fast, and lightweight scripting language designed to be embedded in software or hardware. Originally developed in 1993 at the Pontifical Catholic University of Rio de Janeiro, Lua has since become a popular choice for game development, embedded systems, and automation. Lua is an interpreted language. The official Lua interpreter is written in C and is available for most platforms, including Windows, MacOS, Linux, SunOS, Open/Free/Net/Dragonfly BSD, and more. ### History [(1)](https://www.lua.org/history.html) Lua was created at the Pontifical Catholic University of Rio de Janeiro in Brazil (called PUC-Rio for short), by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes of the Grupo de Tecnologia em Computação Gráfica (called TeCGraf for short). Lua was created out of necessity. There were import restrictions on software and other technology coming into Brazil at the time of Lua's development. These restrictions made it difficult for universities or companies in Brazil to use foreign software and tools. So, companies often approached TeCGraf for help in designing software tools. The first language that was created by TeCGraf was the Data Entry Language (DEL). It was designed for the engineers at a Brazilian oil company named PETROBRAS because they needed a better language for preparing input data files. DEL was a success, and soon users began asking for more features like Boolean expressions, control structures, and loops. They were working on another project for PETROBAS at the same time as DEL. It was an application called PGM. PGM was "a configurable report generator for lithology profiles." The end users of PGM were supposed to be able to highly customize the way reports were generated. To allow for this customization, they created a description language called Simple Object Language (SOL). Lua was the result of a combination of the data description capabilities of DEL and the control structures of SOL. This was also how Lua got its name. In Portuguese, *sol* means *sun*. So, they named the new language *lua*, meaning *moon*. Here are the programming languages that Lua inherited features from and what those features are: * C++ - Local variables. * CLU - Multiple assignment and multiple returns from function calls. * Lisp - The lack of a Boolean data type. `nil` represents false and any all other values (except `false` of course) represents true. * Modula - The syntax for a small set of control structures like `while`, `if`, and `repeat ... until`. * SOL - The syntax for record and list constructions. Here are some of the features that were innovated and the considerations that went into them: * Portability - PETROBRAS was state-owned and couldn't buy specific computers because of rules around spending public money. They had a wide variety of systems with different operating systems. Lua had to be portable to ensure it could run on any of their computers. * String concatenation - Since strings can be coerced into numbers, they created an operator specifically for string concatenation: the `..` operator. * Optional semicolons - Engineers with a FORTRAN background might have found semicolons confusing, while engineers with a C or Pascal background might have found the lack of semicolons confusing. So, semicolons were made optional. * Extension language - Lua was always meant to be an *extension* language. C programs can easily register their functions to be called from Lua. You can see a demonstration of this with the [[Part 1 Website#^740efa|Fork (Calling C from Lua)]] example. According to the version history on Lua's official website, the first version of the language was developed sometime in 1993. But, it wasn't released to the public. The first stable, public version of Lua was version 1.1, released on July 8th, 1994. Right now, the current version of Lua is version 5.4.7. ### Resources #### Official Website The official Lua website can be found at [lua.org](https://lua.org/). #### Documentation * [Lua Documentation](https://www.lua.org/docs.html) - This page has links to official documentation. These include reference manuals, books, and papers on Lua. * [Lua 5.4 Reference Manual](https://www.lua.org/manual/5.4/) - This is a reference manual and the official definition of Lua. * [Frequently Asked Questions](https://www.lua.org/faq.html) - Some common questions you have about Lua might be answered here. * [The Evolution of an Extension Language: A History of Lua by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes](https://www.lua.org/history.html) - This is a paper which talks about the history of Lua, from its inception to the version released at the time of the paper (which was Lua 4.0 in the year 2000). It talks about important features of Lua and why they were implemented. #### Tutorials The book [Programming in Lua](https://www.lua.org/pil/1.html) is a great place to learn Lua by following along with their example programs. #### Other Projects ##### LuaJIT There's a Just-In-Time compiler for Lua called [LuaJIT](https://luajit.org/) which is much faster at executing Lua scripts than the official interpreter. ##### Embedded Lua (MicroLua) There are also many projects which package a version of the Lua interpreter to make it suitable for use on a microcontroller. For example, [MicroLua](https://github.com/MicroLua/MicroLua) is a project that takes an unmodified version of the Lua interpreter and adds bindings for the Pico SDK, a C library for developing programs for the RP2040 microcontroller. The RP2040 microcontroller is used on several boards including the Raspberry Pi Pico. ##### LuaRocks There's a package manager for Lua called LuaRocks, though it is not required. #### Other Papers * [Scriptable Operating Systems with Lua by Lourival Vieira Neto, Roberto Ierusalimschy, Ana Lúcia de Moura, and Marc Balmer](https://netbsd.org/~lneto/dls14.pdf) - This is a paper from two NetBSD developers and two members of the Computer Science Department of PUC-Rio (the same university where Lua was created). This paper talks about the concept of "extensible operating systems," which are operating systems that provide extensibility through a kernel-scripting environment. Specifically, an implementation of an environment that allows a user to extend the Linux or NetBSD operating systems with Lua. #### Installation ^a0de0f There are two ways you can install Lua which work regardless of what operating system you may be using: 1. You can always build Lua from source and install it manually, provided that you have a C compiler. The GNU Readline library is required if you want to be able to use the interactive Lua interpreter. 2. You can download a binary release from the website [LuaBinaries](https://luabinaries.sourceforge.net/download.html). It's an unofficial website which compiles the Lua interpreter and packages it for easy installation. They have archives for Windows, MacOS, and Linux which include the necessary executable, library, and include files. If you use a Linux distribution, it's recommended to download Lua through your package manager. ##### Source [(1)](https://lua.org/download.html) If you want to build Lua from source, you can download the source code and compile it. ```bash $ curl -L -R -O https://www.lua.org/ftp/lua-5.4.7.tar.gz $ tar zxf lua-5.4.7.tar.gz $ cd lua-5.4.7 $ make all test ``` ##### Linux The package manager of a Linux system changes between different distributions. Below are the commands to install Lua using the most common package managers. Follow the method appropriate for your system. ###### Apt (*Debian, Deepin, Linux Mint, Peppermint OS, Ubuntu, etc.*) ```bash $ sudo apt update $ sudo apt install lua5.4 ``` ###### DNF (*Fedora Linux*) ```bash $ sudo dnf install lua ``` ###### Pacman (*Arch Linux, Endeavor OS, Manjaro, etc.*) [(1)](https://wiki.archlinux.org/title/Lua) ```bash $ sudo pacman -S lua ``` ###### Portage (*Gentoo*) [(1)](https://wiki.gentoo.org/wiki/Lua) ```bash $ sudo echo "dev-lang/lua readline" > /etc/portage/package.use/lua $ sudo emerge -av dev-lang/lua app-eselect/eselect-lua ``` ###### Yum (*AlmaLinux, CentOS, RHEL, Rocky Linux*) ```bash $ sudo yum install epel-release lua ``` ##### MacOS ###### Homebrew ```bash $ brew update $ brew install lua ``` ### Examples To run any of the examples, you need to have Lua installed on your system. You can refer to the [[Part 1 Website#^a0de0f|Installation]] section of this website. In the case of the [[Part 1 Website#^740efa|Fork (Calling C from Lua)]] example, you need a C compiler, like the GNU C Compiler (GCC) and a UNIX-like operating system. If you want to run the example on another operating system like Windows or MacOS, then my challenge to you is to adjust the example to make it work for your system. In the case of the [[Part 1 Website#^2eaa24|Blinking LED (Embedded Lua)]] example, you need a cross compiler that can compile binaries for ARM architectures (`arm-none-eabi`). #### Input/Output Lua has very simple functions for receiving input and writing output. ##### Simple Output The function to print to `stdout` is built in. There's no need to import any external modules like in C or Java. ###### Code ```lua -- simple-output.lua -- No other imports. print("Hello, world!") --[[ Expected output: Hello, world! ]] ``` ###### Running ```bash $ lua simple-output.lua ``` ##### Formatted Output The `string.format()` function can be used to create formatted strings from other data types. ###### Code ```lua -- formatted-output.lua local number = 15 -- %X displays numbers in hexadecimal format. print(string.format("%X", number)) --[[ Expected output: F ]] ``` ###### Running ```bash $ lua formatted-output.lua ``` #### Loops ##### Numeric Loops The syntax of numeric loops is very simple. ###### Code ```lua -- numeric-loops.lua -- The third 1 is the step, or by how much i changes each iteration. for i = 1, 5, 1 do print("Iteration:", i) end --[[ Expected output: Iteration: 1 Iteration: 2 Iteration: 3 Iteration: 4 Iteration: 5 ]] ``` ###### Running ```bash $ lua numeric-loops.lua ``` ##### Generic Loops The other use for loops in Lua is iterating over data. Again, the syntax for doing so is very simple. This is also where Lua's most versatile data structure can be introduced: tables. ###### Code ```lua --[[ Arrays in Lua are really just tables with key-value pairs. The keys and the values can be of different data types. By convention, keys start at 1 and increment by one (unlike other languages which typically start at index 0). Another language with an array data type that behaves in almost the exact same way as Lua is PHP, so if you're familiar with PHP then this will make ]] a = {"a", "b", "c"} -- Equivalent to {[1] = "a", [2] = "b", [3] = "c"} --[[ Here, the ipairs() function is a stateless iterator. Iterators are typically implemented as closures in Lua. You can read all about stateless iterators like ipairs() here: https://www.lua.org/pil/7.3.html. ]] for key, value in ipairs(a) do print(key, value) end --[[ Expected output: 1 a 2 b 3 c ]] ``` ###### Running ```bash $ lua generic-loops.lua ``` #### Functions Functions are first-class in Lua, meaning they can be stored in variables, passed to functions, and returned from functions like any other variable. ##### Simple Function The most basic usage of a function is, of course, defining it and calling it. ###### Code ```lua -- A simple greet function. function greet (name) -- Notice how the operator to concatenate strings is .. print("Hello, " .. name .. "!") end greet("Saxton") --[[ Expected output: Hello, Saxton! ]] ``` ###### Running ```bash $ lua simple-function.lua ``` ##### Custom Iterator This is where the real power of Lua's functions is shown. This is an example of a custom iterator being implemented as a closure. This iterator can be used in generic loops. ###### Code ```lua -- custom-iterator.lua -- This is an array-like table of three numbers. a = {1, 2, 3} --[[ A stateless iterator which iterates over an array backwards. Iterators return three things: 1. The actual function which iterates through an array. 2. The invariant state (what doesn't change from iteration to iteration). 3. The initial value of our control variable. ]] function rpairs (a) -- A closure is used to get the next value of the array. return function (_a, i) i = i - 1 local v = _a[i] if v then return i, v end end, a, #a + 1 end -- A generic loop uses the rpairs() iterator to iterate through the table in reverse order. for _, value in rpairs(a) do print(value) end --[[ Expected output: 3 2 1 ]] ``` ###### Running ```bash $ lua custom-iterator.lua ``` ##### Variable Arguments 1 Functions can be defined to take a variable number of arguments. You refer to that list of arguments with `...`. ###### Code ```lua -- variable-arguments-1.lua -- Add up a variable list of arguments (numbers). function sum (...) local total = 0 -- Create a table using the list of arguments and iterate through it. for _, v in ipairs({...}) do total = total + v end return total end print(sum(1, 2, 3)) --[[ Expected output: 6 ]] ``` ###### Running ```bash $ lua variable-arguments-1.lua ``` ##### Variable Arguments 2 If you want to get a specific argument at a certain position, you can use the built-in `select()` function. ###### Code ```lua -- variable-arguments-2.lua -- Get the third argument passed to the function. function third (...) -- # can be used to get the number of arguments passed. if select("#", ...) >= 3 then return select(3, ...) else return nil end end print(third("a", "b", "c", "d")) --[[ Expected output: c ]] ``` ###### Running ```bash $ lua variable-arguments-2.lua ``` ##### "Overloaded" Function Lua technically doesn't support overloaded functions. However, you can still call methods even if you don't use the same number of arguments than what the function is defined with. Any missing arguments are set to `nil`, which can be checked in the function body with conditional logic. So, you can emulate them. ###### Code ```lua -- overloaded-function.lua -- Revisiting the greet function from before. function greet (name, time) -- Only false and nil are falsy. 0 and empty strings are considered true. if name and time then print("Good " .. time .. ", " .. name .. "!") elseif name then print("Hello, " .. name .. "!") else print("Hey there!") end end greet() greet("Alice") greet("Alice", "morning") --[[ Expected output: Hey there! Hello, Alice! Good morning, Alice! ]] ``` ###### Running ```bash $ lua overloaded-function.lua ``` #### Object-Oriented Programming Lua is sort of a mix between C, Go, and Java when it comes to classes. There's technically no class data structure in the language, just like in C or Go. But, there are still features in the language which help in doing object-oriented programming. ##### Simple Class This is an example of a "class" in Lua which is a translated version of the Person "class" from the Go example. A very important feature of Lua is introduced with this example: metatables. ###### Code ```lua -- simple-class.lua -- Define a new table for a Person data type. Person = {} -- Set up the inheritance for instances of Person. Person.__index = Person -- Constructor for a new Person. function Person:new (name, age) local obj = setmetatable({}, Person) obj.name = name obj.age = age return obj end --[[ Getter for the name attribute. This syntax for defining a function is equivalent to this: Person["name"] = function (Person) ... end The table itself is passed as a hidden parameter to the function. ]] function Person:get_name () return self.name end -- Getter for the age attribute. function Person:get_age () return self.age end -- Custom function for representing our "class" as a string. function Person:__tostring () return self.name .. " " .. self.age end -- Create a new Person. p = Person:new("Ana", 20) print(p:get_name()) print(p:get_age()) print(p) --[[ Expected output: Ana 20 Ana 20 ]] ``` ###### Running ```bash $ lua simple-class.lua ``` ###### A Short Explanation of Metatables There are two lines which make this sort of class inheritance possible: 1. `Person.__index = Person`. 2. `local obj = setmetatable({}, Person)`. The second line creates an empty table (`{}`) with its metatable set to `Person`. By default, newly created tables don't have a metatable. What are metatables? Metatables are just tables that hold regular fields (called metafields) like `__add`, `__index`, and `__tostring`. These fields are usually assigned functions (called metamethods). The `__index` metafield can also be assigned another table or a value that also has an `__index` metafield. When you perform an operation like addition (`a + b`), and one of the operands is a table or userdata (either `a` or `b`), the Lua interpreter will check if that table has a metatable set (if both are tables, it checks the left one first and the right one second). If it does have a metatable set, it will check if that metatable has the `__add` metafield and if it has a function assigned to it. If it does, the interpreter will pass both operands (`a` and `b`) to the function and the return value will become the final result of the operation. If you're trying to retrieve a value from a table with a key, but the table doesn't have it, the Lua interpreter checks if that table has a metatable set. If it does have a metatable set, it will check if that metatable has the `__index` metafield. If it does, then the interpreter behaves differently depending on what is assigned to it. If the `__index` metafield has been assigned ... * A table that doesn't have an `__index` metafield - The interpreter will check that table for the key being searched and return it. `nil` is returned otherwise. * A table that also has an `__index` metafield (a metatable) - The interpreter uses that table as a new metatable and recursively begins the process of searching again. * A function (metamethod) - The interpreter will call it and pass the instance of the original table and the key being searched as arguments. This brings us back to the first line of code. By assigning the `__index` metafield of the `Person` table to itself, we're allowing any tables which have `Person` set as their metatable to access the fields (and their associated values or functions) of `Person`. #### Complex Examples These examples take the concepts demonstrated in the previous examples and combine them into more complex programs. ##### Hexdump This is a basic hexdump program. It takes the contents of a file through `stdin` and prints the hexadecimal representation of it to the terminal. ###### Code ```lua -- hexdump.lua -- This is the custom stateless iterator from before. function rpairs (a) return function (_a, i) i = i - 1 local v = _a[i] if v then return i, v end end, a, #a + 1 end -- Get the content from stdin. local stdin = io.read("*all") -- Specify the number of columns and the group size. local columns = 8 local group_size = 2 -- If the group size is even, we need to make the ending offset odd. local ending_offset = 0 if group_size % 2 == 0 then ending_offset = 1 end -- Loop through the file group_size characters at a time. for i = group_size, #stdin + ending_offset, group_size do -- Print the current position within the file. if (i - group_size) % (group_size * columns) == 0 then if (i - group_size) > group_size then print("") end io.write(string.format("%07X ", i - group_size)) end -- Get the current character and previous character at the current position. local characters = {} for j = 1, group_size do local character_position = i - group_size + j local character = string.byte(stdin:sub(character_position, character_position)) if character then characters[j] = character else characters[j] = 0 end end -- Print the characters as a hexadecimal group. for _, character in rpairs(characters) do io.write(string.format("%02X", character)) end io.write(" ") end ``` ###### Running If you have the actual hexdump program installed on your system, you can verify its accuracy by running and comparing the output of both. ```bash $ hexdump hexdump.lua 0000000 2d0a 202d 6568 6478 6d75 2e70 756c 0a61 0000010 2d0a 202d 6854 7369 6920 2073 6874 2065 ... $ cat hexdump.lua | lua hexdump.lua 0000000 2D0A 202D 6568 6478 6D75 2E70 756C 0A61 0000010 2D0A 202D 6854 7369 6920 2073 6874 2065 ... ``` ##### Fork (Calling C from Lua) ^740efa To write C code that can be interacted with from Lua, all you need to do is include the Lua header files in your C program, include the necessary functions in your C code, and compile it as a shared library. You can then import that shared library in your Lua script using `require` as if it was written in Lua. In this example, there's a C function named `lua_fork` which wraps around the `fork()` function of the POSIX operating system API (`unistd.h`). Calling `fork()` duplicates the current process and makes it a child process. The value of `fork()` is different for the parent process and the child process, allowing you to write code that only executes in the parent and / or the child. The `fork()` function isn't available in Lua because: * Lua is meant to be portable - Low-level functions that aren't available on all operating systems, like `fork()`, make code less portable. * Lua is meant to be extended - For any primitives or low-level functionality you need, the creators of Lua expected you to use C. ###### Code ```bash $ ls Makefile example.lua lua_fork.c ``` ```makefile # Makefile # Build configuration. Replace with values relevant to your system. CC = gcc CFLAGS = -O2 -fPIC -shared INCLUDES = /usr/include/lua5.4 LUA = lua MODULE_NAME = lua_fork SCRIPT_NAME = example .PHONY: run clean # Run the Lua script. Compile the shared object file if necessary. run: build $(LUA) $(SCRIPT_NAME).lua # Compile any required files. build: $(MODULE_NAME).so # Build the shared library object file. $(MODULE_NAME).so: $(MODULE_NAME).c $(CC) $(CFLAGS) -I$(INCLUDES) -o $@ lt; # Clean object files. clean: rm -f $(MODULE_NAME).so ``` ```lua -- example.lua -- Import the C module. local lua_fork = require("lua_fork") -- Run the lua_fork() method. local pid = lua_fork.lua_fork(); -- If the pid is 0, that means the code being executed is within the child. if pid == 0 then print("This is the child process.") -- Otherwise, the pid is that of the parent. else print("This is the parent process. The child PID is", pid) end ``` ```c /* lua_fork.c */ /* Necessary includes. */ #include <lauxlib.h> #include <lua.h> #include <lualib.h> #include <sys/types.h> #include <unistd.h> /* Function prototypes. */ int lua_fork(lua_State *L); int luaopen_lua_fork(lua_State *L); /* Calls the fork() subroutine from the POSIX operating system API. This function isn't available to Lua because it's a low-level operating system call. One of the purposes for allowing modules to be written in C is so that these types of functions can be made accessible. */ int lua_fork(lua_State *L) { /* fork() returns a pid_t type. According to the GNU libc manual, pid_t is just an int data type. */ pid_t pid = (int)fork(); /* If the pid is less than 0, it failed. */ if (pid < 0) return luaL_error(L, "fork() failed."); /* The pid is returned via the lua_State. */ lua_pushnumber(L, pid); /* The number of results we're giving back to Lua. */ return 1; } /* In order for Lua to recognize the entrypoint of the module, at least one function must be named according to this format: luaopen_<module name>. In the Lua program, you can import the module by writing require(<module name>). */ int luaopen_lua_fork(lua_State *L) { /* Create an array of functions which should be accessible from Lua. */ luaL_Reg functions[] = { {"lua_fork", lua_fork}, {NULL, NULL} }; /* Add those functions to the Lua state so they're accessible. */ #if LUA_VERSION_NUM == 501 luaL_register(L, "lua_fork", functions); #elif LUA_VERSION_NUM >= 502 luaL_newlib(L, functions); #endif /* The number of results we're giving back to Lua. */ return 1; } ``` ###### Running ```bash $ make This is the parent process. The child PID is XXXXX.0 This is the child process. ``` ##### Blinking LED (Embedded Lua) ^2eaa24 To demonstrate how Lua can be used with embedded systems and microcontrollers, I will use MicroLua to create a small demonstration program which blinks the onboard LED of the Raspberry Pi Pico. Here is what you need to run this example: * The build system used by MicroLua is CMake, so you have to make sure it's installed on your system. MicroLua uses CMake because the Pico SDK uses it. * You need a C/C++ cross-compiler which can compile to ARM targets (`arm-none-eabi`). The instructions for doing both will depend on your system. * You also need to clone the MicroLua repository from GitHub, save it somewhere on your system, and add an environment variable named `MLUA_PATH` which points to it. ###### Code ```bash # This is how you would install the dependencies on Gentoo, which is the Linux distribution I use. $ sudo emerge -av dev-build/cmake sys-devel/crossdev app-eselect/eselect-repository $ sudo eselect repository create crossdev $ sudo crossdev --stable --target arm-none-eabi -s4 # Once you install the required programs, you can clone MicroLua's repository. I prefer to save source code in my ~/.local/src directory, however you can save it wherever you want. $ cd ~/.local/src/ $ git clone https://github.com/MicroLua/MicroLua # Add the environment variable to your shell's rc file. The following commands work if you're using Bash. $ echo 'export MLUA_PATH=$HOME/.local/src/MicroLua' >> ~/.bashrc $ source ~/.bashrc # Create a directory named blink_example to store the example files. $ cd ~/projects $ mkdir blink_example ``` For all of the files provided below, you need to save them to the `blink_example` folder. ```bash $ cd blink_example # Save example files to the directory. $ ls CMakeLists.txt blink ``` Copy the `mlua_import.cmake` file from the MicroLua repository into this `blink_example` folder. That file tells CMake where to find MicroLua on our system when it comes time to build the project. ```cmake # CMakeLists.txt # Ensure we meet the minimum required version of CMake. cmake_minimum_required(VERSION 3.24) # This is how our project knows where MicroLua is. include(mlua_import.cmake) # Define our project name, the languages being used, and their versions. project(MicroLua_examples C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) # Calls an initialization macro from MicroLua's build system. mlua_init() macro(check_vars) foreach(var ${ARGN}) if("${${var}}" STREQUAL "") file(RELATIVE_PATH rel "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") message("Skipping ${rel}: ${var} isn't set") return() endif() endforeach() endmacro() # Preprocessor definitions. add_compile_definitions( MLUA_MAIN_SHUTDOWN=1 MLUA_MAIN_TRACEBACK=1 PICO_ENTER_USB_BOOT_ON_EXIT=1 PICO_STACK_SIZE=0x1000 PICO_USE_STACK_GUARDS=1 ) # Add the CMakeLists.txt file in the blink directory. add_subdirectory(blink) ``` ```bash $ ls blink CMakeLists.txt blink.lua ``` ```cmake # blink/CMakeLists.txt # Create a new module based on the blink.lua file. mlua_add_lua_modules(mod_blink blink.lua) # Ensure the required MicroLua libraries are included. target_link_libraries(mod_blink INTERFACE mlua_mod_hardware.gpio mlua_mod_mlua.time mlua_mod_pico ) # Package the program into an executable named blink. mlua_add_executable(blink) # Set the main module as the blink executable. target_compile_definitions(blink PRIVATE MLUA_MAIN_MODULE=blink ) # Ensure the required libraries are linked. target_link_libraries(blink PRIVATE mod_blink pico_stdlib ) ``` ```lua -- blink/blink.lua -- Necessary includes. local gpio = require("hardware.gpio") local time = require("mlua.time") local pico = require("pico") -- The pins we're using for our circuit. local ONBOARD_LED_PIN = 25 -- The main method for the program. function main () -- Initialize the pin of the onboard LED to be an output. gpio.init(ONBOARD_LED_PIN) gpio.set_dir(ONBOARD_LED_PIN, gpio.OUT) -- Endless loop. while true do -- Turns on the pin, waits, turns off the pin, and then waits. gpio.put(ONBOARD_LED_PIN, 1) time.sleep_for(500 * time.msec) gpio.put(ONBOARD_LED_PIN, 0) time.sleep_for(500 * time.msec) end end ``` ###### Running To compile this program as a binary executable that can be flashed to the Raspberry Pi Pico, run the following build commands: ```bash $ cmake -S . -B build -DPICO_BOARD=pico $ make -j9 -C build ``` The program should now be compiled assuming there aren't any errors. Hold down the BOOTSEL button on the Raspberry Pi Pico and plug it into your computer. This makes the Raspberry Pi Pico appear as a flash drive, making it easy to copy the firmware to it. For this example, you need to copy the `build/blink.uf2` file to the Raspberry Pi Pico. Once the file is copied successfully, the onboard LED should start blinking. ### Real-World Uses These are examples of real-world applications or frameworks which use Lua. #### AwesomeWM [(1)](https://github.com/awesomeWM/awesome) AwesomeWM is a popular window manager for the X Window System. It's written in C, however the configuration files are written entirely in Lua. You can write custom themes, widgets, monitors, docking stations, and more without needing to alter the core C code of the program. If you're looking for a good window manager to try out, give AwesomeWM a try. [Here](https://github.com/awesomeWM/awesome/issues/1395) are some screenshots to illustrate how customizable it is. ##### Example [(1)](https://github.com/setkeh/Awesome/blob/master/rc.lua) Here are the top-most lines of an `rc.lua` file, which is the main configuration file of AwesomeWM. Notice how much of the functionality is split into different Lua modules and the file itself defines how those modules interact with one another. ```lua pcall(require, "luarocks.loader") -- Standard awesome library local gears = require("gears") local awful = require("awful") require("awful.autofocus") -- Widget and layout library local wibox = require("wibox") -- Theme handling library local beautiful = require("beautiful") -- Notification library local naughty = require("naughty") local menubar = require("menubar") local hotkeys_popup = require("awful.hotkeys_popup") -- Enable hotkeys help widget for VIM and other apps -- when client with a matching name is opened: require("awful.hotkeys_popup.keys") -- {{{ Error handling -- Check if awesome encountered an error during startup and fell back to -- another config (This code will only ever execute for the fallback config) if awesome.startup_errors then naughty.notify({ preset = naughty.config.presets.critical, title = "Oops, there were errors during startup!", text = awesome.startup_errors }) end ``` #### Garry's Mod Garry's Mod is a sandbox game based on the Source Engine. The Source Engine was developed by the gaming company Valve and first released in 2004 with Half-Life 2 and Counter Strike: Source. To make the Source Engine easily extendable / extensible, Garry's Mod introduced a version of Lua called GLua. Using GLua, mod developers can directly interact with the engine to create custom game modes, GUI systems, models, entities, vehicles, server admin tools, and more. ##### Example [(1)](https://nodecraft.com/support/games/gmod/glua-101-an-introduction-to-garrys-mod-coding#h-dissecting-our-first-script-fbf00b27b7) This is the initialization function for a custom entity called a money printer. Money printers are one of the first things you learn to program when you create mods for Garry's Mod. They're used in a lot of custom game modes, but aren't included in the base game. ```lua function ENT:Initialize() self:SetModel("models/props_c17/consolebox01a.mdl") self:PhysicsInit(SOLID_VPHYSICS) self:SetMoveType(MOVETYPE_VPHYSICS) self:SetSolid(SOLID_VPHYSICS) local phys = self:GetPhysicsObject() if phys:IsValid() then phys:Wake() end self.sparking = false self.damage = 100 self.IsMoneyPrinter = true timer.Simple(27, PrintMore, self) end ``` #### LÖVE LÖVE is a framework for making 2D games entirely in Lua. It provides functions for drawing graphics, playing audio, creating game logic, and receiving user input. It's been used to make popular games like Balatro, Arco, and Gravity Circuit. ##### Example Here is a very simple program that opens a window and draws a red circle under the mouse's current position. ```lua function love.load() love.window.setTitle("Follow the Mouse") end function love.update(dt) -- ... end function love.draw() local x, y = love.mouse.getPosition() love.graphics.setColor(1, 0, 0) love.graphics.circle("fill", x, y, 30) end ``` #### NetBSD [(1)](https://www.netbsd.org/gallery/presentations/mbalmer/fosdem2012/kernel_mode_lua.pdf) NetBSD, a Unix-like operating system, has a version of Lua embedded in its kernel (in the form of a kernel module). This allows for: * Prototyping software in kernel space, specifically in driver development, without needing to recompile the source code of the kernel. * Modifying system behavior. * Configuring kernel subsystems. #### OpenResty NGINX is the most popular web server and reverse proxy in terms of market share. OpenResty is a project that takes NGINX and adds several enhancements to its core, adds an enhanced version of LuaJIT, and adds several Lua libraries. Lua can be used in the request handling flow to dynamically handle requests, create custom authentication checks, modify headers and cookies, rewrite URLs or redirect traffic, and more. ##### Example This is an example server block which defines a single rule for requests whose URI matches `/time`. The server will respond to those requests by sending back `Current time:` followed by the system's time as a formatted string. ```nginx server { location /time { content_by_lua_block { ngx.say("Current time: ", os.date()) } } } ``` ### Comparison ...