% File: precattl.sty
% Copyright 2022 user202729
%
% This work  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:
%
%   http://www.latex-project.org/lppl.txt
%
% This work has the LPPL maintenance status `maintained'.
% 
% The Current Maintainer of this work is user202729.

\ProvidesExplPackage{precattl}{2022/07/07}{0.0.0}{Prepare special catcodes from token list}


% ======== this part is inlined from execinside.sty. It's a bit messy yes, maybe will fix when the 2 LaTeX3 bugs on \peek_analysis_map_inline are fixed
% 

\cs_generate_variant:Nn \EI_append:n {x}
\cs_generate_variant:Nn \int_compare:nNnTF {VNnTF}
\cs_generate_variant:Nn \tl_set:Nn {Nx}
\cs_generate_variant:Nn \int_compare:nNnT {VNnT}
\cs_generate_variant:Nn \cs_if_exist_use:NT {cT}

% ======== define handler functions.
% a few things it can do:
% • \tl_build_gput_right:Nn  to \_EIresult (content being put there must be wrapped in \unexpanded i.e. x-expand to the desired result

\cs_set_protected:Npn \EI_append_raw:n #1 {
	\tl_build_gput_right:Nn \_EIresult {#1}
}

\cs_set_protected:Npn \EI_append:n #1 {
	\EI_append_raw:n {\exp_not:n {#1}}
}

\int_new:N \_EIbalance_level
\int_new:N \_EIcollecting_balanced

% handler can call this to collect following tokens in the input stream as argument.
% #1: the function to pass argument in.  (arguments are passed as brace-hack-included)
% #2: number of arguments
\cs_set_protected:Npn \EI_collect_arg:Nn #1 #2 {
	\int_set:Nn \_EIcollecting_balanced {#2}
	\int_zero:N \_EIbalance_level
	\tl_build_gbegin:N \_EIbalanced_content
	\tl_build_gput_right:Nn \_EIbalanced_content {\noexpand #1}
}

\cs_set_protected:Npn \EIhandler_EIexpand {
	\EI_collect_arg:Nn \EI_append:x {1}
}

\cs_set_protected:Npn \EIhandler_EIexecute {
	\EI_collect_arg:Nn \use:n {1}
}

\cs_set_protected:Npn \execinsideprepare:n #1 {
	\tl_build_gbegin:N \_EIresult
	\int_zero:N \_EIcollecting_balanced

	\tl_analysis_map_inline:nn {#1} {
		% reminder: ##1: token, ##2: char code (int), ##3: cat code (hex digit)

		\int_compare:nNnTF \_EIcollecting_balanced > 0 {
			% collecting content.

			\tl_build_gput_right:Nn \_EIbalanced_content {##1}
			\int_case:nn {"##3} {
				{1} { \int_incr:N \_EIbalance_level }
				{2} { \int_decr:N \_EIbalance_level }
			}

			% we check this every time instead of only after egroup tokens...
			% so that if there's exactly one token to be collected it's still correctly passed through
			% disadvantage: function might get a space
			\int_compare:nNnT \_EIbalance_level = 0 {
				% done, call handler function and revert to normal mode.

				\int_decr:N \_EIcollecting_balanced
				\int_compare:nNnT \_EIcollecting_balanced = 0 {
					\tl_build_gend:N \_EIbalanced_content
					\tl_set:Nx \_EIbalanced_content {\_EIbalanced_content}
					%\pretty:V \_EIbalanced_content
					\_EIbalanced_content  % ← the handler function is embedded in here
					%\pretty:n {done}
				}
			}
		} {
			\let \_EIprocessed \c_false_bool

			% check for \EIexecute etc. and handle accordingly.
			\int_compare:nNnT {##2} = {-1} {
				\cs_if_exist_use:cT {
					EIhandler_ \expandafter \cs_to_str:N ##1
				} {
					\let \_EIprocessed \c_true_bool
				}
			}

			% if not, just append to result.
			\bool_if:NF \_EIprocessed {
				\tl_build_gput_right:Nn \_EIresult {##1}
			}
		}
	}

	\tl_build_gend:N \_EIresult
	\tl_set:Nx \_EIresult {\_EIresult}
}

\cs_set_protected:Npn \execinside:n #1 {
	\execinsideprepare:n {#1}
	\_EIresult
}
\let\execinside\execinside:n  % normal-catcode alias

\cs_set_protected:Npn \execinside_set:Nn #1 #2 {
	\execinsideprepare:n {#2}
	\let #1 \_EIresult
}
\let\execinsideSet\execinside_set:Nn

\cs_set_protected:Npn \execinside_gset:Nn #1 #2 {
	\execinsideprepare:n {#2}
	\global \let #1 \_EIresult
}
\let\execinsideGset\execinside_gset:Nn


%% ======== temporary patch for expl3 bug https://github.com/latex3/latex3/issues/1109
%% okay maybe no need, still use old \tl_analysis implementation
%
%% #1: some explicit char with char 32 and cat X
%% #2: the original explicit char
%\cs_new_protected:Npn \__broken_tl_peek_analysis_char:nN #1#2
%  {
%    \cs_set_protected:Npn \__tl_tmp:w ##1 #1 ##2 ##3 \scan_stop:
%      { \exp_args:No \l__tl_peek_code_tl { \int_value:w `#2 } ##2 }
%    \exp_after:wN \__tl_tmp:w \c__tl_peek_catcodes_tl \scan_stop:
%  }
%
%% only patch in this particular condition.
%\ifx \__tl_peek_analysis_char:nN \__broken_tl_peek_analysis_char:nN
%	\cs_set_protected:Npn \__tl_peek_analysis_char:nN #1#2
%	{
%		\pretty:n  {#1, #2}
%		\token_if_parameter:NTF #1 {
%			\exp_args:No \l__tl_peek_code_tl { \int_value:w `#2 } 6
%		} {
%			\cs_set_protected:Npn \__tl_tmp:w ##1 #1 ##2 ##3 \scan_stop:
%			{ \exp_args:No \l__tl_peek_code_tl { \int_value:w `#2 } ##2 }
%			\exp_after:wN \__tl_tmp:w \c__tl_peek_catcodes_tl \scan_stop:
%		}
%	}
%\fi

% ========


\cs_generate_variant:Nn \exp_args:NN {Nc}
\cs_generate_variant:Nn \EI_append:n {x}
\cs_generate_variant:Nn \exp_args:Nn {No}
\cs_generate_variant:Nn \EI_append:n {o}
\cs_generate_variant:Nn \tl_put_right:Nn {Nx}
\cs_generate_variant:Nn \use:nn {nV}
\cs_generate_variant:Nn \execinside_set:Nn {NV}
	

\def \_precattlappend_space #1 {
	\begingroup
	\lccode `\ =#1
	\lowercase{
		\endgroup \EI_append:n {~}
	}
}
\def \_precattlappend_begin #1 {
	\begingroup
	\lccode `\{=#1
	\lowercase{\endgroup \EI_append_raw:n { \iftrue { \else } \fi }}
}
\def \_precattlappend_end #1 {
	\begingroup
	\lccode `\}=#1
	\lowercase{\endgroup \EI_append_raw:n { \iffalse { \else } \fi }}
}
	
%\def \_precattldo_special_cC #1 { \exp_args:Nc \EI_append:n {#1} }
\def \_precattldo_special_cC #1 { \EI_append_raw:n {\exp_not:c {#1}} }  % do this to handle \outer tokens. but in the x-expansion step below need to wrap in a group level to avoid defining control sequences as \relax.

\def \_precattldo_special_cB #1 { \str_map_inline:nn {#1} { \_precattlappend_begin {`##1} } }
\def \_precattldo_special_cE #1 { \str_map_inline:nn {#1} { \_precattlappend_end {`##1} } }
\def \_precattldo_special_cM #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {3}} } }
\def \_precattldo_special_cT #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {4}} } }
\def \_precattldo_special_cP #1 { \str_map_inline:nn {#1} { \exp_args:No \EI_append:o {\char_generate:nn {`##1} {6}} } }
\def \_precattldo_special_cU #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {7}} } }
\def \_precattldo_special_cD #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {8}} } }
\def \_precattldo_special_cS #1 { \str_map_inline:nn {#1} { \_precattlappend_space {`##1} } }
\def \_precattldo_special_cL #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {11}} } }
\def \_precattldo_special_cO #1 { \str_map_inline:nn {#1} { \EI_append:x {\char_generate:nn {`##1} {12}} } }
\def \_precattldo_special_cA #1 { \str_map_inline:nn {#1} {
	%\exp_args:No \EI_append:o {\char_generate:nn {`##1} {13}}
	\EI_append_raw:n {\expandafter \expandafter \expandafter \noexpand \char_generate:nn {`##1} {13}}  % handle outer token
} }



\def \_precattldo_special #1 #2 {
	% given #1 e.g. \cO, #2 e.g. {abc}, append <abc> in detokenized catcode to EI.
	\begingroup
		\escapechar=-1

		% let \_precattltmp be the concatenation of \string of all tokens within #2.
		\def \_precattltmp {}
		\tl_analysis_map_inline:nn {#2} {
			\tl_put_right:Nx \_precattltmp {\expandafter \string ##1}
		}

		% then pass it to the specific handler.
		\exp_args:NNV
	\endgroup
	#1 \_precattltmp
}


\def \_precattldo_special_cC_outer { \_precattldo_special\_precattldo_special_cC }
\def \_precattldo_special_cB_outer { \_precattldo_special\_precattldo_special_cB }
\def \_precattldo_special_cE_outer { \_precattldo_special\_precattldo_special_cE }
\def \_precattldo_special_cM_outer { \_precattldo_special\_precattldo_special_cM }
\def \_precattldo_special_cT_outer { \_precattldo_special\_precattldo_special_cT }
\def \_precattldo_special_cP_outer { \_precattldo_special\_precattldo_special_cP }
\def \_precattldo_special_cU_outer { \_precattldo_special\_precattldo_special_cU }
\def \_precattldo_special_cD_outer { \_precattldo_special\_precattldo_special_cD }
\def \_precattldo_special_cS_outer { \_precattldo_special\_precattldo_special_cS }
\def \_precattldo_special_cL_outer { \_precattldo_special\_precattldo_special_cL }
\def \_precattldo_special_cO_outer { \_precattldo_special\_precattldo_special_cO }
\def \_precattldo_special_cA_outer { \_precattldo_special\_precattldo_special_cA }

\def \_precattl_tmp_help #1 {
	\def \_precattl_do_frozen_relax { \EI_append:n {#1} }
	% #1 here will be substituted by the frozen relax
}
\expandafter \_precattl_tmp_help \ifnum0=0\fi
\let \_precattl_tmp_help \undefined



\def \precattl_prepare:n #1 {
	\begingroup
		\let \EIhandler_EIexpand \undefined
		\let \EIhandler_EIexecute \undefined
		\def \EIhandler_cC { \EI_collect_arg:Nn \_precattldo_special_cC_outer 1 }
		\def \EIhandler_cB { \EI_collect_arg:Nn \_precattldo_special_cB_outer 1 }
		\def \EIhandler_cE { \EI_collect_arg:Nn \_precattldo_special_cE_outer 1 }
		\def \EIhandler_cM { \EI_collect_arg:Nn \_precattldo_special_cM_outer 1 }
		\def \EIhandler_cT { \EI_collect_arg:Nn \_precattldo_special_cT_outer 1 }
		\def \EIhandler_cP { \EI_collect_arg:Nn \_precattldo_special_cP_outer 1 }
		\def \EIhandler_cU { \EI_collect_arg:Nn \_precattldo_special_cU_outer 1 }
		\def \EIhandler_cD { \EI_collect_arg:Nn \_precattldo_special_cD_outer 1 }
		\def \EIhandler_cS { \EI_collect_arg:Nn \_precattldo_special_cS_outer 1 }
		\def \EIhandler_cL { \EI_collect_arg:Nn \_precattldo_special_cL_outer 1 }
		\def \EIhandler_cO { \EI_collect_arg:Nn \_precattldo_special_cO_outer 1 }
		\def \EIhandler_cA { \EI_collect_arg:Nn \_precattldo_special_cA_outer 1 }
		\let \EIhandler_cFrozenRelax \_precattl_do_frozen_relax
		\execinside_gset:Nn \_precattlvalueg {#1}
	\endgroup
}

\def \precattl_set:Nn #1 #2 {
	\precattl_prepare:n {#2}
	\tl_set_eq:NN #1 \_precattlvalueg
}






\cs_new_protected:Npn \__resume_peek: {
	\peek_analysis_map_inline:n {
		\__process {##1} {##2} {##3}
	}
}

\def \__help:NN #1 #2 {
	\def #1 { \peek_analysis_map_break:n { \_precattldo_special #2 } }
}
\cs_generate_variant:Nn \__help:NN {cc}
\str_map_inline:nn {CBEMTPUDSLOA} {
	\__help:cc {__special c #1} {__special c #1 _continue: }
}

\def \__help:NN #1 #2 {
	\def #1 { \peek_analysis_map_break:n { #2 } }
}

\str_map_inline:nn {BEMTPUDSLOA} {
	\__help:cc {__special c #1} {__special c #1 _continue: }
}


\def \__help:n #1 {
	\def \__specialcFrozenRelax {
		\tl_build_gput_right:Nn \__result { #1 }
	}
	% #1 here will be substituted by the frozen relax
}
\expandafter \__help:n \ifnum0=0\fi
\let \__help:n \undefined



% ======== define \__specialcB_continue:, etc.

\def \__help:Nn #1 #2 {
	\def #1 ##1 {
		\tl_analysis_map_inline:nn {##1} {
			\str_map_inline:xn {\expandafter\string####1} {
				\tl_build_gput_right:Nn \__result {
					\char_generate:nn {`########1} {#2}
				}
			}
		}
		\__resume_peek:
	}
}
\__help:Nn \__specialcB_continue: {1}
\__help:Nn \__specialcE_continue: {2}
\__help:Nn \__specialcM_continue: {3}
\__help:Nn \__specialcT_continue: {4}
\__help:Nn \__specialcU_continue: {7}
\__help:Nn \__specialcD_continue: {8}
\__help:Nn \__specialcS_continue: {10}
\__help:Nn \__specialcL_continue: {11}
\__help:Nn \__specialcO_continue: {12}


\def \__specialcC_continue: #1 {
	\begingroup
		\exp_args:NNo \tl_build_gput_right:Nn \__result {\expandafter \noexpand \csname #1 \endcsname}
	\endgroup
	\__resume_peek:
}
\def \__specialcA_continue: #1 {
	\tl_analysis_map_inline:nn {#1} {
		\str_map_inline:xn {\expandafter\string##1} {
			\tl_build_gput_right:Nn \__result {
				\expandafter \expandafter \expandafter \noexpand \char_generate:nn {`####1} {13}
			}
		}
	}
	\__resume_peek:
}
\def \__specialcP_continue: #1 {
	\tl_analysis_map_inline:nn {#1} {
		\str_map_inline:xn {\expandafter\string##1} {
			\tl_build_gput_right:Nn \__result {
				######## \char_generate:nn {`####1} {6}
				%## \char_generate:nn {`#1} {6}
			}
		}
	}
	\__resume_peek:
}

% ========

\cs_new_protected:Npn \__process #1 #2 #3 {
	\token_if_eq_meaning:NNTF #3 0 {

		%\ifcsname __special \expandafter \cs_to_str:N #1 \endcsname
		%	\csname __special \expandafter \cs_to_str:N #1 \expandafter \endcsname
		%\else
		%	\tl_build_gput_right:Nn \__result {#1}
		%\fi

		\ifcsname __special \expandafter \cs_to_str:N #1 \endcsname
			\expandafter \use_i:nn
		\else
			\expandafter \use_ii:nn
		\fi
		{\csname __special \expandafter \cs_to_str:N #1 \endcsname}
		{\tl_build_gput_right:Nn \__result {#1}}
	}
	{
		\tl_build_gput_right:Nn \__result {#1}
	}
}



\def \_precattl_tmp_help #1 {
	\def \_precattl_do_frozen_relax { \EI_append:n {#1} }
	% #1 here will be substituted by the frozen relax
}
\expandafter \_precattl_tmp_help \ifnum0=0\fi
\let \_precattl_tmp_help \undefined


\def \precattl_setx:Nn #1 #2 {
	\begingroup
	\escapechar=-1~
	\tl_build_gbegin:N \__result
	\cs_set_protected:Npn \__special__stop {
		\tl_build_gend:N \__result
		\endgroup
		\tl_set:Nx #1 {\__result}
		\peek_analysis_map_break:
	}
	\__resume_peek:
	#2
	\__stop
}
\let\precattlSet\precattl_set:Nn

\def \precattl_exec:n #1 {
	\precattl_prepare:n {#1}
	\_precattlvalueg
	%\precattl_set:Nn \__tmp {#1}
	%\__tmp
}
\let\precattlExec\precattl_exec:n
