LuaTeX 中的 Lua 部分可以了解标记吗?

LuaTeX 中的 Lua 部分可以了解标记吗?

使用\directluatex.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特定的含义。

    • 要将标记从 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_tokstoken.get_next等来获取它们。

(*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

另外举几个例子:https://tex.stackexchange.com/a/555222/250119

相关内容