% !TeX encoding = UTF-8
% Ce fichier contient le code de l'extension "tokstools"

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                    %
\def\tktlname                   {tokstools}                          %
\def\tktlver                       {0.2}                             %
%                                                                    %
\def\tktldate                   {2026/04/17}                         %
%                                                                    %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                    %
% Author     : Christian Tellechea                                   %
% Status     : Maintained                                            %
% Email      : unbonpetit@netc.fr                                    %
% Package URL: https://www.ctan.org/pkg/tokstools                    %
% Copyright  : Christian Tellechea 2026                              %
% Licence    : Released under the LaTeX Project Public License v1.3c %
%              or later, see http://www.latex-project.org/lppl.txt   %
% Files      : 1) tokstools.tex    (this file)                       %
%              2) tokstools.sty    (sty file for latex use)          %
%              3) tokstools-fr.tex (source of manual in french)      %
%              4) tokstools-fr.pdf (pdf of manual in french)         %
%              5) tokstools-en.tex (source of manual in english)     %
%              6) tokstools-en.pdf (pdf of manual in english)        %
%              7) README.md                                          %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%---------------------------------------------------------------------
%---------------- Annonce package et autres pré-requis ---------------
%---------------------------------------------------------------------
\csname tktl_load_once\endcsname
\expandafter\let\csname tktl_load_once\endcsname\endinput

\ifdefined\tktlstyfile
\else
	\immediate\write-1 {Package: \tktlname\space\tktldate\space\space v\tktlver\space\space PEG tools for tokens (CT)}%
\fi

