%% %% Package: spectralsequences v1.3.3 2023-01-28 %% Author: Hood Chatham %% Email: hood@mit.edu %% 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.