% \iffalse meta-comment % %% File: groupthm.dtx % % Copyright (C) 2022 Maximilian Keßler % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % ----------------------------------------------------------------------- %\ProvidesExplPackage{groupthm}{2022/01/17}{0.0.1}{Grouped theorems.} % %<*driver> \documentclass[full,kernel]{l3doc} \usepackage{mkessler-todo} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{groupthm} package^^A % } % % \author{^^A % Maximilian Keßler % } % % \date{Released 2022-01-12} % % \maketitle % % \begin{documentation} % % \tableofcontents % % % A central thing in \LaTeX is the usage of \enquote{theorems}. % With \enquote{theorems} we actually mean \enquote{environments} that typically % have a title, some style applied to their contents and are numbered throughout % the document, often later referenced by number and / or name. % % Mechanisms for generating such environments are packages like % \pkg{amsthm}, \pkg{ntheorem}, \pkg{thmtools}. % % While the mechanism in \pkg{thmtools} are pretty versatile and suffice % for almost all needs, it is pretty time-consuming to largely change % the behavior of environments, or have small variants of these. % % This package aims at both providing a versatile mechanism, \meta{theorem group}s, % to structure theorems into groups that can subsequently easily altered, % as well as a mechanism for easily generating \meta{theorem families}. % % As the author is of the opinion that of the mentioned theorem controlling packages % \pkg{thmtools} provides the most versatile interface, the \pkg{groupthm} % will be working on top of \pkg{thmtools} and use this as a backend for declaring % the \meta{theorem}s themselves. % % Thus, any styles supported by \pkg{thmtools} will be supported by \pkg{groupthm} % as well, by passing them to \pkg{thmtools}. % % \section{Concepts} % % \subsection{Theorem groups} % \label{sec:theorem-groups} % A \meta{theorem group} is some named group holding some properties for % the \meta{theorem}s that are contained in this group. % Each \meta{theorem} can, when declared, be part of arbitrarily many \meta{theorem group}s, % and will be subject to the styles these groups defined. % % This enables to group similar \meta{theorem}s and alter them at a late stage of % document development in a unique manner, by only having to change the % definition of the \meta{theorem group}, and not all \meta{theorem}s separately. % % The properties. Such a \meta{theorem group} can hold are as follows % % % \begin{description} % \item[\texttt{prefix}] A prefix (any \meta{token list}) that will be inserted % before the theorem name of each member of this \meta{theorem group}. % \item[\texttt{suffix}] A suffix (any \meta{token list}) that will be % inserted before the theorem name for each member of this \meta{theorem group}. % This could be e.g.~some \enquote{$\star$} appended to the name to indicate % variants of environments. % \item[\texttt{mapname}] A \meta{function} (some macro that takes exactly one argument) % that is applied to the \texttt{name}. % \item[\texttt{thmtools}] A \meta{clist} of key-value pairs that are passed to the underlying % \pkg{thmtools} backend of the \meta{theorem}. % This allows e.g.~to set the \texttt{topskip} of a certain class of \meta{theorem}s. % \end{description} % % The most versatile key here is certainly the \texttt{thmtools} key, % providing the most customization to an end user (like you). % % As mentioned, each \meta{theorem} can be member of arbitrary many \meta{theorem group}s, % and will posses their corresponding properties. % % To adjust finer controlling of these \meta{theorem group}s, \meta{theorem group}s can inherit from each other, and \meta{theorem group}s are subject to a hierarchy that controls precedence in case % of conflicting properties of different \meta{theorem group}s a \meta{theorem} may be part of. % % This hierarchy can of course be controlled by the user. % % \subsection{Grouped theorems} % % A \meta{grouped theorem} is just a theorem that is a member of % a given set of groups (possibly empty). % It behaves just a regular theorem, except that by changing the definition of % its theorem groups, we can alter its behavior. % % % It is the core concept of the \pkg{groupthm} package. % For brevity, we will often talk about \enquote{theorems}, % although in fact we mean \enquote{grouped theorems}. % % \subsection{Theorem families} % Often, one needs some small \meta{theorem variant}s of some \meta{theorem}, the most typical % example being \texttt{starred} version of \meta{theorem}s that are not numbered % in contrast to their counterparts. % % \begin{verbatim} % \begin{theorem} % This theorem is numbered. % \end{theorem} % % \begin{theorem*} % This theorem is not numbered. % Probably because we do not want a reference to it. % \end{theorem*} % \end{verbatim} % % \pkg{groupthm} extends this idea and provides a versatile mechanism to define a % \meta{theorem family}, which is based on some \meta{theorem name} and % parses additional arguments / syntax to control the \meta{theorem groups} % that this environment is a part of. % % So, in addition the name of a \meta{theorem}, the corresponding environment will % accept some options and toggle the membership of certain \meta{theorem groups}, % thus further customizing its appearance. % % This can lead e.g.~to usages like the following: % % \begin{verbatim} % \begin{theorem}* % This theorem has a visual * at its name. % \end{theorem} % \end{verbatim} % % Providing this consists of two parts: % declaring the \meta{theorem family} by listing the groups that can be toggled % by this \meta{theorem family}, and declaring the actual option parsing % of the \meta{theorem family}, which then controls the membership in these groups % (and of course prior to this the definition of the desired \meta{theorem group}s). % % % % \subsection{General notions} % % In many cases, there are a number of variants of some command, % call it for example \cs{Foo}. % Then the documentation will look like % % % \begin{function}{\NewFoo, \RenewFoo, \ProvideFoo, \DeclareFoo} % Defines some \texttt{foo} \ldots % \end{function} % % % and will not mention anything about the variants. % This follows some general naming convention that also \pkg{xparse} uses, % and is the following: % % \begin{description} % \item[\cs{NewFoo}] % % Defines \texttt{foo} if not defined already. % This emits an error in case it has been defined yet. % % \item[\cs{RenewFoo}] % % Redefines \texttt{foo} if defined already. % This emits an error in case it has \emph{not} been defined yet. % % \item[\cs{ProvideFoo}] % % Defines \texttt{foo} if it is not defined already. % This does not emit an error if \texttt{foo} is already defined % (and has no effect in this case). % % \item[\cs{DeclareFoo}] % % Defines \texttt{foo} in disregard of any % existing definitions. Any old definition will be overwritten (if present). % \end{description} % % % The documentation margin will list all variants that are available, % they follow their respective conventions. % % % \begin{texnote} % The \pkg{thmtools} package, unfortunately, dose not follow this convention, % as its \cs{declaretheorem} command actually behaves like a \cs{newtheorem}. % The reason for this is that \pkg{amsthm} already defines \cs{newtheorem}. % % Thus, actually, calls to \cs{NewGroupedTheorem} will have an underlying % \cs{declaretheorem}, but you do not have to bother with this. % \end{texnote} % % \section{Theorem groups} % % \subsection{Defining theorem groups} % % \begin{function}{\NewTheoremGroup, \RenewTheoremGroup, \ProvideTheoremGroup, \DeclareTheoremGroup} % \begin{syntax} % \cs{NewTheoremGroup}\oarg{key=value list}\marg{theorem group} % \end{syntax} % % This introduces a new \meta{theorem group} with the given name. % The \meta{key=value list} available are the same as introduced in \autoref{sec:theorem-groups}: % % \begin{description} % \item % % \texttt{prefix} = \meta{token list}. % Insert the \meta{token list} in front of the theorem name. % % \item % % \texttt{suffix} = \meta{token list}. % Insert the \meta{token list} after the theorem name. % % \item % % \texttt{mapname} = \meta{function}. % Apply this \meta{function} to the theorem name. % % \item % % \texttt{thmtools} = $\{$\meta{clist}$\}$. % Pass these options to \pkg{thmtools}. % % \end{description} % % For uniqueness of the given options, the \meta{clist} given to the \texttt{thmtools} key % has to be surrounded by a pair of braces. % % \begin{texnote} % The \texttt{mapname} is expected to be a function of \cs{fun:n}. % The function call is subject to an \texttt{x}-type expansion prior % to being passed further to \pkg{thmtools}. % \end{texnote} % % \end{function} % % \subsection{Controlling theorem group precedence} % \label{subsec:theorem-group-precedence} % % \begin{function}{\DeclareTheoremGroupRule} % \begin{syntax} % \cs{DeclareTheoremGroupRule}\oarg{keyname}% % \marg{theorem group_1}\marg{relation}\marg{theorem group_2} % \end{syntax} % % This declares some relation between the two theorem groups, % controlling their order of application in case a theorem is member % of both groups. % % The \meta{keyname} can be one of \texttt{prefix}, \texttt{suffix}, \texttt{mapname}, \texttt{thmtools}. % If present, it declares the corresponding relation only for this subkey. % This can lead to \meta{theorem group_} overwriting \meta{theorem group_2} when given % contradictory \pkg{thmtools} options, but the \texttt{prefix} of \meta{theorem group_} % being applied after the one of \meta{theorem group_2}. % When the \meta{keyname} is not given, this applies to all keywords. % % \begin{texnote} % The \meta{keyname} is just passed to the corresponding argument % of the \texttt{lthooks} package. % If the option argument is not present, \texttt{??} is used, this has the described effect. % \end{texnote} % % The behavior of the relations is based on the \cs{DeclareHookRule} command % from the \pkg{xparse} package, and all respective keys are in fact available, % but typically not needed, so the reader of this manual is referred to the % \pkg{lthooks} packages documentation for a list of the full keys. % For us, the following list will suffice: % % \begin{description} % \item[\texttt{higher} or \texttt{after} or \texttt{\string>}] % % \meta{theorem group_1} takes precedence over \meta{theorem group_2}. % Its \texttt{prefix} is applied after the one of \meta{theorem group_2}. % % \item[\texttt{lower} or \texttt{before} or \texttt{\string<}] % % \meta{theorem group_2} takes precedence over \meta{theorem group_1}. % Its \texttt{prefix} is applied after the one of \meta{theorem group_1}. % % \end{description} % % \begin{texnote} % The \meta{relation} is first stripped, % then checked if it matches either \texttt{higher} or \texttt{lower} % and in this case replaced by the corresponding \pkg{lthooks} variant % of the relation. % The rest is passed as is to \pkg{lthooks} and thus subject to the usual % normalization process of \pkg{lthooks}. % \end{texnote} % % \end{function} % % % \subsection{Inheritance of theorem groups} % \begin{function}{\AddTheoremGroupParent} % \begin{syntax} % \cs{AddTheoremGroupParent}\marg{theorem group_1}\marg{theorem group_2} % \end{syntax} % Declares \meta{theorem group_1} to \enquote{inherit} all properties % of \meta{theorem group_2}. % In other words, \meta{theorem group_2} is a parent of \meta{theorem group_1} % in a usual inheritance graph. % % The definitions of the groups themselves are unchanged, % but each new theorem defined with \meta{theorem group_1} will also % have the properties of \meta{theorem group_2}. % % Inheritance is transitive, when defining a new theorem, we just flatten out the % inheritance graph and apply all properties. % % Inheritance is subject to the usual theorem group hierarchies as discussed in % \autoref{subsec:theorem-group-precedence}. % This can even yield situations, where \meta{theorem group_1} inherits % from \meta{theorem group_2}, but \meta{theorem group_2} overwrites % \meta{theorem group_1}. % \end{function} % % \subsection{Appending to theorem groups} % \begin{function}{\AppendToTheoremGroup} % \begin{syntax} % \cs{AppendToTheoremGroup}\oarg{key=value list}\marg{theorem group} % \end{syntax} % Adds the properties given as \meta{key=value list} to the theorem group. % The syntax for the \meta{key=value list} is the same as in \cs{NewTheoremGroup}. % \end{function} % % \subsection{Default theorem groups} % \label{subsec:default-theorem-groups} % % There are a number of theorem groups that \pkg{groupthm} will initially declare % and that have certain special treatment in some places. % % \begin{function}{all} % Every declared grouped theorem is a member of this group. % % Initially, this group has no effect (i.e.~an empty property list). % It can be redefined by the user to alter the behavior of all grouped theorems % in a unified way. % % It is the lowest theorem group in the hierarchy by default. % \end{function} % % \begin{function}{starred} % This is group that shall represent the standard variant of theorems that % are called with a \enquote{*} in the environment name. % Theorems of this group are not numbered. % % The user should not add theorems to this group by hand, % as this is handled in a unified way by default. % See the documentation for \cs{NewGroupedTheorem}, \cs{NewGroupedTheoremFamily} % and \cs{NewGroupedTheoremFamilyOptions} how this group is treated. % % It is the highest theorem group in the hierarchy by default, % except for \texttt{unnumbered}, % with which it has no relation. % \end{function} % \addtocounter{footnote}{-1} % % \begin{function}{unnumbered} % Theorems in this group are not numbered. % Versions of all commands exist that add theorems to this group. % % It is the highest theorem group in the hierarchy by default, % except for \cs{starred}, % with which it has no relation. % \end{function} % % The reason for the two groups \texttt{starred} and \texttt{unnumbered} % to both exist is that the \texttt{starred} group is \emph{meant} to be applied % to theorems that were called with a \enquote{*} in their name (thus the name), % whereas the \enquote{unnumbered} group \emph{means} that the environment % is 'just unnumbered'. % % This has two reasons: % First, this enables more fine-tuning of the behavior of the theorems in post-processing % of a document. % Second, more importantly, this distinguishes semantically between the environments % \texttt{theorem} and \texttt{theorem*}, even if \texttt{theorem} is in the \texttt{unnumbered} group. % % So assuming that \texttt{theorem} is member of the \texttt{unnumbered} group, both calls % % \begin{verbatim} % \begin{theorem} % This is not numbered. % \end{theorem} % \begin{theorem*} % This is not numbered. % \end{theorem*} % \end{verbatim} % % are defined and will produce the same result by default, but we could still % change the definition of the \texttt{starred} group later to do anything we want. % % \begin{texnote} % The mentioned hierarchies are kept intact for newly defined theorem groups, % i.e.~for each new such group, two theorem group rules are created. % \end{texnote} % % \section{Grouped Theorems} % % \subsection{Defining grouped theorems} % % \begin{function}{\NewGroupedTheorem, \ProvideGroupedTheorem} % \begin{syntax} % \cs{NewGroupedTheorem}\oarg{key=value list}\marg{grouped theorem} % \end{syntax} % This defines \meta{grouped theorem} and \meta{grouped theorem*} as new theorem environments. % Its properties can be set by the following keys: % % \begin{description} % % \item % % \texttt{name} $=$ \meta{displayed name}. % If given, this is the displayed name of the environment in the document. % If not present, the \meta{grouped theorem} is also used as the \meta{displayed name} % in capitalized form. % % \item % % \texttt{group} $=$ \marg{clist} % % Makes this theorem a member of the listed groups. % It will inherit all respective properties of these groups. % % If groups are present more than one time, this has no (additional) effect. % % \item % % \texttt{thmtools} = \marg{clist} % % Passes these option to the \pkg{thmtools} environment that is declared internally. % % \end{description} % % The \meta{grouped theorem*} behaves the same as the \meta{grouped theorem}, % but additionally will be a member of the \texttt{starred} theorem group, % see \autoref{subsec:default-theorem-groups}. % % \begin{danger} % If you don't wish the \meta{grouped theorem*} variant to be generated, % you can pass the additional option \texttt{starred version = false}. % If you are not sure about this, you are probably fine without this option. % \end{danger} % % % \end{function} % % \begin{function}{\NewGroupedTheorem*,\ProvideGroupedTheorem*} % \begin{syntax} % \cs{NewGroupedTheorem*}\oarg{key=value list}\marg{grouped theorem} % \end{syntax} % Behaves the same as \cs{NewGroupedTheorem}, % but also adds the theorem(s) to the default \texttt{unnumbered} group, % thus resulting in the environment not being numbered. % % This is thus equivalent to using \cs{NewGroupedTheorem} and adding the % \texttt{unnumbered} group. % \end{function} % % % \subsection{Defining families of grouped theorems} % % % \begin{function}{\NewGroupedTheoremFamily, \ProvideGroupedTheoremFamily} % \begin{syntax} % \cs{NewGroupedTheoremFamily}\oarg{key=value list}\marg{theorem family} % \end{syntax} % % Defines a family of grouped theorems. % The \meta{key=value list} accept the same arguments as the \cs{NewGroupedTheorem} macro. % However, for each \emph{subset} of the given groups, % a grouped theorem is defined. % % These grouped theorems are not meant to be accessed directly (but could), % so we omit their actual (internal) names here. % To call these, some \texttt{GroupedTheoremFamilyOptions} have to specified, % see \cs{NewGroupedTheoremFamilyOptions}. % % Also, to the given groups, the \texttt{starred} group is added automatically. % % \begin{danger} % If you do not wish the \texttt{starred} versions, % you can set the key \texttt{starred version = false}. % \end{danger} % % \end{function} % % \begin{function}{\NewGroupedTheoremFamily*, \ProvideGroupedTheoremFamily*} % Behaves the same as \cs{NewGroupedTheoremFamily}, but also adds each variant % to the default \texttt{unnumbered} group, thus resulting in the environments not being % numbered. % % This is \emph{almost} equivalent to calling \cs{NewGroupedTheoremFamily} % with the \texttt{unnumbered} group being present, as it does not generate the variants % where the \texttt{unnumbered} group is not present. % \end{function} % % % \begin{function}{\AddTheoremToGroup} % \begin{syntax} % \cs{AddTheoremToGroup}\marg{theorem group} % \end{syntax} % % Means that the current invocation of a theorem family should % call the theorem variant with the given group. % % Can only be used in the body of \cs{NewGroupedTheoremFamilyOptions} or similarly. % \end{function} % % \begin{function} % { % \NewGroupedTheoremFamilyOptions, \RenewGroupedTheoremFamilyOptions, % \ProvideGroupedTheoremFamilyOptions, \DeclareGroupedTheoremFamilyOptions % } % \begin{syntax} % \cs{NewGroupedTheoremFamilyOptions}\marg{theorem family}\marg{argument specifiation}% % \marg{selection body} % \end{syntax} % % Defines two new environment with options, given by \meta{theorem family} % and \meta{theorem family*}. % The \meta{argument specification} can be any valid \pkg{xparse} argument specification. % % The \meta{selection body} is there to process the options of % the \meta{argument specification} and select which variant of the \meta{theorem family} % to enter. % The arguments are available as usual with \pkg{xparse} by \texttt{\#1}, \texttt{\#2}, \ldots % % The body may also call any number of \cs{AddTheoremToGroup} calls, % which the enables the corresponding groups to be toggled. % % When the environment is called within the document, the options are parsed % as with \pkg{xparse} and the \meta{selection body} is executed. % Immediately after, the theorem variant of \meta{theorem family} with the specified groups % by \cs{AddTheoremToGroup} is called. % % At the end of the environment, the \meta{selection body} is executed again and the % called theorem variant is ended again. % % The possible theorem variants that the newly declared environment will call % \emph{have to be generated subsequently} by a call to the \cs{NewGroupedTheoremFamily} % function. % \begin{danger} % As always, if you do not wish the \meta{theorem family*} version to be generated, % you can pass \texttt{starred version = false} as an additional key. % \end{danger} % % \end{function} % % \begin{function} % { % \NewGroupedTheoremFamilyOptions*, \RenewGroupedTheoremFamilyOptions*, % \ProvideGroupedTheoremFamilyOptions*, \DeclareGroupedTheoremFamilyOptions* % } % \begin{syntax} % \cs{NewGroupedTheoremFamilyOptions*}\marg{theorem family}\marg{argument specifiation}% % \marg{selection body} % \end{syntax} % % Does the same as \cs{NewGroupedTheoremFamilyOptions}, % but calls the variants with the additional \texttt{unnumbered} group. % % The possible theorem variants have to be generated % with the \cs{NewGroupedTheoremFamily*} command before. % % \end{function} % % % \section{\LaTeX3 interface} % % There is also an underlying \LaTeX3 interface provided by the package % (and in fact, all prior documented macros are just wrappers around this % internal programming interface. % % When building on top of this package, you can also use this interface, % which is possibly easier to use in some cases. % % Most of the time, however, the document level commands will provide % a better interface that just accepts more options than the % underlying \LaTeX3 interface does. % Feel free to just use these directly. % % In general, for functions that use key-value syntax, there are typically % three (public) versions of the command, namely % % \begin{itemize} % \item A \LaTeX3 command that requires all key-values as mandatory arguments, % so this does not use the key-value interface. % Use this if you already know with which keys you deal and know their % corresponding values. % \item A \LaTeX3 command having the first argument accepting the keys as a % comma-separated list. % Use this if you want to profit of the key-value syntax. % \item A \LaTeX2e document command. These were documented before, % and these just wrap the second type of command. % \end{itemize} % % % \subsection{Theorem groups} % % \begin{function} % { % \groupthm_new_group:nn, % \groupthm_renew_group:nn, % \groupthm_provide_group:nn, % \groupthm_declare_group:nn % } % \begin{syntax} % \cs{groupthm_new_group:nn}\marg{key=value list}\marg{theorem group} % \end{syntax} % % \LaTeX3 versions of \cs{NewTheoremGroup}, \cs{RenewTheoremGroup}, % \cs{ProvideTheoremGroup} and \cs{DeclareTheoremGroup} % % \end{function} % % % % \begin{function} % { % \groupthm_new_group:nnnnn, % \groupthm_renew_group:nnnnn, % \groupthm_provide_group:nnnnn, % \groupthm_declare_group:nnnnn, % \groupthm_new_group:nVVVV, % \groupthm_renew_group:nVVVV, % \groupthm_provide_group:nVVVV, % \groupthm_declare_group:nVVVV % } % \begin{syntax} % \cs{groupthm_new_group:nnnnn}\marg{theorem group}\marg{prefix} % \marg{suffix}\marg{mapname clist}\marg{thmtools clist} % \end{syntax} % % Non-keyval versions of % \cs{groupthm_new_group:nn}, % \cs{groupthm_renew_group:nn}, % \cs{groupthm_provide_group:nn} % and % \cs{groupthm_declare_group:nn} % % These take the individual values of the keyval keys directly, in the order % indicated by the syntax specification. % % \end{function} % % % \begin{function}{\groupthm_declare_group_rule:nnnn} % \begin{syntax} % \cs{groupthm_declare_group_rule:nnnn}\marg{keyname}\marg{theorem group_1} % \marg{relation}\marg{theorem group_2} % \end{syntax} % % \LaTeX3 version of \cs{DeclareTheoremGroupRule} % % \end{function} % % % % \begin{function}{\groupthm_add_parent:nn} % \begin{syntax} % \cs{groupthm_add_parent:nn}\marg{theorem group_1}\marg{theorem group_2} % \end{syntax} % % \LaTeX3 version of \cs{AddTheoremGroupParent}. % % \end{function} % % % % \begin{function}{\groupthm_append_to_group:nn} % \begin{syntax} % \cs{groupthm_append_to_group:nn}\marg{key=value list}\marg{theorem group} % \end{syntax} % % \LaTeX3 version of \cs{AppendToTheoremGroup}. % % \end{function} % \subsection{Grouped theorems} % % \begin{function}{\groupthm_new_theorem:nnnn, \groupthm_provide_theorem:nnnn} % \begin{syntax} % \cs{groupthm_new_theorem:nnnn}\marg{grouped theorem}\marg{groups} % \marg{name}\marg{thmtools keys} % \end{syntax} % % % % \end{function} % \begin{function}{\groupthm_new_theorem:nnn, \groupthm_provide_theorem:nnn} % \begin{syntax} % \cs{groupthm_new_theorem:nnn}\marg{key=value list}\marg{theorem group}\marg{bool} % \end{syntax} % % \LaTeX3 version of \cs{NewGroupedTheorem}. The given \meta{bool} indicates the presence of % the \enquote{*}, i.e.~if it is true, the new theorem will be additionally added to the % \texttt{unnumbered} group. % % \end{function} % % % \subsection{Theorem families} % % % \begin{function}{\groupthm_new_family:nnn, \groupthm_provide_family:nnn} % \begin{syntax} % \cs{groupthm_new_family:nnn}\marg{key=value list}\marg{theorem family}\marg{bool} % \end{syntax} % % \LaTeX3 version of \cs{NewGroupedTheoremFamily}. The given \meta{bool} indicates the presence of % the \enquote{*}, i.e.~if it is true, the new theorem will be additionally added to the % \texttt{unnumbered} group. % % \end{function} % \begin{function} % { % \groupthm_new_family:nnnnn, \groupthm_provide_family:nnnnn, % \groupthm_new_family:nVVVV, \groupthm_provide_family:nVVVV % } % \begin{syntax} % \cs{groupthm_new_family:nnnnn}\marg{theorem family}\marg{groups_1}\marg{name} % \marg{thmtools clist}\marg{groups_2} % \end{syntax} % % Non-keyval version of \cs{groupthm_new_family:nnn}. % The \meta{groups_2} will be added to each generated variant, i.e.~we generate a variant % for the union of (the powerset of \meta{groups_1}) and \meta{groups_2}. % % \end{function} % % % % \begin{function}{\groupthm_add_theorem_to_group:n} % \begin{syntax} % \cs{groupthm_add_theorem_to_group:n}\marg{theorem group} % \end{syntax} % % \LaTeX3 version of \cs{AddTheoremToGroup} % % \end{function} % % \end{documentation} % % % \begin{function} % { % \groupthm_new_family_options:nnnn, % \groupthm_renew_family_options:nnnn, % \groupthm_provide_family_options:nnnn, % \groupthm_declare_family_options:nnnn, % } % \begin{syntax} % \cs{groupthm_new_family_options:nnnn}\marg{theorem family}\marg{arg spec} % \marg{selection body}\marg{groups} % \end{syntax} % % \LaTeX3 version of \cs{NewGroupedTheoremFamilyOptions}. % The \meta{groups} is a comma separated list of groups that will always be added % to the variants called. % % So, \cs{NewGroupedTheoremFamilyOptions*} will e.g.~add \texttt{unnumbered} to this list. % % \end{function} % % % % % \begin{implementation} % % \section{\pkg{groupthm} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=groupthm> % \end{macrocode} % % % % % \subsection{Dependencies} % % % % First, we import other packages on which we rely on, and % set up some private wrappers around these. % \begin{macrocode} \RequirePackage{amsthm} \RequirePackage{thmtools} % \end{macrocode} % % \begin{macro}{\@@_thmtools_declare_theorem:nn, \@@_thmtools_declare_theorem:Vn} % \begin{syntax} % \cs{@@_thmtools_declare_theorem:nn} \meta{theorem name}\meta{thmtools keyval args} % \end{syntax} % % This is just a private wrapper around \cs{declaretheorem} of the \pkg{thmtools} package. % % \begin{macrocode} \cs_new:Npn \@@_thmtools_declare_theorem:nn #1 #2 { \tl_log:n { Declaring ~ thmtools ~ theorem ~ #2 } \declaretheorem [ #1 ] { #2 } } \cs_generate_variant:Nn \@@_thmtools_declare_theorem:nn { V n } % \end{macrocode} % \end{macro} % % % % It also comes in handy to have a stronger version of the % hook role setting mechanism: % \begin{macro}{\@@_hook_gset_rule_foreach:nNnn} % \begin{syntax} % \cs{@@_hook_gset_rule_foreach:nNnn}\marg{hook}\meta{clist name}\meta{relation}\meta{label} % \end{syntax} % % This is a wrapper around the \cs{hook_gset_rule:nnnn} macro % that takes a clist name of labels, and executes the corresponding % command for each such label. % % \begin{macrocode} \cs_new:Npn \@@_hook_gset_rule_foreach:nNnn #1 #2 #3 #4 { \cs_set:Npn \@@_map_aux:n ##1 { \hook_gset_rule:nnnn { #1 } { ##1 } { #3 } { #4 } } \clist_map_function:NN #2 \@@_map_aux:n } % \end{macrocode} % \end{macro} % % % % \subsection{Messages} % These are messages that we might emit. % % When an unknown group is used somwhere: % \begin{syntax} % \cs{msg_error:nnn}\{ groupthm \}\{ unknown group \}\marg{groupname} % \end{syntax} % \begin{macrocode} \msg_new:nnn { groupthm } { unknown ~ group } { Unknown ~ group ~ '#1' ~ supplied ~ \msg_line_context: } % \end{macrocode} % % When an unknown key has been used: % \begin{syntax} % \cs{msg_error:nnn}\{ groupthm \}\{ unknown key \}\marg{key} % \end{syntax} % \begin{macrocode} \msg_new:nnn { groupthm } { unknown ~ key } { Unknown ~ key ~ '#1' ~ supplied ~ \msg_line_context: } % \end{macrocode} % % Some data structure is already defined or not defined yet. % \begin{syntax} % \cs{msg_error:nnnnn}\{ groupthm \}\{ wrong definition \} % \marg{type}\marg{name}\marg{already $\|$ not} % \end{syntax} % \begin{macrocode} \msg_new:nnn { groupthm } { wrong ~ definition } { Bad ~ definition ~ of ~ #1 ~ '#2' ~ \msg_line_context:, ~ #1 ~ is ~ #3 ~ defined. } % \end{macrocode} % % % When the special \cs{AddTheoremToGroup} macro is issued outside a theorem family options % body. % \begin{syntax} % \cs{msg_error:nn} \{ groupthm \}\{ misuse add theorem to group \} % \end{syntax} % \begin{macrocode} \msg_new:nnn { groupthm } { misuse ~ add ~ theorem ~ to ~ group } { Bad ~ usage ~ of ~ 'AddTheoremToGroup' ~ macro ~ outside ~ theorem ~ family ~ options ~ \msg_line_context: } % \end{macrocode} % % % % When a theorem family is invoked, but has not been generated yet. % \begin{syntax} % \cs{msg_error:nn} \{ groupthm \}\{ undefined theorem variant \} % \end{syntax} % \begin{macrocode} \msg_new:nnnn { groupthm } { undefined ~ theorem ~ variant } { Bad ~ call ~ of ~ theorem ~ variant ~ of ~ '#1' ~ \msg_line_context: } { You ~ wanted ~ to ~ call ~ the ~ variant ~ with ~ group(s) ~ '#2' ~ of ~ theorem ~ family ~ '#1', ~ but ~ it ~ has ~ not ~ been ~ generated ~ yet. ~ Probably ~ you ~ forgot ~ this. ~ \msg_see_documentation_text:n { groupthm } } % \end{macrocode} % % \subsection{Allocation and initialization} % % We use hooks at several places. However, these are not intended for outer use, % and we thus mark them with a preceding \texttt{__}. % % \begin{macrocode} \hook_new:n { @@/prefix } \hook_new:n { @@/suffix } \hook_new:n { @@/mapname } \hook_new:n { @@/thmtools } % \end{macrocode} % \begin{macrocode} \hook_new:n { @@/groupsort } % \end{macrocode} % % \begin{macro}{\hook_gset_rule:nnVn} % \begin{syntax} % \cs{hook_gset_rule:nnVn}\marg{hook}\marg{label_1}\marg{relation}\marg{label_2} % \end{syntax} % % Just a variant of the usual \cs{hook_gset_rule:nnnn} macro that we use. % % \begin{macrocode} \cs_generate_variant:Nn \hook_gset_rule:nnnn { n n V n } % \end{macrocode} % \end{macro} % % % % \begin{variable} % { % \l_@@_key_starred_version_bool, % \l_@@_key_prefix_tl, \l_@@_key_name_tl, \l_@@_key_suffix_tl, % \l_@@_key_group_clist, \l_@@_key_mapname_clist, \l_@@_key_thmtools_clist % } % These variables will be set by the key-value interface provided by % \pkg{l3keys} and are used in various places in the package. % \begin{macrocode} \bool_new:N \l_@@_key_starred_version_bool \tl_new:N \l_@@_key_prefix_tl \tl_new:N \l_@@_key_name_tl \tl_new:N \l_@@_key_suffix_tl \clist_new:N \l_@@_key_group_clist \clist_new:N \l_@@_key_mapname_clist \clist_new:N \l_@@_key_thmtools_clist % \end{macrocode} % \end{variable} % % \begin{variable} % { % \l_@@_starred_version_bool, % \l_@@_prefix_tl, % \l_@@_name_tl, % \l_@@_suffix_tl, % \l_@@_mapname_clist, % \l_@@_thmtools_clist, % \l_@@_group_clist % } % % General local variables. % Will typically be used to extract the variables set by the \pkg{l3keys} interface, % but also in just a local variable sense. % % \begin{macrocode} \tl_new:N \l_@@_prefix_tl \tl_new:N \l_@@_name_tl \tl_new:N \l_@@_suffix_tl \clist_new:N \l_@@_mapname_clist \clist_new:N \l_@@_thmtools_clist \clist_new:N \l_@@_group_clist % \end{macrocode} % \end{variable} % % % % \begin{variable}{\g_@@_defined_groups_clist} % % This variable will hold a global list of declared theorem groups % % \begin{macrocode} \clist_new:N \g_@@_defined_groups_clist % \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_in_family_options_environment_bool} % This variable indicates whether we are in the special environment % used to parse a set of groups out of options given to a theorem % family. % % This bool toggles the availability of the special \cs{AddTheoremToGroup} macro. % \begin{macrocode} \bool_new:N \l_@@_in_family_options_environment_bool % \end{macrocode} % \end{variable} % \begin{variable}{\g_@@_append_groups_int} % % This counts the number of times we appended to a group. % % \begin{macrocode} \int_new:N \g_@@_append_groups_int % \end{macrocode} % \end{variable} % % % \subsection{Key interface} % As mentioned, all keys will set their corresponding local variables % (containing \enquote{\texttt{_key_}} in their name) and store the % user input in these. % % Additionally, we group these keys by use cases, % and provide defaults that in most cases will not require further handling. % % % % \begin{macrocode} \keys_define:nn { groupthm / theorem ~ group } { prefix .tl_set:N = \l_@@_key_prefix_tl, prefix .default:n = \c_empty_tl, suffix .tl_set:N = \l_@@_key_suffix_tl, suffix .default:n = \c_empty_tl, suffix .groups:n = { theoremgroup }, map ~ name .clist_set:N = \l_@@_mapname_clist, map ~ name .default:n = {}, map ~ name .groups:n = { theoremgroup }, thmtools .clist_set:N = \l_@@_key_thmtools_clist, thmtools .default:n = {}, unknown .code:n = \msg_error:nnn { groupthm } { unknown ~ key } { \l_keys_key_str } } \keys_define:nn { groupthm / grouped ~ theorem } { name .tl_set:N = \l_@@_key_name_tl, name .default:n = \c_novalue_tl, group .clist_set:N = \l_@@_key_group_clist, group .default:n = {}, thmtools .clist_set:N = \l_@@_key_thmtools_clist, thmtools .default:n = {}, starred ~ version .bool_set:N = \l_@@_key_starred_version_bool, starred ~ version .default:n = { true }, unknown .code:n = \msg_error:nnn { groupthm } { unknown ~ key } { \l_keys_key_str } } \keys_define:nn { groupthm / theorem ~ family } { name .tl_set:N = \l_@@_key_name_tl, name .default:n = \c_novalue_tl, group .clist_set:N = \l_@@_key_group_clist, group .default:n = {}, thmtools .clist_set:N = \l_@@_key_thmtools_clist, thmtools .default:n = {}, starred ~ version .bool_set:N = \l_@@_key_starred_version_bool, starred ~ version .default:n = { true }, unknown .code:n = \msg_error:nnn { groupthm } { unknown ~ key } { \l_keys_key_str } } \keys_define:nn { groupthm / theorem ~ family ~ options } { starred ~ version .bool_set:N = \l_@@_key_starred_version_bool, starred ~ version .default:n = { true }, unknown .code:n = \msg_error:nnn { groupthm } { unknown ~ key } { \l_keys_key_str } } % \end{macrocode} % % % The only key whose default requires such handling is the \enquote{\texttt{name}} key, % which will be set to a capitalized version of the environment name % when not specified. % % % \begin{macro}{\@@_set_normalized_keys:nnn} % \begin{syntax} % \cs{@@_set_normalized_keys:nnn}\marg{key=value list}\marg{key group}\marg{fallback name} % \end{syntax} % % Sets the packages keys and normalizes the retrieved values, that is, % clears old set keys, stores all keys in local variables, % and replaces the \cs{l_@@_name_tl} with the capitalized version of the % \meta{fallback name}. % % \begin{macrocode} \cs_new:Npn \@@_set_normalized_keys:nnn #1 #2 #3 { \keys_set:nn { groupthm / theorem ~ group } { prefix, suffix, thmtools, map ~ name } \keys_set:nn { groupthm / grouped ~ theorem } { name, group, thmtools, starred ~ version } \keys_set:nn { groupthm / theorem ~ family } { name, group, thmtools, starred ~ version } \keys_set:nn { groupthm / #2 } { #1 } % \end{macrocode} % % Normalize given name % % \begin{macrocode} \tl_if_eq:NnTF \l_@@_key_name_tl { \c_novalue_tl } { \tl_set:Nx \l_@@_name_tl { \text_titlecase_first:n {#3} } } { \tl_set_eq:NN \l_@@_name_tl \l_@@_key_name_tl } % \end{macrocode} % % Copy set keys into local variables % % \begin{macrocode} \bool_set_eq:NN \l_@@_starred_version_bool \l_@@_key_starred_version_bool \tl_set_eq:NN \l_@@_prefix_tl \l_@@_key_prefix_tl \tl_set_eq:NN \l_@@_suffix_tl \l_@@_key_suffix_tl \clist_set_eq:NN \l_@@_group_clist \l_@@_key_group_clist \clist_set_eq:NN \l_@@_mapname_clist \l_@@_key_mapname_clist \clist_set_eq:NN \l_@@_thmtools_clist \l_@@_key_thmtools_clist } % \end{macrocode} % \end{macro} % % % \subsection{Theorem groups} % % For technical reasons, we need to % some arbitrary, but unique total ordering on the set of all defined theorem groups. % % Since unfortunately (by now) there is no standard mechanism for sorting strings % in \LaTeX3 directly, we use an ugly hack to achieve what we want: % % We will use an internal hook, and apply hook rules to each pair of internal groups % such that the resulting relation is a total order. % Whenever we want to sort a list of groups now, we do the following: % First, for each group element in the list, insert into the hook the function % \enquote{put this group back into the list}, using the group itself as a label. % Then we clear the list, and finally execute the hook. % % Essentially, we thus split up the hook in the single groups, let the \LaTeX3 hook % mechanism take care of the sorting, and restore the sorted single pieces into our list. % Of course, this is very inefficient, but for now it seems to be the simplest solution, % without having to implement an own string sorting function. % % Once there is such a proper mechanism, the author will likely update this to proper % string sorting. % % \begin{macro}{\@@_add_to_group_ordering:n} % \begin{syntax} % \cs{@@_add_to_group_ordering:n}\marg{theorem group} % \end{syntax} % % Sets hook relations for this group and all already defined theorem groups. % % \begin{macrocode} \cs_new:Npn \@@_add_to_group_ordering:n #1 { \@@_hook_gset_rule_foreach:nNnn { @@/groupsort } \g_@@_defined_groups_clist { before } { #1 } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_remove_from_group_ordering:n} % \begin{syntax} % \cs{@@_remove_from_group_ordering:n}\marg{theorem group} % \end{syntax} % % Removes all relations of this theorem group with the currently defined theorem groups. % % \begin{macrocode} \cs_new:Npn \@@_remove_from_group_ordering:n #1 { \@@_hook_gset_rule_foreach:nNnn { @@/groupsort } \g_@@_defined_groups_clist { unrelated } { #1 } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_add_to_sort_hook:n} % \begin{syntax} % \cs{@@_add_to_sort_hook:n}\marg{theorem group} % \end{syntax} % % Adds the theorem group into the sort hook to be restored later from it. % This already uses the assumption, that we want to use the \cs{l_@@_group_clist} % variable (which we do). % % \begin{macrocode} \cs_new:Npn \@@_add_to_sort_hook:n #1 { \hook_gput_code:nnn { @@/groupsort } { #1 } { \clist_put_left:Nn \l_@@_group_clist { #1 } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_sort_group_names:} % % As explained briefly before, we first insert all theorems % into the hook, clear the list, and then use the hook again. % % This then sorts the \cs{l_@@_group_clist} variable, % which is also assumed to hold only defined theorem group names. % % \begin{macrocode} \cs_new:Npn \@@_sort_group_names: { \hook_gremove_code:nn { @@/groupsort }{*} \clist_map_function:NN \l_@@_group_clist \@@_add_to_sort_hook:n \clist_clear:N \l_@@_group_clist \hook_use:n { @@/groupsort } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_define_group:nnnnn} % \begin{syntax} % \cs{@@_define_group:nnnnn}\marg{theorem group}\marg{prefix tl} % \marg{suffix tl}\marg{mapname clist}\marg{thmtools clist} % \end{syntax} % % This creates a new theorem group out of the given parameters. % We store all given contents in our (private) hooks, using the group name as the key % so that we can later retrieve the components of each group separately. % % This is an internal function and assumes that the group is currently not defined, % and also removed from all hooks. % % % \begin{macrocode} \cs_new:Npn \@@_define_group:nnnnn #1#2#3#4#5 { % \end{macrocode} % % \begin{macro}{\@@_use_group__\meta{theorem group}:} % % This is the internal macro that will be called when retrieving contents of a group. % We define this here to store the properties of the group. % % \begin{macrocode} \cs_new:cpn { @@_use_group__#1: } { \hook_gput_code:nnn { @@/prefix } { #1 } { \tl_put_left:Nx \l_@@_prefix_tl { #2 } } \hook_gput_code:nnn { @@/suffix } { #1 } { \tl_put_right:Nx \l_@@_suffix_tl { #3 } } \hook_gput_code:nnn { @@/mapname } { #1 } { \clist_put_right:Nn \l_@@_mapname_clist { #4 } } \hook_gput_code:nnn { @@/thmtools } { #1 } { \clist_put_right:Nn \l_@@_thmtools_clist { #5 } } } % \end{macrocode} % \end{macro} % % This ensures the ordering hacks explained before. % % \begin{macrocode} \@@_add_to_group_ordering:n { #1 } % \end{macrocode} % \begin{variable}{ \g_@@_parents_group__\meta{theorem group}__clist } % % This variable will accumulate the parents of this group. % \begin{macrocode} \clist_new:c { g_@@_parents_group__#1__clist } % \end{macrocode} % This ensures default priorities between groups. % \begin{macrocode} \hook_gset_rule:nnnn { ?? } { all } { before } { #1 } \hook_gset_rule:nnnn { ?? } { unnumbered } { after } { #1 } \hook_gset_rule:nnnn { ?? } { starred } { after } { #1 } % \end{macrocode} % \end{variable} % Add defined group to corresponding list % % \begin{macrocode} \clist_gput_left:Nn \g_@@_defined_groups_clist { #1 } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_undefine_group:n} % \begin{syntax} % \cs{@@_undefine_group:n}\marg{theorem group} % \end{syntax} % % Undeclares / undefines the given theorem group. % This means removing its hook code in the \texttt{prefix}, \texttt{suffix}, % \texttt{mapname} and \texttt{thmtools} hooks, % and removing all relations with other theorem groups globally % as well as for each hook individually. % % This macro assumes that the group was defined prior to calling. % % \begin{macrocode} \cs_new:Npn \@@_undefine_group:n #1 { \tl_log:n { Undefining ~ theorem ~ group ~ '#1' } \cs_undefine:c { @@_use_group__#1: } % \end{macrocode} % % Remove properties from hooks % % \begin{macrocode} \hook_gremove_code:nn { @@/prefix } { #1 } \hook_gremove_code:nn { @@/suffix } { #1 } \hook_gremove_code:nn { @@/mapname } { #1 } \hook_gremove_code:nn { @@/thmtools } { #1 } % \end{macrocode} % % Remove theorem group from list of defined theorems % % \begin{macrocode} \clist_gremove_all:Nn \g_@@_defined_groups_clist { #1 } % \end{macrocode} % % Delete the known parents of this group: % \begin{macrocode} \cs_undefine:c { g_@@_parents_group__#1__clist } % \end{macrocode} % % Now, unset all relations with all defined theorem groups in the internal hooks. % % \begin{macrocode} \@@_hook_gset_rule_foreach:nNnn { ?? } \g_@@_defined_groups_clist { unrelated } { #1 } \@@_hook_gset_rule_foreach:nNnn { @@/prefix } \g_@@_defined_groups_clist { unrelated } { #1 } \@@_hook_gset_rule_foreach:nNnn { @@/suffix } \g_@@_defined_groups_clist { unrelated } { #1 } \@@_hook_gset_rule_foreach:nNnn { @@/mapname } \g_@@_defined_groups_clist { unrelated } { #1 } \@@_hook_gset_rule_foreach:nNnn { @@/thmtools } \g_@@_defined_groups_clist { unrelated } { #1 } % \end{macrocode} % % Also clear all sorting relations % % \begin{macrocode} \@@_remove_from_group_ordering:n { #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_define:nnnNNNn, \@@_define:nnncNNn} % \begin{syntax} % \cs{@@_define:nnnNNNn}\marg{declarator}\marg{type}\marg{instance}\marg{existence cs}\marg{undefine function}\marg{define function}\marg{definition args} % \end{syntax} % % A general definition macro that is used to implement the \texttt{new}, % \texttt{renew}, \texttt{provide} and \texttt{declare} definition variants of \meta{type}s. % For the purpose of this package, \meta{type} will be one of % \enquote{theorem group}, \enquote{grouped theorem}, \enquote {theorem family} % and\enquote{theorem family options}, but could technically be anything. % % The \meta{instance} is the actual thing that will be defined by this function. % The \meta{declarator} is one of \texttt{new}, \texttt{renew}, % \texttt{provide} and \texttt{declare} and indicates the definition behavior: % \texttt{new} only defines % if \meta{instance} does not yet exist and throws an error otherwise, % \texttt{renew} only (re)defines % if \meta{instance} does exist yet and throws an error otherwise, % \texttt{provides} defines if \meta{instance} does not exist yet, % but does nothing otherwise % and % \texttt{declare} defines \meta{instance} in any case, % possibly by overwriting the old definition. % % The \meta{existence cs} is the one whose existence will be checked to % determine whether the \meta{instance} already exists. % % The \meta{undefine function} will be called in case \meta{instance} has to be undefined. % It is assumed to have argument type \texttt{n} and will be given the \meta{instance} % as an argument in this case. % % The \meta{define function} will be called with the arguments % given as \meta{definition args} in case no error occurs and \meta{instance} should be defined. % % % \begin{macrocode} \cs_new:Npn \@@_define:nnnNNNn #1 #2 #3 #4 #5 #6 #7 { % \end{macrocode} % We first check for wrong usage of \texttt{new} % \begin{macrocode} \bool_if:nT { \str_if_eq_p:nn { #1 } { new } && \cs_if_exist_p:N #4 } { \tl_log:n { Wrong ~ 'new' ~ definition ~ of ~ #2 ~ '#3' ~ detected. } \msg_error:nnnnn { groupthm } { wrong ~ definition } { #2 } { #3 } { already } } % \end{macrocode} % Then check for wrong usage of \texttt{renew} % \begin{macrocode} \bool_if:nT { \str_if_eq_p:nn { #1 } { renew } && ! \cs_if_exist_p:N #4 } { \tl_log:n { Wrong ~ 'renew' ~ definition ~ of ~ #2 ~ '#3' ~ detected. } \msg_error:nnnnn { groupthm } { wrong ~ definition } { #2 } { #3 } { not } } % \end{macrocode} % Now, remove the old definition if necessary % \begin{macrocode} \bool_if:nT { ( \str_if_eq_p:nn { #1 } { declare } || \str_if_eq_p:nn { #1 } { renew } ) && \cs_if_exist_p:N #4 } { \tl_log:n { Removing ~ definition ~ of ~ #2 ~ '#3'. } \tl_log:n { Declarator ~ was ~ #1. } #5 { #3 } } % \end{macrocode} % Finally, define new version if not already defined % (this check is necessary for the provide version.) % \begin{macrocode} \bool_if:nTF { \cs_if_exist_p:N #4 && \str_if_eq_p:nn { #1 } { provide } } { \tl_log:n { Providing ~ #2 ~ '#3' ~ skipped: ~ '#3' ~ already ~ defined. } } { \tl_log:n { Defining ~ #2 ~ '#3'. } #6 #7 } } \cs_generate_variant:Nn \@@_define:nnnNNNn { n n n c N N n } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_define_multiple:nnnNNnn} % \begin{syntax} % \cs{@@_define_multiple:nnnNNnn} \marg{declarator list} \marg{type} \marg{existence cs} % \marg{undefine function}\marg{define function}\marg{function name}\marg{definition args} % \end{syntax} % % This is a wrapper around the \cs{@@_define:nnnNNNn} macro. % It is intended to wrap the multiple variants of it into a family of macros indicating % the variant in their name, e.g.~to define \cs{groupthm_new_group:nnnnn}, % \cs{groupthm_renew_group:nnnnn}, % \cs{groupthm_provide_group:nnnnn} and \cs{groupthm_declare_group:nnnnn} % in the same way except for their % indicated declaration behavior, to avoid repetition when defining these. % % The first five arguments work the same way as in \cs{@@_define:nnnnNNn}, % except that \meta{declarator list} is now a comma separated list. % In \meta{existence cs}, \texttt{\#\#1} is used to denote the \meta{instance} % that has currently been called to define. % % The \meta{function name} is expected to contain \texttt{\#1}, % for which the current \meta{declarator} is inserted. % These control sequences will then be defined. % % The \meta{definition args} denote the arguments passed to the \meta{define function} % and may contain \texttt{\#\#1}, \texttt{\#\#2}, etc. for the arguments that the % \meta{function name} received as arguments on expansion. % % % \begin{macrocode} \cs_new:Npn \@@_define_multiple:nnnNNnn #1 #2 #3 #4 #5 #6 #7 { \cs_set:Npn \@@_map_aux:n ##1 { \cs_new:cn { #6 } { \@@_define:nnncNNn { ##1 } { #2 } { ####1 } { #3 } #4 #5 { #7 } } } \clist_map_function:nN { #1 } \@@_map_aux:n } % \end{macrocode} % \end{macro} % % % With these helper functions, we can now easily generate the % \texttt{new}, \texttt{renew}, \texttt{provide} and \texttt{declare} variants % of the theorem group macro: % % \begin{macro} % { % \groupthm_new_group:nnnnn, \groupthm_new_group:nVVVV, % \groupthm_renew_group:nnnnn, \groupthm_renew_group:nVVVV, % \groupthm_provide_group:nnnnn, \groupthm_provide_group:nVVVV, % \groupthm_declare_group:nnnnn, \groupthm_declare_group:nVVVV % } % % \begin{macrocode} \@@_define_multiple:nnnNNnn { new, renew, provide, declare } { theorem group } { @@_use_group__##1: } \@@_undefine_group:n \@@_define_group:nnnnn { groupthm_#1_group:nnnnn } { { ##1 } { ##2 } { ##3 } { ##4 } { ##5 } } % \end{macrocode} % % Finally, generate some extra variants % % \begin{macrocode} \cs_generate_variant:Nn \groupthm_new_group:nnnnn { n V V V V } \cs_generate_variant:Nn \groupthm_renew_group:nnnnn { n V V V V } \cs_generate_variant:Nn \groupthm_provide_group:nnnnn { n V V V V } \cs_generate_variant:Nn \groupthm_declare_group:nnnnn { n V V V V } % \end{macrocode} % \end{macro} % % % With the \cs{@@_set_normalized_keys:nnn} macro at hand, % it is also easy to provide key-value interfaces for these commands: % % \begin{macro}{\@@_wrap_multiple:nnn} % \begin{syntax} % \cs{@@_wrap_multiple:nnn}\marg{declarator list}\marg{function name}\marg{code} % \end{syntax} % % Defines \meta{function name}, which is assumed to contain \cs{declarator} % by \meta{code} for each declarator in \meta{declarator list}. % % \begin{macrocode} \cs_new:Npn \@@_wrap_multiple:nnn #1 #2 #3 { \cs_set:Npn \@@_map_aux:n ##1 { \cs_new:cn { #2 } { #3 } } \clist_map_function:nN { #1 } \@@_map_aux:n } % \end{macrocode} % \end{macro} % \begin{macro} % { % \groupthm_new_group:nn, % \groupthm_renew_group:nn, % \groupthm_provide_group:nn, % \groupthm_declare_group:nn % } % \begin{syntax} % \cs{groupthm_new_group:nn}\marg{key=value list}\marg{theorem group} % \end{syntax} % % \begin{macrocode} \@@_wrap_multiple:nnn { new, renew, provide, declare } { groupthm_#1_group:nn } { \@@_set_normalized_keys:nnn { ##1 } { theorem ~ group } { ##2 } \use:c { groupthm_#1_group:nVVVV } { ##2 } \l_@@_prefix_tl \l_@@_suffix_tl \l_@@_mapname_clist \l_@@_thmtools_clist } % \end{macrocode} % Additional variant % \begin{macrocode} \cs_generate_variant:Nn \groupthm_new_group:nn { n x } % \end{macro} % % % Finally, we provide \LaTeX2e wrappers as document commands for these. % % \begin{macro}{\@@_new_document_command:Nnn, \@@_new_document_command:cnn} % % Private wrappers around \cs{NewDocumentCommand}. % % \begin{macrocode} \cs_new:Npn \@@_new_document_command:Nnn #1 #2 #3 { \NewDocumentCommand { #1 } { #2 } { #3 } } \cs_generate_variant:Nn \@@_new_document_command:Nnn { c n n } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\@@_wrap_multiple_document:nnnn} % \begin{syntax} % \cs{@@_wrap_multiple_document:nnnn}\marg{declarator list}\marg{function name}\marg{arg spec}\marg{code} % \end{syntax} % % This is very similar to \cs{@@_wrap_multiple:nnn}, except that it produces document commands. % For this reason, \cs{declarator} and \cs{Declarator} are available to refer to the lower % and upper-case versions of the current declarator. % % \begin{macrocode} \cs_new:Npn \@@_wrap_multiple_document:nnnn #1 #2 #3 #4 { \cs_set:Npn \@@_map_aux:n ##1 { \cs_set:Nn \@@_Declarator: { \text_titlecase_first:n { ##1 } } \@@_new_document_command:cnn { #2 } { #3 } { #4 } } \clist_map_function:nN { #1 } \@@_map_aux:n } % \end{macrocode} % \end{macro} % % \begin{macro}{\NewTheoremGroup, \RenewTheoremGroup, \ProvideTheoremGroup, \DeclareTheoremGroup} % % These just wrap the \cs{groupthm_\meta{declarator}_group:nn} macros. % % \begin{macrocode} \@@_wrap_multiple_document:nnnn { new, renew, provide, declare } { \@@_Declarator: TheoremGroup } { O{} m } { \use:c { groupthm_#1 _group:nn } { ##1 } { ##2 } } % \end{macrocode} % \end{macro} % % % We also provide the interface for declaring the precedence rules for theorem groups. % % % \begin{macro}{\groupthm_declare_group_rule:nnnn} % \begin{syntax} % \cs{groupthm_declare_group_rule:nnnn}\marg{keyname}\marg{theorem group_1} % \marg{relation}\marg{theorem group_2} % \end{syntax} % % We have to normalize the arguments a little bit, namely replacing \texttt{higher} % and \texttt{lower} with \texttt{before} \texttt{after} respectively, % and prefix the \meta{keyname} with \texttt{__groupthm} % in case it is not the general hook \enquote{\texttt{??}}. % % \begin{macrocode} \cs_new:Npn \groupthm_declare_group_rule:nnnn #1 #2 #3 #4 { \str_set:Nx \l_tmpa_str { \tl_trim_spaces:n { #3 } } \str_if_eq:VnT \l_tmpa_str { higher } { \str_set:Nn \l_tmpa_str { after } } \str_if_eq:VnT \l_tmpa_str { lower } { \str_set:Nn \l_tmpa_str { before } } \str_if_eq:nnTF { #1 } { ?? } { \hook_gset_rule:nnVn {??} {#2} \l_tmpa_str {#4} } { \hook_gset_rule:nnVn { @@ / #1 } {#2} \l_tmpa_str {#4} } } \cs_generate_variant:Nn \groupthm_declare_group_rule:nnnn { n n n x } % \end{macrocode} % \end{macro} % \begin{macro}{\DeclareTheoremGroupRule} % \begin{syntax} % \cs{DeclareTheoremGroupRule}\oarg{keyname}% % \marg{theorem group_1}\marg{relation}\marg{theorem group_2} % \end{syntax} % % \begin{macrocode} \NewDocumentCommand { \DeclareTheoremGroupRule } { O{??} m m m } { \groupthm_declare_group_rule:nnnn {#1} {#2} {#3} {#4} } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\groupthm_add_parent:nn} % \begin{syntax} % \cs{groupthm_add_parent:nn}\marg{theorem group_1}\marg{theorem group_2} % \end{syntax} % % Declares \meta{theorem group_2} as a parent of \meta{theorem group_1} % % \begin{macrocode} \cs_new:Npn \groupthm_add_parent:nn #1 #2 { \@@_ensure_group_exists:n { #1 } \@@_ensure_group_exists:n { #2 } \clist_gput_left:cn { g_@@_parents_group__#1__clist } { #2 } } \cs_generate_variant:Nn \groupthm_add_parent:nn { n x } % \end{macrocode} % \end{macro} % % % \begin{macro}{\AddTheoremGroupParent} % \begin{syntax} % \cs{AddTheoremGroupParent}\marg{theorem group_1}\marg{theorem group_2} % \end{syntax} % % Document command version of \cs{groupthm_add_parent:nn} % % \begin{macrocode} \NewDocumentCommand { \AddTheoremGroupParent } { m m } { \groupthm_add_parent:nn { #1 } { #2 } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_push_tmpa_seq:n} % \begin{syntax} % \cs{@@_push_tmpa_seq:n}\marg{balanced text} % \end{syntax} % % % % \begin{macrocode} % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_flatten_groups_hierarchy:nN} % \begin{syntax} % \cs{@@_flatten_groups_hierarchy:nN}\marg{theorem groups}\marg{clist} % \end{syntax} % % Expects a comma separated list of \meta{theorem group}s. % The inheritance relation is flattened, and the set of % obtained theorem groups is stored in \meta{clist} % % \begin{macrocode} \cs_new:Npn \@@_push_tmpa_seq:n #1 { \seq_push:Nn \l_tmpa_seq { #1 } } \cs_new:Npn \@@_flatten_groups_hierarchy:nN #1 #2 { \clist_clear:N #2 \seq_set_from_clist:Nn \l_tmpa_seq { #1 } \bool_until_do:nn { \seq_if_empty_p:N \l_tmpa_seq } { \seq_pop:NN \l_tmpa_seq \l_tmpa_tl \clist_if_in:NVF #2 \l_tmpa_tl { \clist_put_left:NV #2 \l_tmpa_tl \clist_map_function:cN { g_@@_parents_group__ \l_tmpa_tl __clist } \@@_push_tmpa_seq:n } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\groupthm_append_to_group:nn} % \begin{syntax} % \cs{groupthm_append_to_group:nn}\marg{key=value list}\marg{theorem group} % \end{syntax} % % This works the same as defining a new group, except that we append to the group, % overwriting the old group in case of conflicts. % % \begin{macrocode} \cs_new:Npn \groupthm_append_to_group:nn #1 #2 { \groupthm_new_group:nx { #1 } { __append__ \int_use:N \g_@@_append_groups_int } \groupthm_add_parent:nx { #2 } { __append__ \int_use:N \g_@@_append_groups_int } \groupthm_declare_group_rule:nnnx { ?? } { #2 } { before } { __append__ \int_use:N \g_@@_append_groups_int } \int_gincr:N \g_@@_append_groups_int } % \end{macrocodel} % \end{macro} % % \begin{macro}{\AppendToTheoremGroup} % \begin{syntax} % \cs{AppendToTheoremGroup} \marg{key=value list}\marg{theorem group} % \end{syntax} % % % % \begin{macrocode} \NewDocumentCommand { \AppendToTheoremGroup } { O{} m } { \groupthm_append_to_group:nn { #1 } { #2 } } % \end{macrocode} % \end{macro} % We also provide the three default groups: % % \begin{macrocode} \groupthm_new_group:nnnnn { all } { } { } { } { } \groupthm_new_group:nnnnn { starred } { } { } { } { numbered = no } \groupthm_new_group:nnnnn { unnumbered } { } { } { } { numbered = no } % \end{macrocode} % % \subsection{Iterating over powersets} % For generating the different variants of a theorem family, % we need to iterate over over the powerset of some list. % This is a collection of hacks that perform exactly this, % but these are poorly documented for now. % % % \begin{macro}{\@@_powerset_clist_foreach:Nn} % \begin{syntax} % \cs{@@_powerset_clist_foreach:Nn}\meta{clist}\marg{code} % \end{syntax} % % Executes \meta{code} for each subset of the given clist variable. % The value of the (local) variable is changes throughout the iteration, % and is thus available regularly in \meta{code}. % Its value is restored at the end of the iteration. % % \begin{macrocode} \clist_new:N \l__powerset_copied_clist \seq_new:N \l__powerset_saved_seq \cs_generate_variant:Nn \clist_remove_all:Nn { N V } \cs_new:Npn \__powerset_clist_foreach_aux:Nn #1 #2 { \clist_if_empty:NTF \l__powerset_copied_clist { #2 } { \clist_get:NN \l__powerset_copied_clist \l_tmpa_tl \seq_push:NV \l__powerset_saved_seq \l_tmpa_tl \clist_pop:NN \l__powerset_copied_clist { \l_tmpa_tl } \__powerset_clist_foreach_aux:Nn #1 {#2} \seq_get:NN \l__powerset_saved_seq \l_tmpa_tl \clist_put_left:NV #1 \l_tmpa_tl \__powerset_clist_foreach_aux:Nn #1 {#2} \seq_get:NN \l__powerset_saved_seq \l_tmpa_tl \clist_remove_all:NV #1 \l_tmpa_tl \clist_push:NV \l__powerset_copied_clist \l_tmpa_tl \seq_pop:NN \l__powerset_saved_seq \l_tmpa_tl } } \cs_new:Npn \powerset_clist_foreach:Nn #1 #2 { \clist_set_eq:NN \l__powerset_copied_clist #1 \clist_clear:N #1 \clist_remove_duplicates:N \l__powerset_copied_clist \__powerset_clist_foreach_aux:Nn #1 {#2} \clist_set_eq:NN #1 \l__powerset_copied_clist } % \end{macrocode} % \end{macro} % % % % % \subsection{Grouped Theorems} % % % % \begin{macro}{\@@_ensure_group_exists:n} % \begin{syntax} % \cs{@@_ensure_group_exists:n}\meta{theorem group} % \end{syntax} % % Checks if this group exists. % If not, produces an error message. % % \begin{macrocode} \cs_new:Npn \@@_ensure_group_exists:n #1 { \cs_if_exist:cF { @@_use_group__#1: } { \msg_error:nnn { groupthm } { unknown ~ group } { #1 } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\@@_use_group:n} % \begin{syntax} % \cs{@@_use_group:n}\meta{theorem group} % \end{syntax} % % Uses this theorem group, i.e.~applies its definition by writing % to the internal hooks. % A proper error message is emitted if the group is not defined. % % \begin{macrocode} \cs_new:Npn \@@_use_group:n #1 { \@@_ensure_group_exists:n { #1 } \use:c { @@_use_group__#1: } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_use_function_on_name:n} % \begin{syntax} % \cs{@@_use_function_on_name:n}\meta{function} % \end{syntax} % % The \meta{function} is expected to be of type \texttt{:n}, % This applies the function to the \cs{l_@@_name_tl} % % \begin{macrocode} \cs_new:Npn \@@_use_function_on_name:n #1 { \tl_set:Nx \l_@@_name_tl { #1 { \tl_use:N \l_@@_name_tl } } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_define_theorem:nnnn} % \begin{syntax} % \cs{@@_define_theorem:nnnn}\marg{environment name} % \marg{groups clist}\marg{theorem name}\marg{thmtools keys} % \end{syntax} % % This is the internal backend that declares a grouped theorem % by retrieving all the group properties and issuing a (single) \cs{declaretheorem} % command of \pkg{thmtools}. % % \begin{macrocode} \cs_new:Npn \@@_define_theorem:nnnn #1 #2 #3 #4 { % \end{macrocode} % First, set local variables to default values and store current name. % \begin{macrocode} \tl_clear:N \l_@@_prefix_tl \tl_set:Nn \l_@@_name_tl { #3 } \tl_clear:N \l_@@_suffix_tl \clist_clear:N \l_@@_mapname_clist \clist_clear:N \l_@@_thmtools_clist % \end{macrocode} % Clear all hooks % \begin{macrocode} \hook_gremove_code:nn { @@/prefix }{*} \hook_gremove_code:nn { @@/suffix }{*} \hook_gremove_code:nn { @@/mapname }{*} \hook_gremove_code:nn { @@/thmtools }{*} % \end{macrocode} % Now, retrieve the group properties, by writing these into the hooks % \begin{macrocode} \@@_flatten_groups_hierarchy:nN { #2, all } \l_@@_group_clist \clist_map_function:NN \l_@@_group_clist \@@_use_group:n \tl_log:x { Flattened ~ groups ~ '#2' ~ to ~ '\clist_use:Nn \l_@@_group_clist {,}' ~ when ~ defining ~ theorem ~ '#1' } % \end{macrocode} % Execute the hooks, so that local variables will get modified according to the groups % and in the order that were specified for the hooks. % \begin{macrocode} \hook_use:n { @@/prefix } \hook_use:n { @@/suffix } \hook_use:n { @@/mapname } \hook_use:n { @@/thmtools } % \end{macrocode} % We are left with dealing with the obtained data. % We first map on the name, before appending to it. % \begin{macrocode} \clist_map_function:NN \l_@@_mapname_clist \map_use_on_name:n % \end{macrocode} % We now glue the name together of its three parts, % and pass this key directly to the \pkg{thmtools} arguments % \begin{macrocode} \clist_put_right:Nx \l_@@_thmtools_clist { name = \tl_use:N \l_@@_prefix_tl \tl_use:N \l_@@_name_tl \tl_use:N \l_@@_suffix_tl } % \end{macrocode} % Finally, apply the additional \pkg{thmtools} keys for this specific theorem. % Putting them last will overwrite keys that were given by the groups. % \begin{macrocode} \clist_put_right:Nn \l_@@_thmtools_clist { #4 } % \end{macrocode} % We can now pass our list to \pkg{thmtools}, declaring the theorem. % \begin{macrocode} \@@_thmtools_declare_theorem:Vn \l_@@_thmtools_clist { #1 } } % \end{macrocode} % \end{macro} % % % As usual, we provide a \texttt{new} and a \texttt{provide} variant wrapped % around this that do proper error checking. % % % \begin{macro} % { % \groupthm_new_theorem:nnnn, % \groupthm_new_theorem:nVVV, % \groupthm_new_theorem:xVnn, % \groupthm_provide_theorem:nnnn, % \groupthm_provide_theorem:nVVV, % \groupthm_provide_theorem:xVnn % } % \begin{syntax} % \cs{groupthm_new_theorem:nnnn}\marg{environment name} % \marg{groups clist}\marg{theorem name}\marg{thmtools keys} % \end{syntax} % % \begin{macrocode} \@@_define_multiple:nnnNNnn { new, provide } { grouped ~ theorem } { ##1 } \@@_error: \@@_define_theorem:nnnn { groupthm_#1_theorem:nnnn } { { ##1 } { ##2 } { ##3 } { ##4 } } \cs_generate_variant:Nn \groupthm_new_theorem:nnnn { n V V V } \cs_generate_variant:Nn \groupthm_provide_theorem:nnnn { n V V V } % \end{macrocode} % We need this extra variant here for the generation of theorem families later: % \begin{macrocode} \cs_generate_variant:Nn \groupthm_new_theorem:nnnn { x V n n } \cs_generate_variant:Nn \groupthm_provide_theorem:nnnn { x V n n } % \end{macrocode} % \end{macro} % % % \begin{macro}{\groupthm_new_theorem:nnn, \groupthm_provide_theorem:nnn} % \begin{syntax} % \cs{groupthm_new_theorem:nnn}\marg{key=value list}\marg{grouped theorem} % \marg{bool} % \end{syntax} % % % The third argument indicates whether the generated theorem(s) will be added to the % \texttt{unnumbered} group % % \begin{macrocode} \@@_wrap_multiple:nnn { new, provide } { groupthm_#1_theorem:nnn } { \@@_set_normalized_keys:nnn { ##1 } { grouped ~ theorem } { ##2 } \bool_if:nT { ##3 } { \clist_put_left:Nn \l_@@_group_clist { unnumbered } } \use:c { groupthm_#1_theorem:nVVV } { ##2 } \l_@@_group_clist \l_@@_name_tl \l_@@_thmtools_clist \bool_if:NT \l_@@_starred_version_bool { \clist_put_left:Nn \l_@@_group_clist { starred } \use:c { groupthm_#1_theorem:nVVV } { ##2* } \l_@@_group_clist \l_@@_name_tl \l_@@_thmtools_clist } } % \end{macrocode} % \end{macro} % % On top of these, we can provide the shorter versions that will generate % two theorems each, one with and one without a \enquote{*} in its % environment name % % % Now, we can wrap these into document commands % % % \begin{macro} % { % \NewGroupedTheorem, \NewGroupedTheorem*, % \ProvideGroupedTheorem, \ProvideGroupedTheorem* % } % \begin{syntax} % \cs{NewGroupedTheorem}\oarg{key=value list}\marg{theorem name} % \end{syntax} % % \begin{macrocode} \@@_wrap_multiple_document:nnnn { new, provide } { \@@_Declarator: GroupedTheorem } { s O{} m } { \use:c { groupthm_#1_theorem:nnn } { ##2 } { ##3 } { ##1 } } % \end{macrocode} % \end{macro} % % % % \subsection{Theorem families} % % % We now want to implement the generation of theorem families and their % corresponding options. % As a backend, we use the following auxiliary function % % % \begin{macro}{\@@_define_family:nnnnn} % \begin{syntax} % \cs{@@_define_family:nnnnn}\marg{family name}\marg{groups clist} % \marg{name}\marg{thmtools clist}\marg{extra groups clist} % \end{syntax} % % This will generate a new grouped theorem for each union of a subset of \meta{groups clist} % and the extra set \meta{extra groups clist}, with the given properties, that is % the \meta{nam} and \meta{thmtools clist} will be passed to the grouped theorem. % The \meta{theorem name} of the grouped theorem will be an internal name that contains % the \meta{family name} and the list of groups of this variant, that will be generated % in a unique manner to later retrieve the generated theorems when parsing theorem families. % % \begin{macrocode} \cs_new:Npn \@@_define_family:nnnnn #1 #2 #3 #4 #5 { % \end{macrocode} % Make a local copy of the \meta{groups clist} argument, % and iterate over its powerset % \begin{macrocode} \clist_set:Nn \l_tmpa_clist { #2 } \powerset_clist_foreach:Nn \l_tmpa_clist { % \end{macrocode} % We read out the current value of the list, and append the extra groups % This ensures that now \cs{l_@@_group_clist} iterates over the proper subsets % \begin{macrocode} \clist_set_eq:NN \l_@@_group_clist \l_tmpa_clist \clist_put_right:Nn \l_@@_group_clist { #5 } % \end{macrocode} % This sorting is necessary so that for each theorem family and set of groups, % the generated name will be unique: % \begin{macrocode} \@@_sort_group_names: % \end{macrocode} % Now just declare the grouped theorem, passing the corresponding arguments % \begin{macrocode} \use:c{groupthm_new_theorem:xVnn} {__#1__groups_\clist_use:Nn \l_@@_group_clist {_}} \l_@@_group_clist { #3 } { #4 } } % \end{macrocode} % We save the set of variants we generated for later error checking: % \begin{macrocode} \clist_new:c { @@_family__#1__group_clist } \clist_set_eq:cN {@@_family__#1__group_clist } \l_tmpa_clist \clist_new:c { @@_family__#1__always_group_clist } \clist_set:cn {@@_family__#1__always_group_clist } { #5 } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\groupthm_new_family:nnnnn,\groupthm_new_family:nVVVV} % \begin{syntax} % \cs{groupthm_new_family:nnnnn}\marg{theorem family} % \marg{groups_1}\marg{name}\marg{thmtools clist}\marg{groups_2} % \end{syntax} % % % \begin{macrocode} \@@_define_multiple:nnnNNnn { new, provide } { theorem ~ family } { @@_family__##1__group_clist } \@@_error: \@@_define_family:nnnnn { groupthm_#1_family:nnnnn } { { ##1 } { ##2 } { ##3 } { ##4 } { ##5 }} \cs_generate_variant:Nn \groupthm_new_family:nnnnn { n V V V V } \cs_generate_variant:Nn \groupthm_provide_family:nnnnn { n V V V V } % \end{macrocode} % \end{macro} % % % \begin{macro}{\groupthm_new_family:nnn, \groupthm_provide_family:nnn} % \begin{syntax} % \cs{groupthm_new_family:nnn}\marg{key=value list}\marg{theorem family}\marg{bool} % \end{syntax} % % The third argument indicates whether the generated theorem(s) will all be added to the % \texttt{unnumbered} group. % % \begin{macrocode} \@@_wrap_multiple:nnn { new, provide } { groupthm_#1_family:nnn } { \@@_set_normalized_keys:nnn { ##1 } { theorem ~ family } { ##2 } \bool_if:nTF { ##3} { \clist_set:Nn \l_tmpa_clist { unnumbered } } { \clist_clear:N \l_tmpa_clist } \bool_if:NT \l_@@_starred_version_bool { \clist_put_left:Nn \l_@@_group_clist { starred } } \use:c { groupthm_#1_family:nVVVV } { ##2 } \l_@@_group_clist \l_@@_name_tl \l_@@_thmtools_clist \l_tmpa_clist } % \end{macrocode} % \end{macro} % % % % Finally, we can provide document commands that make these available. % % % \begin{macro} % { % \NewGroupedTheoremFamily,\NewGroupedTheoremFamily*, % \ProvideGroupedTheoremFamily, \ProvideGroupedTheoremFamily* % } % \begin{syntax} % \cs{NewGroupedTheoremFamily}\oarg{key=value list}\marg{family name} % \end{syntax} % % \begin{macrocode} \@@_wrap_multiple_document:nnnn { new, provide } { \@@_Declarator: GroupedTheoremFamily } { s O{} m } { \use:c { groupthm_#1_family:nnn } { ##2 } { ##3 } { ##1 } } % \end{macrocode} % \end{macro} % % % % \subsection{Theorem family options} % % % % \begin{macro}{\groupthm_add_theorem_to_group:n} % \begin{syntax} % \cs{groupthm_add_theorem_to_group:n}\marg{theorem group} % \end{syntax} % % % \begin{macrocode} \cs_new:Npn \groupthm_add_theorem_to_group:n #1 { % \end{macrocode} % As mentioned earlier, this bool will indicate whether we are % executing a \meta{selection body} from some family options. % If used outside, we emit an error message. % \begin{macrocode} \bool_if:NTF \l_@@_in_family_options_environment_bool { \clist_put_left:Nn \l_@@_group_clist { #1 } } { \msg_error:nn { groupthm } { misuse ~ add ~ theorem ~ to ~ group } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\AddTheoremToGroup} % \begin{syntax} % \cs{AddTheoremToGroup}\marg{theorem group} % \end{syntax} % % % % \begin{macrocode} \NewDocumentCommand { \AddTheoremToGroup } { m } { \groupthm_add_theorem_to_group:n { #1 } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_define_family_options:nnnn} % \begin{syntax} % \cs{@@_define_family_options:nnnn}\marg{theorem family} % \marg{argument specification}\marg{selection body}\marg{extra groups} % \end{syntax} % % This declares a new theorem variant option parser, i.e.~ introduces the environment % \meta{theorem family} with signature \marg{argument specification}. % % The \meta{selection body} will be executed and selects some groups the environment shall have. % The \meta{extra groups} will be added regardless of the arguments given % to the \meta{theorem family} % The \meta{declaring backend} is one of \texttt{New}, \texttt{Renew}, \texttt{Provide} % and \texttt{Declare} and is given to the \texttt{DocumentEnvironment} command from \pkg{xpars}. % % \begin{macrocode} \cs_new:Npn \@@_define_family_options:nnnn #1 #2 #3 #4 { \DeclareDocumentEnvironment { #1 } { #2 } { % \end{macrocode} % We can now clear the group list and execute the \meta{selection body} % that populates this list again. % Additionally, we add the groups that should always be present and activate the % \cs{AddTheoremToGroup} macro by setting the bool. % \begin{macrocode} \clist_clear:N \l_@@_group_clist \bool_set_true:N \l_@@_in_family_options_environment_bool #3 \bool_set_false:N \l_@@_in_family_options_environment_bool \clist_put_right:Nn \l_@@_group_clist { #4 } % \end{macrocode} % We now got the list of groups parsed. We sort this and start the corresponding % environment that has been generated by a \cs{NewGroupedTheoremFamily} command % or similar. % \begin{macrocode} \@@_sort_group_names: \cs_if_exist:cTF { __#1__groups_ \clist_use:Nn \l_@@_group_clist { _ } } { \begin { __#1__groups_ \clist_use:Nn \l_@@_group_clist { _ } } } { \msg_error:nnxx { groupthm } { undefined ~ theorem ~ variant } { #1 } { \clist_use:Nnnn \l_@@_group_clist { ~ and ~ } {, ~} { , ~ and ~ } } } } { % \end{macrocode} % At the end of the environment, we have to do the same parsing again. % \begin{macrocode} \clist_clear:N \l_@@_group_clist \bool_set_true:N \l_@@_in_family_options_environment_bool #3 \bool_set_false:N \l_@@_in_family_options_environment_bool \clist_put_right:Nn \l_@@_group_clist { #4 } % \end{macrocode} % End the corresponding environment. % \begin{macrocode} \@@_sort_group_names: \end { __#1__groups_ \clist_use:Nn \l_@@_group_clist { _ } } } } % \end{macrocode} % \end{macro} % % % All other macros are now essentially wrappers around this aux macro, % passing different \meta{extra groups} to them % % \begin{macro} % { % \groupthm_new_family_options:nnnn, % \groupthm_renew_family_options:nnnn, % \groupthm_provide_family_options:nnnn, % \groupthm_declare_family_options:nnnn, % } % \begin{syntax} % \cs{groupthm_new_family_options:nnnn}\marg{theorem family} % \marg{signature}\marg{selection body}\marg{groups} % \end{syntax} % % \begin{macrocode} \@@_define_multiple:nnnNNnn { new, renew, provide, declare } { theorem ~ family ~ options } { ##1 } \use_none:n \@@_define_family_options:nnnn { groupthm_#1_family_options:nnnn } { { ##1 } { ##2 } { ##3 } { ##4 } } \cs_generate_variant:Nn \groupthm_new_family_options:nnnn { n n n V } \cs_generate_variant:Nn \groupthm_renew_family_options:nnnn { n n n V } \cs_generate_variant:Nn \groupthm_provide_family_options:nnnn { n n n V } \cs_generate_variant:Nn \groupthm_declare_family_options:nnnn { n n n V } % \end{macrocode} % \end{macro} % % % It remains to wrap these into document commands % % % \begin{macro} % { % \NewGroupedTheoremFamilyOptions, % \NewGroupedTheoremFamilyOptions*, % \RenewGroupedTheoremFamilyOptions, % \RenewGroupedTheoremFamilyOptions*, % \ProvideGroupedTheoremFamilyOptions, % \ProvideGroupedTheoremFamilyOptions*, % \DeclareGroupedTheoremFamilyOptions, % \DeclareGroupedTheoremFamilyOptions*, % } % \begin{syntax} % \cs{NewGroupedTheoremFamilyOptions}\marg{family name}\marg{signature} % \marg{selection body} % \end{syntax} % % \begin{macrocode} \@@_wrap_multiple_document:nnnn { new, renew, provide, declare } { \@@_Declarator: GroupedTheoremFamilyOptions } { s O{} m m m } { \keys_set:nn { groupthm / theorem ~ family ~ options } { starred ~ version } \keys_set:nn { groupthm / theorem ~ family ~ options } { ##2 } \bool_if:nTF { ##1 } { \clist_set:Nn \l_tmpa_clist { unnumbered } } { \clist_clear:N \l_tmpa_clist } \use:c { groupthm_#1_family_options:nnnV } { ##3 } { ##4 } { ##5 } \l_tmpa_clist \bool_if:NT \l_@@_starred_version_bool { \use:c { groupthm_#1_family_options:nnnV } { ##3* } { ##4 } { ##5 \groupthm_add_theorem_to_group:n { starred } } \l_tmpa_clist } } % \end{macrocode} % \end{macro} % % % % % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % % % \newpage % \PrintIndex