是否有可扩展的 TeX 基元列表?LaTeX?e-TeX?其他?

是否有可扩展的 TeX 基元列表?LaTeX?e-TeX?其他?

根据Christian Hupfer 的评论keyval我理解当宏可扩展时,列表中的计算是可能的。宏\numexpr可扩展,因此计算是可能的。

因此,我们想知道哪些宏并且哪个不是可从 TeX、LaTeX、ε-TeX 等扩展。

我不是问是否有测试,而是问是否有列表已经完成。

答案1

约瑟夫已经回答了,将你问题中的“可扩展”解释为“完全可扩展”(或者可能更好地命名为“仅在扩展环境中安全”,请参阅

完全可扩展宏的优点和缺点

然而,要回答实际提出的问题,请注意它既\numexpr不是宏也不是可扩展的。

根据定义,所有宏都是可扩展的,宏通过扩展为替换文本来工作。没有这些列表,因为它是通过\def或其变体定义的任何命令\edef,,\gdef\xdef

TeXBook 列出了所有的 TeX 原语,这些原语*在索引中以 标记,但您需要单独检查每个原语的描述以查看它们是否可扩展。


经典 TeX,可扩展原语是(我认为:-)

\endinput
\expandafter
\csname
\the
\number
\romannumeral
\if
\ifx
\ifcat
\ifcase
\ifnum
\ifodd
\ifdim
\ifeof
\iftrue
\iffalse
\ifhbox
\ifvbox
\ifvoid
\ifinner
\ifvmode
\ifhmode
\ifmmode
\else
\or
\fi
\input % (\@@input in LaTeX)
\jobname
\meaning
\noexpand
\string
\topmark
\firstmark
\botmark
\fontname

电子特克斯额外的可扩展基元是

\topmarks
\firstmarks
\botmarks
\ifdefined
\ifcsname
\iffontchar
\unless
\eTeXrevision
\unexpanded
\detokenize
\scantokens

除了以上所有pdfTeX添加以下可扩展原语

\pdfescapestring
\pdfescapename
\pdfescapehex
\pdfstrcmp
\pdfmatch
\ifpdfabsnum
\ifpdfabsdim
\pdfuniformdeviate
\pdfnormaldeviate
\pdffilemoddate
\pdffilesize
\pdfmdfivesum
\pdffiledump
\pdfcolorstackinit
\ifincsname
\ifpdfprimitive
\pdfcreationdate
\pdfinsertht
\pdftexbanner
\pdftexrevision
\expanded

特克斯具有 etex 的原语,并添加了以下可扩展原语

\ifincsname
\ifprimitive
\normaldeviate
\uniformdeviate
\creationdate
\filedump
\filemoddate
\filesize
\mdfivesum
\expanded
\XeTeXrevision
\Uchar
\Ucharcat
\strcmp

LuaTeX具有 etex plus 的可扩展原语(至少)

\luatexbanner
\luatexrevision
\formatname
\Uchar
\directlua
\luaescapestring
\scantextokens
\csstring
\expanded
\ifincsname
\pdfvariable
\pdffeedback

特克斯(日文 TeX 引擎)添加了以下可扩展基元,可用于ptexuptexeptexeuptex

\euc
\ifdbox
\ifddir
\ifjfont
\ifmbox
\ifmdir
\iftbox
\iftdir
\iftfont
\ifybox
\ifydir
\jis
\kansuji
\kuten
\ptexfontname
\ptexrevision
\sjis
\tojis
\toucs
\ucs

更新TeX(支持 Unicode 的 pTeX)添加了以下可扩展原语,可在uptex和中使用euptex

\uptexrevision

普特克斯(pTeX + e-TeX)添加了以下可扩展原语,可在eptex和中使用euptex

\expanded
\ifincsname
\ifpdfprimitive
\pdfcreationdate
\pdffiledump
\pdffilemoddate
\pdffilesize
\pdfmdfivesum
\pdfnormaldeviate
\pdfstrcmp
\pdfuniformdeviate
\Uchar
\Ucharcat

如果您发现缺失的内容,请随意在此处编辑此答案....

答案2

的列表全部可扩展宏是完全不可能提供的:有无限多的案例。我们可以做的是提供一个通过扩展工作的原语列表,以及宏的一般规则。

本质上,任何执行任务或者排版不可扩展,而其他基元则可以。例如\def\let不可扩展,而其他基元则ETC。都是不可扩展的,例如\hbox\raise、,\setbox\expandafter\the\ifx都是可扩展的。请注意,一些基元将是可扩展的在正确的背景下:\numexpr本身不可扩展,但它是紧随其后的有效\the标记\number

\edef\testa{\numexpr 1+2\relax}\show\testa
\edef\testb{\number\numexpr 1+2\relax}\show\testb

就宏而言,我们必须清楚“可扩展”是什么意思。TeX 是一种宏扩展语言,因此,如果我们有

\def\baz{}
\def\foo{\def\baz{bar}}

然后我们

\edef\test{\foo}\show\test

尽管结果可能不是我们想要的/有用的。因此,当我们谈论可扩展宏时,我们通常指的是宏它只包含可扩展的原语,因此可以在\edef或类似的。使用 e-TeX,可以确保不符合此标准的宏不会“爆炸”:

\def\baz{}
\protected\def\foo{\def\baz{bar}}
\edef\test{\foo}\show\test

这将带我们去哪里?任何包含以下内容的宏:

  • 任何赋值原语
  • 任何排版原语
  • 任意\protected
  • 任何满足上述三个标准之一的宏

不是可扩展。这是大多数“有用”的宏,因此如评论中所述,如果您不确定,请假设不可扩展。


请注意,不可扩展原语列表很长,我们必须记住上下文。例如,像 这样的函数\tracingcommands充当计数寄存器,因此除非 后面跟着 ,否则它是不可扩展的\the。因此,除非我们实现逐个标记的处理器,否则仅仅看到\tracingcommands并不能告诉我们包含此标记的宏是不可扩展的。

\def\foo{\tracingcommands=1 }% Not expandable
\def\baz{\the\tracingcommands}% Expandable

在 LaTeX3/ 中expl3,所有宏要么完全可扩展,要么是\protected。此外,可扩展的宏在文档中都标记为可扩展(带有星号)。原因是需要一些知识才能判断某个东西是否可扩展,特别是检查所有宏“依赖项”。因此,必须小心谨慎地跟踪可扩展的宏。

答案3

虽然没有明确要求进行测试,但值得一提的是,对标记可扩展性的测试可以基于以下情况:对于可扩展标记和未定义标记,测试

\expandafter\ifx\noexpand⟨token⟩⟨token⟩⟨token not expandable⟩\else⟨token expandable or undefined⟩\fi

传递 -branch 的 token \else/传递 -branch 的 token ⟨token expandable or undefined⟩。当⟨token expandable or undefined⟩-branch 被采用时,您可以评估\meaning⟨token⟩

\meaning在任何情况下都将提供类别代码 12(其他)的字符。唯一的例外是空格字符。\meaning将始终提供类别代码 10(空格)(和字符代码 32)的空格。

当且仅当⟨token⟩未定义时,\meaning才会传递前导 catcode-12-token 序列undefined

当且仅当⟨token⟩是宏,则 传递的标记\meaning将包含序列->

某些可扩展原语在某些情况下可能需要特殊处理/注意,例如\input\@@input在 LaTeX 中重命名为)、、\endinput\noexpand这些可能也可以通过评估来得出\meaning

