编写包时出现奇怪的错误

编写包时出现奇怪的错误

这份简短的文件:

\documentclass[a4paper]{report}
\usepackage{gnu}

\begin{document}
\end{document}

使用以下软件包:

%!TEX encoding = UTF-8 Unicode
\def\fileversion{2.7}
\def\filedate{%
Tue 2014-07-22
}
\NeedsTeXFormat{LaTeX2e}[1996/06/01]
\typeout{Package 'mworx' (\filedate).}
\ProvidesPackage{gnu}


\RequirePackage{kvoptions}

\SetupKeyvalOptions{family=mworx,prefix=mw@,setkeys=\setkeys}

\newif\if@read\@readfalse
\newif\if@grave\@gravefalse

\DeclareBoolOption[true]{greek}
\DeclareVoidOption{ifgrave}{\@gravetrue}
\DeclareVoidOption{read}{\@readtrue}
\DeclareBoolOption[true]{save}
\DeclareBoolOption[false]{inpenc}
\DeclareBoolOption[false]{pack}
\DeclareBoolOption[true]{rootsyst}
\DeclareBoolOption[true]{loop}
\DeclareBoolOption[true]{fnsect}
\DeclareBoolOption[false]{fonts}
\DeclareBoolOption[false]{ptmx}
\DeclareBoolOption[true]{bb}
\DeclareBoolOption[true]{misc}
\DeclareBoolOption[true]{lengths}
\DeclareBoolOption[true]{dps}
\DeclareBoolOption[true]{xtras}
\DeclareBoolOption[true]{thm}
\DeclareBoolOption[true]{block}
\DeclareBoolOption[true]{mathdel}
\DeclareBoolOption[true]{mwmath}
\DeclareBoolOption[true]{oper}
\DeclareBoolOption[true]{mathsymb}
\ProcessKeyvalOptions*\relax


%Option "fonts"
\ifmw@fonts
\RequirePackage{ifxetex,ifluatex}
\newif\ifengineright\enginerightfalse%Define new if to check if luatex or xetex are being used. To avoid typing the xetex/luatex commands twice.
\ifengineright %Add those commands
    \setmathrm{Times New Roman}
    \setmathsf{Times New Roman}
    \setmathtt{Times New Roman}
    \setboldmathrm[BoldFont={Optima ExtraBlack}]{Optima Bold}
    \defaultfontfeatures{Mapping=tex-text}
    \setromanfont{Times New Roman}
    \setsansfont{Times New Roman}
    \setmonofont{Helvetica}
    \newfontfamily{\vrb}{Helvetica}
    \newfontfamily{\Ak}{Akkadian}%
    \newfontfamily{\mapu}{Arial Unicode MS}
    \newfontfamily{\Ar}{Geeza Pro}
    \newfontfamily{\Chs}{STSong}
    \newfontfamily{\Cht}{PMingLiU}
    \newfontfamily{\Ja}{MS Mincho}
    \newfontfamily{\Ru}{Times New Roman}
    \newfontfamily{\Gr}{Times New Roman}
    \newfontfamily{\He}{Times New Roman}
    \newfontfamily{\Ko}{Batang}
    \newfontfamily{\Ind}{Devanagari MT}
    \newfontfamily{\Mal}{Malayalam MN}
    \newfontfamily{\Sinh}{Free Sans}
    \newfontfamily{\Thai}{Free Serif}
    \newcommand{\€}{\Gr{ }}
\else
    {}
