[log in to unmask] wrote: > > \begingroup > \catcode`\:=13 > \catcode`\.=13\relax > \gdef\chemin#1{% > \begingroup > \catcode`\:=13 > \catcode`\.=13\relax > \let.\cheminsommet > \let:\cheminface > \endlinechar=-1 > \everyeof{}% > \scantokens{#1}% > \endgroup > } > \endgroup A couple of things here. First, active characters tend to be bad news. In my own siunitx package, I've moved from using an approach like yours in v1 to trying a different system in v2, based on \tl_replace_all_in:Nnn: \cs_set:Nn \my_int_function:n #1 { \tl_set:Nn \l_my_tmp_tl {#1} \tl_replace_all_in:Nnn \l_my_tmp_tl { . } { \cheminsommet } \tl_replace_all_in:Nnn \l_my_tmp_tl { : } { \cheminface } % Do more stuff with the input } The reason for this is two-fold. First, active characters can cause a lot of problems (see what happens when two packages try to do something like this). Second, the \tl_replace_all_in:Nnn approach means that there is only one level of replacement, which is often what people expect if they protect characters. I think the general feeling is that the team are aiming to avoid active characters as far as possible. If you do want to use \tl_rescan:Nnx, what is wrong with \group_begin: \char_make_active:N \: \char_make_active:N \. \cs_gset:Npn \my_int_function:n #1 { \tl_set_rescan:Nnx \l_my_tmp_tl { \char_make_active:N \: \char_make_active:N \. \cs_set_eq:NN . \cheminsommet \cs_set_eq:NN : \cheminface } {#1} % Stuff with \l_my_tmp_tl } \group_end: or \group_begin: \char_make_active:N \: \char_make_active:N \. \cs_gset_eq:NN . \cheminsommet \cs_gset_eq:NN : \cheminface \cs_gset:Npn \my_int_function:n #1 { \tl_set_rescan:Nnn \l_my_tmp_tl { \char_make_active:N \: \char_make_active:N \. } {#1} % Stuff with \l_my_tmp_tl } \group_end: (The later makes active . and : globally available, so works without x-type expansion of the tl. I'd personally go for the first of these two solutions as it again is independent of what others do.) -- Joseph Wright