使用 \input|texlua... 在 pdfTeX 中模拟 \directlua

使用 \input|texlua... 在 pdfTeX 中模拟 \directlua

--shell-escape启用后,pdfTeX允许运行外部程序并使用 扩展输入结果\input|"..."。与 Lua 解释器 一起使用texlua,这可用于提供 的简化版本\directlua。但是,texlua需要文件名作为其参数。

在我的设置中(Ubuntu 10.04,安装了最新的 TeXLive 2012),可以pdftex --shell-escape运行

\everyeof{\noexpand}
\message{\input|"echo print\string\(3.4+5.7\string\) > tmp.lua ; texlua tmp.lua" a}
\bye

得到\message{9.1}。请注意,\everyeof{\noexpand}部分是为了方便,可以在没有这个不可扩展的步骤的情况下使构造工作(不是完全稳健地)。

这种方法不可移植,因为它依赖于echo,而 Windows 环境中可能不存在 。解决此问题的一种方法是使用一个简单的 Lua 脚本将代码print\string\(3.4+5.7\string\)作为其参数(请参阅最近有一个关于 texlua 参数的问题),然后以某种方式执行它。这可能吗?否则,我们需要一种依赖于操作系统的方法。

第二个问题是特殊字符(例如括号)需要转义。哪些字符需要转义?我特别担心引号",它用于括住参数\input|。给定要执行的转义列表,我有一些代码可以扩展地执行此操作(请参阅l3kernel-extrasLaTeX3 svn 存储库中的包)。

基本上,我的问题可以归结为:“如何尽可能方便地使用任意 Lua 代码texlua进行调用?”pdfTeX

答案1

内置功能echo在 Unix 和 Windows 上均可用,因此只要平台检测可用,那么问题主要在于决定要转义哪些字符。对于 LaTeX,可以使用ifplatform提供\ifwindows开关的包进行平台检测。

第一个需要担心的字符是“链式进程”。在 Windows 上,这是&,而在 Unix 上;是正确的。它使用类似

\RequirePackage{ifplatform}
\edef\luachainprocess{\ifwindows\string&\else;\fi }

然后是部分中特殊字符的问题echo。对于 Windows,所需的替换是

  • >取而代之^>
  • <取而代之^<
  • |取而代之^|
  • &取而代之^&
  • ^取而代之^^

其余一切都被留下了echo

在 Unix 上,适当的列表似乎是

  • >取而代之\>
  • <取而代之\<
  • (取而代之\(
  • )取而代之\)
  • &取而代之\&
  • |取而代之\|
  • `取而代之\`
  • \取而代之\\

(注意:这是使用 bash shell,但其他的可能有所不同。)

^(由于Windows 允许使用(,因此最简单的方法可能是使用一组通用字符进行替换,而仅转义字符本身会有所不同。(对于 Unix 也是如此,例如\^。)

有一件事完全不清楚,那就是在这两种情况下是否有办法处理",因为管道语法\input没有提供这种转义,所以无论你做什么,它似乎都会被误解。这对于 Unix 的情况尤其令人沮丧,因为生成有效的

 echo "<code>"

可以避免需要转义"代码之外的任何内容。

答案2

我发现了loadstringLua 中的函数,它将字符串转换为 Lua 代码。为了避免 TeX → Lua 转换中的任何转义问题,TeX 代码将 的参数转换为\texlua以空格分隔的字符代码列表,然后将其作为 的额外参数提供texlua。Lua 代码使用 对其进行解码str.char

剩下的薄弱环节是\input|...,就像任何其他的\input或一样\scantokens,会插入文件结束标记,而该文件结束标记可能不会出现在任何命令的参数中。我们需要删除它,一种方法是在文件结束之前和之后添加一些内容,即此处\romannumeral\numexpr0\noexpand\relax\noexpandis 键)。不幸的是,如果出现 Lua 错误,则不会插入代码\romannumeral\numexpr0\noexpand;在这种情况下,文件结束标记不会被删除,一切都会变得一团糟。

\begingroup
  \catcode `@ = 11
  %
  % Get \texlua@input = primitive \input
  \expandafter\ifx\csname @@input\endcsname\relax
    \global\let\texlua@input\input
  \else
    \global\let\texlua@input\@@input
  \fi
  %
  % Write very simple Lua file.
  \newwrite \texlua@write
  \xdef \texlua@file {\jobname.texlua}
  \immediate \openout \texlua@write \texlua@file \relax
  \immediate \write \texlua@write
    {%
      assert(loadstring(string.char(unpack(arg))))()%
      \detokenize{print("\\romannumeral\\numexpr0\\noexpand")}%
    }
  \immediate \closeout \texlua@write
  %
  % Detokenize the argument and convert spaces to category other.
  \gdef\texlua@str#1%
    {%
      \expandafter\texlua@str@i
        \detokenize{#1}\\ \\\texlua@str@ii/ \relax
    }
  \lccode `* = 32
  \lowercase{\gdef\texlua@str@i#1 #2\\#3 {#3\texlua@str@i#1*#2\\{#3} }}
  \gdef\texlua@str@ii/#1#2\\#3\relax{#2}
  %
  % Convert each character to its character code, space-separated.
  % This is not "f-expandable", it is "restricted expandable" in
  % expl3 terms.
  \gdef\texlua@num#1%
    {\expandafter\texlua@num@i\romannumeral-`q\texlua@str{#1}\relax}
  \gdef\texlua@num@i#1%
    {%
      \ifx #1\relax
      \else
        \number `#1 \space
        \expandafter\texlua@num@i
      \fi
    }
  %
  % Calling `\input|`.
  \gdef\texlua#1%
    {%
      \texlua@input|"texlua \texlua@file \space \texlua@num{#1}"%
      \relax
    }
\endgroup

\message{a\texlua{io.write(3+4)}b}

答案3

筛选ConTeXt 中的模块提供了通过外部命令运行环境或宏内容所需的样板(坦白说,我是该模块的作者)。它不用于\input|读取输出,而是假设外部程序将文件写入外部文件,然后用户可以指定如何读取文件(默认为\ReadFile,类似于\input)。

下面的例子说明了如何使用进行处理texlua

% engine=pdftex

\usemodule[filter]

\defineexternalfilter
    [luatex]
    [filtercommand={texlua \externalfilterinputfile > \externalfilteroutputfile}]

\starttext

\inlineluatex{print((3+5)*3)}

\startluatex
  local t = {1,2,3}
  print("Number of entries " .. #t)
\stopluatex

\stoptext

笔记:该\inlineluatex宏不可扩展。

相关内容