aplat_1_documents_structures_unix.conv (15321B)
1 titre:aplat(1) : documents structurés pour Unix 2 date_pub:25 janvier 2024 3 date_mod:4 juin 2024 4 date_pub:21a1 5 date_mod:2224 6 auteur:~ldp 7 type:article 8 uuid:89352ae7-b5d5-4c87-bf51-edd280ae0c62 9 10 Les utilitaires classiques de Unix se prêtent très bien à la manipulation de données structurées par des listes ou par des tableaux, c’est-à-dire à la manipulation de données qu’on encode aisément avec des lignes et des champs. Un bon nombre de ces utilitaires (grep, sed, awk, cut, sort, diff, uniq, etc.) ont en effet été conçus spécialement pour l’analyse et le traitement des données ligne par ligne. Aussi, ils offrent souvent la possibilité de reconnaître des segments de ligne séparés par un délimitateur arbitraire, faisant émerger ce deuxième niveau d’organisation qu’est le champs. 11 12 Il existe cependant d’autres types de strutures de données qu’on ne représente pas instinctivement par ces éléments, et les utilitaires qui s’attendent à trouver des données organisées ligne par ligne ont évidemment bien souvent de la difficulté à traiter correctement les formats de document structurés selon un autre principe. Parmi ces formats, on compte notamment le XML, le TeX, le Markdown, etc., mais aussi le C, le Shell, le Scheme, etc. Parce qu’ils supposent une structure au nombre de niveaux fixe et assez limité (souvent deux : la ligne et le champ), ces utilitaires semblent parfaitement inadaptés au traitement de ces autres formats, d’autant plus que plusieurs d’entre eux ont une structure récursive, admettant donc un nombre illimité de niveaux. 13 14 Bien que, donc, les utilitaires de traitement de texte conçus pour bien fonctionner dans un environnement de type Unix n’ont pas une portée absolument générale, ils restent très utiles pour mener à bien un grand nombre de tâches. Comme beaucoup d’autres, j’attribue cette utilité à leur simplicité, à leur (relative) cohérence, à leur composabilité et à leur omniprésence. Je les trouve en outre agréables à utiliser. Mais je ne venterai pas davantage leurs mérites, car le programme informatique que cet article doit présenter s’adresse avant tout à ceux que ces mérites séduisent déjà, surtout que ce programme est parfaitement inutile lorsqu’utilisé seul et qu’il ne trouve sa valeur que dans la coopérations avec ces autres utilitaires. 15 16 Puisqu’il est de toute façon utile d’apprendre à maîtriser ces utilitaires Unix et que je suis maintenant devenu à l’aise avec eux, je me suis donné pour objectif d’étendre leur portée en créant un format de document structuré selon le principe des lignes et des champs qui permettrait de représenter des données structurées selon n’importe quel autre principe. 17 18 Je présente ici le programme aplat(1) et les deux formats qu’il utilise. Le programme lit en entrée standard un document dans un certain format et exporte en sortie standard sa représentation dans un format pont organisé selon des lignes et des champs. 19 20 J’ai conçu le format de sortie pour qu’il soit facile pour les utilitaires classiques de Unix de le manipuler. Puisqu’il présente la structure des documents sous une forme applatie, j’ai décidé de lui donner le nom de plat. 21 22 J’ai conçu le format d’entrée pour qu’il soit facile pour une personne de le manipuler. C’est un format beaucoup plus simple à comprendre et selon moi beaucoup moins pénible à écrire à la main que le XML. Il s’inspire beaucoup du SXML. Puisqu’il s’oppose en quelque sorte au format plat(5), je lui ai donné le nom de aplat (non-plat). Il s’agit d’un a privatif, qu’un trouve par exemple dans des mots comme apatride ou anonyme. 23 24 Quant au programme, il se serait appelé àplat si la tradition restreignait pas le nom des programmes à l’ensemble des suite de caractères ASCII affichables. Sa tâche est en effet de passer d’aplat(5) à plat(5). 25 26 ## aplat(5) 27 28 Le format aplat(5) permet une organisation hiérarchique de données étiquettées. Il est inspiré du SXML et est aussi puissant que lui. C’est surtout pour des raisons de simplicité que j’ai choisi de créer un nouveau format de document plutôt que d’en utiliser un qui existait déjà. Bien que cette entreprise aurait été tout à fait possible, je ne voulais pas avoir à composer avec la complexité du XML, par exemple. Aussi, la création d’un nouveau format de toutes pièces donne une liberté bien plus grande. De toute façon, l’intérêt d’aplat(1)se trouve selon moi bien plus dans son format de sortie que dans son format d’entrée, et il est possible de créer un programme distinct traduisant le XML, le SXML, le HTML ou n’importe quel autre format vers plat(5). 29 30 Voici un exemple de document valide. 31 32 ``` 33 (doc 34 (titre Un\ programme\ en\ C) 35 (auteur ~ldp) 36 (date-pub 2023 - 11 - 18) 37 (date-mod (vide)) 38 (par "Voici un exemple de document dans le format aplat.") 39 (lien 40 (url "https://asteride.xyz/~ldp/exemple.txt") 41 (texte "Un exemple d’URL")) 42 (pre 43 """ langue=C 44 #include <stdio.h> 45 46 int 47 main(void) 48 { 49 printf("Bonjour tout le monde!\n"); 50 return 0; 51 } 52 """ 53 ) 54 (par "Ce paragraphe affiche " (it "Bonjour tout le monde!"))) 55 ``` 56 57 Je veux faire quelques remarques sur le contenu de cet exemple. Sachez cependant que cet article complète plutôt qu’il ne remplace les pages du manuel livrées avec le programme. 58 59 => /~ldp/man/aplat.1.txt aplat(1) 60 => /~ldp/man/aplat.5.txt aplat(5) 61 => /~ldp/man/plat.5.txt plat(5) 62 63 La structure hiérarchique d’un document aplat(5) est explicitée par les parenthèses qui encadrent une section de ce document. J’appelle domaine l’espace se trouvant entre deux parenthèses de même niveau. 64 65 Un domaine, s’il n’est pas vide, est composé d’atomes. Un atome est une chaîne de caractères séparés par une parenthèse ou par des blancs (espaces, caractères de tabulation ou nouvelles lignes). 66 67 Le premier atome d’un domaine correspond à l’étiquette. Il nomme un domaine. Tous les autres atomes d’un même domaine sont comme concatenés pour former une longue chaîne de caractères. Je nomme cette chaîne le contenu. « doc », « titre », « auteur », « vide », etc. sont tous des exemples d’étiquettes ; « ~ldp » est un exemple de contenu. Aussi, notons que, puisque les atomes adjacents sont concatenés, le segment « 2023 - 11 - 18 » correspond à l’atome « 2023-11-18 ». 68 69 Il est possible d’inclure des caractères spéciaux dans des atomes en les échappant. Ils seront alors traités comme des caractères normaux. 70 71 Il existe trois manières d’échapper des caractères spéciaux. 72 73 D’abord, on peut utiliser la barre oblique inversée (« \\ »). Son effet varie selon le caractère échappé. Les parenthèses, les espaces normaux, les caractères de tabulation, les guillemets et le caractère d’échappement lui-même sont interprétés dans leur sens littéral lorsque ce caractère les précède. Cependant, une nouvelle ligne échappée est ignorée. Dans l’exemple ci-dessus, la chaîne « Un\ programme\ en\ C » est interprété comme un seul atome et correspond à « Un programme en C ». 74 75 Ensuite, on peut placer les caractères à échapper entre guillemets. Les parenthèses et les blancs placés entre deux guillemets reçoivent leur interprétation littérale. Par exemple, ici la chaîne « "Voici un exemple de document dans le format aplat." » forme un seul atome. 76 77 Finalement, on peut créer un bloc en plaçant les caractères à échapper entre deux triples guillemets droits doubles (« """ »). Tous les caractères reçoivent alors leur interprétation littérale. À l’intérieur d’un bloc, il est possible d’échapper la séquence « """ » en la faisant suivre d’un point d’exclamation (« """! »). 78 79 Tous les caractères entre la séquence d’ouverture d’un bloc et la première nouvelle ligne, inclusivement sont ignorés. C’est pour faciliter le travail de préprocesseurs, qui fonctionneront d’une manière analogue à ceux de troff. Ces préprocesseurs, contrairement aux postprocesseurs, opéreront sur l’entrée de aplat(1) plutôt que sur sa sortie. On pourra alors leur fournir de l’information sur cette première ligne. 80 81 Les blocs sont utiles pour inclure verbatim un bloc de texte, comme un programme informatique ou un document HTML. 82 83 ### Métaformats 84 85 À partir de cette description, il est possible — nécessaire, même — de créer un métaformat adapté à certains besoins. Ainsi, on peut imaginer une manière de représenter les métadonnées comme le fait le XML. Cette idée est empruntée au SXML. Est une métadonnée (paire attribut-valeur) un domaines et son contenu s’ils se trouvent à l’intérieur d’un domaine étiquetté avec « @ ». 86 87 Voici l’exemple précédent converti à cette convention. 88 89 ``` 90 (doc 91 (@ (titre "Un programme en C") 92 (auteur "~ldp") 93 (date-pub "2023-11-18") 94 (date-mod (vide)) 95 (métaformat "genre-de-sxml")) 96 (par "Voici un exemple de document dans le format aplat.") 97 (lien (@ (ref "https://asteride.xyz/~ldp/exemple.txt")) 98 "Un exemple d’URL") 99 (pre 100 """ lang=C 101 #include <stdio.h> 102 103 int 104 main(void) 105 { 106 printf("Bonjour tout le monde!\n"); 107 return 0; 108 } 109 """ 110 ) 111 (par "Ce paragraphe affiche " (it "Bonjour tout le monde!"))) 112 ``` 113 114 Ici, les attributs « titre », « auteur », « date-pub », etc. portent sur le domaine nommé « doc » et l’attribut « ref » porte sur celui nommé « lien ». 115 116 ## plat(5) 117 118 Le format plat(5) est une représentation équivalente au aplat(5), au XML, etc. 119 120 Voici la représentation en plat du dernier exemple en aplat. 121 122 ``` 123 :doc: ( 124 :doc:@: ( 125 :doc:@:titre: () Un programme en C 126 :doc:@:auteur: () ~ldp 127 :doc:@:date-pub: () 2023-11-18 128 :doc:@:date-mod: ( 129 :doc:@:date-mod:vide: () 130 :doc:@:date-mod: ) 131 :doc:@:métaformat: () genre-de-sxml 132 :doc:@: ) 133 :doc:par: () Voici un exemple de document dans le format aplat. 134 :doc:lien: ( 135 :doc:lien:@: ( 136 :doc:lien:@:ref: () https://asteride.xyz/~ldp/exemple.txt 137 :doc:lien:@: ) 138 :doc:lien: ) Un exemple d’URL 139 :doc:pre: () #include <stdio.h>\n\nint\nmain(void)\n{\n\tprintf("Bonjour tout le monde!\\n");\n\treturn 0;\n} 140 :doc:par: ( Ce paragraphe affiche 141 :doc:par:it: () Bonjour tout le monde! 142 :doc:par: ) 143 :doc: ) 144 ``` 145 146 On voit tout de suite pourquoi ce format n’est pas destiné à être manipulé par des humains. Il est en revanche très adapté à des outils analysant les fichiers ligne par ligne et champ par champ. 147 148 Un document plat(5) est composé d’une série de lignes et de trois champs, séparés par des caractères de tabulation. 149 150 Le premier champ correspond au nom du domaine. Il prend la forme de la liste des toutes les étiquettes des domaines parents, précédées d’un deux-points (« : »). La dernière étiquette est aussi suivie d’un deux-points. Il n’est pas possible d’échapper ce caractère, car plusieurs outils auraient du mal à composer avec une telle syntaxe. 151 152 J’appelle domaine cadet le domaine le plus imbriqué. Ainsi, à la ligne commençant par « :doc:@:date-mod:vide: », le domaine cadet est « :vide: », alors qu’à la première ligne le domaine cadet est « :doc: ». 153 154 Tout nom de domaine doit apparaître au moins une fois en position de domaine cadet, et ce même s’il est vide. Par exemple, bien que « :@: », dans le domaine « :doc:@: », ne renferme que d’autres domaines, il a droit à sa ligne, de même que « :vide: ». 155 156 Le second champ est une liste de drapeaux. Pour l’instant — et je ne pense pas en ajouter de si tôt —, il en existe deux: la parenthèse ouvrante (« ( ») et la parenthèse fermante (« ) »), dénotant respectivement la limite initiale et la limite finale d’un domaine. 157 158 Le troisième champ correspond au « contenu » du format aplat(5). 159 160 plat(5) est organisé selon le principe des lignes et des champs ; les lignes sont séparées par le caractère de saut de ligne et les champs par un caractère de tabulation. Pour utiliser ces caractères au sens propre, il suffit de les échapper. On peut voir le mécanisme d’échappement à l’œuvre à la ligne commençant par « :doc:pre: » dans l’exemple ci-dessus. 161 162 ## Utilisation d’aplat(1) 163 164 Le programme aplat(1) ne lit aucun argument en ligne de commandes : ni options ni nom de fichier ; il se contente de lire en entrée standard et d’écrire en sortie standard. 165 166 Il s’ulitise donc de la manière suivante. 167 168 ``` 169 $ aplat <entree.aplat >sortie.plat 170 ``` 171 172 En supposant que le document aplat(5) de l’exemple précédent se trouve dans le fichier bonjour.aplat, on pourrait écrire la séquence de commandes suivantes pour en extraire le programme, le compiler et l’exécuter. 173 174 ``` 175 $ aplat <bonjour.aplat | grep '^:doc:pre: ' | cut -f3 | 176 { tr -d '\n'; echo; } | xargs -0 printf '%b' | 177 cc -o bonjour -x c - && ./bonjour 178 Bonjour tout le monde! 179 ``` 180 181 Ici, aplat fait passer du format aplat(5) au format plat(5) le contenu du fichier bonjour.aplat ; grep ne retient de la sortie de la commande précédente que les lignes dont l'étiquette est :doc:pre: ; cut isole le troisième champ ; tr fusionne toutes les lignes en supprimant tous les caractère de saut de ligne ; echo réinsère la dernière nouvelle ligne, que tr avait supprimé en trop ; xargs -0 printf '%b' a pour effet de déséchapper les caractères de nouvelle ligne et de tabulation, cc compile le texte résultant et, finalement, ./bonjour exécute le programme. 182 183 ## Conclusion 184 185 J’ai créé cet utilitaire après de nombreuses heures de patentatage avec les solutions existantes. Je cherchais un format de document sémantique qui soit à la fois agréable à utiliser et qui s’intégre bien avec les utilitaires Unix. Je suis content de dire que cette recherche est maintenant terminée ; je suis satisfait de ma création. 186 187 Je ne prétends pas que ce programme et que les formats qu’il utilise soient d’une utilité universelle. On a vu dans le dernier exemple que les chaînes de commandes qui manipulent des documents plat(5) sont assez complexes. Cet exemple est même relativement simple dans son genre, puisqu’il ignore complètement le deuxième champ, celui des parenthèses, dont on n’aurait pas pu se passer si on n’avait pas préssuposé que le champ ne contiendrait qu’un seul domaine avec l’étiquette :doc:pre:. Aussi le format plat(5) est-il mieux adapté à être manipulé dans des scripts qu’à la ligne de commandes. On peut cependant abstraire une partie de la chaîne de commandes de cet exemple et en la plaçant dans un fichier compiler_c. 188 189 ``` 190 $ cat compiler_c 191 #!/bin/sh 192 193 grep '^:doc:pre: ' | 194 cut -f3 | 195 { tr -d '\n'; echo; } | 196 xargs -0 printf '%b' | 197 cc -o "$1" -x c - 198 $ chmod +x compiler_c 199 ``` 200 201 On peut alors composer : 202 203 ``` 204 $ aplat <bonjour.aplat | ./compiler_c "bonjour" && ./bonjour 205 Bonjour tout le monde! 206 ``` 207 208 J’ai de nombreuses idées de programmes auxiliaires qui permettront de faciliter l’utilisation des formats aplat(5) et plat(5). Je leur dédierai certainement d’autres articles. 209 210 ## Voir aussi 211 212 => /~ldp/publications/aplat/aplat.tgz Télécharger aplat(1) 213 => https://git.asteride.xyz/~selve/aplat/ Dépôt git du programme.