%%%============================================================================== %% Copyright 2026-present by Alceu Frigeri %% %% This work may be distributed and/or modified under the conditions of %% %% * The [LaTeX Project Public License](http://www.latex-project.org/lppl.txt), %% version 1.3c (or later), and/or %% * The [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html), %% version 3 (or later) %% %% This work has the LPPL maintenance status *maintained*. %% %% The Current Maintainer of this work is Alceu Frigeri %% %% This is version {1.0a} {2026/01/04} %% %% The list of files that compose this work can be found in the README.md file at %% https://ctan.org/pkg/xstacks %% %%%============================================================================== \NeedsTeXFormat{LaTeX2e}[2023/11/01] %%%%%%% %%% %%% Just an attempt at having my package's info in a regular way %%% \pkginfograb_set:nn {} { props} sets package info %%% %%% \pkginfograbProvidesExplPackage {} { props} sets package's info %%% and calls \ProvidesExplPackage %%% %%%%%%% \RequirePackage{pkginfograb} \pkginfograbProvidesExplPackage {xstacks} { name = {xstacks} , prefix = {xstacks} , date = {2026/01/04}, version = {1.0a} , description = {xstacks - after group(s) and dedicated global stack(s) implementations} } %%%%%%% %%% End of cut-n-paste %%%%%%% %%%%%%% %%% 'pure' aftergroup %%%%%%% \int_new:N \l__xstacks_level_int \cs_new_protected:Npn \xstacks_groupmark: { \int_set_eq:NN \l__xstacks_level_int \currentgrouplevel \int_incr:N \l__xstacks_level_int } \cs_new_protected:Npn \xstacks_aftergroup:N #1 { \if_int_compare:w \l__xstacks_level_int < \currentgrouplevel \group_insert_after:N \xstacks_aftergroup:N \group_insert_after:N #1 \else: \group_insert_after:N #1 \fi: } % % The difference between \int_compare:nNnTF and \if_int_compare:w is brutal... % %\cs_new_protected:Npn \xstacks_aftergroup:N #1 % { % \int_compare:nNnTF \l__xstacks_level_int < \currentgrouplevel % { % \group_insert_after:N \xstacks_aftergroup:N % \group_insert_after:N #1 % } % { % \group_insert_after:N #1 % } % } %%% \cs_new_protected:Npn \xstacks_groupmark:N #1 { \int_set_eq:NN #1 \currentgrouplevel \int_incr:N #1 } \cs_new_protected:Npn \xstacks_aftergroup:NN #1#2 { \if_int_compare:w #1 < \currentgrouplevel \group_insert_after:N \xstacks_aftergroup:NN \group_insert_after:N #1 \group_insert_after:N #2 \else: \group_insert_after:N #2 \fi: } % % The difference between \int_compare:nNnTF and \if_int_compare:w is brutal... % %\cs_new_protected:Npn \xstacks_aftergroup:NN #1#2 % { % \int_compare:nNnTF #1 < \currentgrouplevel % { % \group_insert_after:N \xstacks_aftergroup:NN % \group_insert_after:N #1 % \group_insert_after:N #2 % } % { % \group_insert_after:N #2 % } % } %%%%%%% %%% 'cs' generate case. %%%%%%% \cs_new_protected:Npn \xstacks_cs_gset:N #1 { \int_gzero_new:c {g__xstacks_ #1 _int} \cs_gset:cpe {#1_gpush:n} ##1 { \exp_not:N \int_gincr:N \exp_not:c {g__xstacks_ #1 _int} \exp_not:N \tl_gset:cn {g__xstacks_ \exp_not:N \int_to_Alph:n \exp_not:c {g__xstacks_ #1 _int} _tl} {##1} } \cs_gset:cpe {#1_gput_right:n} ##1 { \exp_not:N \tl_gput_right:cn {g__xstacks_ \exp_not:N \int_to_Alph:n \exp_not:c {g__xstacks_ #1 _int} _tl} {##1} } \cs_gset:cpe {#1_gput_left:n} ##1 { \exp_not:N \tl_gput_left:cn {g__xstacks_ \exp_not:N \int_to_Alph:n \exp_not:c {g__xstacks_ #1 _int} _tl} {##1} } % \cs_gset:cpe {#1_gpop:} % { % \exp_not:N \int_if_zero:nF \exp_not:c {g__xstacks_ #1 _int} % { % \exp_not:N \use:c % {g__xstacks_ \exp_not:N \int_to_Alph:n \exp_not:c {g__xstacks_ #1 _int} _tl} % \exp_not:N \int_gdecr:N \exp_not:c {g__xstacks_ #1 _int} % } % } \cs_gset:cpe {#1_gpop:} { \exp_not:N \if_int_compare:w \exp_not:c {g__xstacks_ #1 _int} = \c_zero_int \exp_not:N \else: \exp_not:N \use:c {g__xstacks_ \exp_not:N \int_to_Alph:n \exp_not:c {g__xstacks_ #1 _int} _tl} \exp_not:N \int_gdecr:N \exp_not:c {g__xstacks_ #1 _int} \exp_not:N \fi: } } %%%%%%% %%% Note: %%% \__xstacks_cs:NNnN absorbs 4 tokens, 3 'internal' + 1 given by the user %%% except in the push case, whereas it will absorb 5 tokens! %%% %%% a local group is created (except in the pop case) %%% so there is no need to reset \__xstacks_cs:NNnN %%%%%%% \cs_set_eq:NN \__xstacks_cs:NNnN \use_none:nnn \cs_new_protected:Npn \xstacks_gset:N #1 { \tl_set:Ne \l__xstacks_tmpA_tl {\cs_to_str:N #1} \tl_gset:cn {g__xstacks_ \l__xstacks_tmpA_tl _tl} {} \int_gzero_new:c {g__xstacks_ \l__xstacks_tmpA_tl _int} \tl_gset:Ne #1 { \exp_not:N \__xstacks_cs:NNnN \exp_not:c {g__xstacks_ \l__xstacks_tmpA_tl _tl} \exp_not:c {g__xstacks_ \l__xstacks_tmpA_tl _int} {\l__xstacks_tmpA_tl} } } \cs_new_protected:Npn \__xstacks_gpush:NNnNn #1#2#3#4#5 { \int_gincr:N #2 \tl_gset:cn {g__xstacks_ #3 _ \int_to_Alph:n #2 _tl}{#5} \tl_gset:Ne #4 { \exp_not:N \__xstacks_cs:NNnN \exp_not:c {g__xstacks_ #3 _ \int_to_Alph:n #2 _tl} \exp_not:N #2 {#3} } } \cs_new_protected:Npn \xstacks_gpush:Nn #1#2 { { \cs_set_eq:NN \__xstacks_cs:NNnN \__xstacks_gpush:NNnNn #1 #1 {#2} } } \cs_new_protected:Npn \__xstacks_gput_right:NNnN #1#2#3#4 { \tl_gput_right:Nn #1 {#4} } \cs_new_protected:Npn \xstacks_gput_right:Nn #1#2 { { \cs_set_eq:NN \__xstacks_cs:NNnN \__xstacks_gput_right:NNnN #1 {#2} } } \cs_new_protected:Npn \__xstacks_gput_left:NNnN #1#2#3#4 { \tl_gput_left:Nn #1 {#4} } \cs_new_protected:Npn \xstacks_gput_left:Nn #1#2 { { \cs_set_eq:NN \__xstacks_cs:NNnN \__xstacks_gput_left:NNnN #1 {#2} } } %\cs_new_protected:Npn \__xstacks_gpop:NNnN #1#2#3#4 % { % \cs_set_eq:NN \__xstacks_cs:NNnN \use_none:nnn % \int_if_zero:nF #2 % { % \int_gdecr:N #2 % \tl_gset:Ne #4 % { % \exp_not:N \__xstacks_cs:NNnN % \exp_not:c {g__xstacks_ #3 _ \int_to_Alph:n #2 _tl} % \exp_not:N #2 % {#3} % } % #1 % } % } \cs_new_protected:Npn \__xstacks_gpop:NNnN #1#2#3#4 { \cs_set_eq:NN \__xstacks_cs:NNnN \use_none:nnn \if_int_compare:w #2 = \c_zero_int \else: \int_gdecr:N #2 \tl_gset:Ne #4 { \exp_not:N \__xstacks_cs:NNnN \exp_not:c {g__xstacks_ #3 _ \int_to_Alph:n #2 _tl} \exp_not:N #2 {#3} } #1 \fi: } \cs_new_protected:Npn \xstacks_gpop:N #1 { \cs_set_eq:NN \__xstacks_cs:NNnN \__xstacks_gpop:NNnN #1 #1 }