% \iffalse meta-comment (by the way, this file is contains UTF-8) % % Written in 2010, 2011 by Manuel Pégourié-Gonnard % % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Manuel Pégourié-Gonnard % % This work consists of the main source file luacode.dtx % and the derived files % luacode.sty luacode.pdf % % Unpacking: % tex luacode.dtx % Documentation: % pdflatex luacode.dtx % %<*ignore> \begingroup \def\x{LaTeX2e}% \expandafter\endgroup \ifcase 0\ifx\install y1\fi\expandafter \ifx\csname processbatchFile\endcsname\relax\else1\fi \ifx\fmtname\x\else 1\fi\relax \else\csname fi\endcsname % %<*install> \input docstrip.tex \keepsilent \askforoverwritefalse \preamble See the source file for author and licensing information. \endpreamble \generate{% \usedir{tex/lualatex/luacode}% \file{luacode.sty}{\from{luacode.dtx}{texpackage}}% } \generate{% \usedir{doc/lualatex/luacode}% \file{test-luacode.tex}{\from{luacode.dtx}{testlatex}}% } \obeyspaces \Msg{************************************************************************} \Msg{*} \Msg{* To finish the installation you have to move the following} \Msg{* files into a directory searched by TeX:} \Msg{*} \Msg{* luacode.sty} \Msg{*} \Msg{* Happy TeXing!} \Msg{*} \Msg{************************************************************************} \endbatchfile % %<*ignore> \fi % %<*driver> \documentclass{ltxdoc} \usepackage[utf8]{inputenc} \usepackage[T1]{fontenc} \usepackage{textcomp} \usepackage{lmodern} \usepackage{fixltx2e, mparhack} \usepackage{xcolor} \usepackage{booktabs} \usepackage{xspace} \usepackage[a4paper]{geometry} \usepackage[english]{babel} \usepackage[colorlinks]{hyperref} \usepackage{bookmark} \newcommand*\email [1] {<\href{mailto:#1}{#1}>} \newcommand \file {\nolinkurl} \newcommand \pk {\textsf} \newcommand \cmdname {\texttt} \pdfstringdefDisableCommands{% \def\cs#1{\@backslashchar #1}} \MakeShortVerb\| \renewcommand\MacroFont{\ttfamily\small\color{blue}} \begin{document} \DocInput{luacode.dtx}% \end{document} % % \fi % % \CheckSum{0} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % \title{The \pk{luacode} package} % \author{Manuel P\'egouri\'e-Gonnard \email{mpg@elzevir.fr}} % \date{v1.2a 2012/01/23} % % \maketitle % % \begin{abstract} % Executing Lua code from within \TeX\ with |\directlua| can sometimes be % tricky: there is no easy way to use the percent character, counting % backslashes may be hard, and Lua comments don't work the way you expect. % This package provides the |\luaexec| command and the |luacode(*)| % environments to help with these problems, as well as helper macros and a % debugging mode. % \end{abstract} % % \tableofcontents % % \section{Documentation} % % \subsection{Lua code in \LaTeX} % % For an introduction to the most important gotchas of |\directlua|, see % \file{lualatex-doc.pdf}. Before presenting the tools in this package, let me % insist that the best way to manage a non-trivial piece of Lua code is % probably to use an external file and source it from Lua, as explained in the % cited document. % % \medskip % % \DescribeMacro\luadirect % First, the exact syntax of |\directlua| has changed along version of % Lua\TeX, so this package provides a |\luadirect| command which is an exact % synonym of |\directlua| except that it doesn't have the funny, changing % parts of its syntax, and benefits from the debugging facilities described % below (\ref{dbg}).\footnote{And expands in two steps instead of one. If % you don't know what it means, then you hopefully don't need to.} % % The problems with |\directlua| (or |\luadirect|) are mainly with \TeX\ % special characters. Actually, things are not that bad, since most special % characters do work, namely: |_|, |^|, |&|, \verb+$+, |{|, |}|. Three are a % bit tricky but they can be managed with |\string|: |\|, |#| and |~|. Only % |%| is really hard to obtain. Also, \TeX\ macros are expanded, which is good % since it allows to pass information from \TeX\ to Lua, but you must be % careful and use only purely expandable macros. % % \DescribeMacro\luaexec % The |\luaexec| command is similar to |\luadirect| but with a few additional % features:\footnote{And one major drawback: it is not purely expandable. See % previous note.} |\\| gives a double backslash (see note below) % |\%| a percent character, and |~| just works. For single backslashes, % |\string| is still needed. Also, \TeX\ macros are expanded. % % \DescribeEnv{luacode} % The |luacode| environment is similar to |\luaexec|, except that you can now % use |%| and |#| directly (but |\%| and |\#| also work) and the line breaks % are respected, so that you can use line-wise Lua comments in the normal way, % without mistakenly commenting the rest of the chunk. % % Only the backslash and the braces keep their special meaning, so that macros % still work as usual, and you still need to use |\string| to get a single % backslash. % % \DescribeEnv{luacode*} % The variant |luacode*| goes further and makes even backslash a normal % character, so that you don't need any trick to obtain a single backslash. On % the other end, macros don't work any more. So, the content of a |luacode*| % is interpreted exactly as if it were in a normal Lua file, directly fed to % the Lua interpreter without any \TeX\ intervention. % % \medskip % % The following table summarizes how to use special characters with the % various commands and environments. % % \begin{center} % \begin{tabular}{rcccc} % \toprule % & |\luadirect| & |\luaexec| & |luacode| & |luacode*| \\ % \midrule % Macros & Yes & Yes & Yes & No \\ % Single backslash & |\string\| & |\string\| & |\string\| & Just |\| \\ % Double backslash & |\string\\| & |\\| & |\\| & |\\| \\ % Tilde & |\string~| & |~| & |~| & |~| \\ % Sharp & |\string#| & |\#| & |#| (or |\#|) & |#| \\ % Percent & No easy way & |\%| & |%| (or |\%|) & |%| \\ % \TeX\ comments & Yes & Yes & No & No \\ % Lua line comments & No & No & Yes & Yes \\ % \bottomrule % \end{tabular} % \end{center} % % \noindent\textbf{Backslashes and Lua strings.} % In the table and descriptions above, ``double backslash'' means that the Lua % interpreter will see a double backslash. It may then turn it into a single % backslash in the context of a Lua string delimited by single or double quotes % as opposed to a Lua string delimited by brackets, see % \href{http://www.lua.org/pil/2.4.html}{\emph{Programming in Lua} section % 2.4}. Similarly, a single backslash may or may not be interpreted as % starting an escape sequence. For example: % \begin{verbatim} % \begin{luacode} % a = "\\" -- a contains a single backslash % b = [[\\]] -- b contains two backslashes % c = "\\\\" -- c contains two backslashes too % d = "line one\nline two" -- d contains a newline character % e = [[single\nline]] -- e contains no newline character % \end{luacode} % \end{verbatim} % The alert reader may notice that in the case of |\luadirect| and |\luaexec|, % single backslashes are a bit weird. For example with % \begin{verbatim} % \luaexec{texio.write_nl("line one\string\nline two")} % \end{verbatim} % \TeX\ will see |\nline| as a control sequence which is the ``argument'' of % |\string| and the Lua interpreter will consider only |\n| as an escape % sequence, and |line| as independent characters. In practice, this should not % have any unwanted consequences (except perhaps on the sanity of the reader). % % \medskip % % \noindent\textbf{Technical notes on environments.} % \DescribeEnv{luacodestar} % The environments will not work inside the argument of a command (just as % with verbatim commands and environments). Also, you are supposed to leave a % space (or end-of-line) after the |\begin{luacode}| or |\begin{luacode*}|, % which is probably a natural thing to do anyway. Finally, if you wish to % define derived environments, you'll need to use |\luacode| \dots % |\endluacode| instead of the usual |\begin| |\end| pair in your % environment's definition. For the stared variant, use |\luacodestar| and % |\endluacodestar|. % % \medskip % % The test file (section~\ref{test}, or \file{test-luacode.tex} in the same % directory as this document) provides stupid but complete examples. % % \subsection{Helper macros} % % As mentioned in the previous section, except for trivial pieces of codes (or % examples) it is good practice to keep all your Lua code in separate |.lua| % files and then use |\luadirect| only to |require()| or |dofile()| it and % define \LaTeX\ wrappers for some functions, eg: % \begin{quote} % |\newcommand*\foo[2]{\luadirect{foo("#1", #2)}}| % \end{quote} % This way, problems with \TeX\ special characters are avoided, since most of % the Lua is never seen by \TeX. Unfortunately, there is still potential % for problems. For example |\foo{a"b}{2}| will cause the Lua interpreter to % complain since the |"| in |#1| will end the string; we want the Lua % interpreter to see |"a\"b"| as the first argument. % % \DescribeMacro\luastring % Fortunately, Lua\TeX\ offers a primitive that does exactly what we need: % escape characters that need to be escaped in a Lua string. Unfortunately, it % has a very long name (especially in the prefixed form available in \LaTeX): % |\luatexluaescapestring|. Also, you need to think to use quotes in addition % to this primitive. So this package provides a shorter version: |\luastring| % that also include the quotes, so a safer version of |\foo| might be defined % as % \begin{quote} % |\newcommand*\foo[2]{\luadirect{foo(\luastring{#1}, #2)}}| % \end{quote} % % \DescribeMacro\luastringN % \DescribeMacro\luastringO % It should be noted that the argument of |\luastring| is fully % expanded\footnote{If you don't know what this means, just skip this % paragraph.} before being turned into a Lua string. In case where such an % expansion is unwanted, two variants are provided: |\luastringN| for no % expansion, and |\luastringO| for one-level expansion (of the first token) % only. % % \subsection{Debugging} \label{dbg} % % \DescribeMacro\LuaCodeDebugOn % \DescribeMacro\LuaCodeDebugOff % The commands |\luadirect| and |\luaexec| as well as the environments % |luacode| and |luacode*| can optionally print the Lua code as it will be % seen by the Lua interpreter in the log file before executing it. The feature % is disabled by default and can be turned on and off using |\LuaCodeDebugOn| % and |\LuaCodeDebugOff| (which obey the usual \TeX\ scoping rules). % % \section{Implementation} % % \begin{macrocode} %<*texpackage> % \end{macrocode} % % \subsection{Preliminaries} % % Catcode defenses. % % \begin{macrocode} \begingroup\catcode61\catcode48\catcode32=10\relax% = and space \catcode123 1 % { \catcode125 2 % } \catcode 35 6 % # \toks0{\endlinechar\the\endlinechar}% \edef\x{\endlinechar13}% \def\y#1 #2 {% \toks0\expandafter{\the\toks0 \catcode#1 \the\catcode#1}% \edef\x{\x \catcode#1 #2}}% \y 13 5 % carriage return \y 61 12 % = \y 32 10 % space \y 123 1 % { \y 125 2 % } \y 35 6 % # \y 64 11 % @ (letter) \y 39 12 % ' \y 40 12 % ( \y 41 12 % ) \y 42 12 % * \y 45 12 % - \y 46 12 % . \y 47 12 % / \y 91 12 % [ \y 93 12 % ] \y 94 7 % ^ \y 96 12 % ` \y 126 13 % ~ \toks0\expandafter{\the\toks0 \relax\noexpand\endinput}% \edef\y#1{\noexpand\expandafter\endgroup% \noexpand\ifx#1\relax \edef#1{\the\toks0}\x\relax% \noexpand\else \noexpand\expandafter\noexpand\endinput% \noexpand\fi}% \expandafter\y\csname luacode@sty@endinput\endcsname% % \end{macrocode} % % Package declaration. % % \begin{macrocode} \ProvidesPackage{luacode}[2012/01/23 v1.2a lua-in-tex helpers (mpg)] % \end{macrocode} % % Make sure Lua\TeX\ is used. % % \begin{macrocode} \RequirePackage{ifluatex} \ifluatex\else \PackageError{luacode}{LuaTeX is required for this package. Aborting.}{% This package can only be used with the LuaTeX engine\MessageBreak (command `lualatex'). Package loading has been stopped\MessageBreak to prevent additional errors.} \expandafter\luacode@sty@endinput \fi % \end{macrocode} % % Use \pk{luatexbase} for catcode tables. % % \begin{macrocode} \RequirePackage{luatexbase} % \end{macrocode} % % \subsection{Internal code} % % Produce Lua code printing debug info for the given argument. % % \begin{macrocode} \newcommand \luacode@printdbg [1] {% texio.write_nl('log', '-- BEGIN luacode debug (on input line \the\inputlineno)') texio.write_nl('log', "\luatexluaescapestring{#1}") texio.write_nl('log', '-- END luacode debug (on input line \the\inputlineno)') } % \end{macrocode} % % Execute a piece of Lua code, possibly printing debug info. % |maybe@printdbg| will be either |printdbg| or |gobble|, see user macros. % % \begin{macrocode} \newcommand \luacode@dbg@exec [1] {% \directlua { \luacode@maybe@printdbg{#1} #1 }% } % \end{macrocode} % Execute a piece of code, with shortcuts for double-backslash, percent and % tilde, and trying to preserve newlines. This internal macro is long so % that we can use in the environment, while the corresponding user command % will be short. Make sure |~| is active. % % \begin{macrocode} \begingroup \catcode`\~\active \expandafter\endgroup \@firstofone{% \newcommand \luacode@execute [1] {% \begingroup \escapechar92 \newlinechar10 \edef\\{\string\\}% \edef~{\string~}% \let\%=\luacode@percentchar \let\#=\luacode@sharpchar \expandafter\expandafter\expandafter\endgroup \luacode@dbg@exec{#1}} } % \end{macrocode} % % Catcode 12 percent and sharp characters for use in the previous command. % % \begin{macrocode} \begingroup \escapechar\m@ne \edef\aux{\endgroup \unexpanded{\newcommand\luacode@percentchar}{\string\%}% \unexpanded{\newcommand\luacode@sharpchar }{\string\#}% }\aux % \end{macrocode} % % Generic code for environments; the argument is the name of a catcode % table. We're normally inside a group, but let's open a new one in case % we're called directly rather that using |\begin|. Define the end marker % to be |\end{}| with current catcodes. % % \begin{macrocode} \newcommand*\luacode@begin [1] {% \begingroup \escapechar92 \luatexcatcodetable#1\relax \edef\luacode@endmark{\string\end{\@currenvir}}% \expandafter\def \expandafter\luacode@endmark \expandafter{% \luatexscantextokens \expandafter{\luacode@endmark}}% \luacode@grab@body} % \end{macrocode} % % We'll define the body grabber in a moment, but let's see how the % environment ends now. % % \begin{macrocode} \newcommand\luacode@end{% \edef\luacode@next{% \noexpand\luacode@execute{\the\luacode@lines}% \noexpand\end{\@currenvir}}% \expandafter\endgroup \luacode@next} % \end{macrocode} % % It is not possible to grab the body using a macro with delimited % argument, since the end marker may contains open-group characters, % depending on the current catcode regime. So we collect it linewise and % check each line against the end marker. % % Storage for lines. % % \begin{macrocode} \newtoks\luacode@lines \newcommand*\luacode@addline [1] {% \luacode@lines\expandafter{\the\luacode@lines#1^^J}} % \end{macrocode} % % Loop initialisation. Set endlinechar explicitely so that we can use it % as a delimiter (and later when writing the code to Lua). Eat up the first % token which is supposed to be a (catcode 12) |\endlinechar| character % token. % % \begin{macrocode} \newcommand \luacode@grab@body [1] {% \luacode@lines{}% \endlinechar10 \luacode@grab@lines} % \end{macrocode} % % The actual line-grabbing loop. % % \begin{macrocode} \long\def\luacode@grab@lines#1^^J{% \def\luacode@curr{#1}% \luacode@strip@spaces \ifx\luacode@curr\luacode@endmark \expandafter\luacode@end \else \expandafter\luacode@addline\expandafter{\luacode@curr}% \expandafter\luacode@grab@lines \fi} % \end{macrocode} % % Strip catcode 12 spaces from the beginning of the token list inside % |\luacode@curr|. First we need catcode 12 space, then we procede in the % usual way. % % \begin{macrocode} \begingroup\catcode32 12 \expandafter\endgroup \@firstofone{\newcommand\luacode@spaceother{ }} \newcommand \luacode@strip@spaces {% \expandafter\luacode@strip@sp@peek\luacode@curr\@nil} \newcommand \luacode@strip@sp@peek {% \futurelet\@let@token\luacode@strip@sp@look} \newcommand \luacode@strip@sp@look {% \expandafter\ifx\luacode@spaceother\@let@token \expandafter\@firstoftwo \else \expandafter\@secondoftwo \fi{% \afterassignment\luacode@strip@sp@peek \let\@let@token= }{% \luacode@strip@sp@def }} \@ifdefinable \luacode@strip@sp@def \relax \def \luacode@strip@sp@def #1\@nil{% \def\luacode@curr{#1}} % \end{macrocode} % % Finally, we need a custom catcode table for the default environment: % everything other, except backslash, braces and letters which retain their % natural catcodes. % % Be carefull about the name of the macro for setting catcode ranges which % is currently changing in luatexbase. The group here doesn't matter since % catcode table settings are always global. % % \begin{macrocode} \newluatexcatcodetable \luacode@table@soft \begingroup \ifdefined\SetCatcodeRange \else \let\SetCatcodeRange\setcatcoderange \fi \setluatexcatcodetable \luacode@table@soft {% \luatexcatcodetable\CatcodeTableOther \catcode 92 0 \catcode 123 1 \catcode 125 2 \SetCatcodeRange {65}{90} {11} \SetCatcodeRange {97}{122}{11} } \endgroup % \end{macrocode} % % \subsection{Public macros and environments} % % Debugging. % % \begin{macrocode} \newcommand \LuaCodeDebugOn {\let \luacode@maybe@printdbg \luacode@printdbg} \newcommand \LuaCodeDebugOff {\let \luacode@maybe@printdbg \@gobble} \LuaCodeDebugOff % \end{macrocode} % % The |\luadirect| and |\luaexec| macros. % % \begin{macrocode} \@ifdefinable\luadirect {\let\luadirect\luacode@dbg@exec} \newcommand*\luaexec [1] {\luacode@execute{#1}} % \end{macrocode} % % Environments using different catcode tables. % % \begin{macrocode} \newenvironment {luacode} {\luacode@begin\luacode@table@soft} {} \newenvironment {luacode*} {\luacode@begin\CatcodeTableOther} {} \newcommand \luacodestar {\@nameuse{luacode*}} \def \endluacodestar {\@nameuse{endluacode*}} % \end{macrocode} % % Helper macros % % \begin{macrocode} \newcommand \luastring [1] {"\luatexluaescapestring{#1}"} \newcommand \luastringO [1] {\luastring{\unexpanded\expandafter{#1}}} \newcommand \luastringN [1] {\luastring{\unexpanded{#1}}} % \end{macrocode} % % We're already done! % % \begin{macrocode} \luacode@sty@endinput % % \end{macrocode} % % \section{Test file} \label{test} % % TODO: this test files requires manual checking that the output (pdf and % log file) is correct; this should be fixed. % % \begin{macrocode} %<*testlatex> \documentclass{minimal} \usepackage{luacode} \begin{document} \newcommand\foo{3} \( \luadirect{ texio.write_nl("Special chars: _ ^ & $ { } working.\string\n" .. "Backslashes need a bit of care.\string\n" .. "Sharps and tildes too: # doubled, but \string# and \string~") % a tex comment: no easy way to get a % tex.sprint("\string\\pi \string\\neq", tostring(math.pi)) % we can use TeX macros tex.sprint("-", math.sqrt(\foo)) } \) \( \luaexec{ texio.write_nl("Special chars: _ ^ & $ { } ~ working.\string\n" .. "Backslashes still need a bit of care.\string\n" .. "Single sharps are easier now: \#") % a tex comment: we also get a % below tex.sprint("\\pi \\neq ", tostring(math.pi):gsub('\%.', '+')) % we can use TeX macros tex.sprint("-", math.sqrt(\foo)) } \) \[ \begin{luacode} texio.write_nl("Special chars: _ ^ & $ { } ~ # % working.\string\n" .. "Only backslashes still need a bit of care.\string\n") -- a lua comment: we could use \% below, too tex.sprint("\\pi \\neq ", tostring(math.pi):gsub('%.', '+')) -- we can use TeX macros tex.sprint("-", math.sqrt(\foo)) \end{luacode} \] \[ \begin{luacode*} texio.write_nl("Special chars: _ ^ & $ { } ~ # % \\ working.\n") -- a lua comment: the backlash is doubled as in normal Lua code tex.sprint("\\pi \\neq ", tostring(math.pi):gsub('%.', '+')) -- no way to use a TeX variable here \end{luacode*} \] \newenvironment{mathluacode} { \[ \luacode }{ \endluacode \] } \newenvironment{mathluacode*}{ \[ \luacodestar }{ \endluacodestar \] } \begin{mathluacode} local foo = "A full line.\string\n" tex.sprint("\\pi \\neq ", tostring(math.pi):gsub('%.', '+')) -- a lua comment: we could have used \% above, too tex.sprint("-", math.sqrt(\foo)) \end{mathluacode} \begin{mathluacode*} local foo_bar = "A full line.\n" tex.sprint("\\pi \\neq ", tostring(math.pi):gsub('%.', '+')) -- a lua comment: no way to use a TeX variable here \end{mathluacode*} \begin{luacode*} function myfunc(str) assert(type(str) == 'string') tex.sprint(-2, str) end \end{luacode*} \newcommand*\mymac [1] {\texttt{\luadirect{myfunc(\luastring{#1})}}\par} \mymac{abc} \mymac{123} \mymac{a"b\string\nc'd} \def\mac{\onelevel} \def\onelevel{fully expanded} string : \texttt{\luadirect{myfunc(\luastring \mac)}}\par stringO: \texttt{\luadirect{myfunc(\luastringO\mac)}}\par stringN: \texttt{\luadirect{myfunc(\luastringN\mac)}}\par \LuaCodeDebugOn \luadirect {local foo = 'bar' .. \luastring{a"b'c}} \luaexec {local foo = 'bar\%\#' .. \luastring{a"b'c}} \begin{luacode} local foo = 'bar' local baz = 12 % 2 assert(\luastring\mac == 'fully expanded') -- assert(\luastringO\mac == '\\onelevel') -- assert(\luastringN\mac == '\\mac') \end{luacode} \LuaCodeDebugOff \luadirect{local rem = 'dbg should be disabled here'} % \end{macrocode} % % Now track spurious spaces. This is the only part that is automatically % checked, using grep in the Makefile. % % \begin{macrocode} \tracingcommands1 \luadirect{local foo}% \luaexec{local foo}% \begin{luacode} local foo \end{luacode} \begin{luacode*} local foo \end{luacode*} \tracingcommands0 \end{document} % % \end{macrocode} % % % \Finale \endinput