% !TeX encoding = ISO-8859-1 % Ce fichier contient le code commenté de l'extension "simplekv" % % IMPORTANT : pour que les commentaires s'affichent correctement, % ouvrir ce fichier avec l'encodage ISO-8859-1 % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % \def\skvname {simplekv} % \def\skvver {0.1} % % % \def\skvdate {2017/08/08} % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % -------------------------------------------------------------------- % This work may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.3 % of this license or (at your option) any later version. % The latest version of this license is in % % % http://www.latex-project.org/lppl.txt % % and version 1.3 or later is part of all distributions of LaTeX % version 2005/12/01 or later. % -------------------------------------------------------------------- % This work has the LPPL maintenance status `maintained'. % % The Current Maintainer of this work is Christian Tellechea % Copyright : Christian Tellechea 2017 % email: unbonpetit@netc.fr % Commentaires, suggestions et signalement de bugs bienvenus ! % Comments, bug reports and suggestions are welcome. % Copyright: Christian Tellechea 2017 % -------------------------------------------------------------------- % L'extension simplekv est composée des 5 fichiers suivants : % - code : simplekv (.tex et .sty) % - manuel en français : simplekv-fr (.tex et .pdf) % - fichier lisezmoi : README % -------------------------------------------------------------------- \expandafter\edef\csname skv_restorecatcode\endcsname{\catcode`\noexpand\_=\the\catcode`\_\relax} \catcode`\_11 %################################################ %################################################ % Cette macro est équivalente à 0 et sert notamment à stopper le développement % de \romannumeral \chardef\skv_stop 0 % Définition du quark, notamment inséré à la fin d'une liste pour en reconnaitre % la fin et stopper la récursivité \def\skv_quark{\skv_quark} % Voici les macros habituelles de sélection d'arguments \long\def\skv_first#1#2{#1} \long\def\skv_second#1#2{#2} % Voici la macro pour 1-développer ou 2-développer le 2e argument (le 1er étant % dépouillé des accolades) % \skv_exparg{}{} devient {} % \skv_eearg{}{} devient {**} \long\def\skv_exparg#1#2{\expandafter\skv_exparg_i\expandafter{#2}{#1}}% \long\def\skv_eearg#1#2{\expandafter\expandafter\expandafter\skv_exparg_i\expandafter\expandafter\expandafter{#2}{#1}}% \long\def\skv_exparg_i#1#2{#2{#1}} % Et la macro pour 1-développer le 2e argument (le 1er et le 2e argument sont % dépouillés des accolades)% % \skv_expafter{}{} devient <*b> \long\def\skv_expafter#1#2{\expandafter\skv_expafter_i\expandafter{#2}{#1}} \long\def\skv_expafter_i#1#2{#2#1} % Enfin, la macro pour former le nom du 2e argument (le 1er est dépouillé des % accolades) % \skv_argcsname{}{} devient \ \def\skv_argcsname#1#{\skv_argcsname_i{#1}} \def\skv_argcsname_i#1#2{\skv_expafter{#1}{\csname#2\endcsname}} \def\skv_eaddtomacro#1#2{\skv_exparg{\skv_exparg{\def#1}}{\expandafter#1#2}} %################################################ %################ macros de test ################ %################################################ % Voici quelques macros à sélection d'arguments pour les tests \def\skv_ifcsname#1{\ifcsname#1\endcsname\expandafter\skv_first\else\expandafter\skv_second\fi} \long\def\skv_ifx#1{\ifx#1\expandafter\skv_first\else\expandafter\skv_second\fi} \long\def\skv_ifempty#1{\skv_exparg\skv_ifx{\expandafter\relax\detokenize{#1}\relax}} % Ces macros sont utiles pour \skv_removeextremespaces, qui retire les % espaces extrêmes de son argument % Voir codes 320-324 (http://progtex.fr/wp-content/uploads/2014/09/code.txt) % et pages 339-343 de "Apprendre à programmer en TeX" \long\def\skv_ifspacefirst#1{\expandafter\skv_ifspacefirst_i\detokenize{#10} \_nil} \long\def\skv_ifspacefirst_i#1 #2\_nil{\skv_ifempty{#1}} \expandafter\def\expandafter\skv_gobspace\space{} \def\skv_removefirstspaces{\romannumeral\skv_removefirstspaces_i} \long\def\skv_removefirstspaces_i#1{\skv_ifspacefirst{#1}{\expandafter\skv_removefirstspaces_i\expandafter{\skv_gobspace#1}}{\skv_stop#1}} \begingroup \catcode0 12 \long\gdef\skv_removelastspaces#1{\romannumeral\skv_removelastspaces_i#1^^00 ^^00\_nil} \long\gdef\skv_removelastspaces_i#1 ^^00{\skv_removelastspaces_ii#1^^00} \long\gdef\skv_removelastspaces_ii#1^^00#2\_nil{\skv_ifspacefirst{#2}{\skv_removelastspaces_i#1^^00 ^^00\_nil}{\skv_stop#1}} \endgroup \long\def\skv_removeextremespaces#1{% \romannumeral\expandafter\expandafter\expandafter\skv_removelastspaces\expandafter\expandafter\expandafter {\expandafter\expandafter\expandafter\skv_stop\skv_removefirstspaces{#1}}% } %################################################ %############## système clé/valeur ############## %################################################ % Ceci est le booléen indiquant si la lecture de = définit les % _par défaut_ ou qu'il s'agit d'une _redéfinition_ des déjà % existantes \newif\ifskv_default % macros chapeau appelant la même macro avec le booléen préalablement défini \def\setKVdefault{\skv_defaulttrue\skv_readKV} \def\setKV{\skv_defaultfalse\skv_readKV} % L'argument obligatoire #1 est le nom du et #2 est l'ensemble % des = \def\skv_readKV[#1]#2{% \skv_ifempty{#2} % Si aucune = alors qu'on définit les par défaut, % message d'erreur et on s'arrête là {\ifskv_default\errmessage{No key/val found, no default key/val defined}\fi} % Sinon, initialiser à la macro \skv_[] uniquement si on % créé les par défaut {\ifskv_default\skv_argcsname\let{skv_[#1]}\empty\fi % Puis on passe aux choses sérieuses, on va lire un par un tous les éléments % = (contenus dans #2) en mettant le quark comme dernier couple % pour montrer la fin de la liste \skv_readKV_i[#1]#2,\skv_quark,% }% } \def\skv_readKV_i[#1]#2,{% % #2 est le premier couple "=" de la liste qui reste à traiter : % tout d'abord, on se débarrasse des espaces extrêmes \skv_eearg{\def\__temp}{\skv_removeextremespaces{#2}}% % Si ce qui en résulte est égal au , \skv_ifx{\__temp\skv_quark} % alors, on a fini et on ne fait rien (fin du processus) {} % Sinon, si ce qui en résulte est vide (le couple "=" était donc % vide ou composé d'un espace) {\skv_ifx{\__temp\empty} % On a fini et on ne fait rien (fin du processus) {} % dans le cas contraire, on va isoler la et la du couple lu en % prenant soin de mettre à la fin "=" pour se prémunir du cas où % "=" ne contient que la "" et pas de signe "=", ce qui % ferait planter la macro à arguments délimités {\expandafter\skv_find_kv\__temp=\skv_quark\_nil[#1]% }% % Lorsque la et la est trouvée et stockée, recommencer et aller % lire le prochain couple "=" \skv_readKV_i[#1]% }% } % Voici la macro à arguments délimités à qui on a transmis % == (si = est l'élément lu) % ou % = (si est seule) % et qui va isoler la de la . \def\skv_find_kv#1=#2\_nil[#3]{% % #1 est ce qui se trouve avant le _premier_ signe "=" et % #2 est ce qui se trouve entre le premier signe "=" et le \_nil % Pour la , pas de problème, c'est _obligatoirement_ ce qui est avant le % signe "=", que ce signe soit présent dans le couple lu ou pas puisqu'on y a % rajouté "=". % On élimine les espaces extrêmes pour obtenir la définitive stockée dans % \__key (il faut 2-développer \skv_removeextremespaces pour qu'elle donne son % argument sans espace extrême) \edef\__key{\detokenize\expandafter\expandafter\expandafter{\skv_removeextremespaces{#1}}}% % Pour la , on lui ôte d'abord les espaces extrêmes \skv_eearg{\def\__val}{\skv_removeextremespaces{#2}}% \skv_ifx{\__val\skv_quark} % Si elle est égale au , alors la vaut "true" {\def\__val{true}}% % Sinon, ôter "=" de la fin de l'argument #2, éliminer les espaces % extrêmes de ce qui en résulte et stocker le tout dans \__val (tout ceci est % effectué par la macro \skv_find_val =) {\skv_find_val#2}% % Si on lit les = par défaut, \ifskv_default % assigner à la macro "\skv_[]_" la trouvée \skv_argcsname\let{skv_[#3]_\detokenize\expandafter{\__key}}\__val % Puis ajouter à la macro "\skv_[]", qui a été préalablement % initialisée à : % \def\skv_[]_{} \skv_argcsname\skv_eaddtomacro{skv_[#3]}% {\expandafter\def\csname skv_[#3]_\detokenize\expandafter{\__key}\expandafter\endcsname\expandafter{\__val}}% % C'est selon ce hashage que sont enregistrés les couples / : les % sont contenues dans les noms des macros tandis que les sont % les textes de remplacement de ces macros. % C'est rapide et simple : % a) pour trouver une d'après sa , il suffit de développer la % macro \skv_[]_ % b) pour redéfinir une , il suffit de redéfinir cette macro avec la % nouvelle % c) il est facile de vérifier qu'une existe en vérifiant que la macro % associée est définie, la primitive \ifcsname le fait très bien % d) en revanche, on ne peut pas faire de recherche _inverse_ de façon % pratique : il est en effet plus difficile de trouver la (ou les) % contenant une donnée, mais cette limitation n'a pas grande % importance ici (je ne sais pas si les autres systèmes de / % sont programmés de telle sorte que cela soit simple...) % Bref, la macro "\skv_[]" contient donc _toutes_ les définitions % des macros définissant les / _par défaut_ et exécuter % "\skv_[]" remet donc toutes les à leur par défaut. \else % Dans le cas où on _lit_ des nouvelles pour des \skv_ifcsname{skv_[#3]_\__key} % Si la existe (ssi la macro "\skv_[]_" est définie), % alors assigner la à cette macro {\skv_argcsname\let{skv_[#3]_\__key}\__val}% % Sinon, émettre un message d'erreur et ne rien faire de plus {\errmessage{Key "\__key" is not defined: nothing is modified}}% \fi } % Cette macro à qui on a transmis "=" ne garde que , en % ôte les espaces extrêmes et stocke le résultat dans \__val \def\skv_find_val#1=\skv_quark{\skv_eearg{\def\__val}{\skv_removeextremespaces{#1}}} % Cette macro remet toutes les à leur par défaut en exécutant % la macro "\skv_[]" \def\useKVdefault[#1]{% \skv_ifcsname{skv_[#1]} {\csname skv_[#1]\endcsname} % Si la macro "\skv_[]" n'existe pas, message d'erreur {\errmessage{Undefined set of keys "#1"}}% } % Cette macro donne la correspondant à la contenue dans #2 \def\useKV[#1]#2{% % Avec \romannumeral, la sera obtenue après _2_ développements de % \useKV[]{} \romannumeral\skv_ifempty{#2} % Si la est vide, message d'erreur (il ne peut y avoir de % associée à une vide) {\skv_stop\errmessage{Key name missing}} {\skv_ifcsname{skv_[#1]_\skv_removeextremespaces{#2}} % Si la macro "\skv_[]_" existe, 2-développer le \csname pour % avoir la {\expandafter\expandafter\expandafter\skv_stop\csname skv_[#1]_\skv_removeextremespaces{#2}\endcsname} % Sinon, message d'erreur {\skv_stop\errmessage{Key "\skv_removeextremespaces{#2}" not defined}}% }% } % Voici une macro purement développable qui teste si #2 (la du % #1) est égale à "true" ou à "false" et selon l'issue, exécute le 1er ou 2e % argument qui suit (arguments appelés et ) \def\ifboolKV[#1]#2{% % Cette macro donnera un des 2 arguments ou en _2_ développements % grâce au \romannumeral \romannumeral\skv_ifempty{#2} % Si la est vide, message d'erreur {\skv_stop\errmessage{Key name missing}\skv_second} {\skv_ifcsname{skv_[#1]_\skv_removeextremespaces{#2}} % Si la débarrassée de ses espaces extrêmes existe, tester son contenu {\skv_eearg\ifboolKV_i{\csname skv_[#1]_\skv_removeextremespaces{#2}\endcsname}} % Sinon, message d'erreur {\skv_stop\errmessage{Key "\skv_removeextremespaces{#2}" not defined}\skv_second}% }% } % Cette macro teste si #1, qui est une , vaut "true" ou "false" \def\ifboolKV_i#1{% % Tester d'abord si elle vaut "true" \skv_ifargtrue{#1} {\expandafter\skv_stop\skv_first} {\skv_ifargfalse{#1} % Puis si elle vaut "false" {\expandafter\skv_stop\skv_second} % Si ni l'un ni l'autre, la n'est pas un booléen acceptable {\skv_stop\errmessage{Value "#1" is not a valid boolean}\skv_second}% }% } % La macro \skv_ifargtrue{} teste de façon purement développable si % vaut "true" ou "false". % Pour cela, on transmet à \skv_ifargtrue_i l'argument "true" qui est % délimité par \_nil \def\skv_ifargtrue#1{\skv_ifargtrue_i#1true\_nil} % Dans la macro \skv_ifargtrue_i, l'argument #1 est ce qui se trouve avant % "true" dans "true" : % - s'il n'est pas vide, sélectionner l'argument % - s'il est vide, cela signifie que commence par "true" ; il est % donc de la forme "true" % L'argument #2 est ce qui se trouve après "true" dans "truetrue", % c'est donc "true". % Pour être sûr que est , on transmet "true" à % \skv_ifargtrue_ii qui teste si la réunion de ce qui est avant le % premier "true" et ce qui est après est \def\skv_ifargtrue_i#1true#2\_nil{\skv_ifempty{#1}{\skv_ifargtrue_ii#2\_nil}\skv_second} \def\skv_ifargtrue_ii#1true#2\_nil{\skv_ifempty{#1#2}} % On procède de même pour tester "false" \def\skv_ifargfalse#1{\skv_ifargfalse_i#1false\_nil} \def\skv_ifargfalse_i#1false#2\_nil{\skv_ifempty{#1}{\skv_ifargfalse_ii#2\_nil}\skv_second} \def\skv_ifargfalse_ii#1false#2\_nil{\skv_ifempty{#1#2}} \def\showKV[#1]#2{% % Ecrire dans le fichier log "Key []=" \immediate\write-1 {Key [#1]\skv_removeextremespaces{#2}=% \skv_ifcsname{skv_[#1]_\skv_removeextremespaces{#2}} % si la est définie, prendre le \meaning de la macro correspondante {\expandafter\expandafter\expandafter\skv_show\expandafter \meaning\csname skv_[#1]_\skv_removeextremespaces{#2}\endcsname} % Sinon, afficher {not defined}% }% } % Mange ce qui se trouve jusqu'à "->" dans le développement de \meaning \def\skv_show#1->{} \skv_restorecatcode \endinput Versions : _____________________________________________________________________________ | Version | Date | Changements | |-----------------------------------------------------------------------------| | 0.1 | 08/08/2017 | Première version | |-----------------------------------------------------------------------------|