| ------------------------------------------------------------------------------- |
| -- Copyright (c) 2006-2013 Fabien Fleutot and others. |
| -- |
| -- All rights reserved. |
| -- |
| -- This program and the accompanying materials are made available |
| -- under the terms of the Eclipse Public License v1.0 which |
| -- accompanies this distribution, and is available at |
| -- http://www.eclipse.org/legal/epl-v10.html |
| -- |
| -- This program and the accompanying materials are also made available |
| -- under the terms of the MIT public license which accompanies this |
| -- distribution, and is available at http://www.lua.org/license.html |
| -- |
| -- Contributors: |
| -- Fabien Fleutot - API and implementation |
| -- |
| ------------------------------------------------------------------------------- |
| |
| -------------------------------------------------------------------------------- |
| -- Execute an `mlc.*_to_ast()' 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 |
| if status then |
| local check_status, check_msg = pcall (mlc.check_ast, 'block', ast) |
| if not check_status then status, ast = false, check_msg end |
| 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 ( |
| [=[lua -l metalua.mlc_xcall -e "mlc_xcall.server([[%s]], [[%s]], %s)"]=], |
| 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 |