如何通过命令做出声明?

如何通过命令做出声明?

设置字体样式有两种形式:声明式方法(例如)\itshape和命令式形式(例如)\textit。命令式形式带有参数,而声明式形式则不带有参数 --- 它只是将自身应用于随后出现的任何内容。

我想开发一种声明形式,比如\UppercaseIt命令形式的\MakeUppercase{<text>}。我已经在source2e.pdfCTAN 上阅读了实现的代码\textit\itshape和,\MakeUppercase但我不能说我理解其中任何一个。我想要的是类似的东西,{\UppercaseIt some text}而不是\MakeUppercase{some text}

我只有一个我想要的代码大纲,因为我不知道该怎么做:

\documentclass{article}

\newcommand{\UppercaseIt}{%
  %... magic
}

\begin{document}

\MakeUppercase{some text}% Yields SOME TEXT

{\UppercaseIt some text}% Should yield SOME TEXT

\end{document}

答案1

其他人已经说过为什么这实际上是不可能的,所以我不会把更多的沙子带到这个海滩。这是一个通过向前扫描直到下一个来伪造该行为的实现明确的右括号,然后抓取所有内容并将其\MakeUppercase作为参数传递给。

该实现使用expl3的 new\peek_analysis_map_inline:n逐个标记读取输入流,并根据该标记的 catcode 决定要做什么。如果找到 catcode 1(开始组)的标记,则增加计数器。类似地,如果找到 catcode 2(结束组)的标记,则减少该计数器。如果找到 catcode 2 的标记,并且计数器为零,则扫描停止,到目前为止收集的标记作为参数传递给\MakeUppercase\text_uppercase:n实际上),并且找到的最终结束组标记留给 TeX 执行其工作。

\itshape这与遵循 TeX 组的 字体命令大不相同。 \itshape(以此为例)将一直有效,直到当前组关闭,或直到文档结束。组结尾可能是显式结束组标记(如)},隐式结束组标记(如)\egroup,或实际结束组标记(如\endgroup),或其他一些不太常见的标记,并且此标记可能隐藏在宏内。此处定义的命令仅适用于显式结束组标记(}),无需扩展标记即可看到(基本上是 TeX 在抓取宏参数时接受的右括号)。

以下是代码:

\documentclass{article}

