diff --git a/doc/environments/groupthm/Makefile b/doc/environments/groupthm/Makefile index 5d55cef..7a033e9 100644 --- a/doc/environments/groupthm/Makefile +++ b/doc/environments/groupthm/Makefile @@ -1,4 +1,4 @@ -all: clean pdf +all: clean-sty pdf sty pdf: pdflatex groupthm.dtx @@ -6,10 +6,16 @@ pdf: pdflatex groupthm.dtx pdflatex groupthm.dtx -clean: +clean: clean-sty latexmk -c groupthm.dtx rm -f groupthm.hd rm -f groupthm.glo rm -f groupthm.pdf +clean-sty: + rm -f groupthm.sty + +sty: + pdflatex groupthm.ins + .PHONY: pdf clean diff --git a/doc/environments/groupthm/groupthm.dtx b/doc/environments/groupthm/groupthm.dtx index 6230b19..5ea52c8 100644 --- a/doc/environments/groupthm/groupthm.dtx +++ b/doc/environments/groupthm/groupthm.dtx @@ -810,6 +810,10 @@ % \end{macrocode} % % \subsection{Dependencies} +% Identify package +% \begin{macrocode} +\ProvidesExplPackage{groupthm}{2022/01/17}{0.0.1}{Grouped theorems.} +% \end{macrocode} % First, we import other packages on which we rely on, and % set up some private wrappers around these. % \begin{macrocode} @@ -836,6 +840,29 @@ % % % +% 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}\ma{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 + { + \hooks_gset_rule:nnnn { #1 } { ##1 } { #3 } { #4 } + } + \clist_map_function:NN #2 \@@_map_aux:n + } +% \end{macrocode} +% \end{macro} +% % % % \subsection{Messages} @@ -1051,7 +1078,66 @@ % % \subsection{Theorem groups} % +% For technical reasons explained in the \todoref section, 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_theorem_group_ordering:n} +% \begin{syntax} +% \cs{@@_add_to_theorem_group_ordering:n}\ma{theorem group} +% \end{syntax} +% +% Sets hook relations for this group and all already defined theorem groups. +% +% \begin{macrocode} +\cs_new:Npn \@@_add_to_theorem_group_ordering:n #1 + { + \@@_hook_gset_rule_foreach:nNnn + { @@/groupsort } + \g_@@_defined_theorem_groups_clist + { before } + { #1 } + } +% \end{macrocode} +% \end{macro} +% +% +% +% \begin{macro}{\@@_remove_from_theorem_group_ordering:n} +% \begin{syntax} +% \cs{@@_remove_from_theorem_group_ordering:n}\ma{theorem group} +% \end{syntax} +% +% Removes all relations of this theorem group with the currently defined theorem groups. +% +% \begin{macrocode} +\cs_new:Npn \@@_remove_from_theorem_group_ordering:n #1 + { + \@@_hook_gset_rule_foreach:nNnn + { @@/groupsort } + \g_@@_defined_theorem_groups_clist + { unrelated } + { #1 } + } +% \end{macrocode} +% \end{macro} % \begin{macro}{\@@_declare_theorem_group_aux:nnnnn} % \begin{syntax} % \cs{@@_declare_theorem_group_aux:nnnnn}\{\meta{theorem group}\}\{\meta{prename tl}\} @@ -1099,10 +1185,10 @@ % \end{macrocode} % \end{macro} % -% This ensures some ordering hacks explained later. +% This ensures the ordering hacks explained before. % % \begin{macrocode} - \@@_update_ordering:n { #1 } + \@@_add_to_theorem_group_ordering:n { #1 } % \end{macrocode} % % Add defined group to corresponding list @@ -1176,11 +1262,12 @@ \g_@@_defined_theorem_groups_clist { #1 } { unrelated } { #1 } - \@@_hook_gset_rule_foreach:nNnn - { @@/groupsort } - \g_@@_defined_theorem_groups_clist { #1 } - { unrelated } - { #1 } +% \end{macrocode} +% +% Also clear all sorting relations +% +% \begin{macrocode} + \@@_remove_from_theorem_group_ordering:n { #1 } } % \end{macrocode} % \end{macro} @@ -1192,7 +1279,7 @@ % \begin{macro}{\groupthm_new_theorem_group:nnnnn, \groupthm_new_theorem_group:nVVVV} % % \begin{macrocode} -\cs_new:Npn \groupthm_new_theorem_group:nnnnn #1 #2 #3 #3 #5 +\cs_new:Npn \groupthm_new_theorem_group:nnnnn #1 #2 #3 #4 #5 { \cs_if_exist:cTF { @@_use_group_#1 } { @@ -1217,7 +1304,7 @@ % \begin{macro}{\groupthm_renew_theorem_group:nnnnn, \groupthm_renew_theorem_group:nVVVV} % % \begin{macrocode} -\cs_new:Npn \groupthm_renew_theorem_group:nnnnn #1 #2 #3 #3 #5 +\cs_new:Npn \groupthm_renew_theorem_group:nnnnn #1 #2 #3 #4 #5 { \cs_if_exist:cTF { @@_use_group_#1 } { @@ -1243,7 +1330,7 @@ % \begin{macro}{\groupthm_provide_theorem_group:nnnnn, \groupthm_provide_theorem_group:nVVVV} % % \begin{macrocode} -\cs_new:Npn \groupthm_provide_theorem_group:nnnnn #1 #2 #3 #3 #5 +\cs_new:Npn \groupthm_provide_theorem_group:nnnnn #1 #2 #3 #4 #5 { \cs_if_exist:cF { @@_use_group_#1 } { @@ -1265,7 +1352,7 @@ % % % \begin{macrocode} -\cs_new:Npn \groupthm_declare_theorem_group:nnnnn #1 #2 #3 #3 #5 +\cs_new:Npn \groupthm_declare_theorem_group:nnnnn #1 #2 #3 #4 #5 { \cs_if_exist:cT { @@_use_group_#1 } { @@ -1431,4 +1518,4 @@ % \end{implementation} % \end{documentation} % \newpage -% +% \PrintIndex diff --git a/doc/environments/groupthm/groupthm.ins b/doc/environments/groupthm/groupthm.ins new file mode 100644 index 0000000..89a4f4b --- /dev/null +++ b/doc/environments/groupthm/groupthm.ins @@ -0,0 +1,4 @@ +\input docstrip.tex +\keepsilent +\generate{\file{groupthm.sty}{\from{groupthm.dtx}{package}}} +\end diff --git a/doc/environments/groupthm/groupthm.sty b/doc/environments/groupthm/groupthm.sty new file mode 100644 index 0000000..fcd7826 --- /dev/null +++ b/doc/environments/groupthm/groupthm.sty @@ -0,0 +1,287 @@ +%% +%% This is file `groupthm.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% groupthm.dtx (with options: `package') +%% +%% IMPORTANT NOTICE: +%% +%% For the copyright see the source file. +%% +%% Any modified versions of this file must be renamed +%% with new filenames distinct from groupthm.sty. +%% +%% For distribution of the original source see the terms +%% for copying and modification in the file groupthm.dtx. +%% +%% This generated file may be distributed as long as the +%% original source files, as listed above, are part of the +%% same distribution. (The sources need not necessarily be +%% in the same archive or directory.) +%% File: l3prop.dtx +\ProvidesExplPackage{groupthm}{2022/01/17}{0.0.1}{Grouped theorems.} +\RequirePackage{amsthm} +\RequirePackage{thmtools} +\RequirePackage{mkessler-powerset} +\cs_new:Npn \__groupthm_declare_theorem_group:nn #1 #2 +{ + \declaretheorem [ #2 ] { #1 } +} +\cs_generate_variant:Nn \__groupthm_declare_theorem_group:nn { n V } +\cs_new:Npn \__groupthm_hook_gset_rule_foreach:nNnn #1 #2 #3 #4 + { + \cs_set:Npn \__groupthm_map_aux:n + { + \hooks_gset_rule:nnnn { #1 } { ##1 } { #3 } { #4 } + } + \clist_map_function:NN #2 \__groupthm_map_aux:n + } +\msg_new:nnn { groupthm } { unknown ~ group } +{ + Unknown ~ group ~ '#1' ~ supplied ~ \msg_line_context: +} +\msg_new:nnn { groupthm } { unknown ~ key } +{ + Unknown ~ key ~ '#1' ~ supplied ~ \msg_line_context: +} +\msg_new:nnn { groupthm } { wrong ~ definition } +{ + Bad definition of #1 ~ '#2' ~ \msg_line_context:, #1 ~ is ~ #3 defined. +} +\hook_new:n { __groupthm/prename } +\hook_new:n { __groupthm/postname } +\hook_new:n { __groupthm/mapname } +\hook_new:n { __groupthm/thmtools } +\hook_new:n { __groupthm/groupsort } +\cs_generate_variant:Nn \hook_gset_rule:nnnn { n n V n } +\tl_new:N \l__groupthm_key_prename_tl +\tl_new:N \l__groupthm_key_name_tl +\tl_new:N \l__groupthm_key_postname_tl +\clist_new:N \l__groupthm_key_group_clist +\clist_new:N \l__groupthm_key_mapname_clist +\clist_new:N \l__groupthm_key_thmtools_clist +\tl_new:N \l__groupthm_prename_tl +\tl_new:N \l__groupthm_name_tl +\tl_new:N \l__groupthm_postname_tl +\clist_new:N \l__groupthm_mapname_clist +\clist_new:N \l__groupthm_thmtools_clist +\clist_new:N \l__groupthm_group_clist +\clist_new:N \g__groupthm_defined_theorem_groups_clist +\keys_define:nn { groupthm } + { + prename .tl_set:N = \l__groupthm_key_prename_tl, + prename .default:n = \c_empty_tl, + prename .groups:n = { theoremgroup }, + name .tl_set:N = \l__groupthm_key_name_tl, + name .default:n = \c_novalue_tl, + name .groups:n = { groupedtheorem, theoremvariants }, + postname .tl_set:N = \l__groupthm_key_postname_tl, + postname .default:n = \c_empty_tl, + postname .groups:n = { theoremgroup }, + group .clist_set:N = \l__groupthm_key_group_clist, + group .default:n = {}, + group .groups:n = { groupedtheorem, theoremvariants }, + mapname .clist_set:N = \l__groupthm_mapname_clist, + mapname .default:n = {}, + mapname .groups:n = { theoremgroup }, + thmtools .clist_set:N = \l__groupthm_key_thmtools_clist, + thmtools .default:n = {}, + thmtools .groups:n = + { theoremgroup, groupedtheorem, theoremvariants }, + unknown .code:n = + \msg_error:nnn { groupthm } { unknown ~ group } { \l_keys_key_str } + } +\cs_new:Npn \__groupthm_set_normalized_keys:nnn #1 #2 #3 + { + \keys_set:nn { __groupthm } { prename, name, postname, group, mapname, thmtools } + \keys_set_groups:nnn { __groupthm } { #2 } { #1 } + \tl_if_eq:NnTF \l__groupthm_key_name_tl { \c_novalue_tl } + { + \tl_set:Nx \l__groupthm_name_tl + { + \text_titlecase_first:n {#3} + } + } + { + \tl_set_eq:NN \l__groupthm_name_tl \l__groupthm_key_name_tl + } + \tl_set_eq:NN \l__groupthm_prename_tl \l__groupthm_key_prename_tl + \tl_set_eq:NN \l__groupthm_postname_tl \l__groupthm_key_postname_tl + \clist_set_eq:NN \l__groupthm_group_clist \l__groupthm_key_group_clist + \clist_set_eq:NN \l__groupthm_mapname_clist \l__groupthm_key_mapname_clist + \clist_set_eq:NN \l__groupthm_thmtools_clist \l__groupthm_key_group_clist + } +\cs_new:Npn \__groupthm_add_to_theorem_group_ordering:n #1 + { + \__groupthm_hook_gset_rule_foreach:nNnn + { __groupthm/groupsort } + \g__groupthm_defined_theorem_groups_clist + { before } + { #1 } + } +\cs_new:Npn \__groupthm_remove_from_theorem_group_ordering:n #1 + { + \__groupthm_hook_gset_rule_foreach:nNnn + { __groupthm/groupsort } + \g__groupthm_defined_theorem_groups_clist + { unrelated } + { #1 } + } +\cs_new:Npn \__groupthm_declare_theorem_group_aux:nnnnn #1#2#3#4#5 + { + \cs_new:cpn { __groupthm_use_group_#1: } + { + \hook_gput_code:nnn { __groupthm/prename } { #1 } + { + \tl_put_left:Nx \l__groupthm_prename_tl { #2 } + } + \hook_gput_code:nnn { __groupthm/postname } { #1 } + { + \tl_put_right:Nx \l__groupthm_postname_tl { #3 } + } + \hook_gput_code:nnn { __groupthm/mapname } { #1 } + { + \clist_put_right:Nn \l__groupthm_mapname_clist { #4 } + } + \hook_gput_code:nnn { __groupthm/thmtools } { #1 } + { + \clist_put_right:Nn \l__groupthm_thmtools_clist { #5 } + } + } + \__groupthm_add_to_theorem_group_ordering:n { #1 } + \clist_gput_left:Nn \g__groupthm_defined_theorem_groups_clist { #1 } + } +\cs_new:Npn \__groupthm_undeclare_theorem_group_aux:n #1 + { + \cs_undefine:c { __groupthm_use_group_#1: } + \hook_gremove_code:nn { __groupthm/prename } + \hook_gremove_code:nn { __groupthm/postname } + \hook_gremove_code:nn { __groupthm/mapname } + \hook_gremove_code:nn { __groupthm/thmtools } + \clist_remove_all:Nn \g__groupthm_defined_theorem_groups_clist { #1 } + \__groupthm_hook_gset_rule_foreach:nNnn + { ?? } + \g__groupthm_defined_theorem_groups_clist { #1 } + { unrelated } + { #1 } + \__groupthm_hook_gset_rule_foreach:nNnn + { __groupthm/prename } + \g__groupthm_defined_theorem_groups_clist { #1 } + { unrelated } + { #1 } + \__groupthm_hook_gset_rule_foreach:nNnn + { __groupthm/postname } + \g__groupthm_defined_theorem_groups_clist { #1 } + { unrelated } + { #1 } + \__groupthm_hook_gset_rule_foreach:nNnn + { __groupthm/mapname } + \g__groupthm_defined_theorem_groups_clist { #1 } + { unrelated } + { #1 } + \__groupthm_hook_gset_rule_foreach:nNnn + { __groupthm/thmtools } + \g__groupthm_defined_theorem_groups_clist { #1 } + { unrelated } + { #1 } + \__groupthm_remove_from_theorem_group_ordering:n { #1 } + } +\cs_new:Npn \groupthm_new_theorem_group:nnnnn #1 #2 #3 #4 #5 + { + \cs_if_exist:cTF { __groupthm_use_group_#1 } + { + \msg_error:nnnnn { groupthm } { wrong ~ definition } + { group } { #1 } { already } + } + { + \__groupthm_declare_theorem_group_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } + } + } +\cs_generate_variant:Nn \groupthm_new_theorem_group:nnnnn { n V V V V } +\cs_new:Npn \groupthm_renew_theorem_group:nnnnn #1 #2 #3 #3 #5 + { + \cs_if_exist:cTF { __groupthm_use_group_#1 } + { + \__groupthm_undeclare_theorem_group_aux:n { #1 } + \__groupthm_declare_theorem_group_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } + } + { + \msg_error:nnnnn { groupthm } { wrong ~ definition } + { group } { #1 } { not } + } + } +\cs_generate_variant:Nn \groupthm_renew_theorem_group:nnnnn { n V V V V } +\cs_new:Npn \groupthm_provide_theorem_group:nnnnn #1 #2 #3 #3 #5 + { + \cs_if_exist:cF { __groupthm_use_group_#1 } + { + \__groupthm_declare_theorem_group_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } + } + } +\cs_generate_variant:Nn \groupthm_provide_theorem_group:nnnnn { n V V V V } +\cs_new:Npn \groupthm_declare_theorem_group:nnnnn #1 #2 #3 #3 #5 + { + \cs_if_exist:cT { __groupthm_use_group_#1 } + { + \__groupthm_undeclare_theorem_group_aux:n { #1 } + } + \__groupthm_declare_theorem_group_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } + } +\cs_generate_variant:Nn \groupthm_declare_theorem_group:nnnnn { n V V V V } +\cs_new:Npn \groupthm_new_theorem_group_by_keys:nn #1#2 +{ + \__groupthm_set_normalized_keys:nnn { #1 } { theoremgroup } { #2 } + \groupthm_new_theorem_group:nVVVV { #1 } + \l__groupthm_prename_tl + \l__groupthm_postname_tl + \l__groupthm_mapname_clist + \l__groupthm_thmtools_clist +} +\cs_new:Npn \groupthm_renew_theorem_group_by_keys:nn #1#2 +{ + \__groupthm_set_normalized_keys:nnn { #1 } { theoremgroup } { #2 } + \groupthm_renew_theorem_group:nVVVV { #1 } + \l__groupthm_prename_tl + \l__groupthm_postname_tl + \l__groupthm_mapname_clist + \l__groupthm_thmtools_clist +} +\cs_new:Npn \groupthm_provide_theorem_group_by_keys:nn #1#2 +{ + \__groupthm_set_normalized_keys:nnn { #1 } { theoremgroup } { #2 } + \groupthm_provide_theorem_group:nVVVV { #1 } + \l__groupthm_prename_tl + \l__groupthm_postname_tl + \l__groupthm_mapname_clist + \l__groupthm_thmtools_clist +} +\cs_new:Npn \groupthm_declare_theorem_group_by_keys:nn #1#2 +{ + \__groupthm_set_normalized_keys:nnn { #1 } { theoremgroup } { #2 } + \groupthm_declare_theorem_group:nVVVV { #1 } + \l__groupthm_prename_tl + \l__groupthm_postname_tl + \l__groupthm_mapname_clist + \l__groupthm_thmtools_clist +} +\NewDocumentCommand{\NewTheoremGroup}{ O{} m } + { + \groupthm_new_theorem_group_by_keys:nn { #1 } { #2 } + } +\NewDocumentCommand{\RenewTheoremGroup}{ O{} m } + { + \groupthm_renew_theorem_group_by_keys:nn { #1 } { #2 } + } +\NewDocumentCommand{\ProvideTheoremGroup}{ O{} m } + { + \groupthm_provide_theorem_group_by_keys:nn { #1 } { #2 } + } +\NewDocumentCommand{\DeclareTheoremGroup}{ O{} m } + { + \groupthm_declare_theorem_group_by_keys:nn { #1 } { #2 } + } +\endinput +%% +%% End of file `groupthm.sty'.