diff options
Diffstat (limited to 'macros/latex/contrib/l3kernel/l3luatex.dtx')
-rw-r--r-- | macros/latex/contrib/l3kernel/l3luatex.dtx | 750 |
1 files changed, 0 insertions, 750 deletions
diff --git a/macros/latex/contrib/l3kernel/l3luatex.dtx b/macros/latex/contrib/l3kernel/l3luatex.dtx deleted file mode 100644 index 3d177f6345..0000000000 --- a/macros/latex/contrib/l3kernel/l3luatex.dtx +++ /dev/null @@ -1,750 +0,0 @@ -% \iffalse meta-comment -% -%% File: l3luatex.dtx -% -% Copyright (C) 2010-2024 The LaTeX Project -% -% It may be distributed and/or modified under the conditions of the -% LaTeX Project Public License (LPPL), either version 1.3c of this -% license or (at your option) any later version. The latest version -% of this license is in the file -% -% https://www.latex-project.org/lppl.txt -% -% This file is part of the "l3kernel bundle" (The Work in LPPL) -% and all files in that bundle must be distributed together. -% -% ----------------------------------------------------------------------- -% -% The development version of the bundle can be found at -% -% https://github.com/latex3/latex3 -% -% for those people who are interested. -% -%<*driver> -\documentclass[full,kernel]{l3doc} -\begin{document} - \DocInput{\jobname.dtx} -\end{document} -%</driver> -% \fi -% -% \title{^^A -% The \pkg{l3luatex} module\\ \LuaTeX-specific functions^^A -% } -% -% \author{^^A -% The \LaTeX{} Project\thanks -% {^^A -% E-mail: -% \href{mailto:latex-team@latex-project.org} -% {latex-team@latex-project.org}^^A -% }^^A -% } -% -% \date{Released 2024-03-14} -% -% \maketitle -% -% \begin{documentation} -% -% The \LuaTeX{} engine provides access to the \Lua{} programming language, -% and with it access to the \enquote{internals} of \TeX{}. In order to use -% this within the framework provided here, a family of functions is -% available. When used with \pdfTeX{}, \pTeX{}, \upTeX{} or \XeTeX{} these raise an -% error: use \cs{sys_if_engine_luatex:T} to avoid this. Details on using -% \Lua{} with the \LuaTeX{} engine are given in the \LuaTeX{} manual. -% -% \section{Breaking out to \Lua{}} -% -% \begin{function}[EXP, added = 2018-06-18]{\lua_now:n, \lua_now:e} -% \begin{syntax} -% \cs{lua_now:n} \Arg{token list} -% \end{syntax} -% The \meta{token list} is first tokenized by \TeX{}, which includes -% converting line ends to spaces in the usual \TeX{} manner and which -% respects currently-applicable \TeX{} category codes. The resulting -% \meta{\Lua{} input} is passed to the \Lua{} interpreter for processing. -% Each \cs{lua_now:n} block is treated by \Lua{} as a separate chunk. -% The \Lua{} interpreter executes the \meta{\Lua{} input} immediately, -% and in an expandable manner. -% \begin{texnote} -% \cs{lua_now:e} is a macro wrapper around \tn{directlua}: when -% \LuaTeX{} is in use two expansions are required to yield the -% result of the \Lua{} code. -% \end{texnote} -% \end{function} -% -% \begin{function}[added = 2018-06-18]{\lua_shipout_e:n, \lua_shipout:n} -% \begin{syntax} -% \cs{lua_shipout:n} \Arg{token list} -% \end{syntax} -% The \meta{token list} is first tokenized by \TeX{}, which includes -% converting line ends to spaces in the usual \TeX{} manner and which -% respects currently-applicable \TeX{} category codes. The resulting -% \meta{\Lua{} input} is passed to the \Lua{} interpreter when the -% current page is finalised (\emph{i.e.}~at shipout). Each -% \cs{lua_shipout:n} block is treated by \Lua{} as a separate chunk. -% The \Lua{} interpreter will execute the \meta{\Lua{} input} during the -% page-building routine: no \TeX{} expansion of the \meta{\Lua{} input} -% will occur at this stage. -% -% In the case of the \cs{lua_shipout_e:n} version the input is fully -% expanded by \TeX{} in an \texttt{e}-type manner during the shipout -% operation. -% \begin{texnote} -% At a \TeX{} level, the \meta{\Lua{} input} is stored as a -% \enquote{whatsit}. -% \end{texnote} -% \end{function} -% -% \begin{function}[EXP, added = 2015-06-29]{\lua_escape:n, \lua_escape:e} -% \begin{syntax} -% \cs{lua_escape:n} \Arg{token list} -% \end{syntax} -% Converts the \meta{token list} such that it can safely be passed to -% \Lua{}: embedded backslashes, double and single quotes, and newlines -% and carriage returns are escaped. This is done by prepending an extra -% token consisting of a backslash with category code~$12$, and for the line -% endings, converting them to |\n| and |\r|, respectively. -% \begin{texnote} -% \cs{lua_escape:e} is a macro wrapper around \tn{luaescapestring}: -% when \LuaTeX{} is in use two expansions are required to yield the -% result of the \Lua{} code. -% \end{texnote} -% \end{function} -% -% \begin{function}[added = 2022-05-14]{\lua_load_module:n} -% \begin{syntax} -% \cs{lua_load_module:n} \Arg{Lua module name} -% \end{syntax} -% Loads a Lua module into the Lua interpreter. -% -% \cs{lua_now:n} passes its \Arg{token list} argument to the Lua interpreter -% as a single line, with characters interpreted under the current catcode -% regime. These two facts mean that \cs{lua_now:n} rarely behaves as expected -% for larger pieces of code. Therefore, package authors should \textbf{not} -% write significant amounts of Lua code in the arguments to \cs{lua_now:n}. -% Instead, it is strongly recommended that they write the majorty of their Lua -% code in a separate file, and then load it using \cs{lua_load_module:n}. -% \begin{texnote} -% This is a wrapper around the Lua call |require '|\meta{module}|'|. -% \end{texnote} -% \end{function} -% -% \section{Lua interfaces} -% -% As well as interfaces for \TeX{}, there are a small number of Lua functions -% provided here. -% -% \begin{function}{ltx.utils} -% Most public interfaces provided by the module are stored within the -% |ltx.utils| table. -% \end{function} -% -% \begin{function}{ltx.utils.filedump} -% \begin{syntax} -% \meta{dump}| = ltx.utils.filedump(|\meta{file}|,|\meta{offset}|,|\meta{length}|)| \\ -% \end{syntax} -% Returns the uppercase hexadecimal representation of the content of the -% \meta{file} read as bytes. If the \meta{length} is given, only this part -% of the file is returned; similarly, one may specify the \meta{offset} from -% the start of the file. If the \meta{length} is not given, the entire file -% is read starting at the \meta{offset}. -% \end{function} -% -% \begin{function}{ltx.utils.filemd5sum} -% \begin{syntax} -% \meta{hash}| = ltx.utils.filemd5sum(|\meta{file}|)| \\ -% \end{syntax} -% Returns the MD5 sum of the file contents read as bytes; note that -% the result will depend on the nature of the line endings used in the file, -% in contrast to normal \TeX{} behaviour. If the \meta{file} is not found, -% nothing is returned with \emph{no error raised}. -% \end{function} -% -% \begin{function}{ltx.utils.filemoddate} -% \begin{syntax} -% \meta{date}| = ltx.utils.filemoddate(|\meta{file}|)| \\ -% \end{syntax} -% Returns the date/time of last modification of the \meta{file} in the -% format -% \begin{quote} -% |D:|\meta{year}\meta{month}\meta{day}\meta{hour}\meta{minute}^^A -% \meta{second}\meta{offset} -% \end{quote} -% where the latter may be |Z| (UTC) or -% \meta{plus-minus}\meta{hours}|'|\meta{minutes}|'|. If the \meta{file} is -% not found, nothing is returned with \emph{no error raised}. -% \end{function} -% -% \begin{function}{ltx.utils.filesize} -% \begin{syntax} -% |size = ltx.utils.filesize(|\meta{file}|)| \\ -% \end{syntax} -% Returns the size of the \meta{file} in bytes. If the \meta{file} is not -% found, nothing is returned with \emph{no error raised}. -% \end{function} -% -% \end{documentation} -% -% \begin{implementation} -% -% \section{\pkg{l3luatex} implementation} -% -% \begin{macrocode} -%<*package> -% \end{macrocode} -% -% \subsection{Breaking out to \Lua{}} -% -% \begin{macrocode} -%<*tex> -% \end{macrocode} -% -% \begin{macrocode} -%<@@=lua> -% \end{macrocode} -% -% \begin{macro}[EXP]{\@@_escape:n, \@@_now:n, \@@_shipout:n} -% Copies of primitives. -% \begin{macrocode} -\cs_new_eq:NN \@@_escape:n \tex_luaescapestring:D -\cs_new_eq:NN \@@_now:n \tex_directlua:D -\cs_new_eq:NN \@@_shipout:n \tex_latelua:D -% \end{macrocode} -% \end{macro} -% -% These functions are set up in \pkg{l3str} for bootstrapping: we want to -% replace them with a \enquote{proper} version at this stage, so clean up. -% \begin{macrocode} -\cs_undefine:N \lua_escape:e -\cs_undefine:N \lua_now:e -% \end{macrocode} -% -% \begin{macro}[EXP]{\lua_now:n, \lua_now:e} -% \begin{macro}{\lua_shipout_e:n, \lua_shipout:n} -% \begin{macro}[EXP]{\lua_escape:n, \lua_escape:e} -% Wrappers around the primitives. -% \begin{macrocode} -\cs_new:Npn \lua_now:e #1 { \@@_now:n {#1} } -\cs_new:Npn \lua_now:n #1 { \lua_now:e { \exp_not:n {#1} } } -\cs_new_protected:Npn \lua_shipout_e:n #1 { \@@_shipout:n {#1} } -\cs_new_protected:Npn \lua_shipout:n #1 - { \lua_shipout_e:n { \exp_not:n {#1} } } -\cs_new:Npn \lua_escape:e #1 { \@@_escape:n {#1} } -\cs_new:Npn \lua_escape:n #1 { \lua_escape:e { \exp_not:n {#1} } } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\lua_load_module:n} -% Wrapper around |require'|\meta{module}|'|. -% \begin{macrocode} -\str_new:N \l_@@_err_msg_str -\cs_new_protected:Npn \lua_load_module:n #1 - { - \bool_if:nF { \@@_load_module_p:n { #1 } } - { - \msg_error:nnnV - { luatex } { module-not-found } { #1 } \l_@@_err_msg_str - } - } -% \end{macrocode} -% \end{macro} -% -% As with engines other than \LuaTeX{} -% these have to be macros, we give them the same status in all cases. -% When \LuaTeX{} is not in use, simply give an error message/ -% \begin{macrocode} -\sys_if_engine_luatex:F - { - \clist_map_inline:nn - { - \lua_escape:n , \lua_escape:e , - \lua_now:n , \lua_now:e - } - { - \cs_gset:Npn #1 ##1 - { - \msg_expandable_error:nnn - { luatex } { luatex-required } { #1 } - } - } - \clist_map_inline:nn - { \lua_shipout_e:n , \lua_shipout:n, \lua_load_module:n } - { - \cs_gset_protected:Npn #1 ##1 - { - \msg_error:nnn - { luatex } { luatex-required } { #1 } - } - } - } -% \end{macrocode} -% \subsection{Messages} -% -% \begin{macrocode} -\msg_new:nnnn { luatex } { luatex-required } - { LuaTeX~engine~not~in~use!~Ignoring~#1. } - { - The~feature~you~are~using~is~only~available~ - with~the~LuaTeX~engine.~LaTeX3~ignored~'#1'. - } - -\msg_new:nnnn { luatex } { module-not-found } - { Lua~module~`#1'~not~found. } - { - The~file~`#1.lua'~could~not~be~found.~Please~ensure~ - that~the~file~was~properly~installed~and~that~the~ - filename~database~is~current. \\ \\ - The~Lua~loader~provided~this~additional~information: \\ - #2 - } - -\prop_gput:Nnn \g_msg_module_name_prop { luatex } { LaTeX } -\prop_gput:Nnn \g_msg_module_type_prop { luatex } { } -% \end{macrocode} -% -% \begin{macrocode} -%</tex> -% \end{macrocode} -% -% \subsection{\Lua{} functions for internal use} -% -% \begin{macrocode} -%<*lua> -% \end{macrocode} -% -% Most of the emulation of \pdfTeX{} here is based heavily on Heiko Oberdiek's -% \pkg{pdftexcmds} package. -% -% \begin{macro}{ltx.utils} -% Create a table for the kernel's own use. -% \begin{macrocode} -ltx = ltx or {utils={}} -ltx.utils = ltx.utils or { } -local ltxutils = ltx.utils -% \end{macrocode} -% \end{macro} -% -% Local copies of global tables. -% \begin{macrocode} -local io = io -local kpse = kpse -local lfs = lfs -local math = math -local md5 = md5 -local os = os -local string = string -local tex = tex -local texio = texio -local tonumber = tonumber -% \end{macrocode} -% -% Local copies of standard functions. -% \begin{macrocode} -local abs = math.abs -local byte = string.byte -local floor = math.floor -local format = string.format -local gsub = string.gsub -local lfs_attr = lfs.attributes -local open = io.open -local os_date = os.date -local setcatcode = tex.setcatcode -local sprint = tex.sprint -local cprint = tex.cprint -local write = tex.write -local write_nl = texio.write_nl -local utf8_char = utf8.char -local package_loaded = package.loaded -local package_searchers = package.searchers -local table_concat = table.concat - -local scan_int = token.scan_int or token.scan_integer -local scan_string = token.scan_string -local scan_keyword = token.scan_keyword -local put_next = token.put_next -local token_create = token.create -local token_new = token.new -local set_macro = token.set_macro -% \end{macrocode} -% -% Since token.create only returns useful values after the tokens -% has been added to TeX's hash table, we define a variant which -% defines it first if necessary. -% \begin{macrocode} -local token_create_safe -do - local is_defined = token.is_defined - local set_char = token.set_char - local runtoks = tex.runtoks - local let_token = token_create'let' - - function token_create_safe(s) - local orig_token = token_create(s) - if is_defined(s, true) then - return orig_token - end - set_char(s, 0) - local new_token = token_create(s) - runtoks(function() - put_next(let_token, new_token, orig_token) - end) - return new_token - end -end - -local true_tok = token_create_safe'prg_return_true:' -local false_tok = token_create_safe'prg_return_false:' -% \end{macrocode} -% In Con\TeX{}t lmtx \texttt{token.command_id} does not exist, -% but it can easily be emulated with Con\TeX{}t's \texttt{tokens.commands}. -% \begin{macrocode} -local command_id = token.command_id -if not command_id and tokens and tokens.commands then - local id_map = tokens.commands - function command_id(name) - return id_map[name] - end -end -% \end{macrocode} -% -% Deal with Con\TeX{}t: doesn't use |kpse| library. -% \begin{macrocode} -local kpse_find = (resolvers and resolvers.findfile) or kpse.find_file -% \end{macrocode} -% -% \begin{macro}[int]{escapehex} -% An internal auxiliary to convert a string to the matching hex escape. -% This works on a byte basis: extension to handled UTF-8 input is -% covered in \pkg{pdftexcmds} but is not currently required here. -% \begin{macrocode} -local function escapehex(str) - return (gsub(str, ".", - function (ch) return format("%02X", byte(ch)) end)) -end -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{ltx.utils.filedump} -% Similar comments here to the next function: read the file in binary mode -% to avoid any line-end weirdness. -% \begin{macrocode} -local function filedump(name,offset,length) - local file = kpse_find(name,"tex",true) - if not file then return end - local f = open(file,"rb") - if not f then return end - if offset and offset > 0 then - f:seek("set", offset) - end - local data = f:read(length or 'a') - f:close() - return escapehex(data) -end -ltxutils.filedump = filedump -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[int]{md5.HEX} -% Hash a string and return the hash in uppercase hexadecimal format. -% In some engines, this is build-in. For traditional \LuaTeX{}, the conversion -% to hexadecimal has to be done by us. -% \begin{macrocode} -local md5_HEX = md5.HEX -if not md5_HEX then - local md5_sum = md5.sum - function md5_HEX(data) - return escapehex(md5_sum(data)) - end - md5.HEX = md5_HEX -end -% \end{macrocode} -% \end{macro} -% \begin{macro}{ltx.utils.filemd5sum} -% Read an entire file and hash it: the hash function itself is a built-in. -% As Lua is byte-based there is no work needed here in terms of UTF-8 -% (see \pkg{pdftexcmds} and how it handles strings that have passed through -% \LuaTeX{}). The file is read in binary mode so that no line ending -% normalisation occurs. -% \begin{macrocode} -local function filemd5sum(name) - local file = kpse_find(name, "tex", true) if not file then return end - local f = open(file, "rb") if not f then return end - - local data = f:read("*a") - f:close() - return md5_HEX(data) -end -ltxutils.filemd5sum = filemd5sum -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{ltx.utils.filemoddate} -% There are two cases: If the C standard library is C99 compliant, -% we can use |%z| to get the timezone in almost the right format. -% We only have to add primes and replace a zero or missing offset -% with |Z|. -% -% Of course this would be boring, so Windows does things differently. -% There we have to manually calculate the offset. -% See procedure \texttt{makepdftime} in \texttt{utils.c} of -% \pdfTeX{}. -% \begin{macrocode} -local filemoddate -if os_date'%z':match'^[+-]%d%d%d%d$' then - local pattern = lpeg.Cs(16 * - (lpeg.Cg(lpeg.S'+-' * '0000' * lpeg.Cc'Z') - + 3 * lpeg.Cc"'" * 2 * lpeg.Cc"'" - + lpeg.Cc'Z') - * -1) - function filemoddate(name) - local file = kpse_find(name, "tex", true) - if not file then return end - local date = lfs_attr(file, "modification") - if not date then return end - return pattern:match(os_date("D:%Y%m%d%H%M%S%z", date)) - end -else - local function filemoddate(name) - local file = kpse_find(name, "tex", true) - if not file then return end - local date = lfs_attr(file, "modification") - if not date then return end - local d = os_date("*t", date) - local u = os_date("!*t", date) - local off = 60 * (d.hour - u.hour) + d.min - u.min - if d.year ~= u.year then - if d.year > u.year then - off = off + 1440 - else - off = off - 1440 - end - elseif d.yday ~= u.yday then - if d.yday > u.yday then - off = off + 1440 - else - off = off - 1440 - end - end - local timezone - if off == 0 then - timezone = "Z" - else - if off < 0 then - timezone = "-" - off = -off - else - timezone = "+" - end - timezone = format("%s%02d'%02d'", timezone, hours // 60, hours % 60) - end - return format("D:%04d%02d%02d%02d%02d%02d%s", - d.year, d.month, d.day, d.hour, d.min, d.sec, timezone) - end -end -ltxutils.filemoddate = filemoddate -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{ltx.utils.filesize} -% A simple disk lookup. -% \begin{macrocode} -local function filesize(name) - local file = kpse_find(name, "tex", true) - if file then - local size = lfs_attr(file, "size") - if size then - return size - end - end -end -ltxutils.filesize = filesize -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[int]{luadef} -% An internal function for defining control sequences form Lua which behave -% like primitives. This acts as a wrapper around |token.set_lua| which accepts -% a function instead of an index into the functions table. -% \begin{macrocode} -local luacmd do - local set_lua = token.set_lua - local undefined_cs = command_id'undefined_cs' - - if not context and not luatexbase then require'ltluatex' end - if luatexbase then - local new_luafunction = luatexbase.new_luafunction - local functions = lua.get_functions_table() - function luacmd(name, func, ...) - local id - local tok = token_create(name) - if tok.command == undefined_cs then - id = new_luafunction(name) - set_lua(name, id, ...) - else - id = tok.index or tok.mode - end - functions[id] = func - end - elseif context then - local register = context.functions.register - local functions = context.functions.known - function luacmd(name, func, ...) - local tok = token_create(name) - if tok.command == undefined_cs then - token.set_lua(name, register(func), ...) - else - functions[tok.index or tok.mode] = func - end - end - end -end -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[int]{try_require} -% Loads a Lua module. This function loads the module similarly to the standard -% Lua global function |require|, with a few differences. On success, -% |try_require| returns |true, module|. If the module cannot be found, it -% returns |false, err_msg|. If the module is found, but something goes wrong -% when loading it, the function throws an error. -% \begin{macrocode} -local function try_require(name) - if package_loaded[name] then - return true, package_loaded[name] - end - - local failure_details = {} - for _, searcher in ipairs(package_searchers) do - local loader, data = searcher(name) - if type(loader) == 'function' then - package_loaded[name] = loader(name, data) or true - return true, package_loaded[name] - elseif type(loader) == 'string' then - failure_details[#failure_details + 1] = loader - end - end - - return false, table_concat(failure_details, '\n') -end -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_load_module_p:n} -% Check to see if we can load a module using |require|. If we -% can load the module, then we load it immediately. Otherwise, we save the -% error message in |\l_@@_err_msg_str|. -% \begin{macrocode} -local char_given = command_id'char_given' -local c_true_bool = token_create(1, char_given) -local c_false_bool = token_create(0, char_given) -local c_str_cctab = token_create('c_str_cctab').mode - -luacmd('@@_load_module_p:n', function() - local success, result = try_require(scan_string()) - if success then - set_macro(c_str_cctab, 'l_@@_err_msg_str', '') - put_next(c_true_bool) - else - set_macro(c_str_cctab, 'l_@@_err_msg_str', result) - put_next(c_false_bool) - end -end) -% \end{macrocode} -% \end{macro} -% -% \subsection{Preserving iniTeX Lua data for runs} -% -% \begin{macrocode} -%<@@=lua> -% \end{macrocode} -% -% The Lua state is not dumped when a forat is written, therefore any Lua -% variables filled doing format building need to be restored in order to -% be accessible during normal runs. -% -% We provide some kernel-internal helpers for this. They will only be available if -% \texttt{luatexbase} is available. This is not a big restriction though, because -% Con\TeX{}t (which does not use \texttt{luatexbase}) does not load \pkg{expl3} -% in the format. -% -% \begin{macrocode} -local register_luadata, get_luadata - -if luatexbase then - local register = token_create'@expl@luadata@bytecode'.index - if status.ini_version then -% \end{macrocode} -% -% \begin{macro}[int]{register_luadata} -% \texttt{register_luadata} is only available during format generation. -% It accept a string which uniquely identifies the data object and has to be -% provided to retrieve it later. Additionally it accepts a function which is -% called in the \texttt{pre_dump} callback and which has to return a string that -% evaluates to a valid Lua object to be preserved. -% \begin{macrocode} - local luadata, luadata_order = {}, {} - - function register_luadata(name, func) - if luadata[name] then - error(format("LaTeX error: data name %q already in use", name)) - end - luadata[name] = func - luadata_order[#luadata_order + 1] = func and name - end -% \end{macrocode} -% \end{macro} -% -% The actual work is done in \texttt{pre_dump}. The \texttt{luadata_order} is used -% to ensure that the order is consistent over multiple runs. -% \begin{macrocode} - luatexbase.add_to_callback("pre_dump", function() - if next(luadata) then - local str = "return {" - for i=1, #luadata_order do - local name = luadata_order[i] - str = format('%s[%q]=%s,', str, name, luadata[name]()) - end - lua.bytecode[register] = assert(load(str .. "}")) - end - end, "ltx.luadata") - else -% \end{macrocode} -% -% \begin{macro}[int]{get_luadata} -% \texttt{get_luadata} is only available if data should be restored. -% It accept the identifier which was used when the data object was registered and -% returns the associated object. Every object can only be retrieved once. -% \begin{macrocode} - local luadata = lua.bytecode[register] - if luadata then - lua.bytecode[register] = nil - luadata = luadata() - end - function get_luadata(name) - if not luadata then return end - local data = luadata[name] - luadata[name] = nil - return data - end - end -end -% \end{macrocode} -% \end{macro} -% -% \begin{macrocode} -%</lua> -% \end{macrocode} -% -% \begin{macrocode} -%</package> -% \end{macrocode} -% -%\end{implementation} -% -%\PrintIndex |