\path 等如何解析其参数?

\path 等如何解析其参数?

我真的很好奇是否有人愿意花时间解释如何创建与pgf/TiKz捆绑包中的某些宏类似的宏。

我特别感兴趣的是如何创建一个命令,其作用如下

\<csname> <content> [bare word directive] <more content>;

为了清楚地表达我的意思,我指的是如何(\path以及\draw其它)被解析并且可以包含光说不练node如下图所示的命令edge

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}

\begin{tikzpicture}
  \path[blue] node (B) at (0,3) {CORNER} edge [->] (0,0) edge [->] (2,4);
  \draw  (0,0) -- (2,4) node [midway] {HELLO} edge [->] (B);
  \draw  (2,4) -- (4,2) edge [->] (B)         node [midway] {HELLO} ;
\end{tikzpicture}

\end{document}

我是谁不是感兴趣的是如何node知道使用什么或如何edge知道从哪里绘制。

相反,我很好奇的是,人们LaTeX最终是如何将光秃秃的单词node看作本质上,另一个命令,同样如此edge。所以这是一个关于解析参数并解释这些参数内容的问题。

我查阅了大量tikz.code.tex其他文件,试图弄清楚如何实现这种解析,但很快就迷失在代码中。

我甚至无法使如下这样简单的事情发挥作用:

\documentclass{article}
\makeatletter

\newcommand\hello{Hello World!}
\newcommand\ciao{ciao?}
\newcommand\secretmessage{\ae@message}
\def\ae@message(#1)#2;{%%
  \def\ae@continue{}%%
  \hspace{1em}\emph{#1}%%
  \expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax
  \else
    \def\ae@continue{\ae@parse #2 ;}%%
  \fi
  \ae@continue
  \def\ae@continue{}%%
  }
\def\ae@parse#1 ;{\typeout{===>"#1"}\csname#1\endcsname}
\def\ae@trim#1{\ignorespaces#1\unskip}

\makeatother
\setlength\parskip{2ex}
\setlength\parindent{0pt}
\begin{document}

\secretmessage(This is my message.);

\secretmessage(This is my message.) hello;

\secretmessage(This is my message.) ciao;

\secretmessage(This is my message.);

The above should have been interpreted as equivalent to:

\secretmessage(This is my message.);

\secretmessage(This is my message.); \hello

\secretmessage(This is my message.); \ciao

\secretmessage(This is my message.);

\end{document}

在此处输入图片描述

我不知道如何才能把上面的空格去掉,而不会产生像

 \csname\space hello\endcsname

虽然我真的很想做一些更复杂的事情,例如:

\secretmessage(This is my message.) hello ciao;

这实际上应该被解释为

\secretmessage(This is my message.); \hello\space \ciao

尽管我强烈怀疑,如果没有进一步论证的帮助,hello这些ciao相邻的裸词指令不太可能在没有大量工作的情况下得到正确解析。

答案1

简短回答:它确实需要一个工作量极大

长答案:不,你不想知道细节。我不想写它们。真的……(更新:@percusse 有一个很好的解释这里

Tikz 不使用 TeX 宏引擎。它构建自己的解析器,该解析器基本上处理给定命令(例如\tikz, \path)之后的所有字符,期望一些有效的关键字,并逐个字母检查该关键字是否确实存在,直到;找到 a(这标志着此解析器的输入结束)。它还会进行特殊处理(等等。大多数字符的含义都与上下文相关,因此它必须根据先前解析的内容调用适当的宏来处理它们。

为了让您了解我们正在讨论的复杂程度,这里有一小段代码,tikz.code.tex它试图确定以下“标记”(不是 TeX 意义上的)是否是单词“坐标”。Tikz 在期望节点作为路径的一部分时“调用”此宏。

\def\tikz@parse@child@node{%
  \pgfutil@ifnextchar n{\tikz@parse@child@node@n}%
  {\pgfutil@ifnextchar c{\tikz@parse@child@node@c}%
    {\tikz@parse@child@node@rest}}}
\def\tikz@parse@child@node@rest#1\pgf@stop{\def\tikz@child@node@rest{#1}}
\def\tikz@parse@child@node@c c{\pgfutil@ifnextchar o{\tikz@parse@child@node@co}{\tikz@parse@child@node@rest c}}
\def\tikz@parse@child@node@co o{\pgfutil@ifnextchar o{\tikz@parse@child@node@coordinate}{\tikz@parse@child@node@rest co}}
\def\tikz@parse@child@node@coordinate ordinate{%
  \pgfutil@ifnextchar ({\tikz@@parse@child@node@coordinate}{%
    \def\tikz@child@node@text{[shape=coordinate]{}}%
    \tikz@parse@child@node@rest}}%}
\def\tikz@@parse@child@node@coordinate(#1){%
  \pgfutil@ifnextchar a{\tikz@p@c@n@c@at(#1)}{%
    \def\tikz@child@node@text{[shape=coordinate,name=#1]{}}%
    \tikz@parse@child@node@rest}}
\def\tikz@p@c@n@c@at(#1)at#2({%
  \def\tikz@child@node@text@pre{[shape=coordinate,name=#1]at}%
  \tikz@scan@one@point\tikz@p@c@n@c@at@math(%
}

如您所见(您看不出来吗?:-))它首先检查字母是否n存在。如果存在,可能是因为单词node来了,因此它使用宏\tikz@parse@child@node@n继续。如果不存在,它会检查它是否是字母c。如果是,那么很可能是单词的开头coordinate,因此它会调用宏\tikz@parse@child@node@c继续,检查下一个字符是否是o,依此类推。

\expandafter这是、\edef\pgfutil@ifnextchar \ifx的巨大噩梦中的 \t 一小部分\tikz@really@long@macro@name@s。我不知道 Till Tantau 在写完这些之后是如何保持理智的,除非这些代码是用工具生成的。

相关内容