Lua Compiler In Lua

lua-users home
wiki

The standard Lua compiler [luac.c] is a C program included with the Lua distribution.

Lua compiler in Lua

A Lua compiler that compiles Lua sourcecode to Lua bytecode could be used to bootstrap and run Lua in different environments. You only need a Lua VM that executed Lua bytecode and the Lua compiler in Lua to run any Lua program in the new environment.

Use the Lua interpreter as compiler

It is possible to compile a Lua script using only the standard interpreter:

lua -e 'io.write(string.dump(assert(loadfile())))' <sample.lua >sample.out

A basic version of this is also included in the Lua 5.1 distribution (test/luac.lua).

If you want to emulate the behavior of luac when given multiple input files:

-- http://lua-users.org/wiki/LuaCompilerInLua

-- compile the input file(s)
local chunk = {}
for _, file in ipairs(arg) do
  chunk[#chunk + 1] = assert(loadfile(file))
end

if #chunk == 1 then
  chunk = chunk[1]
else
  -- combine multiple input files into a single chunk
  for i, func in ipairs(chunk) do
    chunk[i] = ("%sloadstring%q(...);"):format(
      i==#chunk and "return " or " ",
      string.dump(func))
  end
  chunk = assert(loadstring(table.concat(chunk)))
end

local out = assert(io.open("luac.lua.out", "wb"))
out:write(string.dump(chunk))
out:close()

Here's a more complete version that seeks to emulate the behavior of luac (luac.c) as much as reasonably possible:

-- luac.lua - partial reimplementation of luac in Lua.
-- http://lua-users.org/wiki/LuaCompilerInLua
-- David Manura et al.
-- Licensed under the same terms as Lua (MIT license).

local outfile = 'luac.out'

-- Parse options.
local chunks = {}
local allowoptions = true
local iserror = false
local parseonly = false
while arg[1] do
  if     allowoptions and arg[1] == '-' then
    chunks[#chunks + 1] = arg[1]
    allowoptions = false
  elseif allowoptions and arg[1] == '-l' then
    io.stderr:write('-l option not implemented\n')
    iserror = true
  elseif allowoptions and arg[1] == '-o' then
    outfile = assert(arg[2], '-o needs argument')
    table.remove(arg, 1)
  elseif allowoptions and arg[1] == '-p' then
    parseonly = true
  elseif allowoptions and arg[1] == '-s' then
    io.stderr:write("-s option ignored\n")
  elseif allowoptions and arg[1] == '-v' then
    io.stdout:write(_VERSION .. " Copyright (C) 1994-2008 Lua.org, PUC-Rio\n")
  elseif allowoptions and arg[1] == '--' then
    allowoptions = false
  elseif allowoptions and arg[1]:sub(1,1) == '-' then
    io.stderr:write("luac: unrecognized option '" .. arg[1] .. "'\n")
    iserror = true
    break
  else
    chunks[#chunks + 1] = arg[1]
  end
  table.remove(arg, 1)
end
if #chunks == 0 then
  io.stderr:write("luac: no input files given\n")
  iserror = true
end

if iserror then
  io.stdout:write[[
usage: luac [options] [filenames].
Available options are:
  -        process stdin
  -l       list
  -o name  output to file 'name' (default is "luac.out")
  -p       parse only
  -s       strip debug information
  -v       show version information
  --       stop handling options
]]
  os.exit(1)
end

-- Load/compile chunks.
for i,filename in ipairs(chunks) do
  chunks[i] = assert(loadfile(filename ~= '-' and filename or nil))
end

if parseonly then
  os.exit(0)
end

-- Combine chunks.
if #chunks == 1 then
  chunks = chunks[1]
else
  -- Note: the reliance on loadstring is possibly not ideal,
  -- though likely unavoidable.
  local ts = { "local loadstring=loadstring;"  }
  for i,f in ipairs(chunks) do
    ts[i] = ("loadstring%q(...);"):format(string.dump(f))
  end
  --possible extension: ts[#ts] = 'return ' .. ts[#ts]
  chunks = assert(loadstring(table.concat(ts)))
end

-- Output.
local out = outfile == '-' and io.stdout or assert(io.open(outfile, "wb"))
out:write(string.dump(chunks))
if out ~= io.stdout then out:close() end

Note: The listing (-l) can be implemented via lbci [1]. See print.lua in the lbci distribution.

Strip can be implemented in Lua (LuaList:2008-02/msg01158.html), and LuaJit has such an option added to string.dump [2].

See Also


RecentChanges · preferences
edit · history
Last edited August 14, 2023 11:18 pm GMT (diff)