我想提取 LaTeX3 字符串中的宏定义(最好通过任何命令定义),例如稍后将其写入文件中。可以吗?
梅威瑟:
\documentclass[]{article}
\begin{document}
{
\NewDocumentCommand{\Hello}{O{}m}{Hello #2 (arg #1).}
Normal usage: \Hello[hey]{Alice}
%% This part: put the code used to define the macro in a LaTeX3 string
%% (I usually can't change how \Hello is defined, so I should only do something here)
\ExplSyntaxOn
\cs_generate_variant:Nn \str_replace_all:Nnn { Nnx }
\cs_set:Nn \my_set_hash_robust:Nn {
\str_set:Nn {#1} {#2}
\str_replace_all:Nnx {#1} { ## } { \c_hash_str }
}
\cs_generate_variant:Nn \my_set_hash_robust:Nn { cn }
\cs_generate_variant:Nn \my_set_hash_robust:Nn { cx }
%% This line should be replaced by an automatic code somehow
\my_set_hash_robust:Nn \l_contentHello_str {\NewDocumentCommand{\Hello}{O{}m}{Hello ~#2~ (arg~ #1).}}
%%% This part should not really be touched
\ior_new:N \g_myfile
\iow_open:Nn \g_myfile {cmd.tex}
\cs_generate_variant:Nn \iow_now:Nn { NV }
\iow_now:NV \g_myfile {\l_contentHello_str}
\iow_close:N \g_myfile
\ExplSyntaxOff
}
\input{cmd.tex}
\Hello[my args]{Bob}
\end{document}
编辑 我们正在取得进展,看这里如何提取用户在 \NewDocumentCommand 中输入的文本
答案1
因此,感谢 egreg 的指针另一个答案,我设法让它工作:
\documentclass{article}
\usepackage{xparse}
\makeatletter
\ExplSyntaxOn
\cs_generate_variant:Nn \cs_replacement_spec:N { c }
\cs_generate_variant:Nn \cs_argument_spec:N { c }
\cs_new:Nn \__myModule_getCommandDefinitionFromXparse:N
{
\str_clear_new:N \l__myModule_definition_str
\GetDocumentCommandArgSpec { #1 }
\str_set:Nx \l__myModule_definition_str {
\c_backslash_str DeclareDocumentCommand
\c_left_brace_str
\c_backslash_str \cs_to_str:N #1
\c_right_brace_str
\c_left_brace_str \ArgumentSpecification \c_right_brace_str
\c_left_brace_str
\cs_replacement_spec:c { \cs_to_str:N #1 ~ code }
\c_right_brace_str
}%
}
\cs_new:Nn \__myModule_getCommandDefinitionFromDef:N
{
\str_clear_new:N \l__myModule_definition_str
\str_set:Nx \l__myModule_definition_str {
\c_backslash_str def \c_backslash_str \cs_to_str:N #1
\cs_argument_spec:c { \cs_to_str:N #1 }
\c_left_brace_str
\cs_replacement_spec:c { \cs_to_str:N #1 }
\c_right_brace_str
}%
}
\def\myModule@extractDefaultNewcommand#1#2#3#4{
#4%
}%
\cs_new:Nn \__myModule_getCommandDefinitionFromNewcommand:N
{
\str_clear_new:N \l__myModule_definition_str
\str_clear_new:N \l_myModule_tmp
%% \l_myModule_tmp will look like [#1]#2#3#4#5#6#7:
\str_set:Nx \l_myModule_tmp {\cs_argument_spec:c { \c_backslash_str \cs_to_str:N #1 }}%
%% We remove the brackets:
\str_replace_all:Nnn \l_myModule_tmp {[} {}
\str_replace_all:Nnn \l_myModule_tmp {]} {}
\str_set:Nx \l__myModule_definition_str {
%% this line will be like "\newdocumentcommand{\mymacro}"
\c_backslash_str newcommand \c_left_brace_str \c_backslash_str \cs_to_str:N #1 \c_right_brace_str
%% It must have at least one argument, since elements without optional arguments are turned into \def
%% Moreover, macros can't have more than 1 argument, so it will be easier to parse, we just need the last digit
[\str_range:Nnn \l_myModule_tmp {-1} {-1}] %% <- this is the number of mandatory arguments
[\expandafter \myModule@extractDefaultNewcommand #1 ] %% <- this is the value of the default argument
\c_left_brace_str
\cs_replacement_spec:c { \c_backslash_str \cs_to_str:N #1 }
\c_right_brace_str
}%
}
\NewDocumentCommand{\myModuleGetCommandDefinition}{m}{
\cs_if_exist:NTF #1 {
\cs_if_exist:cTF {\cs_to_str:N #1 ~ code}{
% xparse-based definition
\__myModule_getCommandDefinitionFromXparse:N #1%
\l__myModule_definition_str%
} {
\cs_if_exist:cTF {\c_backslash_str \cs_to_str:N #1}{
% \newcommand-based definition
\__myModule_getCommandDefinitionFromNewcommand:N #1
\l__myModule_definition_str%
}{
% \def-based definition
\__myModule_getCommandDefinitionFromDef:N #1
\l__myModule_definition_str%
}
}
}{
\PackageError{myModule}{The~macro~#1~does~not~exist}
}
}
\ExplSyntaxOff
\makeatother
\begin{document}
This works for xparse-based definitions:
\NewDocumentCommand{\testXparse}{O{default}m}{Hey (#1) #2 you are nice.}
\texttt{\myModuleGetCommandDefinition{\testXparse}}\\
This works for simple def:
\def\testDef[#1]#2{I am #1 from #2 in a def.}
\texttt{\myModuleGetCommandDefinition{\testDef}}\\
This works now for simple newcommand-based definitions:
\newcommand{\testNewcommand}[7][mydefaultvalue]{From newcommand (#1) #2 seems to work.}
\texttt{\myModuleGetCommandDefinition{\testNewcommand}}\\
If we do not put any default value, it is equivalent to a def:\\
\newcommand{\testNewcommandNoDefault}[2]{From newcommand (#1) #2 seems to work.}
\texttt{\myModuleGetCommandDefinition{\testNewcommandNoDefault}}
Test without optional argument:
\newcommand{\testNewcommandNoOpt}[7]{From newcommand (#1) #2 without optional arguments.}
\texttt{\myModuleGetCommandDefinition{\testNewcommandNoOpt}}\\
\end{document}