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

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                    %
\def\tktlname                   {tokstools}                          %
\def\tktlver                       {0.1}                             %
%                                                                    %
\def\tktldate                   {2026/03/29}                         %
%                                                                    %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                    %
% 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 Tools for tokens (CT)}%
\fi

\expandafter\edef\csname tktl_restore_catcode\endcsname{\catcode\number`\_=\number\catcode`\_\relax}
\catcode`\_11
% Macros de manipulation d'arguments
\long\def\tktl_exec_first#1#2{#1}
\long\def\tktl_exec_second#1#2{#2}
\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_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{%
	\tktl_ifx{\tktl__futurtok\tktl_sp_token}
		{%
		\afterassignment\tktl_ifnxttok_a
		\let\tktl__futurtok=
		}
		{%
		\tktl_ifx{\tktl__futurtok\tktl_toksmatch}
			\tktl__truecode
			\tktl__falsecode
		}%
}
\def\tktl_futurelet_nospace#1#2{%
	\def\tktl_futurelet_nospace_a{\futurelet#1\tktl_futurelet_nospace_b}%
	\def\tktl_futurelet_nospace_b{%
		\tktl_ifx{\tktl__futurtok\tktl_sp_token}
			{%
			\afterassignment\tktl_futurelet_nospace_a
			\let#1=
			}
			{%
			#2%
			}%
	}%
	\tktl_futurelet_nospace_a
}
% Quarks
\def\active_quark{\active_quark}
\def\end_parse{\end_parse}
\def\tktl_gob_enctoks_to_end_parse#1\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_earg#1#2{\expandafter\tktl_earg_i\expandafter{#2}{#1}}
\long\def\tktl_eearg#1#2{\expandafter\expandafter\expandafter\tktl_earg_i\expandafter\expandafter\expandafter{#2}{#1}}
\long\def\tktl_earg_i#1#2{#2{#1}}
\long\def\tktl_etwoargs#1#2#3{\tktl_earg{\tktl_earg{#1}{#2}}{#3}}
\long\def\tktl_addtomacro#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}
\long\def\tktl_eaddtomacro#1#2{\expandafter\tktl_addtomacro\expandafter#1\expandafter{#2}}
\def\tktl_addtotoks#1#2{#1\expandafter{\the#1#2}}
\def\tktl_eaddtotoks#1#2{\expandafter\tktl_addtotoks\expandafter#1\expandafter{#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\expandafter\tktl_exec_first\else\expandafter\tktl_exec_second\fi}
\def\tktl_ifnum#1{\ifnum#1\expandafter\tktl_exec_first\else\expandafter\tktl_exec_second\fi}
\long\def\tktl_ifcat#1{\ifcat#1\expandafter\tktl_exec_first\else\expandafter\tktl_exec_second\fi}
\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{\tktl_earg{\def\tktl_list_specials}{\tktl_list_specials#1,}}
\let\tktl_bg_token{
\let\tktl_eg_token}
\def\tktl_ifnumcase_endif{\tktl_ifnumcase_endif}
\def\tktl_ifnumcase_elseif{\tktl_ifnumcase_elseif}
\def\tktl_ifnumcase#1#2{% #1=<nombre>   #2=prochain argument
	\tktl_ifx{\tktl_ifnumcase_elseif#2}%
		{%
		\tktl_idto_endif
		}
		{\tktl_ifx{\tktl_ifnumcase_endif#2}%
			{}
			{%
			\tktl_ifnum{#1=#2 }
				{\tktl_firstto_endif}
				{\tktl_ifnumcase_i{#1}}%
			}%
		}%
}
\def\tktl_ifnumcase_i#1#2{\tktl_ifnumcase{#1}}
\def\tktl_idto_endif#1\tktl_ifnumcase_endif{#1}
\long\def\tktl_firstto_endif#1#2\tktl_ifnumcase_endif{#1}

%---------------------------------------------------------------------
%---------------- 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
	\tktl_eearg{\def#2}{\tktl_stripsp{#1}}%
	\tktl_eearg\tktl_ifempty_or_space{\expandafter\tktl_gob_arg#2}
		{%
		\edef#2{\number\expandafter`\csname#2\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