\fi
%More commands that are OK in any engine I know of.
\newcommand{\mbold}{\mathbf}
\newcommand{\tbold}{\textbf}
\newcommand{\ital}{\textit}
\newcommand{\CAPS}{\textsc}
\newcommand{\slant}{\textsl}
\newcommand{\bemph}[1]{\tbold{\emph{#1}}}
\newcommand{\mrm}{\mathrm}
\fi





%Option "misc"
\ifmw@misc
%A few unclassifiable shortcuts.
\RequirePackage{color,graphicx,xparse}
\newcommand{\tick}{\checkmark}
\newcommand{\crux}{\dagger}
\newcommand{\dcrux}{\ddagger}
\newcommand{\hs}{\heartsuit}
\newcommand{\ds}{\diamondsuit}
\newcommand{\cs}{\clubsuit}
\renewcommand{\ss}{\spadesuit}
\newcommand{\tcrux}{$\dagger$}
\newcommand{\tdcrux}{$\ddagger$}
\newcommand{\ths}{$\heartsuit$}
\newcommand{\tds}{$\diamondsuit$}
\newcommand{\tcs}{$\clubsuit$}
\newcommand{\tss}{$\spadesuit$}
\newcommand{\bs}{\backslash}
\newcommand{\tbs}{$\bs$}
\newcommand{\bk}{\Gr{ }}
\NewDocumentCommand{O{1}O{1}}
{\scalebox{#1}[#2]{\color{white}a}}
\fi




\endinput

在第 83 行生成错误Extra \fi(紧接着\newcommand{\mrm}{\mathrm}。如果我添加:

\ifxetex
    \enginerighttrue
\else
    \ifluatex
        \enginerighttrue
    \else
        {}
    \fi
\fi

之前\ifengineright,错误变为Undefined control sequence. l.56 \ifengineright

这里发生了什么事?

答案1

问题出在哪里?下面我们来分析一下。

你有

\ifmw@fonts
\RequirePackage{ifxetex,ifluatex}
\newif\ifengineright\enginerightfalse%Define new if to check if luatex or xetex are being used. To avoid typing the xetex/luatex commands twice.
\ifengineright %Add those commands
    \setmathrm{Times New Roman}
    \setmathsf{Times New Roman}
    % several other lines
\else
    {}
\fi
%More commands that are OK in any engine I know of.
\newcommand{\mbold}{\mathbf}
\newcommand{\tbold}{\textbf}
\newcommand{\ital}{\textit}
\newcommand{\CAPS}{\textsc}
\newcommand{\slant}{\textsl}
\newcommand{\bemph}[1]{\tbold{\emph{#1}}}
\newcommand{\mrm}{\mathrm}
\fi

\ifmw@fonts返回 false 时,“真实代码”不会被执行,因此\ifengineright没有定义,也不被视为条件。因此第一的 \else被认为是 的“真代码”的结束\ifmw@fonts,而“假代码”只包含{}

如果\ifmw@fonts返回 true,TeX 定义\ifengineright. 并且一切都会顺利进行。

让我们看一个简化的例子:

\newif\ifOUTER % \OUTERfalse is implicit

%\OUTERtrue

\ifOUTER
  \newif\ifINNER % \INNERfalse is implicit
  \ifINNER
    \message{We're here}
  \else
    {}
  \fi
\else
  \message{Outer}
\fi

错误信息是

! Extra \else.
l.12 \else

? 
Outer
! Extra \fi.
l.14 \fi

? 

\OUTERtrue取消注释时,我们不会收到任何错误。

所发生的情况很容易解释:当一个条件为真时,TeX 会简单地删除它以及用于确定布尔值的标记,然后只\else删除“错误文本”。

但当条件返回 false 时全部匹配的标记\else(或\fi,如果没有\else后续标记)将被删除,并且不会以任何方式进行解释,除非其中的条件与其对应的 匹配\fi。但是,\ifengineright在您的代码中, 不是条件,而只是未定义的标记。

TeX 如何确定标记是否为条件?当然,任何原始条件都会被识别,此外,\let原始条件的所有标记也是如此。事实上,\enginerighttrue和分别\enginerightfalse执行\let\ifengineright\iftrue\let\ifengineright\iffalse。其他都不是条件。或的标记\let以类似的方式被识别为结束(分支)条件。\else\fi

道理就是“永远不要在条件语句中定义新的条件语句”。好吧,这是可以做到的,但不能用这种方式。


顺便说一句,\else {} \fi空的假文本不是好的编程:条件是完全可扩展的构造,除非需要,否则不要添加不可扩展的标记。

答案2

不要这样做:

\ifmw@fonts
\RequirePackage{ifxetex,ifluatex}
\newif\ifengineright

如果您在条件部分中分配一个新的 if,则\ifengineright可能会或可能不会定义,但\fi始终是 fi,因此在未定义的情况下您会有不匹配的 fi。

相关内容