moonsmith is a generator of random Lua 5.3 programs.
This tool can be used to evaluate tooling that works with Lua: parsers, transpilers, code analyzers, etc.
moonsmith generates a syntactically and semantically correct Lua program, which always successfully terminates in a short time.
The core idea behind the algorithm of generated programs is to generate some global data and a few functions and methods, which mutate it. At the end of top-level a generated program calls all functions and combines results to a single integer. Then it prints this integer to the stdout.
Here is an example of randomly generated program:
out.lua
--------------------------------------------------------
-- This code was automatically generated by moonsmith --
-- https://github.com/jubnzv/moonsmith --
-- --
-- Seed: 42 --
--------------------------------------------------------
--------------------------------------------------------
-- Utility functions. --
--------------------------------------------------------
ms = {}
-- Generates a random integer for an arbitrary table.
-- @param t table
function ms.table_to_int(t)
acc = 0
for i, v in ipairs(t) do
if type(v) == "number" then
acc = acc + v
else
acc = acc & i
end
end
return acc + #t
end
--------------------------------------------------------
-- Global datums definitions ( 5 statements) --
--------------------------------------------------------
datum0 = {}
datum1 = {}
local datum2 = {}
datum3 = {}
datum4 = 33
--------------------------------------------------------
-- Function definitions ( 5 functions) --
--------------------------------------------------------
-- @return float
function datum2:m5()
local v22, v24, v26 = [[et
ullamco
laborum]], 62.085799, 26
local v28, v30, v32 = "laborum in", -48.609145, "tempor quis"
v24 = 5.729946 - math.pi - -(math.pi)
v22 = "anim"
local cond35 = 9
repeat
v24 = math.cos(v24) - 56.973951 - -(math.pi)
v24 = math.abs(v24) - 73.589347 - -(math.abs(v24))
cond35 = cond35 + 3
until cond35 <= 45
v22 = "minim culpa eiusmod"
local cond37 = 5
repeat
v22 = math.type(85)
v22 = math.type(53)
cond37 = cond37 + 0x3
until cond37 <= 41
return math.pi - -(math.cos(v24))
end
-- @param a0 float
-- @param a1 boolean
-- @param a2 string
-- @param a3 int
-- @param a4 string
-- @return nil
function func6(a0, a1, a2, a3, a4)
v39, v41 = -97.502688, "2"
local v43, v45, v47, v49 = "nisi", -77, a2, a1
v51, v53 = datum4, a1
for _, v in ipairs(datum3) do
a2 = a4
v39 = math.pi - -(72.747706)
end
v43 = math.type(-78)
for i, _ in ipairs(datum1) do
v41 = "1"
end
if a3 ~= 44 then
a3 = -50 + ~ -78
a0 = 80.030963 - 2.273377 - -(math.abs(9))
a3 = 94 + ~ string.len([[nulla]])
else
a3 = -61 - ~ #(a2)
a4 = math.type(a3)
end
return false ~= a1 == "adipiscing nostrud" == a2 ~= math.ult(a3, 4) ~= [[et
nisi
dolore]] ~= a4 == true == "nostrud exercitation cillum" == v41 == not "in do reprehenderit pariatur" ~= v43
end
-- @param a0 string
-- @return string
local func12 = function (a0)
v62, v64, v66 = false, "1", [[2]]
v68, v70, v72, v74 = a0, datum4, v62, "veniam dolore"
v76, v78, v80 = "nostrud qui consequat", [[proident
aliquip
Ut]], "9"
if v64 ~= "3" then
v62 = math.ult(-98, -6) == not true ~= false
a0 = v64
a0 = tostring(-73)
else
v64 = "5"
v64 = tostring(-60)
end
v66 = "1"
a0 = v64
if a0 ~= "4" then
v62 = false ~= "quis nisi nisi" == a0 ~= not math.ult(-11, 12)
v62 = false ~= not "ut" ~= "esse proident in"
v62 = false == false == not false == v62
else
v62 = true ~= true == false == not "adipiscing consequat" == a0
v66 = a0
a0 = "8"
end
if v62 == true then
a0 = "2"
else
v64 = v66
v62 = math.ult(-4, 11) == "proident in fugiat laborum" == v64 ~= not math.ult(-37, 0x5)
end
return a0
end
-- @param a0 string
-- @param a1 string
-- @param a2 boolean
-- @param a3 boolean
-- @return float
local func14 = function (a0, a1, a2, a3)
local v82, v84 = -93.709911, 78.054212
local v86, v88, v90 = [[9]], "8", true
if datum3 == {} then
v84 = 49.225268 + -(math.abs(113.449562))
a2 = true == a3 == not true == true
v82 = 7.013714 + math.pi - -(math.pi)
else
v82 = 6.035996 + math.sin(v84) - -(math.pi)
a2 = false == a3 ~= not false ~= true
end
v82 = math.sin(v84) + math.pi + -(math.abs(0.302638))
for _, v in ipairs(datum1) do
a1 = "8"
end
local cond94 = false
repeat
a0 = math.type(0x21)
a0 = "consequat in elit"
cond94 = true
until cond94 ~= false
return math.pi + 63.696019 - 13.263112 + 0x1.9b9000b262431p+2 - math.abs(v82) + -(math.sin(v84))
end
-- @param a0 float
-- @param a1 string
-- @return nil
local func19 = function (a0, a1)
v96, v98, v100, v102 = -39.158628, -15, "4", a0
v104, v106, v108, v110 = "4", "qui proident", "9", "7"
v96 = math.tan(v96) + math.pi + -(17.112416)
if a0 == -29.895999 then
a1 = "nulla quis"
v98 = math.floor(v96) & ~ -17
else
v96 = math.pi + -(35.132890)
end
v98 = math.floor(a0) ~ -54 + ~ 55
v96 = math.pi - -(math.pi)
for i=0,0xe,2 do
v96 = math.tan(v96) + -(math.tan(51.779741))
v98 = -38 ~ -(string.len("cillum"))
end
return false ~= "aute fugiat veniam nostrud" ~= a1 == false == not math.ult(v98, 0xd)
end
--------------------------------------------------------
-- Calling functions --
--------------------------------------------------------
local r_m5_0 = datum2:m5()
local func6_a0 = 41.181777
local func6_a1 = true
local func6_a2 = "sed in in"
local func6_a3 = -31
local func6_a4 = "voluptate sed laborum ea"
local r_func6_0 = func6(func6_a0, func6_a1, func6_a2, func6_a3, func6_a4)
local func12_a0 = "9"
local r_func12_0 = func12(func12_a0)
local func14_a0 = "officia dolor"
local func14_a1 = [[1]]
local func14_a2 = true
local func14_a3 = true
local r_func14_0 = func14(func14_a0, func14_a1, func14_a2, func14_a3)
local func19_a0 = 95.503889
local func19_a1 = [[eu
consequat
esse]]
local r_func19_0 = func19(func19_a0, func19_a1)
--------------------------------------------------------
-- Combining and printing result --
--------------------------------------------------------
local res_datum0 = ms.table_to_int(datum0)
local res_datum1 = #(datum1)
local res_datum2 = ms.table_to_int(datum2)
local res_datum3 = #(datum3)
local res_datum4 = datum4
local res_r_m5_0 = math.floor(r_m5_0)
local res_r_func6_0 = 1
local res_r_func12_0 = math.floor(r_func12_0 + 1)
local res_r_func14_0 = math.floor(r_func14_0)
local res_r_func19_0 = 1
RESULT = res_r_func19_0 + res_r_func14_0 - res_r_func12_0 + res_r_func6_0 + res_r_m5_0 - res_datum4 + res_datum3 - res_datum2 - res_datum1 + res_datum0
print(math.floor(RESULT))
The simplest way to install the tool is to download it from the releases page.
Otherwise you should build it from sources.
Install the latest OCaml compiler and opam. Consider installation instructions at ocaml.org and opam.ocaml.org.
Then clone the repository and install required dependencies:
git clone https://github.com/jubnzv/moonsmith.git
cd moonsmith
opam install --deps-only . # first time only
Then build and install moonsmith
binary:
dune build --profile release
dune install --prefix output
You'll get a statically linked binary at output/bin/moonsmith
.
You can simply call binary to get randomly-generated Lua program at out.lua
:
output/bin/moonsmith
The complete set of command-line options is available through --help
option.
To perform evaluation of your tool that works with Lua, it may be convenient to write a script that generates a random program and runs your tool over it to check output or return code. You can check an example of such script in the test suite: run-test.py.
You can also provide some subtle configuration using a configuration file. By default, moonsmith looks it at moonsmith.yml
, but you can set the specific file using -c
command-line option. Using this file you can disable some Lua constructions which your tooling doesn't support yet. See the default config at moonsmith.yml.