除此之外,在 LaTeX 中,有些标记有时是可扩展/不可扩展的原语,有时是可扩展的宏。在写这句话的时候,我会想到标记\protect。有时\protect让 等于不可扩展的原语\relax。有时\protect让 等于可扩展的原语\noexpand。有时\protect让 等于\@unexpandable@protect扩展为 的宏\noexpand\protect\noexpand


这里是一个 LaTeX 实现的测试,用于测试宏参数是否具有第一个可扩展原语的标记:

(首先,您需要知道参数是否为空。如果不是,您需要知道它的第一个标记是否可以提取为未分隔的参数。当且仅当它是 catcode 10 和 charcode 32 的显式空间或 catcode 1 的左括号/显式字符标记,则它不能被提取为未分隔的参数。但它也不是可扩展的原语。如果可以提取,则让 TeX 提取它并检查\expandafter\ifx\noexpand#1#1它是否可扩展/未定义。如果它不可扩展,则它不是可扩展的原语。如果它是可扩展的或未定义的,请检查它\meaning是否是宏或未定义的情况。)

\errorcontextlines=10000
\documentclass{article}

\makeatletter
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange,  \UD@CheckWhetherNull, 
%%    \UD@CheckWhetherBrace, \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>

\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
}%
%% With eTeX-engines you can also do:
%%\newcommand\UD@CheckWhetherNull[1]{%
%%  \romannumeral0\ifcat$\detokenize{#1}$%
%%  \expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
%%  {\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
%%  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
%%}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is a
%%                               space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%   \romannumeral0\UD@ExtractFirstArgLoop{<argument>UD@SelDOm}%
%%   yields <argument>'s 1st undlimited argument.
%%   <argument> must not be blank, i.e., must not be empty or consist of
%%   explicit character tokens of catcode 10 and charcode 32 only.
%%.............................................................................
\@ifdefinable\UD@RemoveTillUD@SelDOm{%
  \long\def\UD@RemoveTillUD@SelDOm#1#2UD@SelDOm{{#1}}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  { #1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% Check whether token is macro (only with macros the \meaning contains the
%% sequence ->) - the argument of \UD@CheckWhetherMacro must be a single token;
%% the argument of \UD@CheckWhetherMacro must not be empty:
%%=============================================================================
\newcommand\UD@CheckWhetherMacro[1]{%
  \expandafter\UD@@CheckWhetherMacro\meaning#1->X%
}%
\@ifdefinable\UD@@CheckWhetherMacro{%
  \def\UD@@CheckWhetherMacro#1->#2X{%
    \UD@CheckWhetherNull{#2}{\UD@secondoftwo}{\UD@firstoftwo}%
  }%
}%
%%=============================================================================
%% Check whether token is undefined control sequence (only with undefined 
%% control sequences the \meaning has the leading phrase "undefined"; besides
%% this \meaning never delivers no tokens at all) - the argument of 
%% \UD@CheckWhetherUndefined must be a single token; the argument of 
%% \UD@CheckWhetherUndefined must not be empty:
%%=============================================================================
\begingroup
\def\UD@CheckWhetherUndefined#1{%
  \endgroup
  \newcommand\UD@CheckWhetherUndefined[1]{%
    \expandafter\UD@@CheckWhetherUndefined\meaning##1#1X%
  }%
  \@ifdefinable\UD@@CheckWhetherUndefined{%
    \def\UD@@CheckWhetherUndefined##1#1##2X{%
      \UD@CheckWhetherNull{##1}{\UD@firstoftwo}{\UD@secondoftwo}%
    }%
  }%
}%
\escapechar=-1\relax
\expandafter\UD@CheckWhetherUndefined\expandafter{\string\undefined}%
%%=============================================================================
%% Check whether argument has first token which is an expandable primitive
%%=============================================================================
\newcommand\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive[1]{%
  \romannumeral0%
  \UD@CheckWhetherNull{#1}{\UD@firstoftwo\expandafter{} \UD@secondoftwo}{%
    \UD@CheckWhetherBrace{#1}{\UD@firstoftwo\expandafter{} \UD@secondoftwo}{%
      \UD@CheckWhetherLeadingSpace{#1}{\UD@firstoftwo\expandafter{} \UD@secondoftwo}{%
        \expandafter\UD@CheckWhetherArgExamineArgsFirstToken
        \romannumeral0\UD@ExtractFirstArgLoop{#1UD@SelDOm}%
      }%
    }%
  }%
}%
\newcommand\UD@CheckWhetherArgExamineArgsFirstToken[1]{%
  \expandafter\ifx\noexpand#1#1%
  \expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi{%
    \UD@firstoftwo\expandafter{} \UD@secondoftwo
  }{%
    \UD@CheckWhetherMacro{#1}{\UD@firstoftwo\expandafter{} \UD@secondoftwo}{%
      \UD@CheckWhetherUndefined{#1}{\UD@firstoftwo\expandafter{} \UD@secondoftwo}{%
        \UD@firstoftwo\expandafter{} \UD@firstoftwo
      }%
    }%
  }%
}%
\makeatother
\begin{document}

These tests are "negative"

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{{\ifx}\relax}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{\relax\number}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{\TeX}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{ something}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{\UnDeFInEdSelDOMWeiRDStrangeToken}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}


These tests are "positive"

\makeatletter

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{\@@input myfilewith weirdcode.tex }%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\makeatother

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{\number\relax}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{\ifx aa aa \else aa \fi}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{\noexpand\relax}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\CheckWhetherArgHasFirstTokenWhichIsExpandablePrimitive{\fontname}%
                                                       {Argument does have a first token which is an expandble primitive}%
                                                       {Argument does not have a first token which is an expandble primitive}

\end{document}

在此处输入图片描述

相关内容