-------------------------------------------------------------------------------- -- Execute an `mlc.ast_of_*()' in a separate lua process. -- Communication between processes goes through temporary files, -- for the sake of portability. -------------------------------------------------------------------------------- mlc_xcall = { } -------------------------------------------------------------------------------- -- Number of lines to remove at the end of a traceback, should it be -- dumped due to a compilation error in metabugs mode. -------------------------------------------------------------------------------- local STACK_LINES_TO_CUT = 7 -------------------------------------------------------------------------------- -- (Not intended to be called directly by users) -- -- This is the back-end function, called in a separate lua process -- by `mlc_xcall.client_*()' through `os.execute()'. -- * inputs: -- * the name of a lua source file to compile in a separate process -- * the name of a writable file where the resulting ast is dumped -- with `serialize()'. -- * metabugs: if true and an error occurs during compilation, -- the compiler's stacktrace is printed, allowing meta-programs -- debugging. -- * results: -- * an exit status of 0 or -1, depending on whethet compilation -- succeeded; -- * the ast file filled will either the serialized ast, or the -- error message. -------------------------------------------------------------------------------- function mlc_xcall.server (luafilename, astfilename, metabugs) -- We don't want these to be loaded when people only do client-side business require 'metalua.compiler' require 'serialize' mlc.metabugs = metabugs -- compile the content of luafile name in an AST, serialized in astfilename --local status, ast = pcall (mlc.luafile_to_ast, luafilename) local status, ast local function compile() return mlc.luafile_to_ast (luafilename) end if mlc.metabugs then print 'mlc_xcall.server/metabugs' --status, ast = xpcall (compile, debug.traceback) --status, ast = xpcall (compile, debug.traceback) local function tb(msg) local r = debug.traceback(msg) -- Cut superfluous end lines local line_re = '\n[^\n]*' local re = "^(.-)" .. (line_re) :rep (STACK_LINES_TO_CUT) .. "$" return r :strmatch (re) or r end --status, ast = xpcall (compile, debug.traceback) status, ast = xpcall (compile, tb) else status, ast = pcall (compile) end local out = io.open (astfilename, 'w') if status then -- success out:write (serialize (ast)) out:close () os.exit (0) else -- failure, `ast' is actually the error message out:write (ast) out:close () os.exit (-1) end end -------------------------------------------------------------------------------- -- Compile the file whose name is passed as argument, in a separate process, -- communicating through a temporary file. -- returns: -- * true or false, indicating whether the compilation succeeded -- * the ast, or the error message. -------------------------------------------------------------------------------- function mlc_xcall.client_file (luafile) -- printf("\n\nmlc_xcall.client_file(%q)\n\n", luafile) local tmpfilename = os.tmpname() local cmd = string.format ( [=[%s -l metalua.mlc_xcall -e "mlc_xcall.server([[%s]], [[%s]], %s)"]=], (_PROGDIR and _PROGDIR.."/lua" or "lua"), luafile, tmpfilename, mlc.metabugs and "true" or "false") -- printf("os.execute [[%s]]\n\n", cmd) local status = (0 == os.execute (cmd)) local result -- ast or error msg if status then result = (lua_loadfile or loadfile) (tmpfilename) () else local f = io.open (tmpfilename) result = f :read '*a' f :close() end os.remove(tmpfilename) return status, result end -------------------------------------------------------------------------------- -- Compile a source string into an ast, by dumping it in a tmp -- file then calling `mlc_xcall.client_file()'. -- returns: the same as `mlc_xcall.client_file()'. -------------------------------------------------------------------------------- function mlc_xcall.client_literal (luasrc) local srcfilename = os.tmpname() local srcfile, msg = io.open (srcfilename, 'w') if not srcfile then print(msg) end srcfile :write (luasrc) srcfile :close () local status, ast = mlc_xcall.client_file (srcfilename) os.remove(srcfilename) return status, ast end return mlc_xcall