%%
%% Package: spectralsequences v1.3.3 2023-01-28
%% Author: Hood Chatham
%% Email:
[email protected]
%% Date: 2023-01-28
%% License: Latex Project Public License
%%
%% File: sseqforeach.code.tex
%%
%% Patches the \foreach command to obtain better error reporting
%% Also defines some of my own looping commands, \Do, \DoUntilOutOfBounds, \DoUntilOutOfBoundsThenNMore
%% All of the \foreach stuff we are modifying is defined by tikz in the file /pgf/utilities/pgffor.code.tex
%%
\def\sseq@for@vars{}
\def\sseq@for@savemacro#1{\sseq@d@addto@macro\sseq@for@vars{\sseq@for@vars@do#1}}
\def\sseq@for@savemacro@noslash#1{\sseq@d@addto@macro\sseq@for@vars{\sseq@for@vars@do@noslash#1}}
\def\sseq@for@vars@do#1{; \string#1 = #1}
\def\sseq@for@vars@do@noslash#1{; \sseq@macroname#1 = #1}
\ExplSyntaxOn
\def\sseq@for@printvars{\ifx\sseq@for@vars\pgfutil@empty\else\exp_last_unbraced:Nf\@gobble\sseq@for@vars\fi}
\newcount\sseq@Do@depth
\def\sseq@save@Do@var{
\sseq@atbeginforeach@msgsetup
\advance\sseq@Do@depth\@ne
\exp_args:Nc \sseq@for@savemacro@noslash { iteration ~ \the \sseq@Do@depth }
\def\iteration{0}
}
\def\sseq@stepiteration{
\edef\iteration{\the\numexpr\iteration+1}
\cs_set_eq:cN { iteration ~ \the \sseq@Do@depth } \iteration
}
\protected\def\sseq@Do{
\begingroup
\edef\sseq@inputline{\the\inputlineno}
\sseq@callas{\Do}
\sseq@call{\sseq@Do@}
}
\def\sseq@Do@#1#2{
\sseq@esetthiscall{\string\Do\unexpanded{#1}}
\sseq@opushstacktrace{\string\Do{#1}}
\sseq@ifintexpr{#1}{
\sseq@savestack
\sseq@save@Do@var
\prg_replicate:nn {#1} {
\sseq@stepiteration
#2
\relax
}
\sseq@restorestack
}{\sseq@error@xx{Do-invalid-int-expr}{\string\Do}{\unexpanded{#1}}}
\endgroup
}
\protected\def\sseq@DoUntilOutOfBounds{
\begingroup
\edef\sseq@inputline{\the\inputlineno}
\sseq@callas{\DoUntilOutOfBounds}
\sseq@call{\sseq@DoUntilOutOfBounds@}
}
\def\sseq@DoUntilOutOfBounds@ #1 {
\sseq@esetthiscall{\string\DoUntilOutOfBounds}
\sseq@opushstacktrace{\string\DoUntilOutOfBounds}
\ifx\sseq@xminmax\@gobbletwo\else\ifx\sseq@yminmax\@gobbletwo\else
\sseq@error@x{DoUntil-no-bound}{\string\DoUntilOutOfBounds}
\sseq@breakfifi
\fi\fi
\def\sseq@commandname{\string\DoUntilOutOfBounds}
\sseq@savestack % so we can nest these
\sseq@DoUntilOutOfBounds@body{#1}
\sseq@restorestack
\sseq@breakpoint
\endgroup
}
\protected\def\sseq@DoUntilOutOfBoundsThenNMore{
\begingroup
\edef\sseq@inputline{\the\inputlineno}
\sseq@callas{\DoUntilOutOfBoundsThenNMore}
\sseq@call{\sseq@DoUntilOutOfBoundsThenNMore@}
}
\def\sseq@DoUntilOutOfBoundsThenNMore@ #1#2 {
\sseq@esetthiscall{\string\DoUntilOutOfBoundsThenNMore\unexpanded{#1}}
\sseq@opushstacktrace{\string\DoUntilOutOfBoundsThenNMore{#1}}
\ifx\sseq@xminmax\@gobbletwo\else\ifx\sseq@yminmax\@gobbletwo\else
\sseq@error@n{DoUntil-no-bound}{\DoUntilOutOfBoundsThenNMore}
\sseq@breakfifi
\fi\fi
\sseq@ifintexpr{#1}{}{
\sseq@error@xx{Do-invalid-int-expr}{\string\DoUntilOutOfBoundsThenNMore}{\unexpanded{#1}}
\sseq@break
}
\def\sseq@commandname{\string\DoUntilOutOfBoundsThenNMore}
\sseq@savestack
\sseq@DoUntilOutOfBounds@body{#2}
\prg_replicate:nn {#1} {
\sseq@stepiteration
#2
\relax
}
\sseq@restorestack
\sseq@breakpoint
\endgroup
}
\def\sseq@DoUntilOutOfBounds@body#1{
\sseq@save@Do@var % Set iteration so that \DoUntilOutOfBoundsThenNMore doesn't get upset if we're already out of bounds
% If we're already out of bounds, we'll just do nothing.
\sseq_if_out_of_bounds_noparse:nnTF{\lastx{0}}{\lasty{0}}{
\bool_set_true:N \l_tmpa_bool
}{
\bool_set_false:N \l_tmpa_bool
% Now we need to set up the loop descent condition.
\sseq@tempxb=\lastx{0}\relax % Record current x and y values
\sseq@tempyb=\lasty{0}\relax
\sseq@stepiteration
#1 % run loop body once
\relax % protect from \d page grabber
\sseq_if_out_of_bounds_noparse:nnTF{\lastx{0}}{\lasty{0}}{ % If we're out of bounds now, we can quit
\bool_set_true:N \l_tmpa_bool
}{ % Otherwise, determine descent check
\sseq@tempx=\lastx{0}\relax % store new last value for comparison
\sseq@tempy=\lasty{0}\relax
\bool_set_false:N \l_tmpb_bool % This is to record whether there is a defined x range or y range. If neither, we'll throw an error.
% \def\sseq@checkbound{\bool_set_false:N \l_tmpb_bool}
\ifx\sseq@xminmax\@gobbletwo
\ifnum\sseq@tempx>\sseq@tempxb
\bool_set_true:N \l_tmpb_bool % We have a descent condition
% \sseq@d@addto@macro\sseq@checkbound{
% \ifnum\sseq@tempx>\sseq@tempxb % Any stage of the loop is okay as long as it increases x
% \bool_set_true:N \l_tmpb_bool
% \fi
% }
\else
\ifnum\sseq@tempx<\sseq@tempxb
\bool_set_true:N \l_tmpb_bool
% \sseq@d@addto@macro\sseq@checkbound{ % Any stage of the loop is okay as long as it decreases x
% \ifnum\sseq@tempx<\sseq@tempxb
% \bool_set_true:N \l_tmpb_bool
% \fi
% }
\fi
\fi
\fi
\ifx\sseq@yminmax\@gobbletwo
\ifnum\sseq@tempy>\sseq@tempyb
\bool_set_true:N \l_tmpb_bool
% \sseq@d@addto@macro\sseq@checkbound{
% \ifnum\sseq@tempy>\sseq@tempyb
% \bool_set_true:N \l_tmpb_bool
% \fi
% }
\else
\ifnum\sseq@tempy<\sseq@tempyb
\bool_set_true:N \l_tmpb_bool
% \sseq@d@addto@macro\sseq@checkbound{
% \ifnum\sseq@tempy<\sseq@tempyb
% \bool_set_true:N \l_tmpb_bool
% \fi
% }
\fi
\fi
\fi
\if_bool:N \l_tmpb_bool\else:
\sseq@error@x{DoUntil-no-progress}{\sseq@commandname}
\fi:
}
}
\bool_until_do:Nn \l_tmpa_bool {
\sseq@stepiteration
#1
\relax
\sseq_if_out_of_bounds_noparse:nnTF{\lastx{0}}{\lasty{0}}{% we're done
\bool_set_true:N \l_tmpa_bool
}{% Check descent condition
% \ifnum\iteration/10*10-\iteration=\z@
% \sseq@tempx=\lastx{0}\relax % store new last value for comparison
% \sseq@tempy=\lasty{0}\relax
% \bool_set_false:N \l_tmpb_bool
% \sseq@checkbound
% \if_bool:N \l_tmpb_bool\else:
% \sseq@error{DoUntilOutOfBounds-descent-failed}
% \fi:
% \sseq@tempxb=\sseq@tempx
% \sseq@tempyb=\sseq@tempx
% \fi
}
}
}
\ExplSyntaxOff
\def\sseq@for@nopatch{\sseq@error{foreach-patch-failed}\def\sseq@patchfor{}\endinput}
\newtoks\sseq@foreachcall
\def\sseq@pgffor@atbeginforeach{%
\begingroup %
\sseq@atbeginforeach@msgsetup
% \pgffor@macro@list calls \pgffor@normal@list, so we need to mark that the list has already been added to foreachcall.
\sseq@tempiftrue
}
\def\sseq@patchfor{%
\let\pgffor@atbeginforeach\sseq@pgffor@atbeginforeach
\let\pgffor@@vars@opt\sseq@pgffor@@vars@opt
}
\def\sseq@pgffor@modify#1{%
\@xp\let\csname sseq@\sseq@macroname#1\endcsname #1%
\eappto\sseq@patchfor{\let\@nx#1 \@xp\@nx\csname sseq@\sseq@macroname#1\endcsname}%
}
\def\sseq@pgffor@recordarg#1#2{
\sseq@pgffor@modify#1
\@xp\pretocmd\csname sseq@\sseq@macroname#1\endcsname{\sseq@foreachcall\@xp{\the\sseq@foreachcall#2}}{}{\sseq@for@nopatch}
}
\def\sseq@pgffor@erecordarg#1#2{
\sseq@pgffor@modify#1
\@xp\pretocmd\csname sseq@\sseq@macroname#1\endcsname{\sseq@eval{\sseq@foreachcall{\the\sseq@foreachcall#2}}}{}{\sseq@for@nopatch}
}
\bgroup\lccode`\*=`\#\lowercase{\egroup
% Modify the foreach argument parser commands to put the call into \sseq@foreachcall and to tell us what the variables are
\def\sseq@pgffor@@vars@opt[#1]{\sseq@foreachcall\@xp{\the\sseq@foreachcall#1}\pgfkeys{/sseqpages/foreach/.cd,#1}\pgffor@vars}
\sseq@pgffor@recordarg\pgffor@@vars{*1}
\pretocmd\sseq@pgffor@@vars{\sseq@for@savemacro*1}{}{\sseq@for@nopatch}
\sseq@pgffor@recordarg\pgffor@@vars@slash@gobble{/}
\sseq@pgffor@recordarg\pgffor@macro@list{in *1}
\pretocmd\sseq@pgffor@macro@list{\sseq@tempiffalse}{}{\sseq@for@nopatch}% Don't add this again in \pgffor@normal@lis
\sseq@pgffor@modify\pgffor@normal@list
% Add list to argument if it wasn't a macro
\pretocmd\sseq@pgffor@normal@list{\ifsseq@tempif\sseq@foreachcall\@xp{\the\sseq@foreachcall in {*1}}\fi\sseq@tempiftrue}{}{\sseq@for@nopatch}
\sseq@pgffor@recordarg\pgffor@collectforeach@macro{\foreach}
\sseq@pgffor@recordarg\pgffor@collectforeach@normal{\foreach}
\sseq@pgffor@modify\pgffor@iterate
\pretocmd\sseq@pgffor@iterate{\sseq@opushstacktrace{\the\sseq@foreachcall}\sseq@thiscalltoks\@xp{\the\sseq@foreachcall}}{}{\sseq@for@nopatch}
\sseq@pgffor@modify\pgffor@doloop
\sseq@pgffor@modify\pgffor@invokebody
\patchcmd\sseq@pgffor@doloop{\pgffor@begingroup}{\pgffor@begingroup\sseq@xsetlastcall{\the\sseq@foreachcall}}{}{\sseq@for@nopatch}
\patchcmd\sseq@pgffor@invokebody{\pgffor@begingroup}{\pgffor@begingroup\sseq@xsetlastcall{\the\sseq@foreachcall}}{}{\sseq@for@nopatch}
}
%TODO: also hook \pgffor@assign@parse, \pgffor@remember@parse, \pgffor@count@parse.