Lua Compiler In Lua


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 5:18 pm GMT (diff)

AltStyle によって変換されたページ (->オリジナル) /