moonsmith icon indicating copy to clipboard operation
moonsmith copied to clipboard

A random generator of Lua programs

moonsmith

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.

Description

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))

Installation

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.

Usage

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.