expl3에서는 토큰 목록을 다음과 같이 두 개의 매개변수를 사용하는 \l_greet_tl
제어 시퀀스로 변환할 수 있습니다.\greet
\tl_new:N
\l_greet_tl
\tl_set:Nn
\l_greet_tl
{ Hello,~#1!~How~is~#2~doing? }
\cs_generate_variant:Nn
\cs_generate_from_arg_count:NNnn
{ NNnV }
\cs_generate_from_arg_count:NNnV
\greet
\cs_set:Npn
{ 2 }
\l_greet_tl
그러면 \greet { world } { favorite~species~(humans,~presumably) }
다음 확장을 작성하고 받을 수 있습니다.
안녕, 세상! 당신이 가장 좋아하는 종(아마 인간)은 어떻게 지내나요?
그러나 이제는 그 반대 작업을 수행하고 싶습니다. 즉, 제어 시퀀스의 대체 텍스트를 \greet
다시 토큰 목록으로 변환하여 제어 시퀀스로 다시 변환하기 전에 추가할 수 있도록 하고 싶습니다. 다음과 같이 두 매개변수에 대해 이 작업을 수동으로 수행할 수 있습니다.
\tl_set:No
\l_greet_tl
{ \greet { #1 } { #2 } }
\tl_put_right:Nn
\l_greet_tl
{ ~Have~a~great~#3! }
\cs_generate_from_arg_count:NNnV
\greet
\cs_set:Npn
{ 3 }
\l_greet_tl
이제 \greet { world } { favorite~species~(humans,~presumably) } { evolutionary~leap }
다음 확장을 작성하고 받을 수 있습니다.
안녕, 세상! 당신이 가장 좋아하는 종(아마 인간)은 어떻게 지내나요? 위대한 진화적 도약을 이루세요!
그러나 나는 다음과 같이 단지 두 개가 아닌 여러 매개변수에 대해 작동하는 방식으로 이 작업을 수행할 수 있기를 원합니다.
\tl_set_from_cs:NNn
\l_greet_tl % token list (output)
\greet % control sequence (input)
{ 2 } % parameter count
\tl_put_right:Nn
\l_greet_tl
{ ~Have~a~great~#3! }
\cs_generate_from_arg_count:NNnV
\greet
\cs_set:Npn
{ 3 }
\l_greet_tl
기존 함수와 달리 \cs_replacement_spec:N
새 함수는 \tl_set_from_cs:NNn
대체 텍스트의 카테고리 코드를 유지합니다.
편집: 추가 설명(2024-04-26)
Max Chernoff의 답변과 달리 답변은 LuaTeX뿐만 아니라 expl3에서 지원하는 모든 TeX 엔진에 적용되어야 합니다.
나와 @egreg의 답변과 달리 #
대체 텍스트의 doubled s는 유지되어야 합니다. 예를 들어 제어 시퀀스를 다음과 같이 재정의한다고 가정합니다 \greet
.
\cs_new_protected:Npn
\greet
#1#2
{
Hello,~#1!
\group_begin:
\cs_set:Npn
\greet
##1
{ ~How~is~##1~doing? }
\greet { #2 }
\group_end:
}
이제 \greet { world } { favorite~species~(humans,~presumably) }
다음 확장을 작성하고 계속 받을 수 있습니다.
안녕, 세상! 당신이 가장 좋아하는 종(아마 인간)은 어떻게 지내나요?
그러나 다음과 같이 제어 시퀀스를 토큰 목록으로 변환하면 더 이상 예상대로 작동하지 않습니다.
\tl_set:No
\l_greet_tl
{ \greet { #1 } { #2 } }
\tl_put_right:Nn
\l_greet_tl
{ ~Have~a~great~#3! }
\cs_generate_from_arg_count:NNnV
\greet
\cs_set:Npn
{ 3 }
\l_greet_tl
그 이유는 \greet
이제 다음과 같은 대체 텍스트가 포함되어 있기 때문입니다.
Hello,~#1!
\group_begin:
\cs_set:Npn
\greet
#1
{ ~How~is~#1~doing? }
\greet { #2 }
\group_end:
~Have~a~great~#3!
보시다시피 #1
와 사이의 구별 ##1
이 사라졌습니다. 지금 작성하면 \greet { world } { favorite~species~(humans,~presumably) } { evolutionary~leap }
TeX는 다음 오류를 생성합니다.
\greet의 사용이 해당 정의와 일치하지 않습니다.
이중 #
s를 보존하는 것은 도전의 일부입니다.
답변1
인수가 무제한인 매크로의 경우:
\documentclass{article}
\newcommand{\test}[2]{-#1-#2-}
\ExplSyntaxOn
\seq_new:N \l__witiko_getreplacement_seq
\tl_new:N \l__witiko_getreplacement_tmp_tl
\seq_set_from_clist:Nn \l__witiko_getreplacement_seq
{
{},{#1},{},{#1}{#2},{},{#1}{#2}{#3},{},{#1}{#2}{#3}{#4},
{},{#1}{#2}{#3}{#4}{#5},{},{#1}{#2}{#3}{#4}{#5}{#6},
{},{#1}{#2}{#3}{#4}{#5}{#6}{#7},
{}.{#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8},
{},{#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}
}
\cs_new_protected:Nn \witiko_getreplacement:NN
{
\tl_set:Ne \l__witiko_getreplacement_tmp_tl
{
\seq_item:Nn \l__witiko_getreplacement_seq { \tl_count:e { \cs_parameter_spec:N #1 } }
}
\__witiko_getreplacement:NNV #2 #1 \l__witiko_getreplacement_tmp_tl
}
\cs_new_protected:Nn \__witiko_getreplacement:NNn
{
\tl_set:No #1 { #2 #3 }
}
\cs_generate_variant:Nn \__witiko_getreplacement:NNn { NNV }
\witiko_getreplacement:NN \test \l_tmpa_tl
\tl_show:N \l_tmpa_tl
\tl_set:Nn \l_tmpb_tl { -#1-#2- }
\tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { \typeout{EQUAL} } { \typeout{DIFFERENT} }
\stop
콘솔은 인쇄합니다
> \l_tmpa_tl=-##1-##2-.
<recently read> }
l.36 \tl_show:N \l_tmpa_tl
?
EQUAL
가능한 매개변수 텍스트를 순서대로 저장하고, \tl_set:No
수행 시 전달하기 위해 적합한 항목을 추출합니다.
답변2
LuaTeX를 사용할 수 있다면 구분된 인수가 있는 매크로에 대해서도 이 작업을 수행할 수 있습니다. 매크로 정의의 캣코드를 얻는 것은 예상보다 훨씬 어렵습니다. 따라서 이 작업을 수행하려면 많은 더러운 트릭을 사용해야 합니다.
\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
-----------------
--- Constants ---
-----------------
local assign_toks_cmdcode = token.command_id("assign_toks")
local car_ret_cmdcode = token.command_id("car_ret")
local cs_token_flag = 0x1FFFFFFF
local first_digit_chrcode = string.byte("0")
local first_letter_chrcode = string.byte("a")
local hash_chrcode = string.byte("#")
local last_letter_chrcode = string.byte("z")
local let_token = token.create("let")
local mac_param_cmdcode = token.command_id("mac_param")
local other_char_cmdcode = token.command_id("other_char")
local par_end_cmdcode = token.command_id("par_end")
local random_csname_length = 8
local slice = table.sub
local stop_cmdcode = token.command_id("stop")
local tokspre_token = token.create("tokspre")
----------------------------
--- Function Definitions ---
----------------------------
-- Gets a table representing tokens of a macro definition
function get_toktab_from_macro(value_csname)
local value_token = token.create(value_csname)
-- By default, LuaTeX only gives us the contents of a macro as a string.
-- However, it will give us the contents of a mark node as a table of
-- tokens, so we create a fake mark node that points to the macro's
-- definition.
local tmp_nd = node.direct.new("mark")
node.direct.setprev(tmp_nd + 1, value_token.mode)
return node.direct.getfield(tmp_nd, "mark")
end
-- Splits a macro definition token table into its parameters and its
-- replacement text.
local function split_macro_toktab(meaning_toktab)
local stop_index
local args_count = 0
for i, t in ipairs(meaning_toktab) do
-- Separator between parameters and replacement text (represented by
-- "->" inside of \meaning).
if t[1] == stop_cmdcode then
stop_index = i
-- Convert a macro parameter token in the body back into a "#"
-- token.
elseif t[1] == mac_param_cmdcode and t[3] == 0 then
table.insert(
meaning_toktab,
i + 1,
{ mac_param_cmdcode, hash_chrcode, 1 }
)
elseif t[1] == mac_param_cmdcode and t[3] == 1 then
t[3] = 0
-- Convert a macro parameter token in the body back into a <digit>
-- token.
elseif t[1] == car_ret_cmdcode then
table.insert(
meaning_toktab,
i + 1,
{ other_char_cmdcode, first_digit_chrcode + t[2], 0 }
)
t[2] = hash_chrcode
t[1] = mac_param_cmdcode
-- Convert a macro parameter token in the parameters back into a
-- pair of tokens {"#", <digit>}.
elseif t[1] == par_end_cmdcode then
args_count = args_count + 1
t[1] = mac_param_cmdcode
t[2] = hash_chrcode
table.insert(
meaning_toktab,
i + 1,
{ other_char_cmdcode, first_digit_chrcode + args_count, 0 }
)
end
end
-- Split the token table
return slice(meaning_toktab, 2, stop_index - 1),
slice(meaning_toktab, stop_index + 1, nil )
end
-- Generates a random control sequence name.
local function random_csname()
local random_table = {}
for i = 1, random_csname_length do
local random_letter = string.char(
math.random(first_letter_chrcode, last_letter_chrcode)
)
table.insert(random_table, random_letter)
end
return table.concat(random_table)
end
-- Converts a token table into a \toks token (without giving it a name).
local function toktab_to_token(value_toktab)
local tmp_csname = random_csname()
-- First, we create a mark node to store the raw token in.
local tmp_nd = node.direct.new("mark")
node.direct.setfield(tmp_nd, "mark", value_toktab)
-- TeX expects two levels of indirection for a \toks token, so we first
-- point a \chardef token to the token created by the mark node.
token.set_char(tmp_csname, node.direct.getprev(tmp_nd + 1), "global")
-- Then, we create a \toks token that points to the \chardef token.
return token.create(
token.create(tmp_csname).tok - cs_token_flag,
assign_toks_cmdcode
)
end
-- \let's a token to a control sequence name.
local function let_csname_token(name_csname, value_token)
-- We need to create a token with the name first, otherwise we get an
-- "undefined_cs" token which is useless.
token.set_char(name_csname, 0)
local name_token = token.create(name_csname)
-- There's no way to do this directly from Lua, so we start a new TeX
-- run and use \let to assign the token.
tex.runtoks(function()
token.put_next(let_token, name_token, value_token)
end)
return token.create(name_csname)
end
-- Copies a fake \toks token into a real \toks register.
--
-- The token created by "let_csname_token" is a semi-valid \toks register:
-- it behaves like a \toks register with \the and similar, but it gives a
-- (mostly harmless) error with \show and \meaning. To fix this, we copy
-- the token's contents into a real \toks register.
local function token_to_toksreg(toksreg_csname, value_token)
-- Clear the register
tex.toks[toksreg_csname] = ""
local toksreg_token = token.create(toksreg_csname)
local value_toksreg = let_csname_token(random_csname(), value_token)
-- Prepend the fake \toks register onto the empty real one, giving
-- us a real \toks register with the correct value.
tex.runtoks(function()
token.put_next(tokspre_token, toksreg_token, value_toksreg)
end)
end
-- Registers a TeX command that calls the given Lua function.
local function register_tex_cmd(target_csname, func, args)
local scanners = {}
for _, arg in ipairs(args) do
scanners[#scanners+1] = token["scan_" .. arg]
end
local function scanning_func()
local values = {}
for _, scanner in ipairs(scanners) do
values[#values+1] = scanner()
end
func(table.unpack(values))
end
local index = luatexbase.new_luafunction(target_csname)
lua.get_functions_table()[index] = scanning_func
token.set_lua(target_csname, index, "global")
end
--------------------
--- TeX Commands ---
--------------------
register_tex_cmd("__example_macro_to_toks:N", function(value_csname)
-- Get a table representing the macro's definition tokens
local meaning_toktab = get_toktab_from_macro(value_csname)
-- Split the macro into its parameters and replacement text
local params_toktab, replacement_toktab = split_macro_toktab(meaning_toktab)
-- Save the parameters in a \toks register
local params_token = toktab_to_token(params_toktab)
token_to_toksreg("l__example_parameters_toks", params_token)
-- Save the replacement text in a \toks register
local replacement_token = toktab_to_token(replacement_toktab)
token_to_toksreg("l__example_replacement_toks", replacement_token)
end, {"csname"})
\end{luacode*}
\ExplSyntaxOn
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Variable Declarations %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\exp_args_generate:n { NNNV }
\newtoks \l__example_parameters_toks
\newtoks \l__example_replacement_toks
\tl_new:N \l_example_parameters_tl
\tl_new:N \l_example_replacement_tl
\scan_new:N \s_example
%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Macro Definitions %%%
%%%%%%%%%%%%%%%%%%%%%%%%%
% Sets "\l_example_parameters_tl" with the parameters of the provided macro
% and "\l_example_replacement_tl" with the replacement text of the same
% macro.
\cs_new:Nn \example_macro_to_tl:N {
\__example_macro_to_toks:N #1
\tl_set:NV \l_example_parameters_tl \l__example_parameters_toks
\tl_set:NV \l_example_replacement_tl \l__example_replacement_toks
}
% Defines the provided macro with parameters "\l_example_parameters_tl" and
% replacement text "\l_example_replacement_tl".
\cs_new:Nn \example_tl_to_macro:N {
\exp_args:NNNNV \exp_last_unbraced:NNV \cs_set:Npn #1 \l_example_parameters_tl \l_example_replacement_tl
}
\ExplSyntaxOff
%%%%%%%%%%%%%%%%%%%%%
%%% Demonstration %%%
%%%%%%%%%%%%%%%%%%%%%
\pagestyle{empty}
\begin{document}
\ExplSyntaxOn
% Define a test macro with weird parameters and body
\cs_new:Npn \example_test:w #1 #2 \s_example {
A B C~
<#1>
{ #2 }
\par $a^2 + b^\bgroup \pi^2\egroup$
}
% Get the parameters and replacement text of the test macro
\example_macro_to_tl:N \example_test:w
% Inspect the extracted token lists
\tl_analysis_show:N \l_example_parameters_tl
\tl_analysis_show:N \l_example_replacement_tl
% Modify the extracted token lists
\tl_put_right:Nn \l_example_parameters_tl { #3 }
\tl_put_left:Nn \l_example_replacement_tl { \textbf{#3} }
% Assemble a new macro from the modified token lists
\example_tl_to_macro:N \test
% Test the new macro
\test X {\itshape y}\s_example Z
% Compare the meanings of the original and new macros
\par \meaning\example_test:w
\par \meaning\test
\ExplSyntaxOff
\end{document}
이를 작동시키는 데에는 두 가지 주요 "비법"이 있습니다.
이것이 일반적으로 작동하려면 Lua 문자열뿐만 아니라 토큰 목록(적절한 catcode 포함)으로 매크로를 정의해야 합니다. 따라서 매크로 정의를 얻으려면 먼저 빈
\mark
노드를 만듭니다. 다음으로, 매크로 정의를 나타내는 정수로 내용을 설정하기 위해 몇 가지 트릭을 사용합니다. 그런 다음 노드 값을 요청하면\mark
실제로 Lua 테이블로 표시되는 매크로 내용을 얻습니다.그런 다음 Lua 테이블의 토큰으로 레지스터를 설정하기 위해
\toks
동일한\mark
속임수를 사용하지만 그 반대입니다. 그런 다음\chardef
가리키는 동일한 토큰 목록을 가리키는 토큰을 만듭니다\mark
. 다음으로, 레지스터의 인덱스가 토큰을\toks
가리키도록 조심스럽게 오버플로합니다\chardef
. 오프셋을 신중하게 선택했기 때문에 이 "가짜"\toks
레지스터는 이제\mark
.
또한 내부 TeX 토큰 코드를 프런트엔드가 예상하는 형식으로 다시 변환하는 코드와 가짜 \toks
레지스터를 실제 레지스터로 이동하는 코드가 필요하지만 이는 상대적으로 간단합니다.
답변3
\tl_set_from_cs:NNn
다음은 토큰 목록, 제어 시퀀스 및 매개변수 수를 가져와 제어 시퀀스의 대체 텍스트를 토큰 목록에 할당하는 함수입니다 .
\cs_new_protected:Nn
\tl_set_from_cs:NNn
{
\tl_set:Nn
\l_tmpa_tl
{ #2 }
\int_step_inline:nn
{ #3 }
{
\tl_put_right:Nn
\l_tmpa_tl
{ { #### ##1 } }
}
\exp_args:NNV
\tl_set:No
#1
\l_tmpa_tl
}
egreg의 답변과 달리 추가 데이터 구조가 필요하지 않습니다. 이는 추가 확장 단계의 비용이 들기 때문에 함수가 자주 호출되는 경우 egreg의 접근 방식이 더 나을 수 있습니다.
다음은 이 기능을 사용하는 방법을 보여줍니다 \tl_set_from_cs:NNn
.
\cs_new:Npn
\greet
#1#2
{ Hello,~#1!~How~is~#2~doing? }
\tl_set_from_cs:NNn
\l_greet_tl
\greet
{ 2 }
\tl_put_right:Nn
\l_greet_tl
{ ~Have~a~great~#3! }
\cs_generate_variant:Nn
\cs_generate_from_arg_count:NNnn
{ NNnV }
\cs_generate_from_arg_count:NNnV
\greet
\cs_set:Npn
{ 3 }
\l_greet_tl
이제 \greet { world } { favorite~species~(humans,~presumably) } { evolutionary~leap }
다음 확장을 작성하고 받을 수 있습니다.
안녕, 세상! 당신이 가장 좋아하는 종(아마 인간)은 어떻게 지내나요? 위대한 진화적 도약을 이루세요!
\tl_set_from_cs:NN
egreg의 답변에서 알 수 있듯이 매개변수 사양에서 매개변수 수를 추론하여 함수 서명을 단순화할 수도 있습니다 .
\cs_new_protected:Nn
\tl_set_from_cs:NN
{
\tl_set:Ne
\l_tmpa_tl
{ \cs_parameter_spec:N #2 }
\int_set:Nn
\l_tmpa_int
{ \tl_count:N \l_tmpa_tl / 2 }
\tl_set_from_cs:NNV
#1
#2
\l_tmpa_int
}
\cs_generate_variant:Nn
\tl_set_from_cs:NNn
{ NNV }
다음은 이 기능을 사용하는 방법을 보여줍니다 \tl_set_from_cs:NN
.
\cs_new:Npn
\greet
#1#2
{ Hello,~#1!~How~is~#2~doing? }
\tl_set_from_cs:NN
\l_greet_tl
\greet
\tl_put_right:Nn
\l_greet_tl
{ ~Have~a~great~#3! }
\cs_generate_from_arg_count:NNnV
\greet
\cs_set:Npn
{ 3 }
\l_greet_tl
이제 \greet { world } { favorite~species~(humans,~presumably) } { evolutionary~leap }
다음 확장을 다시 작성하고 받을 수 있습니다.
안녕, 세상! 당신이 가장 좋아하는 종(아마 인간)은 어떻게 지내나요? 위대한 진화적 도약을 이루세요!
expl3의 l3tl 모듈에 함수 \tl_set_from_cs:NN
를 추가하려면 \tl_gset_from_cs:NN
.
또한 무제한 매개변수가 있는 제어 시퀀스만 이 방식으로 토큰 목록으로 변환될 수 있다는 점을 명확하게 문서화해야 합니다.
편집: 추가 설명(2024-04-27)
#
다음과 같이 대체 텍스트에서 이중 s를 유지할 수 있습니다 .
- 대체 텍스트에 나타날 가능성이 없는
\witiko_parameter_1
,\witiko_parameter_2
, 등과 같은 제어 시퀀스를 매개변수로 사용하여 제어 시퀀스를 확장합니다 .\witiko_parameter_3
#
확장 결과에 s를 두 배로 늘립니다 .- 확장 결과의 , , 등을 , , 등 으로 대체합니다
\witiko_parameter_1
.\witiko_parameter_2
\witiko_parameter_3
#1
#2
#3
구체적인 구현은 다음과 같습니다.
\cs_new_protected:Nn
\tl_set_from_cs:NNn
{
\tl_set:Nn
\l_tmpa_tl
{ #2 }
\int_step_inline:nn
{ #3 }
{
\exp_args:NNc
\tl_put_right:Nn
\l_tmpa_tl
{ witiko_parameter_ ##1 }
}
\exp_args:NNV
\tl_set:No
\l_tmpb_tl
\l_tmpa_tl
\regex_replace_all:nnN
{ \cP. }
{ \0\0 }
\l_tmpb_tl
\int_step_inline:nn
{ #3 }
{
\regex_replace_all:nnN
{ \c { witiko_parameter_ ##1 } }
{ \cP\# ##1 }
\l_tmpb_tl
}
\tl_set:NV
#1
\l_tmpb_tl
}
구현 시연은 다음과 같습니다.
\cs_new_protected:Npn
\greet
#1#2
{
Hello,~#1!
\group_begin:
\cs_set:Npn
\greet
##1
{ ~How~is~##1~doing? }
\greet { #2 }
\group_end:
}
\tl_set_from_cs:NN
\l_greet_tl
\greet
\tl_put_right:Nn
\l_greet_tl
{ ~Have~a~great~#3! }
\cs_generate_from_arg_count:NNnV
\greet
\cs_set:Npn
{ 3 }
\l_greet_tl
이제 \greet { world } { favorite~species~(humans,~presumably) } { evolutionary~leap }
다음 확장을 작성하고 받을 수 있습니다.
안녕, 세상! 당신이 가장 좋아하는 종(아마 인간)은 어떻게 지내나요? 위대한 진화적 도약을 이루세요!
예상대로입니다.
답변4
#
이중 매개변수 문자( ), 구분된 인수 및 두 가지 제한 사항이 있는 모든 엔진을 사용하여 이 작업을 수행할 수 있습니다 .
대상 매크로할 수 없다
"FF
매크로 매개변수 문자로 사용합니다 .모든 구분 기호~ 해야 하다"정상적인" catcode를 가지고 있습니다.
매크로의 99% 이상이 이러한 요구 사항을 충족할 것으로 기대하므로 실제로는 너무 제한적이지 않습니다.
\documentclass{article}
\pagestyle{empty}
\parindent=0pt
\ExplSyntaxOn
%%%%%%%%%%%%%%%%%%%
%%% Definitions %%%
%%%%%%%%%%%%%%%%%%%
%% Copies the replacement text of a macro (#1) into a token list (#2).
\cs_new_protected:Nn \example_cs_to_tl:NN {
%% Get the parameters used by the macro
\tl_set:Ne #2 { \cs_parameter_spec:N #1 }
%% Convert the parameters into normal catcodes to handle any delimited
%% arguments.
\tl_set_rescan:NnV #2 {} #2
%% Use <"FF>_6 as the macro parameter character instead of the usual <#>_6.
%% We do this so that we can distinguish between any inner macro parameters
%% and the new ones that we're passing in here.
\regex_replace_all:nnN { \# ( \d ) } { { \cP\xFF \1 } } #2
%% Expand the macro to get at its replacement text.
\tl_set:Nf #2 {
\exp_last_unbraced:NNNo \exp_after:wN \exp_stop_f: #1 #2
}
%% Double all the original parameter characters, ignoring our new ones.
\regex_replace_all:nnN { \cP [^ \xFF ] } { \0 \0 } #2
}
%% I've used inline regexes here to make the code easier to follow, but you
%% should use `\regex_const:Nn` in the real code since it's considerably faster.
\cs_generate_variant:Nn \cs_generate_from_arg_count:NNnn { NNnV }
%%%%%%%%%%%%%%%%%%%%%
%%% Demonstration %%%
%%%%%%%%%%%%%%%%%%%%%
%% A test macro with two normal arguments and some inner doubled #'s.
\cs_new:Npn \original #1 #2 {
Hello,~#1!~
\group_begin:
\cs_set:Npn \original ##1 {
~How~is~##1~doing?
\cs_gset:Npn \inner ####1 {
#1~##1~####1.
}
}
\original { #2 }
\group_end:
}
%% Extract the replacement text and define a new macro with the same body.
\example_cs_to_tl:NN \original \l_tmpa_tl
\cs_generate_from_arg_count:NNnV \new \cs_set:Npn { 2 } \l_tmpa_tl
%% A test macro with some weird delimited arguments.
\cs_new:Npn \weirddelims #1, #2 #3 ABC \relax {
<#2>
\def \inner ##1 #3 {
<#1>
}
}
%% Extract the replacement text and define a new macro with the same body.
\example_cs_to_tl:NN \weirddelims \l_tmpa_tl
\cs_generate_from_arg_count:NNnV \newweirddelims \cs_set:Npn { 3 } \l_tmpa_tl
\ExplSyntaxOff
%% Show the comparison between the original and new macros.
\begin{document}
\texttt{\small \meaning\original}%
\par
\original{AAA}{BBB} \inner{CCC}
\bigskip
\texttt{\small \meaning\original}%
\par
\new{DDD}{EEE} \inner{FFF}
\bigskip
\texttt{\small \meaning\weirddelims}%
\par
\weirddelims{GGG},{HHH}{III}ABC\relax \inner{JJJ}III
\bigskip
\texttt{\small \meaning\newweirddelims}%
\par
\newweirddelims{KKK}{LLL}{MMM} \inner{NNN}MMM
\end{document}
여기서의 비결은 "내부" 중첩 매개변수와 가장 외부의 "실제" 매개변수를 구별할 수 있도록 대상 매크로를 확장할 때 다른 매크로 매개변수 문자를 사용하는 것입니다.