关于宏扩展的一些误解

关于宏扩展的一些误解

我有以下不起作用的代码:

\documentclass[12pt]{article}

\tracingcommands=2
\tracingmacros=2
\tracingall

\makeatletter

\let\sep\relax

\def\put@stack@#1#2{\edef#2{#1\sep#2}}

\def\put@queue@#1#2{\edef#2{#2\sep#1}}

\def\get@#1#2{\expandafter\get@@#2\endget@@#1#2}

\def\get@@#1\sep#2\endget@@#3#4{\edef#3{#1}\edef#4{#2}}

\def\empty@stack{\sep}

\newtoks\piecetoks

\def\leftrightbr{
    \let\o@left\left
    \let\o@right\right
    \let\piece@nd\relax
    \let\pieces@stack\empty@stack
    \let\delims@stack\empty@stack
    \let\on@end@line\empty@stack
    \let\on@begin@line\empty@stack
    \def\left##1{
        \piece@nd
        \let\piece@nd\endgroup
        \def\on@first@right{
            \put@stack@{\the\toks0}{\pieces@stack}
        }
        \put@stack@{##1}{\delims@stack}
        \let\repl\relax
        \let\vphantomer####1{}
        \put@stack@{\the\piecetoks}{\pieces@stack}
        \put@stack@{\right.}{\on@end@line}
        \put@stack@{\left.}{\on@begin@line}
        \piecetoks=\begingroup
    }
    \def\right##1{
        \endgroup
        \on@first@right
        \let\on@first@right\relax
        \get@\@dummy\on@end@line
        \get@\@dummy\on@begin@line
        \get@\@lpart\pieces@stack
        \get@\@ldelim\delims@stack
        \ifx\@lpart\empty
            \repl
        \else
            \edef\repl{\o@left\@ldelim\@lpart\vphantomer{\the\piecetoks}\repl\the\piecetoks\vphantomer{\@lpart}\o@right##1}
        \fi
    }
    \def\mathbr{
        \piece@end
        \put@stack@{\piece}{\pieces@stack}
        \let\vphantomer\vphantom
        \edef\repl{\on@end@line \\ \on@begin@line}
        \def\piece\begingroup
    }
}

\begin{document}

\begin{equation}
    \leftrightbr
    \left(a\right)
\end{equation}

\end{document}

那么,问题是什么呢?

编辑:错误是

! Undefined control sequence.
<argument> \@dummy 

l.85     \left(
               a\right)

查看日志后,我们得出结论,问题是由对 的调用引起的\get@\@dummy\on@end@line。这似乎很奇怪,因为直接对 \empty@stack 调用 \get@ 不会导致任何错误。

编辑:代码可以缩小:

\documentclass[12pt]{article}

\tracingcommands=2
\tracingmacros=2
\tracingall

\newtoks\piecetoks

\def\leftrightbr{
    \def\left##1{
        \piecetoks=\begingroup
    }
    \def\right##1{
        \endgroup
    }
}

\begin{document}

\begin{equation}
    \leftrightbr
    \left(a\right)
\end{equation}

\end{document}

这更短,不是吗?:-) 它给出了另一个错误,并且另一个问题出现:我们如何实现 TeX 在两个宏调用之间捕获标记的行为?

答案1

你不能将令牌分配给具有

\piecetoks=\begingroup...\endgroup`

你也不能用一个宏开始任务,用另一个宏结束任务,因为

<toks register>={...}

需要一个明确的末尾加上括号,并且在评估要<balanced text>存储的内容时不会发生扩展。

在 TeXbook 第 276 页,你可以找到 a<variable assignment>是什么;其中一种替代方法是

<token variable> <equals> <general text>

上面有一些你看到

<general text> → <filler> { <balanced text> <right brace>

在语法规则中,{表示类别代码为 1 的隐式或显式字符标记,而<right brace>表示明确的类别代码为 2 的字符标记。

答案2

分隔参数需要查找固定的标记,但您不需要提前查找\\- 或者 -\right您可以提前查找\right,然后查看您收集的标记以查看是否存在\\

所以

\documentclass{article}


\makeatletter
\newtoks\tA
\newtoks\tB

\def\left#1\right{%
  \zz#1\\\right}

\def\zz#1\\#2\right#3{%
\ifx\relax#2\relax
  \tA{\left#1\right#3}%
  \typeout{^^J^^Jno \space\string\\:^^J\the\tA^^J}%
  \expandafter\remove@to@nnil
\else
  \tA{\left#1\right.\\}%
  \expandafter\zzb
 \fi
 #2\right#3\@nnil}

\def\zzb#1\\\right#2\@nnil{%
  \tB{\left.#1\right#2}%
  \typeout{^^J^^Jhas \string\\:^^J\the\tA\space \the\tB^^J}}
\makeatother

\begin{document}

\left( a b\right)

\left( a \\ b\right)

\end{document}

生产

no  \\:
\left ( a b\right )



has \\:
\left ( a \right .\\ \left . b\right )

但是我永远不会在生产中使用它。重新定义\left\right接受宏参数会改变它们的扫描规则,并会引入各种不兼容性。即使你通过使用新名称而不是重新定义原语来避免这种情况,\right.\\ \left.在换行符处使用也不能确保成对的分隔符大小相同,因此无法产生可接受的结果。

实际上最好只\bigl(在一个单元格中使用,然后\bigr)在稍后的单元格中使用。

相关内容