%	- si #1 vide, \tktl_intervalmin=\tktl_intervalmax=0 (\errmessage)
%	- #1 est de la forme
%		. x ou {x}
%		       si x non entier \tktl_intervalmin=\tktl_intervalmax=0 (\errmessage)
%		       si x<0, \tktl_intervalmin=\tktl_intervalmax=-x (\errmessage)
%		       sinon \tktl_intervalmin=\tktl_intervalmax=x
%		. x-y ou {x}-{y}, x et y entiers
%		       si les 2 vides, \tktl_intervalmin=\tktl_intervalmax=1 (\errmessage)
%		       si entier négatif, on prend l'opposé
%		       si x non entier ou vide \tktl_intervalmin=0 (\errmessage)
%		       si y non entier ou vide \tktl_intervalmax=maxint (\errmessage)
%		       si x>y, \tktl_intervalmin=y et \tktl_intervalmax=x (\errmessage)
%		       sinon, \tktl_intervalmin=x et \tktl_intervalmax=y
% Retour :
%	macros \macroA (borne inf) et \macroB (borne sup)
%---------------------------------------------------------------------
\def\test_iftexnumber#1{\test_iftexnumber_i#1`\_nil}
\def\test_iftexnumber_i#1`#2\_nil{\tktl_ifempty_or_space{#1}}%
\edef\tktl_maxint_repeat{1114109}%{\number\dimexpr\maxdimen}
\newcount\tktl_test_cnt
\newif\iftktl_error_
\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_encode_tokensinterval#1#2#3{% #1 est de la forme "n" ou "min-max"  #2=macro recevant min   #3=macro recevant max
	\tktl_error_false
	\tktl_eearg{\def#2}{\tktl_stripsp{#1}}%
	\let\tktl_interval_input#2%
	\ifx#2\empty
		\tktl_error_true%
		\def#2{0}%
		\let#3#2%
	\else
		\tktl_ifinstr-{#1}
			{%
			\tktl_encode_tokensinterval_i#1\_nil#2#3%
			}
			{\tktl_earg\tktl_ifinteger#2%
				{%
				\edef#2{\number\numexpr#2\relax}%
				}%
				{%
				\tktl_error_true
				\def#2{0}%
				}%
			\let#3#2%
			}%
	\fi
}
\def\tktl_encode_tokensinterval_i#1-#2\_nil{%
	\tktl_eearg{\tktl_eearg\tktl_encode_tokensinterval_ii{\tktl_stripsp{#1}}}{\tktl_stripsp{#2}}%
}
\def\tktl_encode_tokensinterval_ii#1#2#3#4{%
	\tktl_ifempty_or_space{#1#2}
		{%
		\def#3{1}%
		\def#4{1}%
		\tktl_error_true
		}
		{%
		\def#3{#1}\def#4{#2}%
		\ifx\empty#3%
			\def#3{0}%
		\fi
		\ifx\empty#4%
			\let#4\tktl_maxint_repeat
		\fi
		\tktl_earg\tktl_ifinteger#3%
			{%
			\edef#3{\number\numexpr#3\relax}%
			}
			{%
			\tktl_error_true
			\def#3{0}%
			}%
		\tktl_earg\tktl_ifinteger#4%
			{%
			\edef#4{\number\numexpr#4\relax}%
			}
			{%
			\tktl_error_true
			\let#4\tktl_maxint_repeat
			}%
		\ifnum#3>#4
			\tktl_error_true
			\let\__#4%
			\let#4#3%
			\let#3\__
		\fi
		}%
	\iftktl_error_
		\errmessage{"\detokenize\expandafter{\tktl_interval_input}" invalid: "#3-#4" retained}%
	\fi
}

%---------------------------------------------------------------------
%--------------------- 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 de
%        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_ifinteger{#1}
		{}
		{\errmessage{"\detokenize{#1}" is not an integer, this error should not occur}}%
	\edef\tktl_int{\number\numexpr#1\relax}%
	\tktl_test_interval_i#2,\end_parse,%
}
\def\tktl_star{*}%
\def\tktl_test_interval_i#1,{%
	\tktl_ifx{\end_parse#1}
		{%
		\tktl_exec_second
		}
		{%
		\tktl_eearg{\def\tedt_currentinterval}{\tktl_stripsp{#1}}%
		\tktl_ifx{\tedt_currentinterval\tktl_star}
			{%
			\tktl_test_interval_gobtoend
			}
			{%
			\tktl_encode_tokensinterval{#1}\__min\__max
			\tktl_ifnum{\numexpr(\tktl_int-\__min)*(\tktl_int-\__max)>0 }% si n'appartient pas à [min ; max]
				\tktl_test_interval_i
				\tktl_test_interval_gobtoend
			}%
		}%
}
\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{%
	\let\tktl_do_next\tktl_do_with_normals% a priori, token normal
	\ifx\tktl_nxt_tok\end_parse\let\tktl_do_next\tktl_encode_gob_to_end\fi% fin de parsing ?
	% les tokens "#", " ", "{" et "}" sont considérés spéciaux
	\ifcat##\noexpand\tktl_nxt_tok
		\let\tktl_do_next\tktl_do_with_specials
		\def\tok_catcode{6}%
	\else
	\ifcat\tktl_sp_token\noexpand\tktl_nxt_tok
		\let\tktl_do_next\tktl_do_with_specials
		\def\tok_catcode{10}%
	\else
	\ifcat\tktl_bg_token\noexpand\tktl_nxt_tok
		\let\tktl_do_next\tktl_do_with_specials
		\def\tok_catcode{1}%
	\else
	\ifcat\tktl_eg_token\noexpand\tktl_nxt_tok
		\let\tktl_do_next\tktl_do_with_specials
		\def\tok_catcode{2}%
	\fi\fi\fi\fi
	\tktl_do_next
}
\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{%
		\tktl_ifcat{\tktl_nxt_tok\tktl_sp_token}
			{%
			\endgroup
			\tktl_addto_special_list{32}% tous les catcode 10 ont un charcode de 32
			\tktl_gob_tok_and_loop
			}
			{%
			\tktl_scan_special_iii
			}%
}
\long\def\tktl_scan_special_iii#1{% traite les autres cas spéciaux
		\tktl_earg{%
	\endgroup
	\tktl_addto_special_list}{\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,{%
		\tktl_ifx{\active_quark#1}
			{}
			{%
			\tktl_ifnum{#1<0 }
				{}
				{%
				\uccode0 #1
				\uppercase{\let^^00\active_quark}%
				}%
			\tktl_active_specials_i
			}%
	}
\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
	\tktl_ifnum{\tktl_current_charcode=-1 }
		{%
		\tktl_encode_macro
		}
		{%
		\tktl_earg{\tktl_earg\tktl_add_token{\tktl_current_charcode}}{\tok_catcode}%
		\tktl_gob_tok_and_loop
		}%
}
\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{%
	\tktl_ifx{\tktl_nxt_tok\end_parse}
		{%
		\tktl_encode_gob_to_end
		}
		{%
		\tktl_read_special_list
		\tktl_clean_special_list_i
		}%
}
\long\def\tktl_encode_normal#1{%
	\tktl_ifcat{\relax\noexpand#1}
		{%
		\tktl_add_token{#1}{16}%
		}
		{%
		\tktl_earg{\tktl_earg\tktl_add_token{\number`#1}}{\number\tktl_thecatcode#1 }%
		}%
	\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#111\else
	\ifcat\tktl_cat_L\noexpand#112\else
	\ifcat\tktl_cat_C\noexpand#13\else
	\ifcat\tktl_cat_D\noexpand#14\else
	\ifcat\tktl_cat_G\noexpand#17\else
	\ifcat\tktl_cat_H\noexpand#18\else
	\ifcat\expandafter\noexpand\tktl_cat_M\noexpand#113\else
		\errmessage{This error in \string\tktl_thecatcode\space should not occur with token "\detokenize{#1}"}%
	\fi\fi\fi\fi\fi\fi\fi
}

%---------------------------------------------------------------------
%--------------------- 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)%
		\tktl_earg{%
	\endgroup
	#2}{\unexpanded\expandafter{\the\tktl_enctoks_toks}}%
}
\long\def\tktl_decode_enctoks_a(#1){%
	\tktl_decode_enctoks_b(#1:0:)%
}
\long\def\tktl_decode_enctoks_b(#1:#2:#3){%  #1=charcode ou macro si #2=0 ; #2=catcode ; #3=reliquat
	\tktl_ifnumcase{#2}
		{-1}{%
			\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_c
				}
				{}%
			\tktl_gob_arg
			}
		{16}{%
			\tktl_add_to_parsetoks{#1}%
			}
		{1} {%
			\global\advance\tktl_openbrace_cnt1
			\begingroup
				\tktl_enctoks_toks{}%
				\lccode`\{ #1
			}
		{2} {%
			\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}%
				}%
			}
		{6} {%
			\begingroup
				\lccode`\# #1
				\lowercase{%
			\endgroup
			\tktl_add_to_parsetoks{##}}%
			}%
	\tktl_ifnumcase_elseif
		\begingroup
				\endlinechar-1
				\everyeof{\noexpand}%
				\scantokens{%
					\catcode`\* #2
					\lccode `\* #1
					\catcode`\_ 11
					\lowercase{%
		\endgroup
		\tktl_add_to_parsetoks{*}%
					}%*
				}%
	\tktl_ifnumcase_endif
	\tktl_decode_enctoks_a
}
\def\tktl_decode_enctoks_c{% 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_c
	\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(pos), 2(match pattern), 3(pos et match pattern)
%	- \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 (position)
%---------------------------------------------------------------------
\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}
\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
		\let\tktl_do_next\tktl_grab_pattern_grab_precapture
	\else
		\let\tktl_do_next\tktl_grab_pattern_search_predicate
	\fi
	\tktl_do_next
}
\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
		\def\tktl_current_predicate{-1}%
		\let\tktl_do_next\tktl_grab_predicate
	\else
	\ifx&\tktl__futurtok
		\def\tktl_current_predicate{1}%
		\let\tktl_do_next\tktl_grab_predicate
	\else
		\def\tktl_current_predicate{0}%
		\let\tktl_do_next\tktl_test_post_predicate
	\fi\fi
	\tktl_do_next
}
\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 "{"
		\let\tktl_do_next\tktl_grab_group_of_patterns% prendre le groupe de patterns
	\else
		\let\tktl_do_next\tktl_grab_pattern_grab_macro% sinon, prendre la macro
	\fi
	\tktl_do_next
}
\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
	\tktl_earg{\tktl_ifinstr{#1}}\tktl_list_of_patterns
		{% si #1 est une macro-pattern
		\tktl_earg{\tktl_ifinstr{#1}}\tktl_list_of_single_patterns
			{% et contient un pattern unique, reprendre avec le 1-dév de #1
			\tktl_ifnum{\tktl_current_predicate\tktl_current_precapture_directive=0 }
				{% si ni prédicat ni capture -> recommencer la capture du motif depuis le début avec le contenu de la macro-pattern
				\expandafter\expandafter\expandafter\tktl_grab_pattern_search_precapture\csname tktl_pattern_\string#1\endcsname
				}
				{% si prédicat et/ou capture -> le pattern unique _doit_ être considéré comme un groupe de patterns
				\def\tktl_current_macro{\tktl_patterngroup}%
				\expandafter\let\expandafter\tktl_current_macro_arg\csname tktl_pattern_\string#1\endcsname%
				\tktl_grab_pattern_grab_repeat_char
				}
			}
			{% si #1 ne contient pas un pattern unique -> groupe de patterns
			\def\tktl_current_macro{\tktl_patterngroup}%
			\expandafter\let\expandafter\tktl_current_macro_arg\csname tktl_pattern_\string#1\endcsname%
			\tktl_grab_pattern_grab_repeat_char
			}%
		}
		{%
		\errmessage{Macro "\string#1" is not \string\r, \string\R, \string\s, \string\S\space, \string\. or a defined pattern}%
		}%
}
\def\tktl_grab_pattern_grab_macro_arg{%
	\tktl_ifx{\bgroup\tktl__futurtok}
		{%
		\tktl_grab_pattern_grab_macro_arg_a
		}
		{%
		\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_gob_enctoks_to_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{%
	\let\tktl_do_next\tktl_grab_pattern_search_postcapture% aucun motif de répétition à priori
	\def\tktl_min_current_repeat{1}%
	\def\tktl_max_current_repeat{1}% 1 répétition a priori
	\ifx*\tktl__futurtok
		\let\tktl_do_next\tktl_grab_pattern_gobble_char
		\def\tktl_min_current_repeat{0}%
		\let\tktl_max_current_repeat\tktl_maxint_repeat
	\else
	\ifx+\tktl__futurtok
		\let\tktl_do_next\tktl_grab_pattern_gobble_char
		\def\tktl_min_current_repeat{1}%
		\let\tktl_max_current_repeat\tktl_maxint_repeat
	\else
	\ifx?\tktl__futurtok
		\let\tktl_do_next\tktl_grab_pattern_gobble_char
		\def\tktl_min_current_repeat{0}%
		\def\tktl_max_current_repeat{1}%
	\else
	\ifx^\tktl__futurtok
		\let\tktl_do_next\tktl_grab_pattern_gobble_char
		\let\tktl_do_next\tktl_grab_pattern_grab_repeat_arg
	\fi\fi\fi\fi
	\tktl_do_next
}
\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}%
		}%
}
\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
	\tktl_earg{\tktl_ifinstr{(\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}%
	\tktl_ifnum{#3=1 }
		{% \c est suivi de "{"
		\advance\tktl_string_len_cnt -1 % retirer le token "{"
		\let\tktl_catcode_directive\empty
		\tktl_catcode_string_b
		}
		{%\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_catcode_string_c
		}%
}
\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 à "}"
	\tktl_ifnum{#2=2 }
		{% si #1="}" -> fini de lire le catcode demandé
		\tktl_earg\tktl_ifempty_or_space{\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
			\tktl_earg\tktl_ifinstr{\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_catcode_string_c
		}
		{% pas d'accolade ouvrante -> on ajoute le chiffre et on recommence
		\edef\tktl_catcode_directive{\tktl_catcode_directive\tktl_charcode_to_digit{#1}}%
		\tktl_catcode_string_b
		}%
}
\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
	\tktl_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 "{"
		\tktl_catcode_string_e
		}
		{% n'est pas suivi d'une "{"
		\ifnum#2=16  % si #1 est une macro
			\ifnum\tktl_catcode_directive=12 % à détokéniser ?
				\begingroup
					\tktl_earg\tktl_encode_tokens{\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_catcode_string_d
		}%
}
\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
						\tktl_earg\tktl_encode_tokens{\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
			\tktl_eearg{\def\tktl_temp_macro_charcode_interval}{\tktl_stripsp{#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
			}%
		}%
}

%---------------------------------------------------------------------
%----- 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  #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_grab_pattern{#1}%
	\ifnum\tktl_current_precapture_directive=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
	\csname tktl_test_match_pattern_\expandafter\string\tktl_current_macro\endcsname
}
\expandafter\def\csname tktl_test_match_pattern_\expandafter\string\tktl_macro_s\endcsname{%
	\tktl_earg\tktl_catcode_string{\tktl_current_macro_arg}% prendre en compte les \c{<catcode>}{texte} dans l'argument de \s
	\tktl_etwoargs\tktl_test_match_s{\the\tktl_string_toks}{\tktl_postmatch_enctokens}%
	\tktl_test_match_pattern_a
}
\expandafter\def\csname tktl_test_match_pattern_\expandafter\string\tktl_macro_dot\endcsname{%
	\expandafter\tktl_test_match_dot\tktl_postmatch_enctokens(-1:-1)%
	\tktl_test_match_pattern_a
}
\expandafter\def\csname tktl_test_match_pattern_\expandafter\string\tktl_macro_S\endcsname{%
	\tktl_earg\tktl_catcode_string{\tktl_current_macro_arg}% prendre en compte les \c{<catcode>}{texte} dans l'argument de \S
	\edef\tktl_pattern_string{\the\tktl_string_toks}%
	\long\def\tktl_token_test_R_or_S(##1:##2){\tktl_earg{\tktl_ifinstr{(##1:##2)}}{\tktl_pattern_string}}%
	\expandafter\tktl_test_match_R_or_S\tktl_postmatch_enctokens(-1:-1)%
	\tktl_test_match_pattern_a
}
\expandafter\def\csname tktl_test_match_pattern_\expandafter\string\tktl_macro_R\endcsname{%
	\tktl_earg\tktl_analyse_R_pattern{\tktl_current_macro_arg}%
	\long\def\tktl_token_test_R_or_S(##1:##2){\tktl_etwoargs{\tktl_iftokmatch{##1}{##2}}{\tktl_charcode_interval}{\tktl_catcode_interval}}%
	\expandafter\tktl_test_match_R_or_S\tktl_postmatch_enctokens(-1:-1)%
	\tktl_test_match_pattern_a
}
\expandafter\def\csname tktl_test_match_pattern_\expandafter\string\tktl_macro_r\endcsname{%
	\tktl_earg\tktl_analyse_R_pattern{\tktl_current_macro_arg}%
	\tktl_earg\tktl_convert_charcsv_to_charcodecsv{\tktl_charcode_interval}%
	\long\def\tktl_token_test_R_or_S(##1:##2){\tktl_etwoargs{\tktl_iftokmatch{##1}{##2}}{\tktl_charcode_interval}{\tktl_catcode_interval}}%
	\expandafter\tktl_test_match_R_or_S\tktl_postmatch_enctokens(-1:-1)%
	\tktl_test_match_pattern_a
}
\expandafter\def\csname tktl_test_match_pattern_\expandafter\string\tktl_macro_group\endcsname{%
	\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
		\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_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
		\tktl_etwoargs\tktl_test_match_full_patterns\tktl_current_macro_arg\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
		\tktl_etwoargs\tktl_test_match_full_patterns\tktl_current_macro_arg\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
		\tktl_earg{\tktl_test_match_pattern{#1}}{\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
		\tktl_earg{\tktl_test_match_and_patterns{#1}}{\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}
				{}
				{%
				\tktl_earg\tktl_ifempty_or_space{\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}
		{%
		\tktl_earg{\tktl_ifinstr{#1}}{#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}
		{%
		\tktl_earg{\tktl_ifinstr{#1}}{#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}%
				}
				{%
				\begingroup% tester si le pattern est un unique motif
					\let\tktl_grab_pattern_remain\tktl_grab_pattern_remain_test_single
					\tktl_grab_pattern{#2}%
					\expandafter
				\endgroup% pas de prédicat ET motif unique ?
				\ifnum\tktl_current_predicate\iftktl_if_single_pattern_0\else1\fi=0
					\tktl_add_pattern_in_list#1\tktl_list_of_single_patterns
				\else
					\tktl_remove_pattern_in_list#1\tktl_list_of_single_patterns
				\fi
				\tktl_add_pattern_in_list#1\tktl_list_of_patterns
				\expandafter\def\csname tktl_pattern_\string#1\endcsname{#2}%
				}%
			}%
		}
		{%
		\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}%
	\expandafter\def\csname tktl_capture_position_maxindex_#2\endcsname{#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>{%
	\tktl_ifnum{#1>0 }
		{%
		\tktl_addtomacro\tktl_capture_pos_list{,#2}%
		\expandafter\def\csname tktl_pos_capture_\tktl_temp_capture_name#1\endcsname{#2}%
		\tktl_process_pos_capture_a
		}
		{%
		\ifx\tktl_capture_pos_list\empty\else% retirer la première virgule
			\tktl_eearg{\def\tktl_capture_pos_list}{\expandafter\tktl_gob_arg\tktl_capture_pos_list}%
		\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}%
	\expandafter\def\csname tktl_capture_tokens_maxindex_#2\endcsname{#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>{%
	\tktl_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}}}%
		\tktl_process_tok_capture_a
		}
		{%
		\ifx\tktl_capture_tok_list\empty\else% retirer la première virgule
			\tktl_eearg{\def\tktl_capture_tok_list}{\expandafter\tktl_gob_arg\tktl_capture_tok_list}%
		\fi
		}%
}
\def\poscapture#1{%
	\expanded{%
		\tktl_ifcontains_ddots{#1}
			{\tktl_poscapture_a#1\_nil}
			{\tktl_eearg{\tktl_poscapture_b{}}{\tktl_stripsp{#1}}}%
	}%
}
\def\tktl_poscapture_a#1:#2\_nil{%
	\tktl_eearg{\tktl_eearg\tktl_poscapture_b{\tktl_stripsp{#1}}}{\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}
			{\tktl_eearg{\tktl_tokcapture_b{}}{\tktl_stripsp{#1}}}%
	}%
}
\def\tktl_tokcapture_a#1:#2\_nil{%
	\tktl_eearg{\tktl_eearg\tktl_tokcapture_b{\tktl_stripsp{#1}}}{\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>}"
%---------------------------------------------------------------------
\defKV[pegmatch]{
	mode = \def\tktl_match_mode{#1},
	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}%
		\tktl_ifnum{\tktl_expand_arg_value>0 }
			{\tktl_earg\tktl_expand_n_times\tktl_expand_arg_value}
			{}%
		\tktl_encode_tokens{#3}%
		\tktl_init_match
		\tktl_match_position_cnt=1
		\let\tktl_prematch_enctokens\empty
		\tktl_earg{\tktl_test_match_full_patterns{#2}}{\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
				\tktl_earg\tktl_ifpegmatch_b{\tktl_postmatch_enctokens}{#2}%
			\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}},
}
\setKVdefault[printtoks]{
	expand arg=0,
	intertoks = 0.33em,
	printcharcode = true,
	printcatcode = true,
	hexcharcode = false,% TODO : convertir en hexadécimal
	baselinecoeff = 0.8,
	vlines=true,
	boxed = true,% le tout dans une \hbox ?
	code={},
}
\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 }
			{\tktl_earg\tktl_expand_n_times\tktl_expand_arg_value}
			{}%
		\tktl_encode_tokens{#2}%
		\ifboolKV[printtoks]{boxed}
			{%
			\hbox{%
				\ifboolKV[printtoks]{vlines}
					{%
					\vrule
					\hskip.5\dimexpr\useKV[printtoks]{intertoks}\relax
					}
					{%
					\hskip0pt
					}%
				\expandafter\tktl_printtoks_b\the\tktl_enctoks_toks(-1:-1)%
				}%
			}
			{%
			\ifboolKV[printtoks]{vlines}
				{%
				\vrule
				\hskip.5\dimexpr\useKV[printtoks]{intertoks}\relax
				}
				{%
				\hskip0pt
				}%
			\expandafter\tktl_printtoks_b\the\tktl_enctoks_toks(-1:-1)%
			}%
	\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}}
							{#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_dectohex#1{% #1=nombre base 10
	\def\tktl_hexbase_result{}%
	\tktl_dectohex_a{#1}%
	\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{\tktl_earg\tktl_dectohex_a{\the\tktl_test_cnt}}%
	\fi
}

%---------------------------------------------------------------------
%--------------------- Macro publique \pegreplace --------------------
%---------------------------------------------------------------------
% \pegreplace[clés=val]{<pattern>}{<texte>}{<remplacement>}
% où :
% 	- <pattern> est un motif ou une succession de motifs à chercher, 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 <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=\tktl_ifempty_or_space{#1}
				{\let\tktl_assign_result\tktl_id}
				{\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}},
}
\setKVdefault[pegreplace]{
	all=true,% true : remplace toutes les occurrences du pattern  false : 1re occurrence seulement
	assign={},% comment assigner les tokens qui ont matché (si vide -> les afficher)
	expand arg=0,% développer le 2e argument obligatoire de \pegreplace ?
}
\def\pegreplace{%
	\tktl_ifnxttok[%]
		{\tktl_pegreplace_a}
		{\tktl_pegreplace_a[]}%
}
\long\def\tktl_pegreplace_a[#1]#2#3#4{% #1=clé=val   #2=pattern   #3=texte   #4=replace directive
	\begingroup
		\tktl_allow_post_pattern_catpure_false
		\tktl_allow_captures_true% permet les captures
		\setKV[pegreplace]{#1}%
		\tktl_ifnum{\tktl_expand_arg_value>0 }
			{\tktl_expand_n_times\tktl_expand_arg_value}
			{}%
			\tktl_encode_tokens{#3}%
		\edef\tktl_replace_text{\the\tktl_enctoks_toks}%
		\tktl_encode_tokens{#4}%
		\edef\tktl_replace_pattern{\the\tktl_enctoks_toks}%
		\let\tktl_prematch_enctokens\empty
		\tktl_capture_pos_cnt=1
		\ifboolKV[pegreplace]{all}
			{\tktl_earg\tktl_pegreplace_all_matches}
			{\tktl_earg\tktl_pegreplace_to_first_match}
				{\tktl_replace_text}{#2}%
		\tktl_eaddtomacro\tktl_prematch_enctokens\tktl_postmatch_enctokens
		\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_decode_enctoks{\unexpanded\expandafter{\tktl_prematch_enctokens}}{%
		\ifx\tktl_assign_result\empty
			\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_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 -> faire les remplacements
			\tktl_pegreplace_to_first_match_b
		\else% si pas de match -> appel de la macro récursive qui mange un à un les enctokens
			\tktl_antefi{\expandafter\tktl_pegreplace_to_first_match_a\tktl_postmatch_enctokens\_nil{#2}}%
		\fi
		}%
}
\def\tktl_pegreplace_to_first_match_a(#1)#2\_nil#3{% #2=enctokens restant sans le 1er  #3=pattern
	\tktl_addtomacro\tktl_prematch_enctokens{(#1)}%
	\tktl_init_match
	\tktl_test_match_full_patterns{#3}{#2}%
	\iftktl_match_success_% si ça matche avec les enctokens #2
		\tktl_pegreplace_to_first_match_b% faire les remplacements
	\else% si pas de match -> recommencer avec les enctokens qui restent
		\tktl_antefi{\tktl_pegreplace_to_first_match{#2}{#3}}%
	\fi
}
\def\tktl_pegreplace_to_first_match_b{%
	\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\tktl_process_replace\tktl_replace_pattern\_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
}
\def\tktl_pegreplace_all_matches#1#2{% #1=encotkens du texte  #2=pattern
	\tktl_pegreplace_to_first_match{#1}{#2}%
	\ifx\tktl_postmatch_enctokens\empty
	\else
		\tktl_antefi{\tktl_earg\tktl_pegreplace_all_matches{\tktl_postmatch_enctokens}{#2}}%
	\fi
}
\long\def\tktl_process_replace#1\_nil#2#3{% #1=liste des remplacements <\d=code>  #2=\<d>  #3=<d>
	\tktl_ifx{\relax#2}
		{%
		\def\tktl_match_full_patterns{#1}%
		}
		{% 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_process_replace_a{#1}{#2}{#3}%
		}%
}
\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}%
		\tktl_ifnum{\tktl_expand_arg_value>0 }
			{\tktl_expand_n_times\tktl_expand_arg_value}
			{}%
			\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_earg\tktl_pegcount_all_matches{\the\tktl_enctoks_toks}{#2}%
		\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{\tktl_earg\tktl_pegcount_all_matches{\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{%
	\tktl_ifx{\bgroup\tktl__futurtok}
		{%
		\tktl_grab_tokpattern_grab_macro_arg_a
		}
		{%
		\errmessage{No open brace found after "\detokenize\expandafter{\tktl_current_macro}", aborting pattern}%
		\let\tktl_current_macro\empty
		\let\tktl_current_macro_arg\empty
		\tktl_gob_enctoks_to_end_parse
		}%
}
\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
			\tktl_earg\tktl_catcode_string{\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
			\tktl_earg\tktl_analyse_R_pattern{\tktl_current_macro_arg}%
			\ifx\tktl_current_macro\tktl_macro_r
				\tktl_earg\tktl_convert_charcsv_to_charcodecsv{\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}%
		\tktl_ifnum{\tktl_expand_arg_value>0 }
			{\tktl_expand_n_times\tktl_expand_arg_value}
			{}%
			\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
\def\tktl_check_valid_assign_directive#1#2{% #1=consigne #2=macro recevant le résultat
	\tktl_ifmacro{#1}
		{%
		\def#2{#1}%
		}
		{%
		\tktl_ifinstr\def{#1}
			{%
			\tktl_check_valid_assign_directive_a#1\_nil#2%
			}
			{%
			\errmessage{Invalid assignment directive \detokenize{#1} ignored}%
			\let#2\empty
			}
		}%
}
\def\tktl_check_valid_assign_directive_a#1\def#2\_nil#3{%
	\tktl_ifempty_or_space{#1}
		{%
		\tktl_ifmacro{#2}
			{%
			\def#3{\def#2}%
			}
			{%
			\errmessage{Invalid assignment directive \detokenize{#1\def#2} ignored}%
			\let#3\empty
			}
		}
		{%
		\errmessage{Invalid assignment directive \detokenize{#1\def#2} ignored}%
		\let#3\empty
		}%
}
\defKV[toksdo]{%
	assign=\tktl_ifempty_or_space{#1}
				{\let\tktl_assign_result\empty}
				{\tktl_check_valid_assign_directive{#1}\tktl_assign_result},
	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
	\tktl_earg\tktl_add_to_parsetoks{\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
	\tktl_ifnum{\tktl_expand_arg_value>0 }
		{\tktl_expand_n_times\tktl_expand_arg_value}
		{}%
		\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
	\tktl_ifx{\tktl_assign_result\empty}
		{%
		\tktl_earg\tktl_decode_enctoks{\tktl_toksdo_enctok_collect}{\tktl_result_toks}%
		\the\tktl_result_toks
		}
		{%
		\tktl_etwoargs\tktl_decode_enctoks{\tktl_toksdo_enctok_collect}{\tktl_assign_result}%
		}%
}
\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
					\tktl_earg\tktl_encode_tokens{\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}%
	\tktl_earg\tktl_compile_tokpatterns\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}%
				\tktl_earg{\expandafter\tktl_setcharcode_a\tktl_current_token}\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
					\tktl_eearg\tktl_encode_tokens{\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
			\tktl_earg\tktl_ifinstr{\expandafter,\tktl_tmp,}{,1,2,3,4,6,7,8,10,11,12,13,}
				{%
				\let\selfcatcode\tktl_tmp
				\tktl_earg{\expandafter\tktl_setcatcode_a\tktl_current_token}\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
