---------------------------------------------------------------------- -- Metalua: $Id$ -- -- Summary: -- -- This set of functions offer transformations between the different -- stages of compilation, namely: -- -- * luafile: source file (generally named *.lua) -- * string: chunk source, as a string -- * lexstream: the stream of lexemes in the sources -- * ast: abstract syntax tree -- * bin: binary representation of the chunk, stored as a string -- * luacfile: compiled file (generally named *.luac) -- * function: evaluable from of the chunk -- -- When we look at how these formats are related to each other, we -- distinguish: -- * a group (A) = { luafile, string } in which both represenations are -- interchangable; -- * a group (B) = { luacfile, bin, function } in which all three -- representations are interchangable; -- * a sequence (A) -> lexstream -> ast -> (B) which is one-way. -- -- Moreover, a last pseudo-stage is handled, called "file": it -- represents a file which might either contain sources or a binary -- chunk. In transformations, it only makes sense as an original -- format (not a target); given uncertainty about its content, the only -- safe target for a "file" transformation are binary chunk, luacfile -- and function. -- -- This module implements all the relevant format transformations, in -- both directions, inside groups (A) and (B); it also implement the -- one-way transformations; finally, it provides aliases for all -- transitive closures of the transformations above. -- -- Functions reading or writing files take the file's name as parameter. -- ---------------------------------------------------------------------- -- -- Copyright (c) 2006-2007, Fabien Fleutot <metalua@gmail.com>. -- -- This software is released under the MIT Licence, see licence.txt -- for details. -- ---------------------------------------------------------------------- -- History: -- ---------------------------------------------------------------------- -------------------------------------------------------------------------------- -- -- Exported API: -- * [mlc.ast_of_lexstream (lexstream)] -- * [mlc.ast_of_luafile (luafile)] -- * [mlc.ast_of_string (string)] -- * [mlc.bin_of_ast (ast, srcname)] -- * [mlc.bin_of_function (f)] -- * [mlc.bin_of_file (filename)] -- * [mlc.bin_of_lexstream (lexstream, srcname)] -- * [mlc.bin_of_luacfile (luacfile)] -- * [mlc.bin_of_luafile (luafile)] -- * [mlc.bin_of_string (string, srcname)] -- * [mlc.function_of_ast (ast)] -- * [mlc.function_of_bin (bin)] -- * [mlc.function_of_file (file)] -- * [mlc.function_of_lexstream (lexstream)] -- * [mlc.function_of_luacfile (luacfile)] -- * [mlc.function_of_luafile (luafile)] -- * [mlc.function_of_string (string)] -- * [mlc.lexstream_of_luafile (luafile)] -- * [mlc.lexstream_of_string (string)] -- * [mlc.luacfile_of_ast (ast, luacfile, srcname)] -- * [mlc.luacfile_of_bin (bin, luacfile)] -- * [mlc.luacfile_of_function (f, luacfile)] -- * [mlc.luacfile_of_file (file, luacfile, srcname)] -- * [mlc.luacfile_of_lexstream (lexstream, luacfile, srcname)] -- * [mlc.luacfile_of_luafile (luafile, luacfile, srcname)] -- * [mlc.luacfile_of_string (string, luacfile, srcname)] -- * [mlc.luafile_of_string (string, luafile)] -- * [mlc.string_of_luafile (filename)] -- * [mlc.is_bin (str)] -- -------------------------------------------------------------------------------- module ("mlc", package.seeall) SHOW_METABUGS = false function is_bin (str) if type(str) ~= "string" then return false end if str:sub (1, 4) == '\027Lua' then return true elseif str:sub(1, 2) == '#!' then local _, x = str:find ("[\n\r]+") if x and str:sub (x+1, x+4) == '\027Lua' then return true end end return false end function string_of_luafile (filename) if not filename then return nil end local file = io.open (filename, "r") if not file then return nil end local string = file:read "*a" file:close() return string end function ast_of_lexstream (lexstream, filename) if not lexstream then return nil end local status, e if SHOW_METABUGS then status, e = true, mlp.block (lexstream) else status, e = pcall (mlp.block, lexstream) end if status and lexstream:peek().tag ~= "Eof" then status, e = false, "Premature Eof" elseif status and lexstream:peek().tag == "End" then status, e = false, "Unexpected 'end' keyword" end if not status and e then e = e:match "[^:]+:[0-9]+: (.*)" or e printf("Parsing error in %s line %s, char %s: \n%s", filename or "?", lexstream.line, lexstream.i, e) return nil else return e end end function luacfile_of_ast (ast, filename, srcname) if not ast or not filename then return nil end local proto = compast.metalua_compile (ast) proto.filename = srcname or filename return compast.dump_file (compast.metalua_compile (ast), filename) end function luacfile_of_luafile (luafile, luacfile) if not luafile or not luacfile then return nil end local ast = ast_of_luafile (luafile) luacfile = luacfile or luafile .. (luafile:match ".*%.lua$" and "c" or ".luac") return luacfile_of_ast (ast, luacfile, luafile) end function luacfile_of_string (string, luacfile, srcname) if not luacfile then return nil end local ast = ast_of_string (string) --luacfile = luacfile or "out.luac" return luacfile_of_ast (ast, luacfile, srcname) end function luacfile_of_lexstream (lexstream, luacfile, srcname) if not lexstream or not luacfile then return nil end local ast = ast_of_lexstream (lexstream) --luacfile = luacfile or "out.luac" return luacfile_of_ast (ast, luacfile, srcname) end function luacfile_of_bin (bin, filename) local file = io.open (filename, "w+") file:write(bin) file:close() end function bin_of_string (str, filename) if not str then return nil end if is_bin (str) then return str end local lx = lexstream_of_string (str) local ast = ast_of_lexstream (lx, filename) local bin = bin_of_ast (ast, filename) return bin end function bin_of_file (filename) local str = string_of_luafile (filename) return bin_of_string (str, filename) end function luacfile_of_file (file, luacfile) return luacfile_of_bin (bin_of_file (file), luacfile) end local function checknil(f) return function(...) if ... then return f(...) end end end function lexstream_of_string (str) if str then return mlp.lexer:newstream(str) end end function bin_of_ast (ast, srcname) if not ast then return nil end local proto = compast.metalua_compile (ast) if not proto then return nil end proto.source = srcname return compast.dump_string (proto) end -- Convenient aliasings: bin_of_function = checknil (string.dump) bin_of_luacfile = checknil (string_of_luafile) luafile_of_string = checknil (luacfile_of_string) function_of_bin = checknil (loadstring) -- Functions obtained by composition: ast_of_luafile = o(ast_of_lexstream, lexstream_of_string, string_of_luafile) ast_of_string = o(ast_of_lexstream, lexstream_of_string) bin_of_lexstream = o(bin_of_ast, ast_of_lexstream) bin_of_luafile = o(bin_of_ast, ast_of_lexstream, lexstream_of_string, string_of_luafile) function_of_ast = o(function_of_bin, bin_of_ast) function_of_lexstream= o(function_of_bin, bin_of_ast, ast_of_lexstream) function_of_luacfile = o(function_of_bin, bin_of_luacfile) function_of_luafile = o(function_of_bin, bin_of_ast, ast_of_lexstream, lexstream_of_string, string_of_luafile) function_of_string = o(function_of_bin, bin_of_ast, ast_of_lexstream, lexstream_of_string) lexstream_of_luafile = o(lexstream_of_string, string_of_luafile) luacfile_of_function = o(luacfile_of_bin, bin_of_function) function_of_file = o(function_of_bin, bin_of_file) function dofile(filename) return mlc.function_of_file(filename)() end _G.loadfile = function_of_file _G.luadstring = function_of_string function _G.load (f, name) local acc = { } while true do local x = f() if not x then break end table.insert (acc, x) end return function_of_string (table.concat (acc)) end