\expandafter\edef\csname tktl_restore_catcode\endcsname{\catcode\number`\_=\number\catcode`\_\relax}
\catcode`\_11
\def\tktl_star{*}
\def\tktl_superscript{^}
% Macros de manipulation d'arguments
\long\def\tktl_exec_first#1#2{#1}
\long\def\tktl_exec_second#1#2{#2}
\long\def\tktl_test_passed\fi\tktl_exec_second#1#2{\fi#1}
\long\def\tktl_ok_this_test#1#2\tktl_else_all_tests#3{\fi#1}
\long\def\tktl_else_all_tests#1{#1}
\long\def\tktl_gob_arg#1{}
\long\def\tktl_id#1{#1}
\long\def\tktl_firsttonil#1#2\_nil{#1}
\def\tktl_def_tok#1#2{\let#1= #2\relax}
\tktl_def_tok\tktl_sp_token{ }
\def\tktl_csdef#1{\expandafter\def\csname#1\endcsname}
\def\tktl_unexpand_earg#1#{%
	\tktl_unexpand_earg_a{#1}%
}
\def\tktl_unexpand_earg_a#1#2{% #1=instructions #2=arg -> #1{*#2}
	\unexpanded{#1}{\unexpanded\expandafter{#2}}%
}
\def\tktl_expand_n_times#1#2#3{% développe #1 fois l'argument #3 de la _macro_ #2 : #2{*^n#3}
	\tktl_ifnum{#1>0 }
		{\expandafter\tktl_expand_n_times\expandafter{\the\numexpr#1-1\expandafter}\expandafter#2\expandafter{#3}}
		{#2{#3}}%
}
\long\def\tktl_antefi#1#2\fi{#2\fi#1}
\def\tktl_ifnxttok#1#2#3{%
	\tktl_def_tok\tktl_toksmatch{#1}%
	\def\tktl__truecode{#2}%
	\def\tktl__falsecode{#3}%
	\tktl_ifnxttok_a
}
\def\tktl_ifnxttok_a{\futurelet\tktl__futurtok\tktl_ifnxttok_b}
\def\tktl_ifnxttok_b{%
	\ifx\tktl__futurtok\tktl_sp_token
		\afterassignment\tktl_ifnxttok_a
		\tktl_antefi{\let\tktl__futurtok= }
	\else
		\tktl_antefi{%
			\ifx\tktl__futurtok\tktl_toksmatch
				\tktl_antefi\tktl__truecode
			\else
				\tktl_antefi\tktl__falsecode
			\fi
		}%
	\fi
}
\def\tktl_futurelet_nospace#1#2{%
	\def\tktl_futurelet_nospace_a{\futurelet#1\tktl_futurelet_nospace_b}%
	\def\tktl_futurelet_nospace_b{%
		\ifx\tktl__futurtok\tktl_sp_token
			\afterassignment\tktl_futurelet_nospace_a
			\tktl_antefi{\let#1= }%
		\else
			\tktl_antefi{#2}%
		\fi
	}%
	\tktl_futurelet_nospace_a
}
% Quarks
\def\active_quark{\active_quark}
\def\end_parse{\end_parse}

% Retrait d'espaces
\def\tktl_temp#1{%
\long\def\tktl_stripsp##1{\expanded{\tktl_stripsp_i\_marksp##1\__nil\_marksp#1\_marksp\_nil}}%
\long\def\tktl_stripsp_i##1\_marksp#1##2\_marksp##3\_nil{\tktl_stripsp_ii##3##1##2\__nil#1\__nil\_nil}%
\long\def\tktl_stripsp_ii##1#1\__nil##2\_nil{\tktl_stripsp_iii##1##2\_nil}%
\long\def\tktl_stripsp_iii##1##2\__nil##3\_nil{\unexpanded{##2}}%
}\tktl_temp{ }
\long\def\tktl_addtomacro#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\long\def\tktl_eaddtomacro#1#2{\edef#1{\unexpanded\expandafter{#1}\unexpanded\expandafter{#2}}}
\def\tktl_addtotoks#1#2{#1\expandafter{\the#1#2}}
\def\tktl_eaddtotoks#1#2{#1\expandafter{\expanded{\the#1\unexpanded\expandafter{#2}}}}
\def\tktl_xaddtotoks#1#2{#1\expandafter{\expanded{\the#1#2}}}
% Macros de test
\long\def\tktl_ifempty_or_space#1{\tktl_ifempty_or_space_i#1\_nil\_nil\tktl_exec_second\tktl_exec_first\__nil}%
\long\def\tktl_ifempty_or_space_i#1#2\_nil#3#4#5\__nil{#4}
\def\tktl_ifx#1{\ifx#1\tktl_test_passed\fi\tktl_exec_second}
\def\tktl_ifnum#1{\ifnum#1\tktl_test_passed\fi\tktl_exec_second}
\long\def\tktl_ifinstr#1#2{% la chaine #1 est-elle dans #2 ?
	\long\def\tktl_ifinstr_i##1#1##2\tktl_ifinstr_i{\ifcat\relax\detokenize{##2}\relax\expandafter\tktl_exec_second\else\expandafter\tktl_exec_first\fi}%
	\tktl_ifinstr_i#2#1\tktl_ifinstr_i
}
% Macros auxiliaires
\def\tktl_addto_special_list#1{%
	\def\tktl__tmp{\def\tktl_list_specials}%
	\expandafter\tktl__tmp\expandafter{\tktl_list_specials#1,}%
}
\let\tktl_bg_token{
\let\tktl_eg_token}

%---------------------------------------------------------------------
%---------------- Macro \tktl_encode_tokens_char_range ---------------
%---------------------------------------------------------------------
% \tktl_encode_tokens_char_range{<char range>}<macroA><macroB>
%
%	analyse le <char range> et renvoie dans les 2 macros les charcodes
%	min et max correspondant à <char range>, sachant que <char range> est de la forme :
%		. <a>-<b> où <a> et <b> sont 2 caractères entourés d'éventuels espaces
%		. <a> un caractère unique entouré d'éventuels d'espaces
%			. "-" compris comme  le caractère unique "-"
%			. " " compris comme le caractère unique <espace> de charcode 32
%		. <espace>-<b> ou <a>-<espace> ou <espace>-<espace> : les <espaces> donnent 32
%
% Retour :
%*	*<macroA> et <macroB> qui seront égales pour un caractère unique
%	 <a> ou pour <a>-<a>
%	* un \errmessage est renvoyé si
%		. <char range> est vide
%		. <a> ou <b> est, après retrait d'espces avant/après, constitué de plusieurs caractères
%		. si le charcode de <a> est supérieur à celui de <b>
%---------------------------------------------------------------------
\def\tktl_char_range_sep{-}
\def\tktl_encode_tokens_char_range#1#2#3{% #1=intervalle de la forme '<a>-<b>' ou '<a>' #2=charcode min #3=charcodemax
	\def\tktl_current_char_range{#1}%
	\ifx\tktl_current_char_range\empty
		\errmessage{Empty char range, '0' inserted}%
		\edef#2{\number`\0}%
		\let#3#2%
	\else
		\ifx\tktl_current_char_range\space
			\def#2{32}%
			\let#3#2%
		\else
			\tktl_ifinstr-{#1}
				{%
				\ifx\tktl_current_char_range\tktl_char_range_sep% si "-" seul, le prendre comme char
					\tktl_encode_tokens_char_range_b{-}#2%
					\let#3#2%
				\else
					\tktl_encode_tokens_char_range_a#1\_nil#2#3%
				\fi
				}
				{%
				\tktl_encode_tokens_char_range_b{#1}#2%
				\let#3#2%
				}%
		\fi
	\fi
}
\def\tktl_encode_tokens_char_range_a#1-#2\_nil#3#4{%
	\def\tktl_current_lo_range{#1}%
	\def\tktl_current_hi_range{#2}%
	\ifnum0\ifx\tktl_current_lo_range\empty1\fi\ifx\tktl_current_hi_range\empty1\fi>0
		\errmessage{Missing bound in char range \detokenize{#1-#2}, "0-0" inserted}%
		\def\tktl_current_lo_range{0}%
		\def\tktl_current_hi_range{0}%
	\fi
	\ifx\tktl_current_lo_range\space
		\def#3{32}%
		\ifx\tktl_current_hi_range\space
			\def#4{32}%
		\else
			\tktl_encode_tokens_char_range_b{#2}#4%
		\fi
	\else
		\tktl_encode_tokens_char_range_b{#1}#3%
		\ifx\tktl_current_hi_range\space
			\def#4{32}%
		\else
			\tktl_encode_tokens_char_range_b{#2}#4%
		\fi
	\fi
	\ifnum#4<#3
		\errmessage{Unsorted interval '\detokenize{#1-#2}', '\detokenize{#2-#1}' inserted}%
		\let\tktl__tmp#4%
		\let#4#3%
		\let#3\tktl__tmp
		
	\fi
}
\def\tktl_encode_tokens_char_range_b#1#2{% #1=char non espace #2=macro recevant le charcode
	\expandafter\tktl_ifempty_or_space\expandafter{\tktl_gob_arg#1}
		{%
		\edef#2{\number\expandafter`\csname#1\endcsname}%
		}
		{%
		\errmessage{Multiple token '\detokenize{#1}', '0' inserted}%
		\edef#2{\number`\0}%
		}%
}
\let\charrange\tktl_encode_tokens_char_range

%---------------------------------------------------------------------
%----------------- Macro \tktl_encode_tokensinterval -----------------
%---------------------------------------------------------------------
% \tktl_encode_tokensinterval admet un argument <intervalle> et
%	retourne dans 2 macros (arg #2 et #3) le minimum et maximum
%	correspondant à l'intervalle
% <intervalle> =  "n" ou "n-m" avec n et m entiers positifs
%                 éventuellement vides
% Utilisation :
%	\tktl_encode_tokensinterval{#1}\macroA\macroB
%
% Retour :
%	macros \macroA (borne inf) et \macroB (borne sup)
%---------------------------------------------------------------------
\newcount\tktl_test_cnt
\def\test_iftexnumber#1{\test_iftexnumber_i#1`\_nil}
\def\test_iftexnumber_i#1`#2\_nil{\tktl_ifempty_or_space{#1}}%
\def\tktl_ifinteger#1{%
	\tktl_ifempty_or_space{#1}
		{%
		\tktl_exec_second
		}
		{%
		\afterassignment\tktl_ifinteger_i
		\tktl_test_cnt\numexpr\test_iftexnumber{#1}{}0#1\relax\relax\_nil
		}%
}
\def\tktl_ifinteger_i#1\relax\_nil{\tktl_ifempty_or_space{#1}}
\def\tktl_maxint_repeat{1114109}
\def\tktl_encode_tokensinterval#1{% #1 est de la forme "n" ou "min-max"  #2=macro recevant min   #3=macro recevant max
	\tktl_encode_tokensinterval_a#1-#1-\_nil
}
\def\tktl_encode_tokensinterval_a#1-#2-#3\_nil#4#5{%
	\edef#4{\the\numexpr#1}%
	\edef#5{\the\numexpr#2}%
}

%---------------------------------------------------------------------
%--------------------- Macro \tktl_test_interval ---------------------
%---------------------------------------------------------------------
% Utilisation :
%	\tktl_test_interval{#1}{#2}{true}{false}
%	- #1 _doit_ être un entier
%	- #2 est une liste i1,i2,...in où les éléments sont des intervalles
%        NE COMPORTANT PAS D'ESPACE de la forme
%		. * (wildcard) qui est toute valeur entière, renvoie true et bypasse les autres intervalles
%		. intervalle au sens de \tktl_encode_tokensinterval (forme "x" ou "x-y")
%	chaque intervalle est 1-développé avant d'etre lu
% Retour :
%	true si #1 appartient à l'un des intervalles, false sinon
%---------------------------------------------------------------------
\def\tktl_test_interval#1#2{% teste si l'entier #1 est dans la liste d'intervalles #2 de type "i..j" ou entier seul "i"
	\tktl_ifinstr{,*,}{,#2,}
		{%
		\tktl_exec_first
		}
		{%
		\edef\tktl_int{\the\numexpr#1\relax}%
		\tktl_test_interval_i#2,\end_parse,%
		}%
}
\def\tktl_test_interval_i#1,{%
	\ifx\end_parse#1%
		\let\tktl_do_next\tktl_exec_second
	\else
		\tktl_encode_tokensinterval{#1}\tktl_tmp_min\tktl_temp_max
		\ifnum\numexpr(\tktl_int-\tktl_tmp_min)*(\tktl_int-\tktl_temp_max)>0 % si n'appartient pas à [min ; max]
			\let\tktl_do_next\tktl_test_interval_i
		\else
			\let\tktl_do_next\tktl_test_interval_gobtoend
		\fi
	\fi
	\tktl_do_next
}
\def\tktl_test_interval_gobtoend#1\end_parse,{\tktl_exec_first}

%---------------------------------------------------------------------
%--------------------- Macro \tktl_encode_tokens ---------------------
%---------------------------------------------------------------------
% Utilisation :
%    \tktl_encode_tokens{#1} où #1 est un texte équilibré en tokens de catcodes 1 et 2
%
% Retours :
%  1) le registre de tokens \tktl_enctoks_toks contient la liste des tokens
%          constituant #1 sous la forme
%     (<x>:<y>) où:
%       - x est le charcode du token ou le token lui-même si séquence de contrôle
%       - y est le catcode du token ou 16 si séquence de contrôle
%  2) le compteur \tktl_enctoks_len_cnt contient le nombre de tokens
%---------------------------------------------------------------------
\newtoks\tktl_enctoks_toks
\newcount\tktl_enctoks_len_cnt
\long\def\tktl_encode_tokens#1{%
	\tktl_enctoks_len_cnt 0
	\let\tktl_list_specials\empty
	\let\tktl_do_with_specials\tktl_scan_special
	\let\tktl_do_with_normals\tktl_gob_tok_and_loop
	% Première passe
	%   tous charcodes des tokens spéciaux (catcode 1, 2, 6 et 10) sont mis dans \tktl_list_specials.
	%   Les autres tokens (les "normaux") sont ignorés
	\tktl_encode_tokens_encode#1\end_parse
	% Avant la 2e passe :
	%   On cherche à écarter les caractères actifs qui ont été lus comme tokens
	%   spéciaux (car ils sont \let-égaux à un token spécial)
	%   Pour ce faire, on modifie via \uccode et \uppercase tous les tokens contenus dans \tktl_list_specials.
	%   Seuls les caractères actifs seront ainsi modifiés, ils sont neutralisés et rendus égaux à
	%   \active_quark, ce qui leur enlèvera leur statut de spécial
	\tktl_active_specials
	% Deuxième passe
	%   les caractères actifs ayant été reprogrammés, seuls les charcodes des _vrais_
	%   tokens spéciaux seront mis dans \tktl_list_specials
	\let\tktl_list_specials\empty
	\tktl_encode_tokens_encode#1\end_parse
	% Troisième passe
	\tktl_enctoks_toks{}%
	\let\tktl_do_with_specials\tktl_encode_specials
	\let\tktl_do_with_normals\tktl_encode_normal
	\tktl_encode_tokens_encode#1\end_parse
}
\long\def\tktl_encode_tokens_encode{%
	\futurelet\tktl_nxt_tok\tktl_encode_tokens_encode_i
}
\def\tktl_encode_tokens_encode_i{%
	\ifx\tktl_nxt_tok\end_parse
		\tktl_ok_this_test\tktl_encode_gob_to_end
	\fi% fin de parsing ?
	% les tokens "#", " ", "{" et "}" sont considérés spéciaux
	\ifcat##\noexpand\tktl_nxt_tok
		\tktl_ok_this_test{%
			\def\tok_catcode{6}%
			\tktl_do_with_specials
		}%
	\fi
	\ifcat\tktl_sp_token\noexpand\tktl_nxt_tok
		\tktl_ok_this_test{%
			\def\tok_catcode{10}%
			\tktl_do_with_specials
		}%
	\fi
	\ifcat\tktl_bg_token\noexpand\tktl_nxt_tok
		\tktl_ok_this_test{%
			\def\tok_catcode{1}%
			\tktl_do_with_specials
		}%
	\fi
	\ifcat\tktl_eg_token\noexpand\tktl_nxt_tok
		\tktl_ok_this_test{%
			\def\tok_catcode{2}%
			\tktl_do_with_specials
		}
	\fi
	\tktl_else_all_tests\tktl_do_with_normals
}
\def\tktl_encode_gob_to_end\end_parse{}% plus sûr que "\let\tktl_encode_gob_to_end\tktl_gob_arg"
\def\tktl_scan_special{%
	\begingroup
		\escapechar\if\tktl_nxt_tok\string @`A\else`@\fi\relax% choisir le bon caractère d'échappement
		\expandafter\tktl_scan_special_i\string% pour le token qui suit. Si c'est une macro, seuls les espaces dans son nom seront comptabilisés en spéciaux
}
\def\tktl_scan_special_i{%
		\futurelet\tktl_nxt_tok\tktl_scan_special_ii
}
\def\tktl_scan_special_ii{%
		\ifcat\tktl_nxt_tok\tktl_sp_token
			\endgroup
			\tktl_addto_special_list{32}% tous les catcode 10 ont un charcode de 32
			\tktl_antefi\tktl_gob_tok_and_loop
		\else
			\tktl_antefi\tktl_scan_special_iii
		\fi
}
\long\def\tktl_scan_special_iii#1{% traite les autres cas spéciaux
		\def\tktl__tmp{%
	\endgroup
	\tktl_addto_special_list}%
	\expandafter\tktl__tmp\expandafter{\number\tktl_ifnum{\escapechar=`#1 }{-1}{`#1} }% si macro : -1 (et eventuellement des 32 si son nom contient des espaces)
	\tktl_encode_tokens_encode
}
\def\tktl_active_specials{%
	\expandafter\tktl_active_specials_i\tktl_list_specials\active_quark,%
}
\begingroup
	\catcode0 13
	\gdef\tktl_active_specials_i#1,{%
		\ifx\active_quark#1\else
			\unless\ifnum#1<0
				\uccode0 #1
				\uppercase{\let^^00\active_quark}%
			\fi
			\tktl_antefi\tktl_active_specials_i
		\fi
	}
\endgroup
\def\tktl_gob_tok_and_loop{%
	\afterassignment\tktl_encode_tokens_encode
	\let\tktl_nxt_tok=
}
\def\tktl_read_special_list{%
	\expandafter\tktl_read_special_list_i\tktl_list_specials\tktl_read_special_list
}
\def\tktl_read_special_list_i#1,#2\tktl_read_special_list{%
	\def\tktl_current_charcode{#1}%
	\def\tktl_list_specials{#2}%
}
\def\tktl_encode_specials{%
	\tktl_read_special_list
	\ifnum\tktl_current_charcode=-1
		\tktl_antefi\tktl_encode_macro
	\else
		\expandafter\tktl_add_token\expanded{{\unexpanded\expandafter{\tktl_current_charcode}}{\tok_catcode}}%
		\tktl_antefi\tktl_gob_tok_and_loop
	\fi
}
\long\def\tktl_encode_macro#1{%
	\tktl_clean_special_list#1% enlève les "32" de la liste qui correspondent aux espaces dans les noms des macros
	\tktl_add_token{#1}{16}%
	\tktl_encode_tokens_encode
}
\def\tktl_clean_special_list#1{%
	\expandafter\tktl_clean_special_list_i\string#1 \end_parse
}
\def\tktl_clean_special_list_i#1 {%
	\futurelet\tktl_nxt_tok\tktl_clean_special_list_ii
}
\def\tktl_clean_special_list_ii{%
	\ifx\tktl_nxt_tok\end_parse
		\tktl_antefi\tktl_encode_gob_to_end
	\else
		\tktl_read_special_list
		\tktl_antefi\tktl_clean_special_list_i
	\fi
}
\long\def\tktl_encode_normal#1{%
	\ifcat\relax\noexpand#1%
		\tktl_add_token{#1}{16}%
	\else
		\expandafter\tktl_add_token\expanded{{\number`#1}{\number\tktl_thecatcode#1 }}%
	\fi
	\tktl_encode_tokens_encode
}
\long\def\tktl_add_token#1#2{%
	\advance\tktl_enctoks_len_cnt1
	\tktl_add_to_parsetoks{(#1:#2)}%
}
% La macro \tktl_thecatcode{<token non spécial>} est purement développable
% et renvoit le catcode de <token>
\begingroup
	\edef\tktl_dotcatcode{\catcode\number`\.=}
	\tktl_dotcatcode3  \gdef\tktl_cat_C{.}
	\tktl_dotcatcode4  \gdef\tktl_cat_D{.}
	\tktl_dotcatcode7  \gdef\tktl_cat_G{.}
	\tktl_dotcatcode8  \gdef\tktl_cat_H{.}
	\tktl_dotcatcode11 \gdef\tktl_cat_K{.}
	\tktl_dotcatcode12 \gdef\tktl_cat_L{.}
	\tktl_dotcatcode13 \gdef\tktl_cat_M{.}
\endgroup
\def\tktl_thecatcode#1{% n'envisager les tokens non spéciaux (qui eux, ont un catcode de 1, 2, 6 ou 10)
	\ifcat\tktl_cat_K\noexpand#1\tktl_ok_this_test{11}\fi
	\ifcat\tktl_cat_L\noexpand#1\tktl_ok_this_test{12}\fi
	\ifcat\tktl_cat_C\noexpand#1\tktl_ok_this_test3\fi
	\ifcat\tktl_cat_D\noexpand#1\tktl_ok_this_test4\fi
	\ifcat\tktl_cat_G\noexpand#1\tktl_ok_this_test7\fi
	\ifcat\tktl_cat_H\noexpand#1\tktl_ok_this_test8\fi
	\ifcat\expandafter\noexpand\tktl_cat_M\noexpand#1\tktl_ok_this_test{13}\fi
	\tktl_else_all_tests{%
		\errmessage{This error in \string\tktl_thecatcode\space should not occur with token "\detokenize{#1}"}%
	}%
}

%---------------------------------------------------------------------
%--------------------- Macro \tktl_decode_enctoks --------------------
%---------------------------------------------------------------------
% Utilisation :
%    \tktl_decode_enctoks{#1}{#2} où #1 est une liste de tokens codés sous la forme et #2 une consigne d'assignation : #2{<résultat>}
%     (<x>:<y>) où:
%       - x est le charcode du token ou le token lui-même si séquence de contrôle
%       - y est le catcode du token ou 0 si séquence de contrôle
% Retour :
%    L'assignation demandée via #2 contient les tokens
%    Si les tokens de catcode 1 et 2 ne sont pas équilibrés dans #1 -> erreur
%---------------------------------------------------------------------
\newcount\tktl_openbrace_cnt
\long\def\tktl_decode_enctoks#1#2{% #1=(a:b)(c:d)...(x:y)   #2=assignation du résultat
	\begingroup
		\global\tktl_openbrace_cnt0
		\tktl_enctoks_toks{}%
		\tktl_decode_enctoks_a#1(-1:-1)%
		\def\tktl__tmp{%
	\endgroup
	#2}%
	\expandafter\tktl__tmp\expandafter{\unexpanded\expandafter{\the\tktl_enctoks_toks}}%
}
\long\def\tktl_decode_enctoks_a(#1:#2){%  #1=charcode ou macro si #2=0 ; #2=catcode ; #3=reliquat
	\ifnum#2=-1
		\tktl_ok_this_test{%
			\tktl_ifnum{\tktl_openbrace_cnt>0 }
				{% trop d'accolades ouvrantes
				\errmessage{Unbalanced open-group token, \the\tktl_openbrace_cnt\space close-group token added}%
				\expandafter\tktl_decode_enctoks_b
				}
				{}%
			\tktl_gob_arg
			}%
	\fi
	\ifnum#2=16
		\tktl_ok_this_test{%
			\tktl_add_to_parsetoks{#1}%
			}%
	\fi
	\ifnum#2=1
		\tktl_ok_this_test{%
			\global\advance\tktl_openbrace_cnt1
			\begingroup
				\tktl_enctoks_toks{}%
				\lccode`\{ #1
			}%
	\fi
	\ifnum#2=2
		\tktl_ok_this_test{%
			\tktl_ifnum{\tktl_openbrace_cnt<1 }
				{%
				\errmessage{Unbalanced close-group token ignored}%
				}
				{%
				\global\advance\tktl_openbrace_cnt-1
				\lccode`\} #1
				\lowercase{\toks0{\expandafter{\the\tktl_enctoks_toks}}}%
				\tktl_enctoks_toks\expandafter\expandafter\expandafter{\the\toks0}%
				\expandafter
			\endgroup
			\expandafter\tktl_add_to_parsetoks\expandafter{\the\tktl_enctoks_toks}%
				}%
			}%
		\fi
		\ifnum#2=6
			\tktl_ok_this_test{%
			\begingroup
				\lccode`\# #1
				\lowercase{%
			\endgroup
			\tktl_add_to_parsetoks{##}}%
			}%
		\fi
	\tktl_else_all_tests{%
		\begingroup
				\endlinechar-1
				\everyeof{\noexpand}%
				\scantokens{%
					\catcode`\* #2
					\lccode `\* #1
					\catcode`\_ 11
					\lowercase{%
		\endgroup
		\tktl_add_to_parsetoks{*}%
					}%*
				}%
		}%
	\tktl_decode_enctoks_a
}
\def\tktl_decode_enctoks_b{% ajoute accolade(s) fermante(s) manquantes à la fin du décodage si besoin
		\global\advance\tktl_openbrace_cnt-1
		\lowercase\expandafter{\expandafter
		\endgroup
	\expandafter\tktl_add_to_parsetoks\expandafter{\expandafter{\the\tktl_enctoks_toks}}}%
	\ifnum\tktl_openbrace_cnt>0 
		\expandafter\tktl_decode_enctoks_b
	\fi
}
\long\def\tktl_add_to_parsetoks#1{\tktl_enctoks_toks\expandafter{\the\tktl_enctoks_toks#1}}

%---------------------------------------------------------------------
%------------------ Capture d'un <motif élémentaire> -----------------
%---------------------------------------------------------------------
% Entrée : le motif est composé de :
%	- <capture> optionnelle indiquée par \c
%	- <prédicat> optionnel : "!" ou "&"
%	- macro ayant 1 caractère qui spécifie le type de motif :
%		\S{<chaine>} : le prochain token matche avec un des tokens de la <chaine>
%		\s{<chaine>} : les prochains tokens sont les mêmes que ceux de la <chaine>
%		\R{csv de charcodes range "<a>-<b>" ou "<c>" ou "*":csv de catcodes "<n>-<m>" ou "<x>" ou "*"} et si csv de catcodes absent: "*". Macros interdites dans les ranges.
%		\r{csv de chars range "<a>-<b>" ou "<c>":csv de catcodes "<n>-<m>" ou "<n>" ou "*"} et si csv de catcodes absent: "*". Macros interdites dans les ranges.
%		\. : correspond avec tout token (équiv à \R{*:*})
%		{<motifs>} groupe de motifs (correspond à la macro interne \tktl_patterngroup)
%	- nombre de correspondance requis (optionnel):
%		+ : 1 ou + , équivalant à ^{1-}
%		* : 0 ou + , équivalant à ^{0-}
%		? : 0 ou 1 , équivalant à ^{0-1}
%		^{n} ou ^{n-m} : de n (0 si absent) à m (max si absent) inlcus
%		si omis : ^{1}
%	-  <capture> optionnelle : \c qui capturera seulement la position
%
% Renvoie :
%	- \tktl_current_precapture_directive est la capture demandée 0(aucune), 1(\c présent)
%	- \tktl_current_predicate est le prédicat capturé : 0 (aucun) 1 (&) ou -1 (!)
%	- \tktl_current_macro est la macro spécifiant le type de motif (\s, \S, \r, \R ou \.)
%	- \tktl_current_macro_arg est l'argument de la macro (forcément vide pour \.)
%	- \tktl_min_current_repeat est le nombre min de répétitions
%	- \tktl_max_current_repeat est le nombre min de répétitions
%	- \tktl_current_postcapture_directive est la capture demandée 0 (aucune) ou 1 (\c présent)
%---------------------------------------------------------------------
\newcount\tktl_capture_pos_cnt% position d'un token (si on capture la position)
\newcount\tktl_position_saved_cnt% position sauvegardée d'un token (si on capture la position)
\newcount\tktl_capture_pos_index_cnt% numéro de la demande de capture de position
\def\tktl_macro_dot{\.}%
\def\tktl_macro_s{\s}%
\def\tktl_macro_S{\S}%
\def\tktl_macro_r{\r}%
\def\tktl_macro_R{\R}%
\def\tktl_macro_group{\tktl_patterngroup}
\def\tktl_macro_pattern{\tktl_patternname}
\newif\iftktl_allow_captures_% tenir compte des captures par \c ou pas ?

\long\def\tktl_grab_pattern#1{% #1= pattern unique ou {<groupe de motifs>}
	\let\tktl_current_predicate\empty
	\let\tktl_current_macro\empty
	\let\tktl_current_macro_arg\empty
	\let\tktl_current_repeat\empty
	\def\tktl_current_precapture_directive{0}%
	\tktl_grab_pattern_search_precapture#1\_nil
}
\def\tktl_grab_pattern_search_precapture{%
	\tktl_futurelet_nospace\tktl__futurtok\tktl_grab_pattern_search_precapture_a
}
\def\tktl_grab_pattern_search_precapture_a{%
	\ifx\tktl__futurtok\c
		\iftktl_allow_captures_
			\def\tktl_current_precapture_directive{1}%
		\fi
		\tktl_antefi\tktl_grab_pattern_grab_precapture
	\else
		\tktl_antefi\tktl_grab_pattern_search_predicate
	\fi
}
\def\tktl_grab_pattern_grab_precapture#1{% #1=\c
	\tktl_futurelet_nospace\tktl__futurtok\tktl_grab_pattern_search_predicate
}
\def\tktl_grab_pattern_search_predicate{%
	\ifx!\tktl__futurtok
		\tktl_ok_this_test{%
			\def\tktl_current_predicate{-1}%
			\tktl_grab_predicate
		}%
	\fi
	\ifx&\tktl__futurtok
		\tktl_ok_this_test{%
			\def\tktl_current_predicate{1}%
			\tktl_grab_predicate
		}%
	\fi
	\tktl_else_all_tests{%
		\def\tktl_current_predicate{0}%
		\tktl_test_post_predicate
	}%
}
\def\tktl_grab_predicate#1{% #1= ! ou &
	\tktl_futurelet_nospace\tktl__futurtok\tktl_test_post_predicate
}
\def\tktl_test_post_predicate{%
	\ifx\bgroup\tktl__futurtok% si après le prédicat il y a "{"
		\tktl_antefi\tktl_grab_group_of_patterns% prendre le groupe de patterns
	\else
		\tktl_antefi\tktl_grab_pattern_grab_macro% sinon, prendre la macro
	\fi
}
\long\def\tktl_grab_group_of_patterns#1{% #1= ensemble de motifs entre accolades
	\def\tktl_current_macro{\tktl_patterngroup}%
	\def\tktl_current_macro_arg{#1}%
	\tktl_grab_pattern_grab_repeat_char
}
\def\tktl_grab_pattern_grab_macro#1{% #1 = macro 
	\tktl_ifinstr{,#1,}{,\s,\S,\r,\R,\.,}
		{%
		\def\tktl_current_macro{#1}%
		\tktl_ifx{\.#1}
			{%
			\tktl_grab_pattern_grab_repeat_char
			}
			{% aller capturer l'argument de la macro
			\tktl_futurelet_nospace\tktl__futurtok\tktl_grab_pattern_grab_macro_arg
			}%
		}
		{%
		\tktl_ifmacro{#1}
			{% aller voir si c'est une macro-pattern
			\tktl_grab_pattern_grab_macro_pattern#1%
			}
			{%
			\errmessage{Found "\detokenize{#1}" when expecting \string\r, \string\R, \string\s, \string\S\space or \string\.}%
			}
		}%
}
\def\tktl_grab_pattern_grab_macro_pattern#1{% #1=macro donnée par l'utilisateur
	\def\tktl_current_macro{\tktl_patternname}%
	\def\tktl_current_macro_arg{#1}%
	\tktl_grab_pattern_grab_repeat_char
}
\def\tktl_grab_pattern_grab_macro_arg{%
	\ifx\bgroup\tktl__futurtok
		\tktl_antefi\tktl_grab_pattern_grab_macro_arg_a
	\else
		\errmessage{No open brace found after "\detokenize\expandafter{\tktl_current_macro}", aborting pattern}%
		\let\tktl_current_predicate\empty
		\let\tktl_current_macro\empty
		\let\tktl_current_macro_arg\empty
		\let\tktl_current_repeat\empty
		\tktl_antefi\tktl_gob_enctoks_to_end_parse
	\fi
}
\def\tktl_gob_enctoks_to_end_parse#1\end_parse{}
\long\def\tktl_grab_pattern_grab_macro_arg_a#1{% argument de la macro
	\def\tktl_current_macro_arg{#1}%
	\tktl_grab_pattern_grab_repeat_char
}
\def\tktl_grab_pattern_grab_repeat_char{%
	\tktl_futurelet_nospace\tktl__futurtok\tktl_grab_pattern_grab_repeat_char_a
}
\def\tktl_grab_pattern_grab_repeat_char_a{%
	\def\tktl_min_current_repeat{1}%
	\def\tktl_max_current_repeat{1}% 1 répétition a priori
	\ifx*\tktl__futurtok
		\tktl_ok_this_test{%
			\def\tktl_min_current_repeat{0}%
			\let\tktl_max_current_repeat\tktl_maxint_repeat
			\tktl_grab_pattern_gobble_char
		}%
	\fi
	\ifx+\tktl__futurtok
		\tktl_ok_this_test{%
			\def\tktl_min_current_repeat{1}%
			\let\tktl_max_current_repeat\tktl_maxint_repeat
			\tktl_grab_pattern_gobble_char
		}%
	\fi
	\ifx?\tktl__futurtok
		\tktl_ok_this_test{%
			\def\tktl_min_current_repeat{0}%
			\def\tktl_max_current_repeat{1}%
			\tktl_grab_pattern_gobble_char
		}%
	\fi
	\ifx^\tktl__futurtok
		\tktl_ok_this_test\tktl_grab_pattern_grab_repeat_arg
	\fi
	\tktl_else_all_tests\tktl_grab_pattern_search_postcapture
}
\def\tktl_grab_pattern_gobble_char#1{% #1 = + * ? ou ^
	\tktl_grab_pattern_search_postcapture
}
\def\tktl_grab_pattern_grab_repeat_arg^#1{% #1=argument spécifiant le nombre de répétitions
	\tktl_encode_tokensinterval{#1}\tktl_min_current_repeat\tktl_max_current_repeat
	\tktl_grab_pattern_search_postcapture
}
\def\tktl_grab_pattern_search_postcapture{%
	\tktl_futurelet_nospace\tktl__futurtok\tktl_grab_pattern_search_postcapture_a
}
\def\tktl_grab_pattern_search_postcapture_a{%
	\ifx\c\tktl__futurtok
		\iftktl_allow_captures_
			\iftktl_allow_post_pattern_catpure_
				\def\tktl_current_postcapture_directive{1}%
				\let\tktl_do_next\tktl_grab_pattern_grab_postcapture
			\else
				\errmessage{Capture after a pattern not allowed, \string\c\space ignored}%
				\def\tktl_current_postcapture_directive{0}%
				\let\tktl_do_next\tktl_grab_pattern_grab_postcapture
			\fi
		\else
			\def\tktl_current_postcapture_directive{0}%
			\let\tktl_do_next\tktl_grab_pattern_grab_postcapture
		\fi
	\else
		\def\tktl_current_postcapture_directive{0}%
		\let\tktl_do_next\tktl_grab_pattern_remain
	\fi
	\tktl_do_next
}
\def\tktl_grab_pattern_grab_postcapture\c{\tktl_grab_pattern_remain}
\def\tktl_grab_pattern_remain#1\_nil{%
	\tktl_ifempty_or_space{#1}
		{}
		{%
		\errmessage{Unexpected "\detokenize{#1}" found, I ignore it}%
		}%
}
\def\tktl_collect_pattern_remain#1\_nil{%
	\def\tktl_after_pattern{#1}%
}
\newif\iftktl_if_single_pattern_% vrai si pattern unique (faux pour un groupe ou plusieurs patterns)
\def\tktl_grab_pattern_remain_test_single#1\_nil{%
	\ifx\tktl_current_macro\tktl_macro_group
		\tktl_if_single_pattern_false
	\else
		\tktl_ifempty_or_space{#1}
			\tktl_if_single_pattern_true
			\tktl_if_single_pattern_false
	\fi
}

%---------------------------------------------------------------------
%-------------- changement de catcode dans des <tokens> --------------
%---------------------------------------------------------------------
% La macro \tktl_catcode_string{<chaine>} agit sur <chaine> lorsqu'elle
% contient :
%	- \c{<nombre>}{<tokens>} ou \c{<nombre>}<token>
%	- \c<chiffre>{<tokens>}  ou \c<chiffre><token>
% et modifie le catcode des tokens concernés.
% Limitations :
%	1) la macro \c ne peut pas se trouver dans son 2e argument (imbrication impossible)
%	2) le premier argument doit être un catcode valide (1,2,3,4,6,7,8,10,11,12 ou 13)
%	3) si une \macro se trouve dans la <chaine>, le seul catcode qu'elle peut revêtir est 12
%      et dans ce cas, les tokens provenant de \string\macro sont créés.
%      Si le catcode demandé est différent de 12, la macro reste inchangée avec son catcode de 16
%
% Entrée : la <chaine> qui contient 0, 1 ou plusieurs occurences de \c avec ses 2 arguments
% Sortie :
%	- le registre de tokens \tktl_string_toks qui contient les tokens composants la <chaine>
%	  encodés sous la forme (<charcode>:<catcode>)
%	- le compteur \tktl_string_len_cnt contient le nombre de tokens de la <chaine>
%---------------------------------------------------------------------
\newtoks\tktl_string_toks
\newcount\tktl_string_len_cnt
\long\def\tktl_catcode_string#1{% #1=tokens contenant ou pas \c
	\tktl_encode_tokens{#1}%
	\tktl_string_len_cnt=\tktl_enctoks_len_cnt
	\expanded{\noexpand\tktl_ifinstr{(\noexpand\c:16)}{\the\tktl_enctoks_toks}}%
		{% si la chaine de tokens contient "\c"
		\tktl_string_toks{}%
		\expandafter\tktl_catcode_string_a\the\tktl_enctoks_toks(0:-1)%
		}
		{%
		\tktl_string_toks=\tktl_enctoks_toks
		}%
}
\def\tktl_catcode_string_a#1(\c:16)(#2:#3){%
	\advance\tktl_string_len_cnt -1 % retirer le token "\c"
	\tktl_addtotoks\tktl_string_toks{#1}%
	\ifnum#3=1 % \c est suivi de "{"
		\advance\tktl_string_len_cnt -1 % retirer le token "{"
		\let\tktl_catcode_directive\empty
		\tktl_antefi\tktl_catcode_string_b
	\else%\c n'est pas suivi de "{" -> le chiffre #2 est le catcode demandé
		% TODO : vérifier que #3=12 ???
		\edef\tktl_catcode_directive{\tktl_charcode_to_digit{#2}}%
		\tktl_antefi\tktl_catcode_string_c
	\fi
}
\def\tktl_catcode_string_b(#1:#2){% \c est suivi de "{" qu'on a déjà lu
	\advance\tktl_string_len_cnt -1 % retirer le token correspondant au chiffre du catcode ou à "}"
	\ifnum#2=2 % si #1="}" -> fini de lire le catcode demandé
		\expandafter\tktl_ifempty_or_space\expandafter{\tktl_catcode_directive}
			{% vérifier que ce catcode n'est pas vide
			\errmessage{Empty argument found after\string\c}%
			\def\tktl_catcode_directive{11}%
			}
			{% et qu'il n'est pas illégal
			\expandafter\tktl_ifinstr\expandafter{\expandafter,\tktl_catcode_directive,}{,1,2,3,4,6,7,8,10,11,12,13,}
				{}
				{%
				\errmessage{Illegal catcode of \tktl_catcode_directive\space in argument of\string\c}%
				\def\tktl_catcode_directive{11}%
				}%
			}%
		\tktl_antefi\tktl_catcode_string_c
	\else% pas d'accolade ouvrante -> on ajoute le chiffre et on recommence
		\edef\tktl_catcode_directive{\tktl_catcode_directive\tktl_charcode_to_digit{#1}}%
		\tktl_antefi\tktl_catcode_string_b
	\fi
}
\newtoks\tktl_string_collect_toks
\def\tktl_catcode_string_c(#1:#2){% \c<catcode> ou \c{<catcode>} a été lu et le <catocde> est dans \tktl_catcode_directive
	\ifnum#2=1% si c'est suivi d'une accolade ouvrante
		\def\tktl_bg_number{0}% nombre d'accolades ouvrante rencontrées
		\tktl_string_collect_toks{}%
		\advance\tktl_string_len_cnt -1 % retirer le token "{"
		\expandafter\tktl_catcode_string_e
	\else% n'est pas suivi d'une "{"
		\ifnum#2=16  % si #1 est une macro
			\ifnum\tktl_catcode_directive=12 % à détokéniser ?
				\begingroup
					\expandafter\tktl_encode_tokens\expandafter{\string#1}%
					\expanded{%
				\endgroup
				\noexpand\tktl_addtotoks\noexpand\tktl_string_toks{\the\tktl_enctoks_toks}%
				\advance\noexpand\tktl_string_len_cnt\the\tktl_enctoks_len_cnt\relax% ajoute les tokens provenant de \string#1
				}%
			\else
				\tktl_addtotoks\tktl_string_toks{(#1:16)}%
			\fi
		\else
			\tktl_addtotoks\tktl_string_toks{(#1:}%
			\tktl_eaddtotoks\tktl_string_toks{\tktl_catcode_directive)}%
		\fi
		\tktl_antefi\tktl_catcode_string_d
	\fi
}
\def\tktl_catcode_string_d#1(0:-1){%
	\tktl_ifinstr{(\c:16)}{#1}
		{%
		\tktl_catcode_string_a#1(0:-1)%
		}
		{%
		\tktl_addtotoks\tktl_string_toks{#1}%
		}%
}
\def\tktl_catcode_string_e(#1:#2){% \c\<catcode> était suivi d'une accolade
	\let\tktl_do_next\tktl_catcode_string_e% on reboucle à priori
	\ifnum#2=2 % si accolade fermante
		\ifnum\tktl_bg_number=0 % et équilibrage en accolades -> fin de l'argument
			\advance\tktl_string_len_cnt -1 % retirer le token "}"
			\tktl_eaddtotoks\tktl_string_toks{\the\tktl_string_collect_toks}%
			\let\tktl_do_next\tktl_catcode_string_d
		\else% sinon, ajouter l'accolade fermante avec le catcode demandé
			\tktl_addtotoks\tktl_string_collect_toks{(#1:}%
			\tktl_eaddtotoks\tktl_string_collect_toks{\tktl_catcode_directive)}%
			\edef\tktl_bg_number{\the\numexpr\tktl_bg_number-1}% et décrémenter
		\fi
	\else
		\ifnum#2=1  % si accolade ouvrante
			\edef\tktl_bg_number{\the\numexpr\tktl_bg_number-1}% incrémenter
			\tktl_addtotoks\tktl_string_collect_toks{(#1:}%
			\tktl_eaddtotoks\tktl_string_collect_toks{\tktl_catcode_directive)}%
		\else
			\ifnum#2=16  % si macro
				\ifnum\tktl_catcode_directive=12 % à détokéniser ?
					\begingroup
						\expandafter\tktl_encode_tokens\expandafter{\string#1}%
						\advance\tktl_enctoks_len_cnt -1 % pour compenser le token que représente la macro #1 elle-même
						\expanded{%
					\endgroup
					\noexpand\tktl_addtotoks\noexpand\tktl_string_collect_toks{\the\tktl_enctoks_toks}%
					\advance\noexpand\tktl_string_len_cnt\the\tktl_enctoks_len_cnt\relax% ajoute les tokens provenant de \string#1
					}%
				\else
					\tktl_addtotoks\tktl_string_collect_toks{(#1:}%
					\tktl_addtotoks\tktl_string_collect_toks{16)}%
				\fi
			\else
				\tktl_addtotoks\tktl_string_collect_toks{(#1:}%
				\tktl_eaddtotoks\tktl_string_collect_toks{\tktl_catcode_directive)}%
			\fi
		\fi
	\fi
	\tktl_do_next
}
\def\tktl_charcode_to_digit#1{%
	\ifcase\numexpr#1-`\0\relax
		0\or1\or2\or3\or4\or5\or6\or7\or8\or9%
	\else
		\errmessage{Catcode directive not a number}%
	\fi
}

%---------------------------------------------------------------------
%--- Test si un token matche les intervalles de charcode et catcode --
%---------------------------------------------------------------------
% teste si les entiers #1 et #2 sont dans les intervalles #3 et #4.
\long\def\tktl_iftokmatch(#1:#2)#3#4{% #1=tok_charcode #2=tok_catcode #3=interval_charcode #4=interval_catcode
	\tktl_ifnum{#2=16 }% si le token est une macro
		{%
		\tktl_test_interval{#2}{#4}%
			{% 16 est dans l'intervalle des catcodes
			\def\tktl_temp_macro_charcode_interval{#3}%
			\tktl_ifx{\tktl_temp_macro_charcode_interval\tktl_star}
				{% si interval charcode = "*" -> renvoyer vrai
				\tktl_exec_first
				}
				{%
				\tktl_ifinstr{#1}{#3}% la macro #1 est-elle dans la liste de macros #3 ?
				}%
			}
			{%
			\tktl_exec_second
			}%
		}
		{%
		\tktl_test_interval{#1}{#3}
			{% charcode correspond
			\tktl_ifempty_or_space{#4}
				{% si catcode non spécifié -> wildcard
				\tktl_exec_first
				}
				{% si catcode spécifié
				\tktl_test_interval{#2}{#4}%
				}%
			}
			{% mauvais charcode -> renvoyer faux
			\tktl_exec_second
			}%
		}%
}
%---------------------------------------------------------------------
%----------------------- Compilation des motifs ----------------------
%---------------------------------------------------------------------
\newtoks\tktl_pattern_compile_collect
\def\tktl_compile_patterns{% compile les patterns avec les opérateurs "|" et ":"
	\begingroup
	\tktl_pattern_compile_collect{}%
	\let\tktl_grab_pattern_remain\tktl_collect_pattern_remain
	\tktl_compile_patterns_a
}
\def\tktl_compile_patterns_a#1{%
	\tktl_ifempty_or_space{#1}
		{%
		\expanded{%
		\endgroup
		\noexpand\tktl_pattern_compile_collect{\the\tktl_pattern_compile_collect}}%
		}
		{%
		\tktl_compile_single_pattern{#1}%
		\expandafter\tktl_ifempty_or_space\expandafter{\tktl_after_pattern}%
			{%
			\expandafter\tktl_compile_patterns_a\expandafter{\tktl_after_pattern}%
			}
			{%
			\expandafter\tktl_futurelet_nospace\expandafter\tktl__futurtok\expandafter\tktl_compile_patterns_b\tktl_after_pattern\_nil
			}%
		}%
}
\def\tktl_compile_patterns_b{%
	\ifx|\tktl__futurtok
		\tktl_ok_this_test\tktl_compile_patterns_c
	\fi
	\ifx:\tktl__futurtok
		\tktl_ok_this_test\tktl_compile_patterns_c
	\fi
	\tktl_else_all_tests\tktl_compile_patterns_d% erreur
}
\def\tktl_compile_patterns_c#1#2\_nil{% #1= | ou :   #2=reste
	\tktl_addtotoks\tktl_pattern_compile_collect{#1}%
	\tktl_compile_patterns_a{#2}%
}
\def\tktl_compile_patterns_d#1\_nil{%
	\errmessage{Something unexpected in pattern here "\detokenize{#1}"}%
}
\def\tktl_compile_single_pattern#1{% #1=<1-pattern>
	\tktl_grab_pattern{#1}%
	\edef\tktl_five_args{{\tktl_current_precapture_directive}{\tktl_current_predicate}{\tktl_min_current_repeat}{\tktl_max_current_repeat}{\tktl_current_postcapture_directive}}%
	\csname tktl_compile_pattern_\expandafter\string\tktl_current_macro\endcsname
}
\def\tktl_define_Rr_test#1#2{%
	\long\def\tktl_token_test_R_or_S(##1:##2){\tktl_iftokmatch(##1:##2){#1}{#2}}%
}
\def\tktl_define_S_test#1{%
	\long\def\tktl_token_test_R_or_S(##1:##2){\tktl_ifinstr{(##1:##2)}{#1}}%
}
\tktl_csdef{tktl_compile_pattern_\expandafter\string\tktl_macro_R}{%
	\expandafter\tktl_analyse_R_pattern\expandafter{\tktl_current_macro_arg}% séparer csv charcode et csv catcode
	\tktl_xaddtotoks\tktl_pattern_compile_collect{%
		\tktl_five_args
		\noexpand\tktl_define_Rr_test{\tktl_charcode_interval}{\tktl_catcode_interval}%
		\noexpand\tktl_test_pattern_R_r_S%
	}%
}
\tktl_csdef{tktl_compile_pattern_\expandafter\string\tktl_macro_r}{%
	\expandafter\tktl_analyse_R_pattern\expandafter{\tktl_current_macro_arg}% séparer csv charcode et csv catcode
	\expandafter\tktl_convert_charcsv_to_charcodecsv\expandafter{\tktl_charcode_interval}%
	\tktl_xaddtotoks\tktl_pattern_compile_collect{%
		\tktl_five_args
		\noexpand\tktl_define_Rr_test{\tktl_charcode_interval}{\tktl_catcode_interval}%
		\noexpand\tktl_test_pattern_R_r_S%
	}%
}
\tktl_csdef{tktl_compile_pattern_\expandafter\string\tktl_macro_S}{%
	\expandafter\tktl_catcode_string\expandafter{\tktl_current_macro_arg}% prendre en compte les \c{<catcode>}{texte} dans l'argument de \S
	\tktl_xaddtotoks\tktl_pattern_compile_collect{%
		\tktl_five_args
		\noexpand\tktl_define_S_test{\the\tktl_string_toks}%
		\noexpand\tktl_test_pattern_R_r_S%
	}%
}
\def\tktl_test_pattern_R_r_S{%
	\expandafter\tktl_test_match_R_or_S\tktl_postmatch_enctokens(-1:-1)%
	\tktl_test_match_pattern_a
}
\tktl_csdef{tktl_compile_pattern_\expandafter\string\tktl_macro_s}{%
	\expandafter\tktl_catcode_string\expandafter{\tktl_current_macro_arg}% prendre en compte les \c{<catcode>}{texte} dans l'argument de \s
	\tktl_xaddtotoks\tktl_pattern_compile_collect{%
			\tktl_five_args
			\noexpand\tktl_test_pattern_s{\the\tktl_string_toks}%
		}%
}
\def\tktl_test_pattern_s#1{%
	\def\tktl__tmp{\tktl_test_match_s{#1}}\expandafter\tktl__tmp\expandafter{\tktl_postmatch_enctokens}%
	\tktl_test_match_pattern_a
}
\tktl_csdef{tktl_compile_pattern_\expandafter\string\tktl_macro_dot}{%
	\tktl_eaddtotoks\tktl_pattern_compile_collect{%
		\tktl_five_args
		\tktl_test_pattern_dot
	}%
}
\def\tktl_test_pattern_dot{%
	\expandafter\tktl_test_match_dot\tktl_postmatch_enctokens(-1:-1)%
	\tktl_test_match_pattern_a
}
\tktl_csdef{tktl_compile_pattern_\expandafter\string\tktl_macro_group}{%
	\begingroup
		\expandafter\tktl_compile_patterns\expandafter{\tktl_current_macro_arg}%
		\expanded{%
	\endgroup
		\noexpand\tktl_addtotoks\noexpand\tktl_pattern_compile_collect{%
				\tktl_five_args
				\noexpand\tktl_test_pattern_group{\the\tktl_pattern_compile_collect}%
			}%
	}%
}
\def\tktl_test_pattern_group#1{%
	\begingroup
		\tktl_repeat_match_cnt 0
		\iftktl_in_capture_
			\tktl_in_patterngroup_true
		\else% si pas de capture en cours -> captures permises à l'intérieur du groupe
			\tktl_in_patterngroup_false
		\fi
		\let\tktl_match_tokens_patterngroup\empty
		\def\tktl_current_macro_arg{#1}%
		\tktl_test_match_patterngroup%
		\expanded{%
	\endgroup
	\iftktl_match_success_
		\tktl_unexpand_earg\def\tktl_postmatch_enctokens{\tktl_postmatch_enctokens}%
		\tktl_unexpand_earg\def\tktl_match_tokens{\tktl_match_tokens_patterngroup}%
		\tktl_unexpand_earg\def\tktl_capture_pos_list{\tktl_capture_pos_list}%
		\tktl_unexpand_earg\def\tktl_capture_tok_list{\tktl_capture_tok_list}%
		\noexpand\tktl_capture_pos_cnt=\the\tktl_capture_pos_cnt\relax
		\noexpand\tktl_capture_pos_index_cnt=\the\tktl_capture_pos_index_cnt\relax
		\noexpand\tktl_capture_tok_index_cnt=\the\tktl_capture_tok_index_cnt\relax
		\noexpand\tktl_match_success_true
	\else
		\noexpand\tktl_match_success_false
	\fi
	}%
	\tktl_test_match_pattern_a
}
\def\tktl_test_macropattern_group#1{%
	\def\tktl__tmp{\tktl_ifinstr{#1}}\expandafter\tktl__tmp\expandafter{\tktl_list_of_patterns}%
		{%
		\expandafter\expandafter\expandafter\tktl_test_pattern_group\expandafter\expandafter\expandafter{\csname tktl_pattern_\string#1\endcsname}%
		}
		{%
		\errmessage{Marker "\string#1" is not a defined pattern, using "\string\." instead}%
		\tktl_test_pattern_dot
		}%
}
\long\def\tktl_read_compiled_pattern#1#2#3#4#5{%
	\iftktl_allow_captures_
		\def\tktl_current_precapture_directive{#1}%
		\iftktl_allow_post_pattern_catpure_
			\def\tktl_current_postcapture_directive{#5}%
		\else
			\def\tktl_current_postcapture_directive{0}%
		\fi
	\else
		\def\tktl_current_precapture_directive{0}%
		\def\tktl_current_postcapture_directive{0}%
	\fi
	\def\tktl_current_predicate{#2}%
	\def\tktl_min_current_repeat{#3}%
	\def\tktl_max_current_repeat{#4}%
	\ifnum#1=1 % si capture de tokens demandée
		\iftktl_in_capture_ % si déjà en train de capturer
			\errmessage{Ignoring nested capture of tokens}%
		\else
			\advance\tktl_capture_tok_index_cnt 1
			\tktl_in_capture_true
		\fi
	\fi
	\tktl_repeat_match_cnt=0
	% --> il reste à lire <instruction> qui est après \tktl_five_args
}

%---------------------------------------------------------------------
%----- Teste si un motif matche avec le début des tokens encodés -----
%---------------------------------------------------------------------
% La macro principale du package :
% 			\tktl_test_match_pattern{<pattern>}{<tokens>}
% Entrée :
%	- le pattern est de la forme
%		<prédicat><macro de pattern>{<tokens>}<occurences>
%	- les <tokens> sont encodés de la forme (charcode:catcode)
% Sortie :
%	1) \iftktl_match_success_ booléen s'il y a correspondance
%	2) les tokens encodés \tktl_match_tokens contiennent la partie du
%	   début des <tokens> qui a matché
%	3) \tktl_postmatch_enctokens contient les tokens encodés qui
%	   restent après la partie qui a matché
%---------------------------------------------------------------------
\newif\iftktl_match_success_
\newif\iftktl_in_capture_ % vrai quand une capture de tokens est en cours
\newif\iftktl_in_patterngroup_
\newcount\tktl_capture_tok_index_cnt % numérote les capture de tokens (non imbriquées)
\newcount\tktl_repeat_match_cnt
\long\def\tktl_test_match_pattern#1#2{% #1=pattern compilé "#1#2#3#4#5<instruction>"  #2=tokens encodés (charcode:catcode)
	\def\tktl_postmatch_enctokens{#2}%
	\let\tktl_match_tokens\empty
	\let\tktl_postmatch_enctokens_saved\tktl_postmatch_enctokens% sauvegarde en cas de prédicat ou de non match
	\tktl_position_saved_cnt=\tktl_capture_pos_cnt
	\let\tktl_capture_pos_list_saved\tktl_capture_pos_list
	\let\tktl_capture_tok_list_saved\tktl_capture_tok_list
	\tktl_read_compiled_pattern#1% #1=pattern compilé
}
\tktl_csdef{tktl_compile_pattern_\expandafter\string\tktl_macro_pattern}{%
	\ifcsname tktl_pattern_\expandafter\string\tktl_current_macro_arg\endcsname
		\tktl_xaddtotoks\tktl_pattern_compile_collect{%
			\tktl_five_args
			\noexpand\tktl_test_pattern_group{\unexpanded\expandafter\expandafter\expandafter{\csname tktl_pattern_\expandafter\string\tktl_current_macro_arg\endcsname}}%
		}%
	\else
		\tktl_xaddtotoks\tktl_pattern_compile_collect{%
			\tktl_five_args\noexpand\tktl_test_macropattern_group\expandafter\noexpand\tktl_current_macro_arg
		}%
	\fi
}
\def\tktl_test_match_pattern_a{%
	\unless\ifnum\tktl_current_predicate=0 % si le motif est un prédicat
		\tktl_capture_pos_cnt=\tktl_position_saved_cnt
		\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_saved% ne rien consommer
		\let\tktl_match_tokens\empty% aucune correspondance
		\ifnum\tktl_current_predicate=-1 % si prédicat "!"
			\iftktl_match_success_% inverser le booléen
				\tktl_match_success_false
			\else
				\tktl_match_success_true
			\fi
		\fi
	\fi
	\ifnum\tktl_current_precapture_directive=1
		\advance\tktl_capture_pos_index_cnt 1
		\edef\tktl_capture_pos_list{\tktl_capture_pos_list<\the\tktl_capture_pos_index_cnt=\the\tktl_position_saved_cnt>}%
	\fi
	\ifnum\tktl_current_postcapture_directive=1
		\advance\tktl_capture_pos_index_cnt 1
		\edef\tktl_capture_pos_list{\tktl_capture_pos_list<\the\tktl_capture_pos_index_cnt=\the\tktl_capture_pos_cnt>}%
	\fi
	\iftktl_in_capture_\unless\iftktl_in_patterngroup_ % si en cours de capture et hors d'un groupe
		\tktl_in_capture_false % fin de la capture de tokens
		\tktl_eaddtomacro\tktl_capture_tok_list{\expanded{<\the\tktl_capture_tok_index_cnt=\unexpanded\expandafter{\tktl_match_tokens}}>}%
	\fi\fi
}
\long\def\tktl_gob_enctoks_to_end#1(-1:-1){}

\def\tktl_test_match_patterngroup{%
	\ifx\tktl_postmatch_enctokens\empty% fin atteinte ?
		\let\tktl_do_next\empty% on sort de toutes façons
		\expandafter\tktl_test_match_full_patterns\expanded{{\unexpanded\expandafter{\tktl_current_macro_arg}}{\unexpanded\expandafter{\tktl_postmatch_enctokens}}}%
		\iftktl_match_success_% si correspondance -> motif epsilon a matché
			\advance\tktl_repeat_match_cnt 1
		\fi
		\ifnum\tktl_repeat_match_cnt<\tktl_min_current_repeat\relax% si nb répétition mini non atteint
			\tktl_match_success_false
			\tktl_capture_pos_cnt=\tktl_position_saved_cnt
			\let\tktl_capture_pos_list\tktl_capture_pos_list_saved
			\let\tktl_capture_tok_list\tktl_capture_tok_list_saved
			\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_saved% ne rien consommer
		\else
			\tktl_match_success_true
			\let\tktl_postmatch_enctokens\empty
		\fi
	\else
		\expandafter\tktl_test_match_full_patterns\expanded{{\unexpanded\expandafter{\tktl_current_macro_arg}}{\unexpanded\expandafter{\tktl_postmatch_enctokens}}}%
		\iftktl_match_success_% si correspondance
			\advance\tktl_repeat_match_cnt 1
			\tktl_eaddtomacro\tktl_match_tokens_patterngroup\tktl_match_full_patterns
			\ifnum\tktl_repeat_match_cnt<\tktl_min_current_repeat\relax% si nb répétition mini non atteint
				\let\tktl_do_next\tktl_test_match_patterngroup
			\else
				\ifnum\tktl_repeat_match_cnt=\tktl_max_current_repeat\relax% max atteint
					\tktl_match_success_true
					\let\tktl_do_next\empty% on sort
				\else
					\let\tktl_do_next\tktl_test_match_patterngroup
				\fi
			\fi
		\else% si pas correspondance
			\let\tktl_do_next\empty% on sort
			\ifnum\numexpr(\tktl_repeat_match_cnt-\tktl_min_current_repeat)*(\tktl_repeat_match_cnt-\tktl_max_current_repeat)>0 % si le nombre de répétition n'est pas atteint
				\tktl_match_success_false
				\let\tktl_match_tokens_patterngroup\empty
				\tktl_capture_pos_cnt=\tktl_position_saved_cnt
				\let\tktl_capture_pos_list\tktl_capture_pos_list_saved
				\let\tktl_capture_tok_list\tktl_capture_tok_list_saved
				\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_saved% ne rien consommer
			\else% nombre de répétition atteint
				\tktl_match_success_true
			\fi
		\fi
	\fi
	\tktl_do_next
}

\long\def\tktl_test_match_dot(#1:#2){% #1=token courant forme (charcode,catcode)
	\ifnum#2=-1 % fin atteinte?
		\let\tktl_do_next\empty
		\ifnum\tktl_repeat_match_cnt<\tktl_min_current_repeat\relax% si nb répétition mini non atteint
			\tktl_match_success_false
			\tktl_capture_pos_cnt=\tktl_position_saved_cnt
			\let\tktl_capture_pos_list\tktl_capture_pos_list_saved
			\let\tktl_capture_tok_list\tktl_capture_tok_list_saved
			\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_saved% ne rien consommer
		\else
			\tktl_match_success_true
			\let\tktl_postmatch_enctokens\empty
		\fi
	\else
		\advance\tktl_repeat_match_cnt 1
		\advance\tktl_capture_pos_cnt1
		\tktl_addtomacro\tktl_match_tokens{(#1:#2)}%
		\ifnum\tktl_repeat_match_cnt<\tktl_min_current_repeat\relax% si nb répétition mini non atteint
			\let\tktl_do_next\tktl_test_match_dot
		\else
			\ifnum\tktl_repeat_match_cnt=\tktl_max_current_repeat\relax% max atteint
				\tktl_match_success_true
				\def\tktl_do_next{\tktl_test_match_assign_remaining_tokens{\def\tktl_postmatch_enctokens}}%
			\else
				\let\tktl_do_next\tktl_test_match_dot
			\fi
		\fi
	\fi
	\tktl_do_next
}
\long\def\tktl_test_match_assign_remaining_tokens#1#2(-1:-1){% #1=consigne de stockage #2=tokens restants
	#1{#2}%
}

\long\def\tktl_test_match_s#1#2{% #1=motif forme (charcode,catcode) #2=texte forme (charcode,catcode)
	\def\tktl_test_match_s_a##1#1##2\_nil{%
		\tktl_ifempty_or_space{##1}
			{% si le texte commence par le motif
			\advance\tktl_capture_pos_cnt\tktl_string_len_cnt
			\tktl_addtomacro\tktl_match_tokens{#1}%
			\advance\tktl_repeat_match_cnt 1
			\edef\tktl_postmatch_enctokens{\tktl_test_match_s_b##2\_nil}% prendre ce qui est après le motif
			\ifnum\tktl_repeat_match_cnt<\tktl_min_current_repeat\relax% si nb répétition mini non atteint
				\def\tktl_do_next{\expandafter\tktl_test_match_s_a\tktl_postmatch_enctokens#1\_nil}% recommencer
			\else% mini de répétitions atteint
				\ifnum\tktl_repeat_match_cnt=\tktl_max_current_repeat\relax% max atteint
					\tktl_match_success_true
					\let\tktl_do_next\empty
				\else% max non atteint -> comportement gourmand
					\def\tktl_do_next{\expandafter\tktl_test_match_s_a\tktl_postmatch_enctokens#1\_nil}% recommencer
				\fi
			\fi
			\tktl_do_next
			}
			{% si le texte ne commence pas par le motif
			\ifnum\tktl_repeat_match_cnt<\tktl_min_current_repeat\relax% si nb répétition mini non atteint
				\tktl_match_success_false
				\tktl_capture_pos_cnt=\tktl_position_saved_cnt
				\let\tktl_capture_pos_list\tktl_capture_pos_list_saved
				\let\tktl_capture_tok_list\tktl_capture_tok_list_saved
				\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_saved% ne rien consommer
			\else
				\tktl_match_success_true
			\fi
			}%
	}%
	\def\tktl_test_match_s_b##1#1\_nil{%
		\unexpanded{##1}% garder ce qui est après le motif et avant le motif mis en dernier
	}%
	\tktl_ifempty_or_space{#1}% si motif ε -> renvoyer vrai
		{%
		\tktl_match_success_true
		}
		{%
		\tktl_ifempty_or_space{#2}
			{%
			\tktl_match_success_false
			}
			{%
			\tktl_test_match_s_a#2#1\_nil
			}%
		}%
}

\def\tktl_analyse_R_pattern#1{% #1 est de la forme "interval charcode" ou "interval charcode:interval catcode"
	\tktl_analyse_R_pattern_a#1:*:\_nil
}
\def\tktl_analyse_R_pattern_a#1:#2:#3\_nil{%
	\def\tktl_charcode_interval{#1}%
	\def\tktl_catcode_interval{#2}%
}
\def\tktl_convert_charcsv_to_charcodecsv#1{%
	\let\tktl_charcode_interval\empty
	\tktl_convert_charcsv_to_charcodecsv_a#1,\tktl_convert_charcsv_to_charcodecsv_a,%
	\ifx\tktl_charcode_interval\empty\else% retirer la dernière virgule
		\expandafter\tktl_convert_charcsv_to_charcodecsv_b\tktl_charcode_interval\_nil\tktl_charcode_interval
	\fi
}
\def\tktl_convert_charcsv_to_charcodecsv_a#1,{%
	\ifx\tktl_convert_charcsv_to_charcodecsv_a#1%
	\else% TODO : tester si #1 est vide ?
		\tktl_encode_tokens_char_range{#1}\tktl_charcode_min\tktl_charcodemax
		\edef\tktl_charcode_interval{\tktl_charcode_interval\tktl_charcode_min-\tktl_charcodemax,}%
		\expandafter\tktl_convert_charcsv_to_charcodecsv_a
	\fi
}
\def\tktl_convert_charcsv_to_charcodecsv_b#1,\_nil#2{%
	\def#2{#1}%
}
\long\def\tktl_test_match_R_or_S(#1:#2){%
	\ifnum#2=-1 % fin atteinte?
		\let\tktl_do_next\empty
		\ifnum\tktl_repeat_match_cnt<\tktl_min_current_repeat\relax% si nb répétition mini non atteint
			\tktl_match_success_false
			\tktl_capture_pos_cnt=\tktl_position_saved_cnt
			\let\tktl_capture_pos_list\tktl_capture_pos_list_saved
			\let\tktl_capture_tok_list\tktl_capture_tok_list_saved
			\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_saved% ne rien consommer
		\else
			\tktl_match_success_true
			\let\tktl_postmatch_enctokens\empty
		\fi
	\else
		\tktl_token_test_R_or_S(#1:#2)
			{% si le token courant correspond aux intervalles
			\advance\tktl_capture_pos_cnt1
			\advance\tktl_repeat_match_cnt 1
			\tktl_addtomacro\tktl_match_tokens{(#1:#2)}%
			\ifnum\tktl_repeat_match_cnt<\tktl_min_current_repeat\relax% si nb répétition mini non atteint
				\let\tktl_do_next\tktl_test_match_R_or_S
			\else
				\ifnum\tktl_repeat_match_cnt=\tktl_max_current_repeat\relax% max atteint
					\tktl_match_success_true
					\def\tktl_do_next{\tktl_test_match_assign_remaining_tokens{\def\tktl_postmatch_enctokens}}% tout manger et assigner
				\else
					\let\tktl_do_next\tktl_test_match_R_or_S
				\fi
			\fi
			}
			{%le token courant ne correspond pas
			\ifnum\numexpr(\tktl_repeat_match_cnt-\tktl_min_current_repeat)*(\tktl_repeat_match_cnt-\tktl_max_current_repeat)>0 % si le nombre de répétition n'est aps atteint
				\tktl_match_success_false
				\let\tktl_match_tokens\empty
				\tktl_capture_pos_cnt=\tktl_position_saved_cnt
				\let\tktl_capture_pos_list\tktl_capture_pos_list_saved
				\let\tktl_capture_tok_list\tktl_capture_tok_list_saved
				\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_saved% ne rien consommer
				\let\tktl_do_next\tktl_gob_enctoks_to_end% tout manger
			\else% nombre de répétition atteint
				\tktl_match_success_true
				\def\tktl_do_next{\tktl_test_match_assign_remaining_tokens{\def\tktl_postmatch_enctokens}(#1:#2)}% remettre le token actuel
			\fi
			}%
	\fi
	\tktl_do_next
}
%---------------------------------------------------------------------
%------------ Test si des motifs qui se suivent matchent -------------
%------------       avec le début des tokens encodés     -------------
%---------------------------------------------------------------------
% Les motifs sont séparés par des ":" qui est le connecteur de concaténation
% Entrée :
%	#1 = motifs séparés par des ":"
%	#2 = tokens encodés sous la forme (charcode:catcode)
% Revoie :
%	\iftktl_match_success_ : le booléen selon qu'il y a correspondance ou pas
%	\tktl_postmatch_enctokens : tokens encodés restant (#1 si booléen  faux)
%	\tktl_match_and_patterns : vide si booléen faux et tokens encodés qui ont matché
%---------------------------------------------------------------------
\long\def\tktl_test_match_and_patterns#1#2{% #1= and_patterns séparés par ":"  #2=tok_encodés (charcode:catcode)
	\begingroup
		\def\tktl_postmatch_enctokens{#2}%
		\let\tktl_match_and_patterns\empty
		\let\tktl_postmatch_enctokens_init\tktl_postmatch_enctokens
		\tktl_test_match_and_patterns_a#1:\relax:%
		\expanded{%
	\endgroup
	\iftktl_match_success_
		\tktl_unexpand_earg\def\tktl_match_and_patterns{\tktl_match_and_patterns}%
		\tktl_unexpand_earg\def\tktl_postmatch_enctokens{\tktl_postmatch_enctokens}%
		\tktl_unexpand_earg\def\tktl_capture_pos_list{\tktl_capture_pos_list}%
		\tktl_unexpand_earg\def\tktl_capture_tok_list{\tktl_capture_tok_list}%
		\noexpand\tktl_capture_pos_cnt=\the\tktl_capture_pos_cnt\relax
		\noexpand\tktl_capture_pos_index_cnt=\the\tktl_capture_pos_index_cnt\relax
		\noexpand\tktl_capture_tok_index_cnt=\the\tktl_capture_tok_index_cnt\relax
		\noexpand\tktl_match_success_true
	\else
		\noexpand\tktl_match_success_false
	\fi
	}%
}
\long\def\tktl_test_match_and_patterns_a#1:{% #1= and_pattern courant
	\def\tktl_current_pattern{#1}%
	\ifx\tktl_current_pattern\tktl_end_of_patterns
		\let\tktl_do_next\empty
	\else
		\let\tktl_match_tokens\empty
		\def\tktl__tmp{\tktl_test_match_pattern{#1}}\expandafter\tktl__tmp\expandafter{\tktl_postmatch_enctokens}%
		\iftktl_match_success_
			\tktl_eaddtomacro\tktl_match_and_patterns\tktl_match_tokens
			\let\tktl_do_next\tktl_test_match_and_patterns_a
		\else% ce match échoue ?
			\let\tktl_match_and_patterns\empty% vider les tokens ayant matché
			\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_init% ne rien consommer
			\let\tktl_do_next\tktl_gob_all_and_patterns% manger tous les pattern qui restent
		\fi
	\fi
	\tktl_do_next
}
\def\tktl_gob_all_and_patterns#1\relax:{}
\def\tktl_end_of_patterns{\relax}

%---------------------------------------------------------------------
%------------- Teste si des motifs quelconques matchent --------------
%-------------     avec le début des tokens encodés     --------------
%---------------------------------------------------------------------
% Entrée :
%	#1 = motifs concaténés séparés par des "|"
%	#2 = tokens encodés sous la forme (charcode:catcode)
% Revoie :
%	\iftktl_match_success_ : le booléen selon qu'il y a correspondance ou pas
%	\tktl_postmatch_enctokens : tokens encodés restant (=#1 si booléen faux)
%	\tktl_match_full_patterns : tokens encodés qui ont matché (vide si booléen faux)
%	\tktl_prematch_pos position avant match
%	\tktl_postmatch_pos position après match (égale à \tktl_prematch_pos si pas de match)
%---------------------------------------------------------------------
\long\def\tktl_test_match_full_patterns#1#2{% #1=patterns séparés par "|"  #2=tok_encodés
	\def\tktl_postmatch_enctokens{#2}%
	\let\tktl_match_full_patterns\empty
	\let\tktl_postmatch_enctokens_init\tktl_postmatch_enctokens
	\edef\tktl_prematch_pos{\the\tktl_capture_pos_cnt}%
	\tktl_test_match_full_patterns_a#1|\relax|%
	\edef\tktl_postmatch_pos{\the\tktl_capture_pos_cnt}%
}
\long\def\tktl_test_match_full_patterns_a#1|{%
	\def\tktl_current_pattern{#1}%
	\ifx\tktl_current_pattern\tktl_end_of_patterns% tous les patterns épuisés :
		\tktl_match_success_false% pas de match
		\let\tktl_match_full_patterns\empty% vider les tokens ayant matché
		\let\tktl_postmatch_enctokens\tktl_postmatch_enctokens_init% ne rien consommer
		\let\tktl_do_next\empty
	\else
		\def\tktl__tmp{\tktl_test_match_and_patterns{#1}}\expandafter\tktl__tmp\expandafter{\tktl_postmatch_enctokens}%
		\iftktl_match_success_
			\tktl_eaddtomacro\tktl_match_full_patterns\tktl_match_and_patterns
			\let\tktl_do_next\tktl_gob_all_full_patterns
		\else
			\let\tktl_do_next\tktl_test_match_full_patterns_a
		\fi
	\fi
	\tktl_do_next
}
\long\def\tktl_gob_all_full_patterns#1\relax|{}

%---------------------------------------------------------------------
%------------- Gestion des macros contenant des patterns -------------
%---------------------------------------------------------------------
\let\tktl_list_of_patterns\empty
\let\tktl_list_of_single_patterns\empty
\def\tktl_ifmacro#1{% teste si #1 est une macro
	\begingroup
		\escapechar=92 % "\"
		\ifnum 0%
			\tktl_ifempty_or_space{#1}
				{}
				{%
				\expandafter\tktl_ifempty_or_space\expandafter{\tktl_gob_arg#1}
					{%
					\ifnum\expandafter\expandafter\expandafter`\expandafter\tktl_firsttonil\string#1.\_nil=92
						1%
					\fi
					}%
					{}%
				}%
				=1
	\endgroup
			\expandafter\tktl_exec_first
		\else
	\endgroup
			\expandafter\tktl_exec_second
		\fi
}
\def\tktl_remove_pattern_in_list#1#2{% #1=macro pattern utilisateur #2=macro contenant une liste
	\tktl_ifmacro{#1}
		{%
		\def\tktl__tmp{\tktl_ifinstr{#1}}\expandafter\tktl__tmp\expandafter{#2}%
			{%
			\tktl_remove_pattern_in_list_a#1#2%
			}
			{}%
		}
		{%
		\errmessage{Internal error in \string\tktl_remove_pattern_in_list : \detokenize{#1} is not a macro}%
		}%
}
\def\tktl_remove_pattern_in_list_a#1#2{% #1=macro pattern interne #2=macro contenant une liste
	\def\tktl_remove_pattern_in_list_b##1#1##2\_nil{\def#2{##1##2}}%
	\expandafter\tktl_remove_pattern_in_list_b#2\_nil
}

\def\tktl_add_pattern_in_list#1#2{% #1=macro pattern utilisateur  #2=macro contenant une liste
	\tktl_ifmacro{#1}
		{%
		\def\tktl__tmp{\tktl_ifinstr{#1}}\expandafter\tktl__tmp\expandafter{#2}%
			{}
			{%
			\tktl_addtomacro#2{#1}%
			}%
		}
		{%
		\errmessage{Internal error in \string\tktl_add_pattern_in_list : \detokenize{#1} is not a macro}%
		}%
}
\long\def\defpattern#1#2{% #1 macro représentant le pattern  #2=code du pattern
	\tktl_ifmacro{#1}
		{%
		\tktl_ifempty_or_space{#2}
			{%
			\errmessage{Pattern code is empty, definition ignored}%
			}
			{%
			\tktl_ifinstr{#1}{\s\S\r\R\.\relax}
				{%
				\errmessage{Macro \string#1\space is reserved, a pattern cannot be named with it}%
				}
				{%
				\let\tktl_grab_pattern_remain\tktl_grab_pattern_remain_test_single
				\tktl_allow_post_pattern_catpure_true
				\tktl_allow_captures_true% permet les captures à priori
				\tktl_compile_patterns{#2}%
				\expandafter\edef\csname tktl_pattern_\string#1\endcsname{\the\tktl_pattern_compile_collect}%
				\tktl_addtomacro\tktl_list_of_patterns#1%
				}%
			}%
		}
		{%
		\errmessage{Found "\detokenize{#1}" after \string\defpattern, definition aborted}%
		}%
}

%---------------------------------------------------------------------
%------------------ Gestion des captures effectuées ------------------
%---------------------------------------------------------------------
\def\tktl_ifcontains_ddots#1{% la chaine #1 contient-elle ":" ?
	\tktl_ifcontains_ddots_a#1:\_nil
}
\def\tktl_ifcontains_ddots_a#1:#2\_nil{%
	\ifcat\relax\detokenize{#2}\relax\expandafter\tktl_exec_second\else\expandafter\tktl_exec_first\fi
}
\def\tktl_process_pos_capture#1#2#3{% #1=liste des captures <index=position> #2=nom des captures #3=maxindex
	\def\tktl_temp_capture_name{#2}%
	\tktl_csdef{tktl_capture_position_maxindex_#2}{#3}%
	\let\tktl_capture_pos_list\empty
	\tktl_process_pos_capture_a#1<-1=-1>%
	\expandafter\let\csname tktl_pos_capture_\tktl_temp_capture_name0\endcsname\tktl_capture_pos_list
}
\def\tktl_process_pos_capture_a<#1=#2>{%
	\ifnum#1>0
		\tktl_addtomacro\tktl_capture_pos_list{,#2}%
		\tktl_csdef{tktl_pos_capture_\tktl_temp_capture_name#1}{#2}%
		\expandafter\tktl_process_pos_capture_a
	\else
		\ifx\tktl_capture_pos_list\empty\else% retirer la première virgule
			\expanded{\def\noexpand\tktl_capture_pos_list{\unexpanded\expandafter\expandafter\expandafter{\expandafter\tktl_gob_arg\tktl_capture_pos_list}}}%
		\fi
	\fi
}
\long\def\tktl_process_tok_capture#1#2#3{% #1=liste des captures <index={tokens}> #2=nom des captures #3=maxindex
	\def\tktl_temp_capture_name{#2}%
	\tktl_csdef{tktl_capture_tokens_maxindex_#2}{#3}%
	\let\tktl_capture_tok_list\empty
	\tktl_process_tok_capture_a#1<-1=-1>%
	\expandafter\let\csname tktl_tok_capture_\tktl_temp_capture_name0\endcsname\tktl_capture_tok_list
}
\newtoks\tktl_decode_capture_toks
\long\def\tktl_process_tok_capture_a<#1=#2>{%
	\ifnum#1>0
		\tktl_decode_enctoks{#2}\tktl_decode_capture_toks
		\expandafter\edef\csname tktl_tok_capture_\tktl_temp_capture_name#1\endcsname{\unexpanded\expandafter{\the\tktl_decode_capture_toks}}%
		\tktl_eaddtomacro\tktl_capture_tok_list{\unexpanded\expandafter{\expandafter,\expandafter{\the\tktl_decode_capture_toks}}}%
		\expandafter\tktl_process_tok_capture_a
	\else
		\ifx\tktl_capture_tok_list\empty\else% retirer la première virgule
			\expanded{\def\noexpand\tktl_capture_tok_list{\unexpanded\expandafter\expandafter\expandafter{\expandafter\tktl_gob_arg\tktl_capture_tok_list}}}%
		\fi
	\fi
}
\def\poscapture#1{%
	\expanded{%
		\tktl_ifcontains_ddots{#1}
			{\tktl_poscapture_a#1\_nil}
			{\expanded{\noexpand\tktl_poscapture_b{}{\unexpanded\expandafter\expandafter\expandafter{\tktl_stripsp{#1}}}}}%
	}%
}
\def\tktl_poscapture_a#1:#2\_nil{%
	\expanded{\noexpand\tktl_poscapture_b{\unexpanded\expandafter\expandafter\expandafter{\tktl_stripsp{#1}}}{\unexpanded\expandafter\expandafter\expandafter{\tktl_stripsp{#2}}}}%
}
\def\tktl_poscapture_b#1#2{%
	\ifcsname tktl_pos_capture_\detokenize{#1}#2\endcsname
		\ifnum\numexpr#2*(#2-\csname tktl_capture_position_maxindex_\detokenize{#1}\endcsname)>0 % si #2 n'appartient pas à [0 ; max]
			\tktl_capture_undefined{position}{#1}{#2}%
		\else
			\unexpanded\expandafter\expandafter\expandafter{\csname tktl_pos_capture_\detokenize{#1}#2\endcsname}%
		\fi
	\else
		\tktl_capture_undefined{position}{#1}{#2}%
	\fi
}
\def\tktl_capture_undefined#1#2#3{%
	\errmessage{Undefined #1 capture \ifcat\relax\detokenize{#2}\relax \else named "\detokenize{#2}"\fi at index "\detokenize{#3}"}%
}

\def\tokscapture#1{% #1= <nom>:<index> ou <index>
	\expanded{%
		\tktl_ifcontains_ddots{#1}
			{\tktl_tokcapture_a#1\_nil}
			{\expanded{\noexpand\tktl_tokcapture_b{}{\unexpanded\expandafter\expandafter\expandafter{\tktl_stripsp{#1}}}}}%
	}%
}
\def\tktl_tokcapture_a#1:#2\_nil{%
	\expanded{\noexpand\tktl_tokcapture_b{\unexpanded\expandafter\expandafter\expandafter{\tktl_stripsp{#1}}}{\unexpanded\expandafter\expandafter\expandafter{\tktl_stripsp{#2}}}}%
}
\def\tktl_tokcapture_b#1#2{%
	\ifcsname tktl_tok_capture_\detokenize{#1}#2\endcsname
		\ifnum\numexpr#2*(#2-\csname tktl_capture_tokens_maxindex_\detokenize{#1}\endcsname)>0 % si #2 n'appartient pas à [0 ; max]
			\tktl_capture_undefined{token}{#1}{#2}%
		\else
			\unexpanded\expandafter\expandafter\expandafter{\csname tktl_tok_capture_\detokenize{#1}#2\endcsname}%
		\fi
	\else
		\tktl_capture_undefined{token}{#1}{#2}%
	\fi
}
%---------------------------------------------------------------------
%--------------------- Macro publique \ifpegmatch --------------------
%---------------------------------------------------------------------
% Utilisation :
%        \ifpegmatch[<clés/valeurs>]{<patterns>}{<tokens>}{T}{F}
% teste si les <tokens> matchent avec les <patterns>,
% et exécute T ou F selon l'issue du test
% 
% Sortie :
%	- les <tokens> qui ont matché sont stockés par défaut dans la macro \matchtoks
%	- les <tokens> qui restent sont stockés par défaut dans la macro \remaintoks
%	- la macro \poscapture{<nom>:<index>} ou \poscapture{<index>} renvoie en 2 développements
%     la position dans les <tokens> capturés par \c de la capture numéro <index>.
%     Si <index> vaut 0, la liste des positions est renvoyée sous la forme csv 
%     "<pos1>,<pos2>,...,<posn>"
%	- la macro \matchposition contient la position du match dans les <tokens>.
%	  Elle vaut 0 si pas de match.
%	- la macro \tokscapture{<nom>:<index>} ou \tokscapture{<index>} renvoie en 2 développements
%     les <tokens> capturés par \c de la capture numéro <index>.
%     Si <index> vaut 0, la liste des <tokens> capturés est renvoyée sous la forme csv
%    "{<tokens1>},{<tokens2>},...,{<tokensn>}"
%---------------------------------------------------------------------
\def\tktl_test_mode#1[#2;#3]#4#5{% teste si #1 est compris entre #2 et #3 et assigne le résultat à #5 ou #4 à #5 si échec
	\tktl_ifinteger{#1}
		{%
		\ifnum\numexpr(#1-#2)*(#1-#3)>0
			\errmessage{Illegal mode "\detokenize{#1}", "mode=#4" inserted}%
			\def#5{#4}%
		\else
			\def#5{#1}%
		\fi
		}
		{%
		\errmessage{Illegal mode "\detokenize{#1}", "mode=#4" inserted}%
		\def#5{#4}%
		}%
}
\defKV[pegmatch]{
	mode = \tktl_test_mode{#1}[0;2]1\tktl_match_mode,
	assign match=\def\tktl_assign_match{#1},
	assign postmatch=\def\tktl_assign_remain{#1},
	assign prematch=\def\tktl_assign_prematch{#1},
	capture name=\edef\tktl_capture_name{\detokenize{#1}},
	expand arg= \tktl_ifinteger{#1}
	                {\def\tktl_expand_arg_value{#1}}
	                {\errmessage{Key "expand arg" require an integer}\def\tktl_expand_arg_value{0}},
}
\setKVdefault[pegmatch]{
	mode=1,% 0=match la totalité  1=match début des <tokens>  2=match à n'importe quelle position
	assign match=\def\matchtoks,% comment assigner les tokens qui ont matché
	assign postmatch=\def\remaintoks,% comment assigner les tokens qui restent
	assign prematch=\def\prematchtoks,
	capture name={},% pour les noms des captures
	expand arg=0,% développer le 2e argument obligatoire de \ifpegmatch ?
}
\newcount\tktl_match_position_cnt
\def\ifpegmatch{%
	\tktl_ifnxttok[%
		{\tktl_ifpegmatch_a}
		{\tktl_ifpegmatch_a[]}%
}
\long\def\tktl_ifpegmatch_a[#1]#2#3{%
	\begingroup
		\tktl_allow_post_pattern_catpure_true
		\tktl_allow_captures_true% permet les captures
		\setKV[pegmatch]{#1}%
		\ifnum\tktl_expand_arg_value>0
			\tktl_antefi{\expandafter\tktl_expand_n_times\expandafter{\tktl_expand_arg_value}}%
		\fi
		\tktl_encode_tokens{#3}%
		\tktl_init_match
		\tktl_match_position_cnt=1
		\let\tktl_prematch_enctokens\empty
		\tktl_compile_patterns{#2}%
		\expandafter\tktl_test_match_full_patterns\expanded{{\the\tktl_pattern_compile_collect}{\the\tktl_enctoks_toks}}%
		\iftktl_match_success_
			\ifnum\tktl_match_mode=0 \unless\ifx\tktl_postmatch_enctokens\empty
				\let\tktl_match_full_patterns\empty% si mode 0 et pas de match au début
				\let\tktl_prematch_enctokens\empty
				\edef\tktl_postmatch_enctokens{\the\tktl_enctoks_toks}%
				\tktl_match_success_false
			\fi\fi
		\else
			\ifnum\tktl_match_mode=2 % mode 2 et pas de match au début
				\tktl_capture_pos_cnt=1
				\expandafter\tktl_ifpegmatch_b\expanded{{\unexpanded\expandafter{\tktl_postmatch_enctokens}}{\the\tktl_pattern_compile_collect}}%
			\fi
		\fi
		\expanded{% sortir du groupe les tokens avant le match, ceux du match, ceux qui restent et les listes de captures
	\endgroup
	\edef\noexpand\matchposition{\iftktl_match_success_\the\tktl_match_position_cnt\else0\fi}%
	\tktl_unexpand_earg\tktl_decode_enctoks{\tktl_match_full_patterns}{\unexpanded\expandafter{\tktl_assign_match}}%
	\tktl_unexpand_earg\tktl_decode_enctoks{\tktl_prematch_enctokens}{\unexpanded\expandafter{\tktl_assign_prematch}}%
	\tktl_unexpand_earg\tktl_decode_enctoks{\tktl_postmatch_enctokens}{\unexpanded\expandafter{\tktl_assign_remain}}%
	\tktl_unexpand_earg\tktl_process_pos_capture{\tktl_capture_pos_list}{\tktl_capture_name}{\the\tktl_capture_pos_index_cnt}%
	\tktl_unexpand_earg\tktl_process_tok_capture{\tktl_capture_tok_list}{\tktl_capture_name}{\the\tktl_capture_tok_index_cnt}%
	\expandafter}% on décide avant de sortir du groupe si on renvoie T ou F
	\csname
		tktl_exec_%
		\iftktl_match_success_ first\else second\fi
	\endcsname
}
\long\def\tktl_ifpegmatch_b#1#2{% racourcir les enctokens #1 jusqu'à vide ou match succes  #2=pattern
	\tktl_ifempty_or_space{#1}
		{}
		{\tktl_ifpegmatch_c#1\_nil{#2}}%
}
\long\def\tktl_ifpegmatch_c(#1)#2\_nil#3{% #2=enctokens restant sans le 1er  #3=pattern
	\advance\tktl_capture_pos_cnt 1
	\advance\tktl_match_position_cnt 1
	\tktl_addtomacro\tktl_prematch_enctokens{(#1)}%
	\tktl_capture_pos_index_cnt=0
	\tktl_capture_tok_index_cnt=0
	\let\tktl_capture_pos_list\empty
	\let\tktl_capture_tok_list\empty
	\tktl_in_capture_false
	\tktl_in_patterngroup_false
	\tktl_test_match_full_patterns{#3}{#2}%
	\unless\iftktl_match_success_
		\tktl_antefi{\tktl_ifpegmatch_b{#2}{#3}}%
	\fi
}

%---------------------------------------------------------------------
%--------------------- Macro publique \printtoks ---------------------
%---------------------------------------------------------------------
% \printtoks{#1} affiche les tokens composant #1
% avec leur charcode et leur catcode si besoin
%---------------------------------------------------------------------
\defKV[printtoks]{
	baselinecoeff=\edef\tktl_baselineskip_coeff{\tktl_ifempty_or_space{#1}{1}{#1}},
	expand arg= \tktl_ifinteger{#1}
	                {\def\tktl_expand_arg_value{#1}}
	                {\errmessage{Key "expand arg" require an integer}\def\tktl_expand_arg_value{0}},
	mode= \tktl_test_mode{#1}[0;2]0\tktl_log_mode,
}
\setKVdefault[printtoks]{
	expand arg=0,
	intertoks = 0.33em,
	printcharcode = true,
	printcatcode = true,
	hexcharcode = false,
	baselinecoeff = 0.8,
	vlines=true,
	boxed = true,% le tout dans une \hbox ?
	code={},
	mode=0,% 0=pas d'envoi sur le log  1=envoi sur le log (et affichage)  2=envoi sur le log seulement
}
\def\printtoks{%
	\tktl_ifnxttok[%
		{\tktl_printtoks_a}
		{\tktl_printtoks_a[]}%
}
\long\def\tktl_printtoks_a[#1]#2{%
	\begingroup
		\setKV[printtoks]{#1}%
		\leavevmode
		\tktl_ifnum{\tktl_expand_arg_value>0 }
			{\expandafter\tktl_expand_n_times\expandafter{\tktl_expand_arg_value}}
			{}%
		\tktl_encode_tokens{#2}%
		\unless\ifnum\tktl_log_mode=2
			\ifboolKV[printtoks]{boxed}
				{\hbox\bgroup}
				{}%
			\ifboolKV[printtoks]{vlines}
				{%
				\vrule
				\hskip.5\dimexpr\useKV[printtoks]{intertoks}\relax
				}
				{%
				\hskip0pt
				}%
			\expandafter\tktl_printtoks_b\the\tktl_enctoks_toks(-1:-1)%
			\ifboolKV[printtoks]{boxed}
				{\egroup}
				{}%
		\fi
		\ifnum\tktl_log_mode>0
			\immediate\write-1{^^J------ Begin token list ------}%
			\expandafter\tktl_printtoks_c\the\tktl_enctoks_toks(-1:-1)%
			\immediate\write-1{------- End token list -------^^J}%
		\fi
	\endgroup
}
\long\def\tktl_printtoks_b(#1:#2){%
	\tktl_ifnum{#2=-1 }
		{\unskip}
		{%
		\vtop{%
			\baselineskip=\tktl_baselineskip_coeff\baselineskip
			\halign{%
				\hss##\hss\cr
				\useKV[printtoks]{code}\tktl_ifnum{#2=16 }\string\char#1\relax\cr
				\ifboolKV[printtoks]{printcharcode}
					{%
					\unless\ifnum#2=16
						$\scriptscriptstyle
						\ifboolKV[printtoks]{hexcharcode}
							{\tktl_dectohex{#1}\tktl_id}
							{#1}%
						$%
					\fi
					\cr
					}
					{}%
				\ifboolKV[printtoks]{printcatcode}
					{$\scriptscriptstyle#2$\cr}
					{}%
				}%
			}%
		\hskip.5\dimexpr\useKV[printtoks]{intertoks}\relax
		\vrule
		\hskip.5\dimexpr\useKV[printtoks]{intertoks}\relax
		\tktl_printtoks_b
	}%
}
\def\tktl_log_output_catcode#1{%
	\ifcase#1\relax
	\or begin-group character% 1
	\or end-group character% 2
	\or math shift character% 3
	\or alignment tab character% 4
	\or
	\or macro parameter charcater% 6
	\or superscript character% 7
	\or subscript character% 8
	\or
	\or blank space% 10
	\or letter% 11
	\or character% 12
	\or active character%13
	\fi
}
\long\def\tktl_printtoks_c(#1:#2){%
	\unless\ifnum#2=-1
		\ifnum#2=16 % si séquence de contrôle
			\tktl_if_primitive{#1}
				{%
				\immediate\write-1{\space\space\string#1\space catcode 16 (primitive)}%
				}
				{%
				\immediate\write-1{\space\space\string#1\space catcode 16 (\meaning#1)}%
				}%
		\else
			\tktl_hex_to_char{#1}\tktl_current_tok
			\immediate\write-1{%
				\space\space\tktl_current_tok\space catcode #2\ifnum#2<10 \space\fi\space(\tktl_log_output_catcode{#2})%
			}%
		\fi
		\expandafter\tktl_printtoks_c
	\fi
}
\long\def\tktl_if_primitive#1{% Vérifie que #1 est une primitive
	\begingroup
		\edef\__tempa{\meaning#1}%
		\edef\__tempb{\string#1}%
		\expandafter
	\endgroup
	\csname tktl_exec_\ifx\__tempa\__tempb first\else second\fi\endcsname
}
\def\tktl_dectohex#1#2{% #1=nombre base 10  #2=consigne d'assignation
	\def\tktl_hexbase_result{}%
	\tktl_dectohex_a{#1}%
	\def\tktl__tmp{#2}\expandafter\tktl__tmp\expandafter{\tktl_hexbase_result}%
}
\def\tktl_dectohex_a#1{%
	\tktl_test_cnt#1\relax
	\divide\tktl_test_cnt16
	\edef\tktl_hexbase_result{%
		\ifcase\numexpr#1-16*\tktl_test_cnt\relax
			0\or1\or2\or3\or4\or5\or6\or7\or8\or9\or A\or B\or C\or D\or E\or F%
		\fi
		\tktl_hexbase_result
	}%
	\unless\ifnum\tktl_test_cnt=0
		\tktl_antefi{\expandafter\tktl_dectohex_a\expandafter{\the\tktl_test_cnt}}%
	\fi
}
\def\tktl_superscript_two{\tktl_superscript\tktl_superscript}
\def\tktl_superscript_six{\tktl_superscript\tktl_superscript\tktl_superscript\tktl_superscript\tktl_superscript\tktl_superscript}
\def\tktl_hex_to_char#1{% #1=code dec #2=macro qui reçoit \string<token> où <token> a pour charcode #1
	\begingroup
		\tktl_dectohex{#1}{\def\tktl_hex_charcode}%
		\ifnum#1<"10
			\tktl_ok_this_test{\let\tktlsuperscriptrepeat\tktl_superscript_two\edef\tktl_hex_charcode{0\tktl_hex_charcode}}%
		\fi
		\ifnum#1<"100
			\tktl_ok_this_test{\let\tktlsuperscriptrepeat\tktl_superscript_two}%
		\fi
		\ifnum#1<"1000 % si #1>0xFF, mettre 6 chiffres hexadécimaux
			\tktl_ok_this_test{\let\tktlsuperscriptrepeat\tktl_superscript_six\edef\tktl_hex_charcode{000\tktl_hex_charcode}}%
		\fi
		\ifnum#1<"10000
			\tktl_ok_this_test{\let\tktlsuperscriptrepeat\tktl_superscript_six\edef\tktl_hex_charcode{00\tktl_hex_charcode}}%
		\fi
		\ifnum#1<"100000
			\tktl_ok_this_test{\let\tktlsuperscriptrepeat\tktl_superscript_six\edef\tktl_hex_charcode{0\tktl_hex_charcode}}%
		\fi
		\tktl_else_all_tests{%
			\ifnum#1>"FFFFFF \errmessage{Charcode too large, this error should not happen}\fi
		}%
		\lowercase\expandafter{\expandafter\tktl_hex_to_char_a\expandafter{\tktl_hex_charcode}}{#1}%
}
\def\tktl_hex_to_char_a#1#2#3{% #1=code hex minuscule #2=code dec  #3=macro qui reçoit \string<token> où <token> a pour charcode #1
		\everyeof{\_nil}%
		\endlinechar=-1
		\catcode#2=12
		\expandafter\tktl_store_char\expandafter#3\scantokens\expandafter{\expanded{\tktlsuperscriptrepeat#1}}%
}
\def\tktl_store_char#1#2#3\_nil{% TODO : tester si #3 est non vide -> erreur ?
	\endgroup
	\def#1{#2}%
}
%---------------------------------------------------------------------
%------------------ Macro \tktl_analyse_replace_patterns ------------------
%---------------------------------------------------------------------
% #1 est une liste csv d'élements "motif -> code"
%
% Stocke les motifs et les codes dans des macros indexées par la macro
% \tktl_replace_patterns_index :
% 	paterns dans \replace_patterns__\romannumeral\tktl_replace_patterns_index
%	codes   dans \replace_code__\romannumeral\tktl_replace_patterns_index
%---------------------------------------------------------------------
\def\tktl_analyse_replace_patterns#1{% #1 est une list csv de "motifs -> code"
	\tktl_ifempty_or_space{#1}
		{%
		\errmessage{Empty pattern not accepted}%
		}
		{%
		\def\tktl_replace_patterns_index{0}%
		\tktl_analyse_replace_patterns_a#1,\relax,%
		}%
}
\def\tktl_analyse_replace_patterns_a#1,{% #1 = "motifs -> code" en cours
	\tktl_ifempty_or_space{#1}
		{% ignorer si élément vide
		\tktl_analyse_replace_patterns_a
		}
		{%
		\def\tktl_current_pattern{#1}%
		\unless\ifx\tktl_end_of_patterns\tktl_current_pattern
			\tktl_antefi{\tktl_analyse_replace_patterns_b#1,}%
		\fi
		}%
}
\def\tktl_analyse_replace_patterns_b#1->{% #1 = motif en cours
	\tktl_compile_patterns{#1}%
	\edef\tktl_replace_patterns_index{\the\numexpr\tktl_replace_patterns_index+1}%
	\expandafter\edef\csname replace_patterns__\romannumeral\tktl_replace_patterns_index\endcsname{\the\tktl_pattern_compile_collect}%
	\tktl_analyse_replace_patterns_c\relax
}
\def\tktl_analyse_replace_patterns_c#1,{% #1 = code en cours précédé de \relax
	\def\tktl__tmp{\expandafter\expandafter\expandafter\tktl_analyse_replace_patterns_d\tktl_stripsp}%
	\expandafter\tktl__tmp\expandafter{\tktl_gob_arg#1}\tktl_analyse_replace_patterns_c
}
\def\tktl_analyse_replace_patterns_d#1\tktl_analyse_replace_patterns_c{% #1 = code en cours final (sans espace avant/après et sans accolades éventuelles)
	\tktl_encode_tokens{#1}%
	\expandafter\edef\csname replace_code__\romannumeral\tktl_replace_patterns_index\endcsname{\the\tktl_enctoks_toks}%
	\tktl_analyse_replace_patterns_a
}

%---------------------------------------------------------------------
%--------------------- Macro publique \pegreplace --------------------
%---------------------------------------------------------------------
% \pegreplace[clés=val]{<csv pattern>}{<texte>}
% où :
% 	- <csv pattern> est une liste csv de "motifs -> code de remplacement", sachant
%	  que chaque motif, groupe de motifs ou macro-motif déclarée par \defpattern
%	  peut être précédé de "\c" pour en faire une capture. Il peut y avoir autant
%	  de captures que l'on souhaite
%	  Les captures \c après pattern sont interdites -> \errmessage
%	- <texte> est un ensemble de tokens équilibrés en accolades dans lequel le <pattern>
%	  est cherché
%	- le "code de remplacement" est un code tex dans lequel \0 signifie la capture entière
%	  correspondant au <pattern>, \1 est la première capture faite par un \c, \2 la deuxième,
%	  etc jusqu'à \9.
%
%	  Pour chaque correspondance au <pattern> trouvée dans le <texte>, Chaque occurence
%	  de \0...\9 dans le <remplacement> est remplacée par la capture concernée.
%
%	Si la clé "assign" est vide, les tokens obtenus après les remplacements sont laissés
%	dans le flux de lecture de TeX. Sinon, une assignation à une macro ou à un registre de
%	tokens (obligatoire si le remplacmeent contient '#') est faite, selon la valeur dubooléen
%	'assign to toks'
%---------------------------------------------------------------------
\newif\iftktl_allow_post_pattern_catpure_
\defKV[pegreplace]{
	assign=\def\tktl_assign_result{#1},
	expand arg= \tktl_ifinteger{#1}
				{\def\tktl_expand_arg_value{#1}}
				{\errmessage{Key "expand arg" require an integer}\def\tktl_expand_arg_value{0}},
	mode=\tktl_test_mode{#1}[0;2]2\tktl_pegreplace_mode,%
}
\setKVdefault[pegreplace]{
	assign= ,% comment assigner les tokens qui ont matché (si vide -> les afficher)
	expand arg=0,% développer le 2e argument obligatoire de \pegreplace ?
	mode=2,% si 0=remplacer la 1re occurence d'un des motifs et fin
	       %    1=remplacer la 1re occurrence de chaque motif si possible
	       %    2=remplacer toutes les occurrences de tous les motifs
}
\def\pegreplace{%
	\tktl_ifnxttok[%]
		{\tktl_pegreplace_a}
		{\tktl_pegreplace_a[]}%
}
\long\def\tktl_pegreplace_a[#1]#2#3{% #1=clé=val   #2=csv "pattern->code"   #3=texte
	\begingroup
		\tktl_allow_post_pattern_catpure_false
		\tktl_allow_captures_true% permet les captures
		\setKV[pegreplace]{#1}%
		\ifnum\tktl_expand_arg_value>0
			\tktl_antefi{\tktl_expand_n_times\tktl_expand_arg_value}%
		\fi
		\tktl_encode_tokens{#3}%
		\edef\tktl_replace_text{\the\tktl_enctoks_toks}%
		\let\tktl_prematch_enctokens\empty
		\tktl_analyse_replace_patterns{#2}% parcourir tous les "motifs -> code" et les stocker
		\let\tktl_replace_patterns_max_index\tktl_replace_patterns_index
		\tktl_capture_pos_cnt=1
		\expandafter\tktl_pegreplace_b\expandafter{\tktl_replace_text}%
		\ifnum\tktl_pegreplace_mode=0 % si on n'a pas testé jusqu'à la fin de #3
			\tktl_eaddtomacro\tktl_prematch_enctokens\tktl_postmatch_enctokens
		\fi
		\expanded{%
	\endgroup
	\noexpand\tktl_decode_enctoks{\unexpanded\expandafter{\tktl_prematch_enctokens}}{%
		\ifx\empty\tktl_assign_result
			\noexpand\tktl_id
		\else
			\unexpanded\expandafter{\tktl_assign_result}%
		\fi
		}%
	}%
}
\def\tktl_init_match{%
	\tktl_capture_pos_cnt=1
	\tktl_capture_pos_index_cnt=0
	\tktl_capture_tok_index_cnt=0
	\let\tktl_capture_pos_list\empty
	\let\tktl_capture_tok_list\empty
	\tktl_in_capture_false
	\tktl_in_patterngroup_false
	\def\c{tktl_capture}%
}
\def\tktl_pegreplace_b#1{% #1=encotkens du texte
	\tktl_ifempty_or_space{#1}% si plus de tokens -> fin
		{}
		{%
		\def\tktl_replace_patterns_index{0}% initialise : numéro du motif en train d'être testé
		\tktl_pegreplace_c#1\_nil
		}%
}
\def\tktl_pegreplace_c(#1)#2\_nil{% #1=premier enctoken #2=enctokens restant
	\tktl_init_match
	\edef\tktl_replace_patterns_index{\the\numexpr\tktl_replace_patterns_index+1}% pattern suivant
	\expandafter\def\expandafter\tktl_pegreplace_currentpattern\expandafter{\csname replace_patterns__\romannumeral\tktl_replace_patterns_index\endcsname}%
	\def\tktl_do_next{\expandafter\expandafter\expandafter\tktl_test_match_full_patterns\expandafter\expandafter\expandafter{\tktl_pegreplace_currentpattern}{(#1)#2}}% tester si match
	\ifnum\tktl_pegreplace_mode=1
		\expandafter\ifx\tktl_pegreplace_currentpattern\relax% si motif déjà rencontré
			\tktl_match_success_false% sauf si mode=1 et motif ayant déjà matché
			\let\tktl_do_next\empty
		\fi
	\fi
	\tktl_do_next
	\iftktl_match_success_% si (#1)#2 matche avec le motif n°\tktl_replace_patterns_index
		\let\tktl_do_next\tktl_pegreplace_d% faire les remplacements
				\ifnum\tktl_pegreplace_mode=1 % annuler le motif actuel
					\expandafter\let\csname replace_patterns__\romannumeral\tktl_replace_patterns_index\endcsname\relax
				\fi
	\else% si pas de match
		\ifnum\tktl_replace_patterns_index=\tktl_replace_patterns_max_index\relax% dernier pattern atteint ?
			\tktl_addtomacro\tktl_prematch_enctokens{(#1)}% échec, ajouter (#1) aux tokens précédent le match
			\def\tktl_do_next{\tktl_pegreplace_b{#2}}% recommencer avec les entokens restants
		\else% s'il reste des patterns, retenter avec le pattern suivant
			\def\tktl_do_next{\tktl_pegreplace_c(#1)#2\_nil}%
		\fi
	\fi
	\tktl_do_next
}
\def\tktl_pegreplace_d{%
	\edef\tktl_capture_tok_list{<0=\unexpanded\expandafter{\tktl_match_full_patterns}>\unexpanded\expandafter{\tktl_capture_tok_list}}%
	\unless\ifx\empty\tktl_capture_tok_list% on procède au remplacement dans \tktl_capture_tok_list
		\expandafter\expandafter\expandafter\tktl_process_replace\csname replace_code__\romannumeral\tktl_replace_patterns_index\endcsname\_nil\00\11\22\33\44\55\66\77\88\99\relax\relax
	\fi
	\tktl_eaddtomacro\tktl_prematch_enctokens\tktl_match_full_patterns% ajout les enctokens ayant matché et fin
	\ifnum\tktl_pegreplace_mode>0 % si on doit continuer avec les enctokens restant
		\tktl_antefi{\expandafter\tktl_pegreplace_b\expandafter{\tktl_postmatch_enctokens}}%
	\fi
}
\def\tktl_pegreplace_all_matches#1#2{% #1=encotkens du texte  #2=pattern
	\tktl_pegreplace_b{#1}{#2}%
	\unless\ifx\tktl_postmatch_enctokens\empty
		\tktl_antefi{\expandafter\tktl_pegreplace_all_matches\expandafter{\tktl_postmatch_enctokens}{#2}}%
	\fi
}
\long\def\tktl_process_replace#1\_nil#2#3{% #1=liste des remplacements <\d=code>  #2=\<d>  #3=<d>
	\ifx\relax#2%
		\def\tktl_match_full_patterns{#1}%
	\else% définition de la macro de remplacement
		\long\def\tktl_process_replace_b##1<#3=##2>##3\_nil##4(#2:16)##5\_nil{%
			\tktl_process_replace_a{##4##2##5}{#2}{#3}% reboucle pour faire tous les remplacements de \<d> s'il y en a plusieurs
			}%
		\tktl_antefi{\tktl_process_replace_a{#1}{#2}{#3}}%
	\fi
}
\long\def\tktl_process_replace_a#1#2#3{%
	\tktl_ifinstr{(#2:16)}{#1}
		{%
		\expandafter\tktl_process_replace_b\tktl_capture_tok_list\_nil#1\_nil
		}
		{%
		\tktl_process_replace#1\_nil
		}%
}

%---------------------------------------------------------------------
%--------------------- Macro publique \pegcount ----------------------
%---------------------------------------------------------------------
% \pegcount[clés=valeur]{<pattern>}{<tokens>}
% compte combien de fois le <pattern> correspond dans les <tokens>
%
% Les captures \c, où qu'elles soient, sont ignorées
%
% Renvoie :
%	- le nombre de correspondances (dans le flux ou dans une macro, selon la clé "assign")
%	- la liste des positions de correspondances (dans la macro spécifiée par la clé "assign positions")
%	- les correspondances trouvées consultables par \tokscapture{<index>}.
%	  Si <index>=0, livre la liste des correspondances dans une csv : {capt1},{capt2},...,{captn}
%---------------------------------------------------------------------
\defKV[pegcount]{
	assign=\def\tktl_assign_result{#1},
	expand arg= \tktl_ifinteger{#1}
					{\def\tktl_expand_arg_value{#1}}
					{%
					\errmessage{Key "expand arg" requires an integer}%
					\def\tktl_expand_arg_value{0}%
					},
	assign positions=\def\tktl_pegcount_assign_list{#1},
	name=\def\tktl_pegcount_capturename{#1},
}
\setKVdefault[pegcount]{
	assign={},% comment assigner les tokens qui ont matché (si vide -> les afficher)
	expand arg=0,% développer le 2e argument obligatoire de \pegcount ?
	assign positions=\def\matchposlist,
	name={},% nom des captures des correspondances
}
\newcount\tktl_pegcount_cnt% compteur des occurrences trouvées
\newcount\tktl_pegcount_pos_cnt% compteur de la position des occurrences trouvées
\def\pegcount{%
	\tktl_ifnxttok[%
		{\tktl_pegcount_a}
		{\tktl_pegcount_a[]}%
}
\long\def\tktl_pegcount_a[#1]#2#3{% #1=clé=val   #2=pattern   #3=texte
	\begingroup
		\tktl_allow_post_pattern_catpure_false
		\tktl_allow_captures_false% ignore les captures
		\setKV[pegcount]{#1}%
		\ifnum\tktl_expand_arg_value>0
			\tktl_antefi{\tktl_expand_n_times\tktl_expand_arg_value}%
		\fi
		\tktl_encode_tokens{#3}%
		\tktl_pegcount_cnt=0
		\tktl_pegcount_pos_cnt=1
		\let\tktl_pegcount_pos_list\empty
		\let\tktl_pegcount_match_list\empty
		\tktl_compile_patterns{#2}%
		\expandafter\tktl_pegcount_all_matches\expanded{{\the\tktl_enctoks_toks}{\the\tktl_pattern_compile_collect}}%
		\expanded{% sortir du groupe les tokens avant le match, ceux du match, ceux qui restent et les listes de captures
	\endgroup
		\tktl_unexpand_earg\tktl_process_tok_capture{\tktl_pegcount_match_list}{\tktl_pegcount_capturename}{\the\tktl_pegcount_cnt}%
		\unexpanded\expandafter{\tktl_pegcount_assign_list}{%
			\unless\ifx\tktl_pegcount_pos_list\empty
				\expandafter\tktl_gob_arg\tktl_pegcount_pos_list
			\fi
			}%
		\ifx\tktl_assign_result\empty
			\noexpand\tktl_id
		\else
			\unexpanded\expandafter{\tktl_assign_result}%
		\fi{\the\tktl_pegcount_cnt}%
	}%
}
\long\def\tktl_pegcount_to_first_match#1#2{% #1=encotkens du texte  #2=pattern
	\tktl_ifempty_or_space{#1}% si plus de tokens -> fin
		{}
		{%
		\tktl_init_match
		\tktl_test_match_full_patterns{#2}{#1}% match ?
		\iftktl_match_success_% si match
			\advance\tktl_pegcount_cnt 1
			\tktl_eaddtomacro\tktl_pegcount_match_list{\expanded{<\the\tktl_pegcount_cnt={\unexpanded\expandafter{\tktl_match_full_patterns}}>}}%
			\tktl_eaddtomacro\tktl_pegcount_pos_list{\expandafter,\the\tktl_pegcount_pos_cnt}%
			\advance\tktl_pegcount_pos_cnt\numexpr\tktl_postmatch_pos-1\relax
		\else% si pas de match -> appel de la macro récursive qui mange un à un les enctokens
			\tktl_antefi{\expandafter\tktl_pegcount_to_first_match_a\tktl_postmatch_enctokens\_nil{#2}}%
		\fi
		}%
}
\long\def\tktl_pegcount_to_first_match_a(#1)#2\_nil#3{%
	\def\tktl_postmatch_enctokens{#2}%
	\advance\tktl_pegcount_pos_cnt 1
	\tktl_pegcount_to_first_match{#2}{#3}%
}

\long\def\tktl_pegcount_all_matches#1#2{% #1=encotkens du texte  #2=pattern
	\tktl_pegcount_to_first_match{#1}{#2}%
	\unless\ifx\tktl_postmatch_enctokens\empty
		\tktl_antefi{\expandafter\tktl_pegcount_all_matches\expandafter{\tktl_postmatch_enctokens}{#2}}%
	\fi
}

%---------------------------------------------------------------------
%-------- Capture d'un motif de correspondance pour *1* token --------
%---------------------------------------------------------------------
%%%%%%%%%%%% capture d'un motif de correspondance pour *1* token
% Entrée : le motif est composé de :
%	- macro ayant 1 caractère qui spécifie le type de motif :
%		\S{<chaine>} : le token matche avec un des tokens de la <chaine>
%		\R{csv de charcodes range "<a>-<b>" ou "<c>" ou "*":csv de catcodes "<n>-<m>" ou "<x>" ou "*"} et si csv de catcodes absent: "*". Macros interdites dans les ranges.
%		\r{csv de chars range "<a>-<b>" ou "<c>":csv de catcodes "<n>-<m>" ou "<n>" ou "*"} et si csv de catcodes absent: "*". Macros interdites dans les ranges.
%
% Renvoie :
%	- \tktl_current_macro est la macro spécifiant le type de motif (\S, \r, ou \R)
%	- \tktl_current_macro_arg est l'argument de la macro
%---------------------------------------------------------------------
\long\def\tktl_grab_tokpattern#1{% #1= pattern unique pour 1 token
	\let\tktl_current_macro\empty
	\let\tktl_current_macro_arg\empty
	\tktl_grab_tokpattern_search_precapture#1\_nil
}
\def\tktl_grab_tokpattern_search_precapture{%
	\tktl_futurelet_nospace\tktl__futurtok\tktl_grab_tokpattern_grab_macro
}
\def\tktl_grab_tokpattern_grab_macro#1{% #1 = macro 
	\tktl_ifinstr{,#1,}{,\S,\r,\R,}
		{%
		\def\tktl_current_macro{#1}%
		\tktl_futurelet_nospace\tktl__futurtok\tktl_grab_tokpattern_grab_macro_arg
		}
		{%
		\errmessage{Found "\detokenize{#1}" when expecting \string\r, \string\R, or \string\S}%
		}%
}
\def\tktl_grab_tokpattern_grab_macro_arg{%
	\ifx\bgroup\tktl__futurtok
		\expandafter\tktl_grab_tokpattern_grab_macro_arg_a
	\else
		\errmessage{No open brace found after "\detokenize\expandafter{\tktl_current_macro}", aborting pattern}%
		\let\tktl_current_macro\empty
		\let\tktl_current_macro_arg\empty
		\expandafter\tktl_gob_enctoks_to_end_parse
	\fi
}
\long\def\tktl_grab_tokpattern_grab_macro_arg_a#1{% argument de la macro
	\def\tktl_current_macro_arg{#1}%
	\tktl_grab_pattern_remain
}
\long\def\tktl_compile_tokpatterns#1#2{% #1=numéro de groupe de pattern (commencer à 0)  #2=groupe de pattern
	\edef\tked_compile_tokpattern_n{\the\numexpr1000*#1}% compte les macros de compilation
	\tktl_compile_tokpatterns_a#2|\relax|%
}
\long\def\tktl_compile_tokpatterns_a#1|{% compiler les n patterns dans n+1 macros
	\edef\tked_compile_tokpattern_n{\the\numexpr\tked_compile_tokpattern_n+1}%
	\ifx\relax#1% macro n+1 -> échec, aucun match
		\expandafter\long\expandafter\def\csname tktl_test_match_tokpattern_\romannumeral\tked_compile_tokpattern_n\endcsname(##1:##2){\tktl_match_success_false}%
	\else
		\tktl_grab_tokpattern{#1}%
		\ifx\tktl_current_macro\tktl_macro_S
			\expandafter\tktl_catcode_string\expandafter{\tktl_current_macro_arg}% prendre en compte les \c{<catcode>}{texte} dans l'argument de \s
			\expandafter\long\expandafter\edef\csname tktl_test_match_tokpattern_\romannumeral\tked_compile_tokpattern_n\endcsname(##1:##2){%
				\noexpand\tktl_ifinstr{(##1:##2)}{\the\tktl_string_toks}%
					{\noexpand\tktl_match_success_true}
					{\expandafter\noexpand\csname tktl_test_match_tokpattern_\romannumeral\the\numexpr\tked_compile_tokpattern_n+1\relax\endcsname(##1:##2)}%
			}%
		\else% si \R ou \r
		\ifnum0\ifx\tktl_current_macro\tktl_macro_r1\else\ifx\tktl_current_macro\tktl_macro_R1\fi\fi=1
			\expandafter\tktl_analyse_R_pattern\expandafter{\tktl_current_macro_arg}%
			\ifx\tktl_current_macro\tktl_macro_r
				\expandafter\tktl_convert_charcsv_to_charcodecsv\expandafter{\tktl_charcode_interval}%
			\fi
			\expandafter\long\expandafter\edef\csname tktl_test_match_tokpattern_\romannumeral\tked_compile_tokpattern_n\endcsname(##1:##2){%
				\noexpand\tktl_iftokmatch(##1:##2){\unexpanded\expandafter{\tktl_charcode_interval}}{\unexpanded\expandafter{\tktl_catcode_interval}}
					{\noexpand\tktl_match_success_true}
					{\expandafter\noexpand\csname tktl_test_match_tokpattern_\romannumeral\the\numexpr\tked_compile_tokpattern_n+1\relax\endcsname(##1:##2)}%
			}%
		\fi\fi
		\expandafter\tktl_compile_tokpatterns_a
	\fi
}

%---------------------------------------------------------------------
%-------------------  Macro utilisateur \tokscount -------------------
%---------------------------------------------------------------------
%	\tokscount[clés=valeurs]{<p1> | <p2> | ... }{texte}
%
% où <pi> est un pattern pour 1 token, c'est-à-dire \r{<consigne>}, \R{<consigne>}, \S{<tokens>}
%
% Compte combien de tokens dans le texte correspondent aux <pi>.
% 
% Renvoie :
%	- si aucun <pi> -> compte le nombre de tokens
%	- sinon : nombre de correspondances (dans le flux ou dans une macro selon la clé "assign")
%	- les tokens ayant matché dans la consigne "assign match". Seuls les tokens de catcode
%	  3,4,7,8,10,11,12,13,16 sont capturés, les autres sont ignorés.
%	  Si la valeur de "assign match" est vide, ne renvoie aucun token
%---------------------------------------------------------------------
\defKV[tokscount]{
	assign=\def\tktl_assign_result{#1},
	expand arg= \tktl_ifinteger{#1}
					{\def\tktl_expand_arg_value{#1}}
					{%
					\errmessage{Key "expand arg" requires an integer}%
					\def\tktl_expand_arg_value{0}%
					},
	assign match= \def\tktl_assign_tokmatch{#1},
}
\setKVdefault[tokscount]{
	assign={},% comment assigner les tokens qui ont matché (si vide -> les afficher)
	expand arg=0,% développer le 2e argument obligatoire de \tokscount ?
	assign match={},% pas d'export de collecte par défaut
}

\def\tokscount{%
	\tktl_ifnxttok[%]
		{\tktl_tokscount_a}
		{\tktl_tokscount_a[]}%
}
\long\def\tktl_tokscount_a[#1]#2#3{% #1=clés/valeurs  #2=patterns  #3=texte
	\begingroup
		\let\tktl_tokscount_collect\empty
		\tktl_allow_post_pattern_catpure_false
		\tktl_allow_captures_false% ignore les captures
		\setKV[tokscount]{#1}%
		\ifnum\tktl_expand_arg_value>0
			\tktl_antefi{\tktl_expand_n_times\tktl_expand_arg_value}%
		\fi
		\tktl_encode_tokens{#3}%
		\tktl_ifempty_or_space{#2}
			{%
			\tktl_pegcount_cnt=\tktl_enctoks_len_cnt
			\let\tktl_tokscount_collect\empty% aucune collecte de tokens si pas de pattern
			}
			{%
			\edef\tktl_tokscount_enctokens{\the\tktl_enctoks_toks}%
			\tktl_compile_tokpatterns0{#2}% compiler les patterns
			\tktl_pegcount_cnt=0
			\expandafter\tktl_tokscount_b\tktl_tokscount_enctokens(-1:-1)%
			}%
		\expanded{%
	\endgroup
		\unless\ifx\tktl_assign_tokmatch\empty
			\tktl_unexpand_earg\tktl_decode_enctoks{\tktl_tokscount_collect}{\unexpanded\expandafter{\tktl_assign_tokmatch}}%
		\fi
		\tktl_ifx{\tktl_assign_result\empty}
			{\noexpand\tktl_id}
			{\unexpanded\expandafter{\tktl_assign_result}}%
		{\the\tktl_pegcount_cnt}%
	}%
}
\long\def\tktl_tokscount_b(#1:#2){% tester tous les enctokens
	\ifnum#2>0
		\tktl_test_match_tokpattern_i(#1:#2)% le token matche-t-il ?
		\iftktl_match_success_
			\tktl_ifinstr{,#2,}{,3,4,7,8,10,11,12,13,16,}% si catcode pas embêtant (différent de 1, 2 ou 6)
				{\tktl_addtomacro\tktl_tokscount_collect{(#1:#2)}}% ajouter au collecteur de tokens ayant matché
				{}%
			\advance\tktl_pegcount_cnt 1
		\fi
		\expandafter\tktl_tokscount_b
	\fi
}

%---------------------------------------------------------------------
%---------------------  Macro utilisateur \toksdo --------------------
%---------------------------------------------------------------------
\newif\iftktl_toksdo_collect_
\newif\iftktl_is_one_token_
\newtoks\tktl_result_toks
\defKV[toksdo]{%
	assign=\def\tktl_assign_result{#1},% fix v0.2
	expand arg= \tktl_ifinteger{#1}
					{\def\tktl_expand_arg_value{#1}}
					{%
					\errmessage{Key "expand arg" requires an integer}%
					\def\tktl_expand_arg_value{0}%
					},
	collect = \testboolKV{#1}\tktl_toksdo_collect_true\tktl_toksdo_collect_false,
}
\setKVdefault[toksdo]{
	assign={},% comment assigner les tokens qui ont matché (si vide -> les afficher)
	expand arg=0,% développer le 2e argument obligatoire de \toksdo ?
	collect = true,% collecter les tokens obtenus ?
}
\long\def\tktl_add_token_toksdo#1#2{%
	\advance\tktl_enctoks_len_cnt1
	\expandafter\tktl_add_to_parsetoks\expandafter{\expandafter(\expandafter[\the\tktl_enctoks_len_cnt]#1:#2)}%
}%
\def\toksdo{%
	\tktl_ifnxttok[%]
		{\tktl_toksdo_a}
		{\tktl_toksdo_a[]}%
}
\long\def\tktl_toksdo_a[#1]#2#3{% #1=clés/valeurs  #2=patterns  #3=texte
	\let\setcharcode_saved\setcharcode   \let\setcharcode\tktl_setcharcode
	\let\setcatcode_saved\setcatcode     \let\setcatcode\tktl_setcatcode
	\let\deltok_saved\deltok             \let\deltok\tktl_deltok
	\let\addtok_saved\addtok             \let\addtok\tktl_addtokt
	\let\selfcharcode_saved\selfcharcode
	\let\selfcatcode_saved\selfcatcode
	\let\selfindex_saved\selfindex
	\let\tktl_toksdo_collect\empty
	\tktl_allow_post_pattern_catpure_false
	\tktl_allow_captures_false% ignore les captures
	\restoreKV[toksdo]%
	\setKV[toksdo]{#1}%
	\let\tktl_add_token_saved\tktl_add_token
	\let\tktl_add_token\tktl_add_token_toksdo
	\ifnum\tktl_expand_arg_value>0
		\tktl_antefi{\tktl_expand_n_times\tktl_expand_arg_value}%
	\fi
	\tktl_encode_tokens{#3}%
	\let\tktl_add_token\tktl_add_token_saved
	\edef\tokslen{\the\tktl_enctoks_len_cnt}% nombre de tokens
	\edef\tktl_toksdo_enctokens{\the\tktl_enctoks_toks}%
	\tktl_ifempty_or_space{#2}
		{%
		\errmessage{No pattern and action found, using \string\R\detokenize{{*:*}->{}}}%
		\tktl_compile_toksdo_patterns{\R{*:*}->{}}%
		}
		{%
		\tktl_compile_toksdo_patterns{#2}%
		}%
	\let\tktl_toksdo_pattern_maxindex\tktl_toksdo_pattern_index
	\let\tktl_toksdo_enctok_collect\empty
	\expandafter\tktl_toksdo_b\tktl_toksdo_enctokens([-1]-1:-1)%
	\let\setcharcode\setcharcode_saved
	\let\setcatcode\setcatcode_saved
	\let\deltok\deltok_saved
	\let\addtok\addtok_saved
	\ifx\tktl_assign_result\empty
		\expandafter\tktl_decode_enctoks\expandafter{\tktl_toksdo_enctok_collect}{\tktl_result_toks}%
		\tktl_antefi{\the\tktl_result_toks}%
	\else
		\tktl_antefi{\expandafter\tktl_decode_enctoks\expanded{{\unexpanded\expandafter{\tktl_toksdo_enctok_collect}}{\unexpanded\expandafter{\tktl_assign_result}}}}%
	\fi
}
\long\def\tktl_toksdo_b([#1]#2:#3){% tester chaque enctokens
	\ifnum#3>0
		\def\selfindex{#1}%
		\def\selfcharcode{#2}%
		\def\selfcatcode{#3}%
		\def\tktl_toksdo_pattern_index{-1}%
		\let\tktl_addtok_code\tktl_addtok_code_default% ajouter le token seul par défaut
		\def\tktl_current_token{(#2:#3)}%
		\tktl_is_one_token_true
		\tktl_toksdo_c(#2:#3)%
		\unless\ifx\tktl_addtok_code\empty \iftktl_toksdo_collect_ % si ajout de token et collecte des tokens
			\ifx\tktl_addtok_code\tktl_addtok_code_default% si pas de redéfinition par \addtok, ne pas perdre de temps et ajouter le token
				\tktl_eaddtomacro\tktl_toksdo_enctok_collect\tktl_current_token
			\else
				\begingroup
					\expandafter\tktl_encode_tokens\expandafter{\tktl_addtok_code}%
					\expanded{%
				\endgroup
					\noexpand\tktl_subst_all% remplacer
						{(\noexpand\self:16)}% tous les \self par \tktl_current_token qui est (charcode:catcode) lorsqu'unique token
						{\unexpanded\expandafter{\tktl_current_token}}
						{\the\tktl_enctoks_toks}% et ajouter le résultat à \tktl_toksdo_enctok_collect
						{\noexpand\tktl_eaddtomacro\noexpand\tktl_toksdo_enctok_collect}%
				}%
			\fi
		\fi\fi
		\expandafter\tktl_toksdo_b
	\fi
}
\long\def\tktl_toksdo_c(#1:#2){% tester le token pour tous les patterns/actions spécifiés par l'utilisateur
	\edef\tktl_toksdo_pattern_index{\the\numexpr\tktl_toksdo_pattern_index+1}%
	\csname tktl_test_match_tokpattern_\romannumeral\numexpr1000*\tktl_toksdo_pattern_index+1\endcsname(#1:#2)% le token matche-t-il ?
	\iftktl_match_success_% si oui, effectuer l'action prévue et fin
		\expandafter\let\expandafter\tktl_do_next\csname tktl_toksdo_action_\romannumeral\tktl_toksdo_pattern_index\endcsname
	\else
		\ifnum\tktl_toksdo_pattern_index=\tktl_toksdo_pattern_maxindex\relax% dernier pattern atteint
			\let\tktl_do_next\empty
		\else
			\def\tktl_do_next{\tktl_toksdo_c(#1:#2)}%
		\fi
	\fi
	\tktl_do_next
}
\long\def\tktl_compile_toksdo_patterns#1{% #1 contient <pattern1> -> <action1>, <pattern2> -> <action2>, etc
	\def\tktl_toksdo_pattern_index{-1}%
	\tktl_compile_toksdo_patterns_a#1,\relax,%
}
\long\def\tktl_compile_toksdo_patterns_a#1,{%
	\tktl_ifempty_or_space{#1}
		{% ignorer si vide
		\tktl_compile_toksdo_patterns_a
		}
		{%
		\def\tktl_current_pattern{#1}%
		\unless\ifx\tktl_end_of_patterns\tktl_current_pattern
			\tktl_antefi{\tktl_compile_toksdo_patterns_b#1,}%
		\fi
		}%
}
\long\def\tktl_compile_toksdo_patterns_b#1->#2,{%
	\edef\tktl_toksdo_pattern_index{\the\numexpr\tktl_toksdo_pattern_index+1}%
	\expandafter\tktl_compile_tokpatterns\expandafter{\tktl_toksdo_pattern_index}{#1}% compiler les patterns
	\expandafter\def
		\csname tktl_toksdo_action_\romannumeral\tktl_toksdo_pattern_index\expandafter\expandafter\expandafter\endcsname
		\expandafter\expandafter\expandafter{\tktl_stripsp{#2}}% sauvegarder l'action à faire
	\tktl_compile_toksdo_patterns_a
}
\def\tktl_addtokt#{% comment ajouter le token en cours. Dans #1, la macro \self serprésente le token modifié (ou pas)
	\def\tktl_addtok_code
}
\def\tktl_addtok_code_default{%
	\self
}
\long\def\tktl_subst_all#1#2#3#4{% remplace toutes les occurrences de #1 par #2 dans #3, #4=consigne d'assignation
	\long\def\tktl_subst_all_a##1#1##2\_nil{%
		\tktl_ifinstr{#1}{##1#2##2}
			{\tktl_subst_all_a##1#2##2\_nil}
			{#4{##1#2##2}}%
	}%
	\tktl_ifinstr{#1}{#3}
		{\tktl_subst_all_a#3\_nil}
		{#4{#3}}%
}
\def\tktl_deltok{% équivalent à \addotk{}
	\let\tktl_addtok_code\empty
}
\def\tktl_setcharcode#1{%
	\iftktl_is_one_token_% que si c'est 1 seul token
		\unless\ifnum\selfcatcode=16 % ne rien toucher si une macro
			\ifnum\numexpr#1<0
				\errmessage{Negative charcode in \string\setcharcode\detokenize{{#1}}, directive ignored}%
			\else
				\edef\selfcharcode{\the\numexpr#1\relax}%
				\def\tktl__tmp{\expandafter\tktl_setcharcode_a\tktl_current_token}\expandafter\tktl__tmp\expandafter{\selfcharcode}%
			\fi
		\fi
	\fi
}
\def\tktl_setcharcode_a(#1:#2)#3{%
	\def\tktl_current_token{(#3:#2)}%
}
\def\tktl_setcatcode#1{%
	\iftktl_is_one_token_% que si c'est 1 seul token
		\edef\tktl_tmp{\the\numexpr#1\relax}%
		\ifnum\selfcatcode=16 % si token courant est une macro
			\ifnum\tktl_tmp=12 % et si catcode demandé=12
				\begingroup
					\expandafter\expandafter\expandafter\tktl_encode_tokens\expandafter\expandafter\expandafter{\expandafter\string\selfcharcode}%
					\expandafter
				\endgroup\expandafter
				\def\expandafter\tktl_current_token\expandafter{\the\tktl_enctoks_toks}%
				\tktl_is_one_token_false
			\fi
		\else % si si token courant  n'est pas une macro
			\expandafter\tktl_ifinstr\expandafter{\expandafter,\tktl_tmp,}{,1,2,3,4,6,7,8,10,11,12,13,}
				{%
				\let\selfcatcode\tktl_tmp
				\def\tktl__tmp{\expandafter\tktl_setcatcode_a\tktl_current_token}\expandafter\tktl__tmp\expandafter{\selfcatcode}%
				}
				{%
				\errmessage{Illegal catcode in \string\setcatcode\detokenize{{#1}}, directive ignored}%
				}%
		\fi
	\fi
}
\def\tktl_setcatcode_a(#1:#2)#3{%
	\def\tktl_current_token{(#1:#3)}%
}
\tktl_restore_catcode
\endinput

% Changelogs
0.2    17/04/2026
    - changement de syntaxe et de fonctionnalité pour \pegreplace
    - ajout d'une clé "mode" pour la macro \printtoks afin de
      pouvoir aussi diriger vers le fichier log les informations
      sur les tokens
    - mise en cohérence des consignes d'assignation. Plus aucune
      vérification n'est faite
    - améliorations du code - notamment compilation des motifs-,
      nettoyage

0.1    29/03/2026
    - version initiale