我的问题背景
我正在开发比斯蒙(暂时的名称),某种用于静态源代码分析的持久领域特定语言,与 GCC 绑定(成为我已故的GCC 熔化)这里的细节并不重要,但好奇的人可能会看看它的自述文件.md在 github 上。
就这个问题而言,比斯蒙可以看作是某种动态类型的 Scheme 或 Lua 类语言(即使它实际上不是)。我完全控制它,我可以调整它,使它更像那样。所以对于这个问题,我们可以假装它bismon
是一些类似 Scheme 的东西,比如诡计
我需要开始写一些关于它的文档(在 GCC MELT 中已经是这样了,我.texi
为 texinfo 生成了文件),其中一些文档(希望越来越多)将由比斯蒙本身(即使显然不是)
我在 Linux/Debian 上运行所有这些,我不关心其他操作系统。我有 texlive,所以 lualatex(版本 1.07),还有橡胶树。我精通 Linux 和 Ocaml(以及某种程度上的 Lua),并且一点点黑客攻击对我来说不是什么问题(例如hevea
在 Ocaml 中稍微修补,或编写简单的lua
脚本)。
我需要生成一份 PDF 报告(其中包含一些超链接)和一些 HTML5 格式的报告(即一组 HTML5 和其他文件,可以通过最近的浏览器很好地查看)。当然,我正在考虑使用 lualatex 和 hevea(但我可以选择其他方法,只要它们基于免费软件,最好是在 Debian 上打包的)。
理想情况下,我希望有一些文档生成,因为显示实际的输出。其中一些可能发生在相同的在整个文档处理过程中运行的进程(例如 Guile 进程)。
对我来说很重要产生一些文档,包括示例“输入”和“输出”(这确保文档符合当前状态bismon
)。
所以我梦想能够排版类似
Here is the factorial in Scheme:
\begin{myguilecode}
(define (fact n) (if (< n 1) 1 (* n (fact (- n 1)))))
\end{myguilecode}
在同一文档的后面,我梦想着
When we ask about \texttt{fact}, our Scheme interpeter shows that
it is some function:
\begin{runguilecode}
fact
\end{runguilecode}
Of course, we can compute the factorial of 5:
\begin{runguilecode}
(fact 5)
\end{runguilecode}
此时,我梦见代码和它的输出完美地出现了。请注意,我需要相同的 guile
过程(概念上)与 LaTeX 过程共存。因此,也许效果类似于生成的LaTeX
When we ask about \texttt{fact}, our Scheme interpeter shows that
it is some function:
\begin{alltt}
fact
\end{alltt}
$\Rightarrow$
\begin{alltt}
\$1 = #<procedure fact (n)>
\end{alltt}
Of course, we can compute the factorial of 5:
\begin{alltt}
(fact 5)
\end{alltt}
$\Rightarrow$
\begin{alltt}
\$2 = 120
\end{alltt}
例如Ocaml 手册有售HTML和PDF格式并生成其中的某些部分。
在两种(半)方法之间进行选择。
我可以使用一些预处理,也许是“文学编程”之类的技术,即生成 LaTeX
.tex
文件。例如,我会编写(也许在 GPP 中)一些可预处理的内容,这些内容会扩展为 LaTeX 代码,例如生成一个包含以下内容的巨大 LaTeX 文件生成的LaTeX我可以使用一些解释技术,即为
lua
LuaLaTeX 编写一些脚本,运行 Guile(在某处)并读取其输出。基本上类似于一些 \input 执行的操作弹出(3)(或者甚至在 Lua 中计算一些),而不是打开(3). 对于 HeVeA 来说,这可能更棘手。我可能会考虑从内部生成所有的 LaTeX
bismon
,但我现在不太愿意接受这个想法。我觉得更合理的做法是让我在emacs
我目前倾向于第二种方法。
欢迎您的评论。
答案1
在 LuaTeX 中这不是问题。借助 FFI(需要 LuaJITTeX 或 LuaTeX ≥ 1.0.3),您可以调用任意 C 函数。由于 Guile 是一种嵌入式语言,因此它带有丰富的 C-API,可以在 LuaTeX 中轻松使用。我只是从 C 头文件中复制了函数声明并将其粘贴到ffi.cdef
.
这是--shell-escape
因为 FFI 被认为是不安全的,但实际上不会调用任何外部程序。
下面的代码示例当然是特定于 Guile 的,但既然您是 bismon 的开发人员,您可以提供这样的功能,将 bismon 编译为动态链接库并将其加载到 Lua 中以执行类似的任务。
\documentclass{article}
\usepackage{luacode}
\begin{luacode}
local ffi = assert(require("ffi"))
local SCM = assert(ffi.load("libguile-2.2.so"))
ffi.cdef[[
typedef struct scm_unused_struct *SCM;
void scm_init_guile (void);
SCM scm_c_eval_string (const char *expr);
SCM scm_c_lookup (const char *name);
SCM scm_variable_ref (SCM var);
SCM scm_object_to_string (SCM obj, SCM printer);
char *scm_to_locale_string (SCM str);
void scm_dynwind_free (void *mem);
]]
SCM.scm_init_guile()
function guile_eval_string(expr)
local object = SCM.scm_c_eval_string(expr);
local display = SCM.scm_variable_ref(SCM.scm_c_lookup("display"))
local result = SCM.scm_object_to_string(object, display);
local c_str = SCM.scm_to_locale_string(result)
local str = ffi.string(c_str)
SCM.scm_dynwind_free(c_str)
return str
end
\end{luacode}
% Run guile code
\newcommand\guile[1]{\directlua{guile_eval_string("\luaescapestring{#1}")}}
% Run guile code and typeset its output using verbatim catcodes
\newcommand\guilerun[1]{\directlua{tex.sprint(-2,guile_eval_string("\luaescapestring{#1}"))}}
\usepackage{alltt}
\usepackage{environ}
\NewEnviron{myguilecode}{%
% Typeset ...
\begin{alltt}
\BODY
\end{alltt}
% ... and run!
\guile{\BODY}%
}
\NewEnviron{runguilecode}{%
% Typeset ...
\begin{alltt}
\BODY
\end{alltt}
$\Rightarrow$
% ... and run and typeset result
\begin{alltt}
\guilerun{\BODY}
\end{alltt}
}
\begin{document}
Here is the factorial in Scheme:
\begin{myguilecode}
(define (fact n) (if (< n 1) 1 (* n (fact (- n 1)))))
\end{myguilecode}
When we ask about \texttt{fact}, our Scheme interpeter shows that
it is some function:
\begin{runguilecode}
fact
\end{runguilecode}
Of course, we can compute the factorial of 5:
\begin{runguilecode}
(fact 5)
\end{runguilecode}
\end{document}