自动检查宏中的意外空格?

自动检查宏中的意外空格?

在宏主体中,很容易忘记命令行末尾的\relax或。例如,在编写%

\def\myMacro{
  \invokeX
  \invokeY{}
  \invokeZ
}

必须在第一行和第三行末尾添加空格吞噬器,但第二行、第四行和第五行末尾可以不添加。

简单的全局搜索/替换{$by{%将修复第一行等遗漏,并且不太可能添加多余的吞食者。但是,全局搜索/替换}$by}%也会在第五行后添加一个百分号。

为了 Google 搜索访问者的利益,让我写下vi这些搜索的命令:

:%s/{$/{%/
:%s/}$/}%/

但正如我提到的,这些并不完美,并且可能无法处理其他棘手的情况。

所以,我的问题是:你能做得更好吗?也许有一个巧妙的重新定义\def可以消除这些丑陋的添加的需要,或者也许有其他一些自动机制可以警告这些潜在的错误?

答案1

我写了一些代码来检查一段代码。它计算 的参数中出现了多少个空格以及多少个空格+行尾字符\cnel,并将其显示到终端。它还运行代码:这样,就不会太混乱。

% Should print 1 and 4.
\cnel{
  \def\foo{b ar
  }
}

% Just to stop the run (press <enter>)
\read-1 to\dummy

% Should print 0 and 0, because there is no space nor end-of-line.
\cnel{%
  \show\foo
}

诀窍是

  • 使用\scantokens各种 catcode 设置来读取参数,以识别行尾字符:忽略或空格。我们首先需要使用 catcode other 来读取它,因为“忽略”它会消失,而“空格”它会变得与真正的空格无法区分。

  • 使用\lowercase将空格转换为其他特点(即^^S),然后\detokenize,并搜索细绳 ^^S(该字符串本身是通过对小写空格进行去标记而生成的。)

% `cnel` = `check if no end-line`.
%
\catcode`\@=11\relax
\newcount\cnel@count


\def\cnel{%
  \begingroup%
  \catcode\endlinechar=12\relax%
  \cnel@aux%
}
\newcommand{\cnel@aux}[2][\^^S]{%
  % Set spaces to lowercase to the first, optional, argument (default ^^S).
  \lccode`\ `#1%
  % First we count the number of spaces with a setting in which 
  % the end-line character is ignored. Then we count it after setting
  % it to be a space instead.
  \typeout{}%
  \typeout{First, we ignore end-of-line characters.}%
  \catcode\endlinechar=9\relax%
  \scantokens{\cnel@count@spaces{#2}}%
  \typeout{}%
  \typeout{Then, they are counted as spaces.}%
  \catcode\endlinechar=10\relax%
  \scantokens{\cnel@count@spaces{#2}}%
  % To execute the code, we keep this endline=space setting.
  \scantokens{\gdef\cnel@gtmp{#2}}%
  \endgroup%
  \cnel@gtmp%
}

\newcommand{\cnel@count@spaces}[1]{%
  \cnel@count0\relax%
  \lowercase{\expandafter\cnel@w\detokenize{. .#1 }\relax%
    \typeout{\unexpanded{#1}}%
  }%
  \typeout{Number of space(s): \the\cnel@count.}%
}
\def\cnel@w.#1.{%
  \def\cnel@w@aux##1#1##2\relax{%
    \ifempty@nTF{##2}{}{%
      \advance \cnel@count by 1\relax%
      \cnel@w@aux##2\relax%
    }%
  }%
  \cnel@w@aux%
}
\def\ifempty@nTF#1{%
  \expandafter\ifx\expandafter a\detokenize{#1}a%
  \expandafter\@firstoftwo%
  \else%
  \expandafter\@secondoftwo%
  \fi%
}

答案2

ConTeXt 提供了\starttexdefinition...\stoptexdefinition环境,可以实现这一点。使用它,您可以这样编写命令:

\starttexdefinition myMacro
  \invokeX
  \invokeY{}
  \invokeZ
\stoptexdefinition

我相信LaTeX3也提供了类似的语法。

答案3

对于 LaTeX,使用expl3包:

\ExplSyntaxOn
\def\myMacro{
  \invokeX
  \invokeY{}
  \invokeZ
}
\ExplSyntaxOff

甚至

\ExplSyntaxOn
\def \myMacro {
  \invokeX
  \invokeY { }
  \invokeZ
}
\ExplSyntaxOff

相关内容