summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/ifallfalse/ifallfalse.dtx
blob: 9d5e630f731addbc4cb390b808588d9700549aeb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
% \iffalse meta-comment
%
% Copyright (C) 2021 Dennis Chen <proofprogram@gmail.com>
%
% This file may be distributed and/or modified under
% the conditions the LaTeX Project Public License (LPPL),
% either version 1.3 of this license or (at your option)
% any later version. The latest version of this license
% can be found in
%   http://www.latex-project.org/lppl.txt
% and version 1.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
% \fi
%
% \iffalse
%<package>
%<package>\NeedsTeXFormat{LaTeX2e}
%<package>\ProvidesPackage{ifallfalse}[2021/07/12 v1.0.1 Compare string against set of other strings]

%<*driver>
\documentclass{ltxdoc}
\usepackage{ifallfalse}
\EnableCrossrefs
\CodelineIndex
\RecordChanges
\begin{document}
    \DocInput{ifallfalse.dtx}
    \PrintIndex
\end{document}
%</driver>
% \fi
%
% \changes{v.1.0.1}{2021/07/12}{Fix errant references to allfalse environment and add limitations}
% \changes{v1.0.0}{2021/07/01}{Initial version}
%
% \GetFileInfo{ifallfalse.sty}
%
% \title{\textsf{ifallfalse} -- Compare string against set of strings}
% \author{Dennis Chen \\ proofprogram@gmail.com}
% \date{\fileversion, v. \filedate\thanks{\url{https://github.com/chennisden/ifallfalse}}}
%
% \maketitle
%
% \begin{abstract}
% The \textsf{ifallfalse} package is a package that allows you to check whether a string is contained within another set of strings, and perform an action if it is not.
% \end{abstract}
%
% \section{Usage}
%
% The package provides an \textsf{allfalse} environment and a macro |\orcheck| to be used inside the \textsf{allfalse} environment.
%
% \DescribeEnv{allfalse}
% To set up an allfalse environment, simply write
% \begin{verbatim}
%\begin{allfalse}{string}{action}
%
%\end{allfalse}
% \end{verbatim}
% \textsf{string} will be compared to the set of strings (which we will declare via |\orcheck|), and if \textsf{string} does not match the set of strings, \textsf{action} will be executed.
%
% \DescribeMacro{\orcheck}
%
% To add strings to the set that \textsf{string} will be compared to, we must write |\orcheck{setstring}| inside the corresponding \textsf{allfalse} environment. Then, \textsf{action} will not execute if \textsf{string} matches \textsf{setstring} or any arguments of previous |\orcheck| declarations.
%
% If no |\orcheck| declarations exist, then \textsf{action} will always be executed.
%
% \subsection{Error Checking}
%
% The package checks whether the macro |\orcheck| is used inside an \textsf{allfalse} environment. If it is not, the package throws an error.
%
% \section{Example}
%
% Here is a simple example to demonstrate how \textsf{allfalse} is used.
% \begin{verbatim}
%\documentclass{minimal}
%
%\begin{document}
%
%\begin{allfalse}{purple}{This color is not red, blue, or green!}
%    \orcheck{red}
%    \orcheck{blue}
%    \orcheck{green}
%\end{allfalse}
%
%\end{document}
% \end{verbatim}
%
% In this case, because \textsf{purple} does not match \textsf{red}, \textsf{blue}, or \textsf{green}, the action --- which is printing \textsf{This color is not red, blue, or green!} --- will execute at that location inside the document.
%
% \section{Implementation}
% 
% These are the implementation details of package \textsf{allfalse}. Because the package is so short, we can explain everything.
% 
% \begin{environment}{allfalse}
% When setting up allfalse, we locally define the |\comparedstring| macro with the first argument that the environment takes in. This is what will be compared against all the strings passed in through the |\orcheck| declarations inside the environment.
%
% Then, we define our body of logic (which we will be adding onto through |\orcheck|) to just initially consist of the action we would like to perform if |\comparedstring| matches none of the strings passed in through |\orcheck|.
%    \begin{macrocode}
\newenvironment{allfalse}[2]
{
    \def\comparedstring{#1}
    \def\logicbody{#2}
}
{
    \logicbody
}
%    \end{macrocode}
% \end{environment}
%
% \begin{macro}{\orcheck}
% We first save \textsf{allfalse} to a macro so we can use |\ifx| to compare the current environment name against it. If we can, then we add some following (somewhat convoluted) code to |\logicbody|. I will explain what each piece of it does, though not in the order the pieces of code appear.
% \begin{itemize}
% \item |\ifx\@currenvir\@allfalsename| evaluates to true if the current environment (whose name is saved to the macro |\@currenvir|) matches the name of |\@allfalsename|, or \textsf{allfalse}.
% \item If it evaluates to \textsf{false}, the package throws an error.
% \item The line |\pdfstrcmp{\comparedstring}{#1}=0| evaluates to true when put with |\ifnum| if the two arguments passed into |\pdfstrcmp| are equal, because |pdfstrcmp| compares their lexographical order and returns 0 if the two strings are lexographically equivalent.
% \item Thus, we can treat |\ifnum\pdfstrcmp{\comparedstring}{#1}=0| as an expression that evaluates to true if |\comparedstring| and |#1| match, and false otherwise.
% \item When all is said and done, the logic reduces to something of the form
% \begin{verbatim}
% \if\else
% \if\else
% \ldots action
% \fi\ldots \fi
% \end{verbatim}
% Logically, \textsf{action} will only execute if all the conditions are false; in other words, it will only execute if |\comparedstring| does not match any of the strings passed in via |\orcheck|. This is because each |\else| branch must execute. 
% \end{itemize}
%    \begin{macrocode}
\newcommand*\@allfalsename{allfalse}

\newcommand{\orcheck}[1]{
    \ifx\@currenvir\@allfalsename
        \protected@edef\logicbody{
            \ifnum\pdfstrcmp{\comparedstring}{#1}=0\else\logicbody\fi
        }
    \else
        \PackageError{ifallfalse}{
            \protect\orcheck\space should be nested within the allfalse environment
        }{}
    \fi
}
%    \end{macrocode}
% \end{macro}
%
% \section{Limitations}
%
% This package cannot be used in the \textsf{lualatex} engine.
%
% \Finale
\endinput