Auteur: Joseph Colineau Date: 27 janvier 2004
1. Introduction
1.1. Origine
1.2. Que fait le logiciel ?
2. Structure du logiciel
2.1. Boucle principale
2.2. Règles
2.3. Fonctions
2.4. gestion du fichier de données
2.5. gestion des fichiers de données
2.6. fonctions de recherche
2.7. accès à l'interpréteur Rebol
2.8. aide
2.9. Personnalisation
2.10. Divers
3. Evolutions
4. Conclusion
En avez-vous eu assez de ces multiples clics qui sont nécessaires pour atteindre une information en environnement bureautique. Le règne du tout graphique ralentit sérieusement l'accès à l'information: annuaire téléphonique en ligne, prise de notes facile, mais nécessitant de lancer une appli, information disponible sur la base de données intranet, mais passage par tous les clicks de lancement du browser. Sans compter toutes les tâches spécifiques et différentes pour chacun, liées à son activité professionnelle quotidienne. Rebol peut vous simplifier la vie !
L'origine de ce projet sans ambition est d'avoir sous la main un moyen simple et rapide de remplacement de ces multiples post-it qui ornent mon bureau. Avoir sur mon écran une fenêtre ouverte en permanence pour écrire en vrac ce qui me passe par la tête, et le retrouver facilement. D'où le nom du logiciel : "NoteTank" ou réservoir à notes. Je me suis vite aperçu qu'avec Rebol il était simple de faire plus, et de personnaliser facilement, comme vous le verrez plus loin. La difficulté, maintenant, est de ne pas multiplier à l'infini les fonctions, afin de conserver un outil simple et efficace.
Le tout en mode console, gràce à l'utilisation d'un petit nombre de commandes simples (et à personnaliser). Par exemple:
- Prise de notes.
- Recherche dans un fichier (N° de téléphones, adresses ...).
- Interprétation d'expressions Rebol.
- Facilité d'introduction de fonctions nouvelles.
- le texte de ma note : prise d'une note dans le fichier de stockage.? tex : recherche et affichage de toutes les notes qui comportent les caractères "tex".
tel dur : recherche dans mon annuaire téléphonique de toutes les personnes dont le nom comporte les caractères "dur".
R do anamonitor.r : exécution de l'expression Rebol "do anamonitor.r".
> word : traduction d'un mot.
aide : affichage des commandes disponibles.
200 * 300 : expression Rebol
script1 : lancement du script script1.r
Après un chargement du fichier de la "base de données", le programme se met en attente d'entrées, qu'il traite à chaque retour chariot
if not exists? to-file rep-files [make-dir to-file rep-files] dbase: load-db fichier-dbaseLa fonction d'interprétation des entrées est input-parse. Elle est basée sur le "parsing" de la phrase soumise à l'entrée par l'utilisateur. On va successivement analyser cette phrase au regard des règles que l'on a spécifiées. Deux cas sont prévus:forever [ in-str: ask ">> " if error? try [input-parse in-str] [print "?"] ]
- mot de commande seul: bye, date, etc l'opération s'écrit, par exemple pour tester l'un des trois mots (synonymes) définis pour sortir du programme, bye, quit, ou exit:
if parse blk [["bye"|"quit"|"exit"] to end][print "bye ..." save-db dbase fichier-dbase ]
- mot de commande avec paramètre: noter message, etc ... le message est copié dans la variable aa, et exploité dans le bloc d'instructions qui suit
voici le code de la fonction de parsing. Le test de sortie du fichier est effectué d'abord. Ensuite, on passe en revue dans une boucle l'ensemble des règles. Comme nous le verrons plus loin, les règles sount définies dans un bloc de blocs. Chaque règle est constituée d'un bloc de trois blocs: le second (regle/2) est la règle de parsing (par exemple [["noter"|"note"|"-"] copy aa to end]), et le troisième (regle/3) le code à exécuter si la règle est vérifiée ([noter aa exit])if parse blk [["noter"|"note"|"-"] copy aa to end][noter aa]
En fait, on teste l'entrée en cherchant successivementinput-parse: func [str /local blk lstr aa] [ blk: copy str if parse blk [["bye" | "quit" | "exit"] to end] [print "bye ..." save-db dbase fichier-dbase break] foreach regle regles [ if parse blk regle/2 regle/3] print "?" i-rec: 0 ]
- les mots de commande définis dans la liste des règles
- le nom d'un script présent dans le répertoire "scripts/"
- une expression Rebol à interpréter
input-parse: func [str /local blk lstr aa] [ blk: copy str ; test fin programme if parse blk [["bye" | "quit" | "exit"] to end] [print "bye ..." save-db dbase fichier-dbase break] ; test règles NoteTank foreach regle regles [ if parse blk regle/2 regle/3] ; test scripts if error? try [do to-file rejoin [rep-scripts blk ".r"] print "ok" exit][] ; test expressions Rebol ... à peaufiner if error? try [ print do-clean blk exit][] ; commande non comprise print "?" i-rec: 0 ]
Les règles sont rassemblées dans un bloc unique, et ont toutes la même structure:
L' intérèt de cette structure est qu'elle est extensible autant qu'on veut. De plus, elle est autodocumentée, le bloc 1 définissant l'usage de la règle. La fonction d'aide se contente de lister le bloc de règles.[ ["fonction réalisée par la règle "] [ règle de parsing ] [ code a exécuter si la règle est vérifiée ] ]Voici la liste des règles introduites dans la version actuelle du logiciel. Libre à vous de la compléter ou de la modifier:
regles: [ [["noter dans le fichier "] [["noter" | "note" | "-"] copy aa to end] [noter aa exit]] [["sauver et quitter "] [["bye" | "quit" | "exit"] to end] [print "bye ..." save-db dbase fichier-dbase exit]] [["changer de fichier "] [["cf" | "changer"] copy aa to end] [save-db dbase fichier-dbase dbase: change-db aa exit]] [["lister les fichiers "] [["???" | "fichiers"] to end] [print read to-file rep-files exit]] [["fichier et dernier enreg "] [["??" | "fichier"] to end] [print [fichier-dbase " " last dbase] exit]] [["chercher "] [["?" | "chercher" | "trouver"] copy aa to end] [chercher aa exit]] [["chercher un n° de tél "] [["tel" | "téléphone"] copy aa to end] [chercher-tel aa fichier-tel exit]] [["chercher une adresse "] [["adr" | "adresse"] copy aa to end] [chercher-adresse aa fichier-adr exit]] [["traduire "] [[">" | "traduire"] copy aa to end] [traduire aa fichier-trad exit]] [["afficher la date "] [["jour" | "date"] to end] [print now/date exit]] [["afficher l'heure "] [["heure"] to end] [print now/time exit]] [["expression Rebol "] [["=" | "R "] copy aa to end] [if error? try [do reform aa] [print "expression mal formulée"] exit]] [["imprimer l'aide "] [["aide" | "help"] to end] [lister-regles exit]] ;[print aide-txt exit]] [["lister le fichier "] ["lister" copy aa to end] [print-db dbase exit]] [["effacer un enregistr "] ["effacer" copy aa to end] [effacer i-rec exit]] [["restaurer un effacement"] ["restaurer"] [dbase: copy dbase-old exit]] [["version du logiciel "] ["version"] [print ["NoteTank, version" system/script/header/version] exit]] [["backup du fichier de notes "] [["backup"] to end] [fdb: copy fichier-dbase change find fdb ".txt" ".bak" save-db dbase fdb exit]] ]
Elles sont très simples et ne nécessitent pas de commentaires. Si vous voulez utiliser les fonctions de recherche dans un fichier d'adresse, ou de téléphone, vous devrez probablement réécrire les fonctions correspondantes, qui dépendent évidemment de la structure de vos fichiers. Pour ma part, j'utilise des fichiers au format csv (fichiers textes avec séparateur point-virgule, très faciles à parser). Beaucoup de programmes proposent ce format d'exportation des données. La fonction de recherche sur Internet (google) n'est bien sûr utilisable que si vous être connectés, et que votre rebol est bien configuré.
l'ensemble des enregistrements est stocké dans le bloc dbase les fonction d'accès au fichier de données sont: noter,chercher, effacer, restaurer:
- Noter enregistre dans le fichier dbase la date et l'heure (now) puis le string str. Il sauvegarde immédiatement dbase dans le fichier disque fichier-dbase, pour des questions de sécurité. Il actualise enfin le pointeur i-rec.
noter: func [str /local record] [ record: copy [] append/only record now append/only record trim str append/only dbase record save-db dbase fichier-dbase i-rec: length? dbase]
- chercher parcourt les enregistrements de la base pour trouver le string str. Il actualise également le pointeur i-rec
chercher: func [str /local ir] [ dbase: head dbase i-rec: 0 ir: 0 foreach record dbase [ ir: ir + 1 if found? find (to-string record/2) (trim str) [i-rec: ir print record/2] ] ]
- effacer supprime l'enregistrement dont l'indice est donné comme variable d'entrée de la fonction. Habituellement, effacer sera appelé avec i-rec en variable d'entrée, c'est-à dire que la fonction efface le dernier enregistrement introduit par la fonction noter, ou le dernier enregistrement trouvé par la fonction chercher:
effacer: func [indice] [ if indice > 0 [ print rejoin ["effacement " dbase/:indice] dbase-old: copy dbase dbase: at dbase indice remove dbase dbase: head dbase save-db dbase fichier-dbase i-rec: 0 ] ]
- restaurer permet d'annuler un effacement demandé par erreur. La fonction effacer sauvegarde l'état de la base dans dbase-old avant de la modifier. Il suffit donc de recopier dans dbase l'état initial, conservé dans dbase-old.
Par défaut, les données sont enregistrées dans le fichier notes.txt. Il est possible de définir d'autres fichiers. Il suffit d'utiliser la commande cf nom-fichier. Si nom-fichier existe, il sera ouvert, et toutes les opérations d'accès seront faites sur ce nouveau fichier. Sinon, il sera créé. La liste des fichiers de données déjà créés est donnée par ???. Le nom du fichier actuellement utilisé, ainsi que le dernier enregistrement de ce fichier son donnés par ??. Les fichiers de données sont rassemblés dans le sous-répertoire /files.
Des fonctions de recherche dans des fichiers de contacts sont faciles à réaliser. En voici un exemple, adaptable à vos propres fichiers, en fonction de leur structure. On notera que ces fonctions impriment tous les strings comportant la séquence alphanumérique présentée comme requête. Il est donc possible de taper une partie seulement du mot recherché.
chercher-tel: func [str doc] [ {fichier de type texte} base: read/lines to-file doc foreach record base [ if found? find (to-string record) (trim str) [rec: parse record none print [rec/1 rec/2 " " rec/3 rec/4 rec/5]] ] ]De même, il est simple d'intégrer un "traducteur". Il suffit de disposer (on en trouve sur internet) d'un fichier texte comportant sur chaque ligne le mot dans la langue d'origine, et le même mot traduit.chercher-adresse: func [str doc] [ {pour fichier au format csv format fichier: Numéro;Nom;Prénom;Adresse1;CodePostal;Ville;Adresse2;CodePostal2;Ville2; TéléphoneDomicile;TéléphoneBureau;Notes} base: read/lines to-file doc base: next base foreach record base [ if found? find (to-string record) (trim str) [rec: parse/all record ";" print [rec/1 rec/2 " " rec/3 rec/4 rec/5 rec/6 rec/7 rec/8 rec/9 rec/10 rec/11 rec/12]] ] ]
La recherche est plus efficace si l'on s'affranchit des accents dans les comparaisons de strings.traduire: func [str doc] [ base: read/lines to-file doc foreach record base [ if found? find (to-string record) (trim str) [print record] ] ]
La fonction de recherche devient:sans-accent: func [str /local str1][ str1: copy str substitutions: ["é" "e" "è" "e" "à" "a" "ê" "e" "ë" "e" "ç" "c" "ô" "o" "ù" "u"] foreach [search value] substitutions [ replace/all str1 search value ] str1 ]
chercher: func [str /local ir] [ dbase: head dbase i-rec: 0 ir: 0 foreach record dbase [ ir: ir + 1 if found? find (sans-accent to-string record/2) (sans-accent trim str) [i-rec: ir print record/2] ] ]
Il est utile aussi d'avoir accès à l'interpréteur Rebol, ne serait-ce que pour faire un calcul simple, ou lancer un script Rebol. C'est très simple à faire, il n'est même pas nécessaire d'appeler une fonction spécifique, "do" suffit. Voici la règle introduite
On l'utilise de cette manière:[["expression Rebol "] [["=" | "R "] copy aa to end] [if error? try [do reform aa] [print "expression mal formulée"] exit]]
= print 1 + 2ou encore
R call "notepad", ou
R browse my-url
Dans cette version du logiciel, il est aussi possible de taper directement l'expression comme on le ferait dans une console Rebol. Cependant, la gestion d'erreur n'est que partiellement traitée, et si l'expression n'est pas comprise, aucun message d'erreur ne s'affiche.
10 * 6.55
call "notepad"
browse "my-url"
Enfin, une fonction d'aide liste l'ensemble des règles déclarées
lister-regles: func [] [ print "----------------" print "commandes mots-clés" print "" foreach regle regles [ print [regle/1 tab parse form regle/2/1 "|"] ] print "----------------" ]
Comme nous l'avons vu il est très facile d'introduire ses propres règles, en respectant la structure définie.Il est également possible de modifier les règles actuelles, par exemple pour introduire de nouveaux mots de commande, ou changer leurs synonymes.
On fera attention à la manière d'introduire les mots de commande. Par exemple, si l'on introduit "R" , la règle sera exécutée dès que l'on introduit un mot de commande commençant par R. Si l'on veut n'exécuter la règle R que lorsque l'on a introduit le mot R seul, penser à introduire un espace après R: "R ".
Afin d'ajouter facilement ses propres règles sans avoir à modifier le programme principal, il suffit de créer un fichier à en-tête Rebol[], comportant un bloc de règles nommé regles1, et éventuellement des fonctions utilisées par ces commandes supplémentaires. Le programme teste la présence de ce fichier, et ajoute les règles personnalisées aux règles prédéfinies:
Par ailleurs, on peut créer des fonctions complexes, à l'aide de scripts, déposés dans le répertoire "scripts/", que l'on appellera simplement en tapant le nom du script (sans le suffixe ".r")fichier-regles: %mes-regles.r if exists? fichier-regles [do fichier-regles append regles regles1]
On n'a pas intégré ici de fonctions de messagerie électronique, mais celles-ci seraient simples à créer, avec, par exemple appel à un fichier d'adresses électroniques.On pourra ici remarquer que, pour quelqu'un qui maîtrise Rebol, il est simple d'écrire directement la plupart des commandes présentées ci-dessus. On pourrait même les intégrer au fichier user.r, afin de les avoir sous la main dans la console Rebol. Pourquoi pas ? Cependant, rappelons que la motivation première de ce travail était d'avoir la possibilité de prendre en vrac des notes, et de les retrouver simplement. Un utilitaire spécifique semble un bon choix pour celà.
Le logiciel présenté ici reste volontairement simple, afin de pouvoir être adapté et remanié en fonction des contraintes de chacun, afin de conserver également son caractère pédagogique. C'est pourquoi, en particulier, on ne l'a pas surchargé par de la gestion d'erreur. On pourra facilement l'enrichir avec ses propres fichiers de données: dictionnaires, listes de traduction, répertoires d'adresses, de numéros de téléphone, de format plus ou moins exotique...Une autre approche pourrait être la création d'un exécutable autonome (ce qui pourrait constituer un moyen de promouvoir Rebol discrètement...). Dans ce cas, il est bon de prévoir d'enregistrer la liste des règles dans un fichier séparé, ce qui permet une personnalisation, ou des adaptations, sans remettre en cause l'exécutable.
On pourrait imaginer une gestion dynamique des règles (possibilité de modification en cours d'exécution), ce qui n'est pas compliqué, mais il faudrait prévoir d'intégrer également des fonctions spécifiques associées avec de nouvelles règles, par exemple pour s'adapter à des formats de fichiers de données particuliers.
Rebol se prête bien au développement et à la personnalisation de ce type d'utilitaire. Les fonctions de parsing sont un moyen puissant pour créer son langage de commande. Il faudra cependant veiller à ne pas le surcharger par un nombre trop grand de mots de commande, pour lui conserver simplicité et efficacité.