diff options
Diffstat (limited to 'Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua')
-rw-r--r-- | Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua b/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua new file mode 100644 index 00000000000..4f904afe157 --- /dev/null +++ b/Master/texmf-dist/tex/lualatex/pyluatex/pyluatex.lua @@ -0,0 +1,153 @@ +--[[ +MIT License + +Copyright (c) 2021 Tobias Enderle + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +--]] + +local json = require("pyluatex-json") +local socket = require("socket") + +pyluatex = pyluatex or { + verbose = false, + session = "default" +} + +-- status.filename: path to pyluatex.sty +local folder = file.pathpart(file.collapsepath(status.filename, true)) +local script = file.join(folder, "pyluatex-interpreter.py") +local tcp = nil + +local python_lines = {} +local python_output = nil + +local env_end = "\\end{python}" + +local function err_cmd(message) + return "\\PackageError{PyLuaTeX}{" .. message .. "}{}" +end + +function pyluatex.start(executable) + local is_windows = package.config:sub(1,1) ~= "/" + local cmd + if is_windows then + cmd = "start /B " .. executable .. " \"" .. script .. "\"" + else + cmd = executable .. " \"" .. script .. "\" &" + end + local f = io.popen(cmd, "r") + local port = f:read("*l"):gsub("\r", ""):gsub("\n", "") + f:close() + + if port then + tcp = socket.tcp() + tcp:connect("127.0.0.1", port) + else + tex.sprint(err_cmd("Python backend (executable: " .. executable .. + ") could not be started")) + end +end + +local function request(data) + tcp:send(json.encode(data) .. "\n") + local output = tcp:receive("*l") + local response = json.decode(output) + return response.success, response.output +end + +local function log_input(code) + texio.write_nl("PyLuaTeX input for session \"" .. pyluatex.session .. "\": " .. code) +end + +local function log_output(code) + texio.write_nl("PyLuaTeX output: " .. code) +end + +local function print_lines(str) + for s in str:gmatch("[^\r\n]+") do + tex.sprint(s) + end +end + +function pyluatex.execute(code, write) + if pyluatex.verbose then log_input(code) end + + local success, output = request({ session = pyluatex.session, code = code }) + if success then + if pyluatex.verbose then log_output(output) end + if write then + print_lines(output) + else + return output + end + else + if not pyluatex.verbose then log_input(code) end + log_output(output) + if write then + tex.sprint(err_cmd("Python error (see above)")) + end + end + return nil +end + +function pyluatex.print_env() + if python_output ~= nil then + print_lines(python_output) + python_output = nil + end +end + +local function record_line(line) + local s, e = line:find(env_end) + if s ~= nil then + luatexbase.remove_from_callback("process_input_buffer", "pyluatex_record_line") + table.insert(python_lines, line:sub(1, s - 1)) + local code = table.concat(python_lines, "\n") + local output = pyluatex.execute(code, false) + if output ~= nil then + python_output = output + return line:sub(s) + else + return env_end .. err_cmd("Python error (see above)") .. line:sub(e + 1) + end + else + table.insert(python_lines, line) + return "" + end +end + +function pyluatex.record_env() + python_lines = {} + python_output = nil + luatexbase.add_to_callback("process_input_buffer", record_line, "pyluatex_record_line") +end + +function pyluatex.run_file(path) + local f = io.open(path, "r") + if f then + local code = f:read("*a") + f:close() + pyluatex.execute(code, true) + else + tex.sprint(err_cmd("File not found: " .. path)) + end +end + +return pyluatex |