\usepackage{xparse}
\ExplSyntaxOn
\tl_new:N \l__wilson_collect_tl
\int_new:N \l__wilson_nesting_int
\NewDocumentCommand \UppercaseIt { }
  {
    % magic follows:
    \wilson_declarative_command:n
      { \text_uppercase:n {##1} }
  }
\cs_new_protected:Npn \wilson_declarative_command:n #1
  {
    \cs_set_protected:Npn \__wilson_do_cmd:n ##1 {#1}
    \tl_clear:N \l__wilson_collect_tl
    \peek_analysis_map_inline:n { \__wilson_analyse:nnn {##1} {##2} {##3} }
  }
\cs_new_protected:Npn \__wilson_analyse:nnn #1 #2 #3
  {
    \if_case:w "#3 \exp_stop_f:
      \exp_after:wN \use_i:nn
    \or:
      \int_incr:N \l__wilson_nesting_int
      \exp_after:wN \use_i:nn
    \or:
      \int_compare:nNnTF { \l__wilson_nesting_int } = { 0 }
        { \exp_after:wN \use_ii:nn }
        {
          \int_decr:N \l__wilson_nesting_int
          \exp_after:wN \use_i:nn
        }
    \else:
      \exp_after:wN \use_i:nn
    \fi:
      { \tl_put_right:Nn \l__wilson_collect_tl {#1} }
      { \__wilson_analyse_end:n {#1} }
  }
\cs_new_protected:Npn \__wilson_analyse_end:n #1
  {
    \tl_set:Nx \l__wilson_collect_tl { \l__wilson_collect_tl }
    \peek_analysis_map_break:n
      {
        \exp_last_unbraced:NNNo
          \exp_args:NV \__wilson_do_cmd:n \l__wilson_collect_tl
          #1
      }
  }
\ExplSyntaxOff

\begin{document}

\MakeUppercase{some text}% Yields SOME TEXT

{\UppercaseIt some text}% Should yield SOME TEXT

{\UppercaseIt {some} text}% Should yield SOME TEXT

\end{document}

但那只是为了炫耀\peek_analysis_map_inline:n真的方便。有了这些限制,你可以得到同样的行为,很多通过使用一些简单的方法可以减少工作量:

\documentclass{article}

\newcommand\UppercaseIt{%
  \expandafter\UppercaseItAux
    \expandafter{\iffalse}\fi}
\newcommand\UppercaseItAux[1]{%
  \def\uppercasetmpa{\MakeUppercase{#1}}%
  \expandafter\uppercasetmpa\iffalse{\fi}}

\begin{document}

\MakeUppercase{some text}% Yields SOME TEXT

{\UppercaseIt some text}% Should yield SOME TEXT

{\UppercaseIt {some} text}% Should yield SOME TEXT

\end{document}

答案2

该软件包的新版本luaotfload(2020-12-31 luaotfload v3.16)引入了新的虚拟字体功能upperlower。使用它们时,转换将在字体级别完成,正如 OP 所期望的那样。

例如,在 OpTeX 中,我们可以尝试:

\fontfam[lmfonts]

\def\UppercaseIt{\setff{upper}\currvar}

Normal text 
{\UppercaseIt a {\bf converted} text, accents: áéčš}, 
normal text.

\bye

答案3

没有任何解决方案可以做到与字体切换完全相同的事情,因为字体切换是在 TeX 的主处理器上完成的,并且下一步处理处于“正常”模式:所有命令都是立即解释的。

您只能进行两次处理,第一次扫描参数,然后将其放入\uppercase原始数据。但行为有所不同,例如您可以:

\fontswitch ... \dosomething exactly now in a macro .... \end{document}

但这行不通:

\UppercaseIt ...  \dosomething exacty now in a macro ... \end{document}

你的任务的实现只是“学术性的”。我可以向你展示比逐个标记扫描标记更简单的实现(在此处的另一个答案中显示):

\def\UppercaseIt{\afterassignment\UppercaseItA 
   \long\expandafter\def\expandafter\tmp\expandafter{\iffalse}\fi}
\def\UppercaseItA{\uppercase\expandafter{\tmp}\egroup}


Test: {\UppercaseIt some text}% Should yield SOME TEXT

\bye

答案4

修改后的答案

使用最新的tokcycle软件包功能,无需显式终止标记循环。相反,循环可以提前查看输入流中是否下一个是组/环境结束符,并据此终止。

此外,我还创建了一个通用命令\MakeDeclarationOf,其语法如下

\MakeDeclarationOf\MakeUppercase\UppercaseIt
\MakeDeclarationOf\bang\Foo

创建声明\UppercaseIt,使后续内容大写,或者\Foo使后续内容由 进行操作\bang

\MakeDeclarationOf\MakeUppercase\UppercaseIt被调用时,它会创建一个名为 的 tokencycle 环境。tokencycle使用\UppercaseIt辅助命令来检查输入流中的组/环境结束标记,如果找到,则将 tokcycle 通用终止符插入输入流中。\testendgroup\endtokcycraw

因此,可以用、用、用、用或用\UppercaseIt来终止(如果在当前环境深度)。\endUppercaseIt}\egroup\endgroup\end

终止时,tokencycle 会获取输入流中收集到的标记,并应用由提供给 的第一个参数组成的包装器\MakeDeclarationOf。因此,类似这样的最终结果{\UppercaseIt abc}是执行以下标记:\MakeUppercase{abc}

以下 MWE 做出了两个这样的声明命令,以证明多个声明不会互相干扰。

\documentclass{article}
\usepackage{tokcycle}
\newcounter{envlevel}
\newcommand\MakeDeclarationOf[2]{
\xtokcycleenvironment#2
  {\addcytoks{####1}\testendgroup}
  {\processtoks{####1}\testendgroup}
  {\trackenvs{####1}\addcytoks{####1}\testendgroup}
  {\addcytoks{####1}\testendgroup}
  {\setcounter{envlevel}{0}}
  {\cytoks\expandafter{\expandafter#1\expandafter{\the\cytoks}}}%
}
\newcommand\trackenvs[1]{%
  \ifx\begin#1\stepcounter{envlevel}\else
   \ifx\end#1\addtocounter{envlevel}{-1}\fi\fi   
}
\newcommand\testendgroup{\tcpeek\z
  \ifx\egroup\z\tcpush{\empty\endtokcycraw}\else
  \ifx\endgroup\z\tcpush{\empty\endtokcycraw}\else
  \ifx\end\z\ifnum\value{envlevel}=0 \tcpush{\empty\endtokcycraw}\fi\fi
  \fi\fi
}

\newcommand\bang[1]{!#1!}

\begin{document}
\MakeDeclarationOf\MakeUppercase\UppercaseIt
\MakeDeclarationOf\bang\Foo
Here is {\Foo declaration test} to see \textbackslash bang

This is a test \UppercaseIt of finding endUppercaseIt
\endUppercaseIt

This is a test {\UppercaseIt of finding right brace} 
continuing...

This is a test \bgroup\UppercaseIt of finding egroup\egroup 
continuing...

This is a test \begingroup\UppercaseIt of finding endgroup
\endgroup continuing...

Test \Foo of \begin{itshape}terminating at\end{itshape} 

the proper \textbackslash end

\end{document}

在此处输入图片描述

相关内容