使用\directlua
和tex.print
,可以在 TeX 和 Lua 之间来回发送字符串。让我们举个例子:从宏 开始\A
,我将其替换文本传递给 Lua 并返回,定义\B
\edef\A{ab\string c}
\directlua{tex.print("\noexpand\\def\noexpand\\B{\unexpanded\expandafter{\A}}")}
\show\A
\show\B
\ifx\A\B
\message{Identical :)!}
\else
\message{Different :(.}
\fi
\bye
这两个宏看起来相同,但实际上并不相同:类别代码(字母和其他)的奇怪/特殊组合在到达 Lua 之前就丢失了,并且回程时的标记化是使用\directlua
执行时有效的类别代码机制完成的。
这行代码能不能\directlua
用其他 Lua 代码替换,使其能够定义\B
为与 相同\A
,包括类别代码?当然可以,
\directlua{\unexpanded{tex.print("\\edef\\B{\\unexpanded\\expandafter{\\A}}")}}
会起作用(通过延迟扩展\A
直到通过 LuaTeX 之后),但我的目标是在 Lua 端对标记列表执行一些复杂的操作。
答案1
你可以玩tex.print()
和 catcode 表格。结果
tex.tprint({"\\def\\B{ab"},{-2,"c"},{"}"})
给出“Identical :)”。这是因为 -2 是一个预定义的 catcode 表,其中除空格 (10) 外,所有字符都分配有 catcode“其他”(12)。因此,您得到的结果与 TeX 中的 edef 相同。
答案2
使用token_filter
回调和token
库(正如 Patrick 在他的回答的评论中向我提到的那样)可以构建任意标记列表并将其插入输入流中。回调只能调用一次,它会自行禁用callback.register(..., nil)
。
\expandafter\def\expandafter\B\expandafter{%
\directlua
{
callback.register
(
'token_filter',
function()
callback.register ('token_filter', nil)
return
{
token.create(\number`\a, 11),
token.create(\number`\b, 11),
token.create(\number`\c, 12)
}
end
)
}%
}
\show\B
\def\test#1#2#3{\show#1\show#2\show#3}
\expandafter\test\B
这个答案只涵盖了 Lua 到 TeX 的部分。应该可以使用它token.expand()
来完成 TeX 到 Lua 的步骤,但我不知道怎么做。
答案3
相关命令的快速摘要/“备忘单”(回答标题中的问题)。
TeX(便捷功能):使用 \usepackage{luacode} 可以使用 \begin{luacode*} ... \end{luacode*} 编写 Lua 代码,而不必担心 catcode。
Lua 内置
inspect()
函数用于打印出有关任何(用户数据)对象/漂亮打印表等的一些基本信息。Lua 中的标记:
从 Lua 获取令牌:(有关更多详细信息,请参阅文档)
token.scan_toks()
:获取参数(\toks0={...}
-style,必须用括号括起来,删除最外层的括号,可以包含哈希表中没有的控制序列)。token.scan_toks(false, true)
:获取参数(\expanded{...}
-style,必须用括号括起来)token.create("<control sequence name>")
,token.create(int charcode, int catcode)
笔记
- 创建不可能的控制序列如果它不在哈希表中
""
无法通过这种方式创建空控制序列(控制序列名称 = )
token.get_next()
返回输入流中的下一个标记,必须位于哈希表中 (*1)token.new
:(大概)创建一个具有明确含义的“新”(冻结)标记。这里有一个示例用法https://tex.stackexchange.com/a/498118/250119
令牌格式:
- 查看.cs名称
如果为零:明确的字符(一些特殊情况除外)
。模式= 字符代码(可能。指数而是在新版本中)
.命令名称∈ (左括号 右括号 数学移位 制表符 mac_param 上标 下标 间隔符 字母 其他字符)。
- 如果它不在这个列表中,它可能是一些内部标记,例如
\endlocalcontrol
插入到里面的tex.runtoks()
,它有。模式= 10 和.命令名称=extension
,但是.cs名称零
- 如果它不在这个列表中,它可能是一些内部标记,例如
别的:
如果。积极的:活动字符(charcode 位于.cs名称, 不是。模式), else: 控制序列
如果 .csname 是精确的字符串“\csname\endcsname”(通常包含反斜杠,并且不管 escapechar 的实际值是什么)并且.tok= 0x20000000: 为空控制序列标记
如果(...)有很多内部令牌,如果你想非常小心,正如 LuaTeX 手册所述,创建一个 token 并获取其.tok价值。
else:是具有给定 csname 的活动字符/控制序列。
查看.命令名称因为它的意义,可以是下列之一
token.commands() = { [0]="relax", "left_brace", "right_brace", "math_shift", "tab_mark", "car_ret", "mac_param", "sup_mark", "sub_mark", "endv", "spacer", "letter", "other_char", "par_end", "stop", "delim_num", "char_num", "math_char_num", "mark", "node", "xray", "make_box", "hmove", "vmove", "un_hbox", "un_vbox", "remove_item", "hskip", "vskip", "mskip", "kern", "mkern", "leader_ship", "halign", "valign", "no_align", "vrule", "hrule", "novrule", "nohrule", "insert", "vadjust", "ignore_spaces", "after_assignment", "after_group", "break_penalty", "start_par", "ital_corr", "accent", "math_accent", "discretionary", "eq_no", "left_right", "math_comp", "limit_switch", "above", "math_style", "math_choice", "non_script", "vcenter", "case_shift", "message", "normal", "extension", "option", "lua_function_call", "lua_bytecode_call", "lua_call", "in_stream", "begin_group", "end_group", "omit", "ex_space", "boundary", "radical", "super_sub_script", "no_super_sub_script", "math_shift_cs", "end_cs_name", "char_ghost", "assign_local_box", "char_given", "math_given", "xmath_given", "last_item", "toks_register", "assign_toks", "assign_int", "assign_attr", "assign_dimen", "assign_glue", "assign_mu_glue", "assign_font_dimen", "assign_font_int", "assign_hang_indent", "set_aux", "set_prev_graf", "set_page_dimen", "set_page_int", "set_box_dimen", "set_tex_shape", "set_etex_shape", "def_char_code", "def_del_code", "extdef_math_code", "extdef_del_code", "def_family", "set_math_param", "set_font", "def_font", "register", "assign_box_direction", "assign_box_dir", "assign_direction", "assign_dir", "combinetoks", "advance", "multiply", "divide", "prefix", "let", "shorthand_def", "def_lua_call", "read_to_cs", "def", "set_box", "hyph_data", "set_interaction", "letterspace_font", "expand_font", "copy_font", "set_font_id", "undefined_cs", "expand_after", "no_expand", "input", "lua_expandable_call", "lua_local_call", "if_test", "fi_or_else", "cs_name", "convert", "variable", "feedback", "the", "top_bot_mark", "call", "long_call", "outer_call", "long_outer_call", "end_template", "dont_expand", "glue_ref", "shape_ref", "box_ref", "data", }
(call long_call outer_call long_outer_call)是用户定义的宏。
在这种情况下。模式(或者。指数) 将具有一些cmdname特定的含义。
- 查看.cs名称
要将标记从 Lua 发送回 TeX:
tex.print(...)
: (带有“尾随换行符”,即在单独的行上)(token1)
{ token1, token2 }
(string content)
(int catcodetablenumber, string content)
(
catcodetablenumber
特殊值:-1 为当前,-2 为去标记化)([int catcodetablenumber], {string1, string2, ...})
tex.sprint
:与上面相同,但没有“尾随换行符”tex.tprint
:快捷方式tex.tprint( {int catcodetablenumber1, string content1}, {int catcodetablenumber2, string content2}, ... )
tex.cprint(int catcode, string content)
token.put_next( token1, token2 )
,token.put_next{ token1, token2 }
笔记:通常不要混合
token.put_next
使用tex.print
(打印总是在所有 put_next 之前!)
在Lua中执行TeX代码:
协同程序:将标记打印到 TeX 并将控制权返回给 TeX在 LuaTeX 中并发交叉执行 Lua 和 TeX
tex.runtoks(function() tex.print(...) end)
:立即运行内容(危险!不要在函数内部更改 TeX 模式)
在 TeX 中执行 Lua 代码:
- \directlua{要执行的lua代码...(完全展开类似于
\message
或\expanded
,#
是双倍的)} - 使用 \luaescapestring{content... (完全展开类似于
\message
或\expanded
,#
不会翻倍)}传递参数 - 或者将参数留在输入流中并让 Lua 使用例如
token.scan_toks
或token.get_next
等来获取它们。
- \directlua{要执行的lua代码...(完全展开类似于
(*1):否则会变成\IMPOSSIBLE
。这里可以安全地使用简单\futurelet
,但有一定的开销。scan_toks()
尽可能使用。
例如,Lua 函数将每个标记的字符代码增加 1,适用于所有组级别并采取一个扩展步骤。(“获取标记列表并进行复杂操作”的演示)
function f()
local tokens=token.scan_toks()
for i=1,#tokens do
if tokens[i].cmdname=="letter" then
tokens[i]=token.create(tokens[i].mode+1, 11)
end
end
tokens[#tokens+1]=token.create(string.byte("}"), 2)
token.put_next{
token.create("pretty:n"),
token.create(string.byte("{"), 1),
table.unpack(tokens)
}
end
\directlua{f()}{ab{c}123} % eventually expand to bc{d}123