% 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 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