背景
代码在启动/停止环境中生成一本书的标题和作者。我想重新定义内容将这些环境作为宏来使用,这样就可以在其他地方重复使用。例如:
\startbooktitle
Famous Novel Title
\stopbooktitle
代码
以下代码(以及它的无数变体)无法创建名为 的BookTitle
宏Famous Novel Title
:
\definestartstop[booktitle][
before={\define\BookTitle\bgroup},
after={\egroup},
]
问题
无论使用bgroup
/egroup
还是begingroup
/ endgroup
,错误都是:
["lasttexerror"]="! Too many }'s",
使用startbuffer
和endbuffer
结果看起来像无限递归。
删除编译define
内部的bgroups
,但\BookTitle
宏在其他地方不可用,即使可用,startbooktitle
/之间的内容stopbooktitle
也不会被捕获:
before={\bgroup\define\BookTitle},
问题
如何使用命令中的before
/选项定义宏,以便在调用时捕获启动/停止命令的全部内容?after
definestartstop
换句话说,你如何转码:
\startbooktitle
Famous Novel Title
\stopbooktitle
进入:
\define\BookTitle{Famous Novel Title}
before
还有其他比/更可取的可能性吗after
?
答案1
TeX 中的宏定义需要显式括号,即
\def\foo{...}
隐式括号,如\bgroup
and\egroup
或任何其他标记,这些标记应\let
起作用{
或}
不起作用。以下是无效的
\def\foo\bgroup...\egroup
TeX 中有几个命令允许隐式括号作为分隔符,例如\hbox
but\def
不是其中之一。由于 TeX 中不能有不匹配的显式括号(除了一些\halign
相关的技巧),因此您无法从 中获取环境的内容\definestartstop
。但是,TeX 为您提供支持,您可以使用分隔宏来实现此目的
\unexpanded\def\stopbooktitle{\stopbooktitle} % Don't expand me, I only delimit things
\unexpanded\def\startbooktitle#1\stopbooktitle{\define\BookTitle{#1}}
\starttext
\startbooktitle
Famous Novel Title
\stopbooktitle
\meaning\BookTitle
\stoptext
不幸的是,正如您所看到的,这不会修剪空格,空行会转换为\par
空格,单词“Title”后面的行尾字符会转换为空格。您现在可以构建一些带有扭曲扩展的复杂宏机制来删除这些标记,但由于我很懒,所以我只在 Lua 中执行此操作。
\startluacode
function userdata.booktitle()
local t = token.scan_toks()
-- trim from the start
for n = 1, #t do
local cur_tok = t[n].cmdname
if cur_tok == "par_end" or cur_tok == "spacer" then
table.remove(t, n)
else
break
end
end
-- trim from the end
for n = #t, 1, -1 do
local cur_tok = t[n].cmdname
if cur_tok == "par_end" or cur_tok == "spacer" then
table.remove(t, n)
else
break
end
end
token.put_next(t)
end
\stopluacode
\unexpanded\def\stopbooktitle{\stopbooktitle} % Don't expand me, I only delimit things
\unexpanded\def\startbooktitle#1\stopbooktitle{%
\expandafter\define\expandafter\BookTitle\expandafter{\ctxlua{userdata.booktitle()}{#1}}}
\starttext
\startbooktitle
Famous Novel Title
\stopbooktitle
\meaning\BookTitle
\stoptext