We provide a formal semantics for a large subset of the Lua programming language, in its version 5.2. The semantics is a major part of an ongoing effort to construct reliable tools to analyze Lua code. In this work, we present the details of several key aspects of the language, like the semantics of its only structured data-type (tables), its meta-programming mechanism (metatables), error handling, and how these mechanisms are used to define a complex dynamic semantics that must deal with several possible erroneous situations during run time, given the nature of the language. The semantics is mechanized in Redex, a DSL specially designed to specify and debug operational semantics. We validated the mechanization in two ways: first, by executing within Redex the test suite of the reference interpreter of Lua, and second, by specifying and performing random testing of its fundamental properties using the redex-check tool. Together, they evidence that our model soundly captures the semantics of the selected fragment of the language. Additionally, we address some of the performance problems that typically arise when testing a mechanization in Redex, by using a simple implementation of a reachability-based garbage collector that captures key aspects of Lua’s. By collecting syntactic garbage, we reduce the size of configurations during run time. Finally, we briefly discuss this avenue of development of our semantics, together with the implementation of a prototype tool to perform static analysis of Lua programs.
Our definition enforces left-to-right evaluation of expressions. Even if this is left unspecified in Lua’s reference manual, that is how expressions are evaluated in the two most popular implementations of Lua, the reference interpreter and LuaJIT (luajit.org).
This distinction helped simplify the model, as we do not need to define new notions of evaluation contexts to distinguish among either case—a task that proved to be cumbersome and difficult to maintain through the evolution of the model.
The language of patterns from Redex is expressive enough to be able to impose, for example, the expected representation invariant for a term that describes a finite functional mapping.
While we do model tail calls, the actual testing of the mechanism in the official test suite consists in performing several recursive calls, beyond the stack limit, to actually test if the mechanism is being used. Given that performing such amount of recursive calls is not feasible within our mechanization (because of the time required), we do not include such tests.
On a system running Arch Linux updated to May, 2022, Racket v8.3, and 12 parallel processes on an Intel Core i7-10870H CPU @ 4GHz \(\times \) 8, with 16GB of RAM.
Note about numeric representations: The official interpreter follows the IEEE 754 standard, while Racket has a richer set of numbers and behaviors, even including complex numbers. Therefore, it is required to convert to the required representation of numbers in order to correctly emulate the behavior of Lua.
To obtain these measures we added custom Racket code to our module Tests/RandomTesting/soundness/soundness_rand_test.rkt. We do not include it in the provided source code, tough the code reduces to counting well-formed configurations and recognizing the biggest configurations generated.