[Tuto] Coder proprement un script en TCL
#1
Bonsoir à tous,

Je vous propose ce soir un petit tuto, car en relisant certains scripts je me dis que parfois ce serait pas du luxe.
Je vais commencer par les règles le plus importantes et quelques petites conventions bien pratique, les réflexes à acquérir, bref, l'indispensable.
Et puis je vais terminer par les trucs un peu moins importants, les petites astuces, etc.


I - Avant de commencer à coder (pensez à préparer du café)
Choisissez un nom évocateur pour votre script c'est toujours utile.
Faites une petit cadre tendance de commentaires en haut de votre script afin d'indiquer le nom du script, son ou ses auteurs (et leurs emails), son utilité, éventuellement sa version et un petit changelog, bugs connus, à faire, etc.
Le tout ne dépassant idéalement pas les 80 colonnes, j'y reviendrai un peu plus tard.

Un bon exemple serait :
tcl
################################################################################
# script.tcl v18.0 par Mareo (mareoh@gmail.com)                                #
# LICENSE : GNU GPLv3 (lien vers le texte de la license qui passe pas)         #
#                                                                              #
#           Encodage : UTF-8      Taille des tabulations : 6 colonnes          #
#                                                                              #
#------------------------------------------------------------------------------#
#                                  Description                                 #
#------------------------------------------------------------------------------#
# Ce script est juste là pour servir d'exemple à mon tuto...                   #
#                                                                              #
#------------------------------------------------------------------------------#
#                              /!\ Attention /!\                               #
#------------------------------------------------------------------------------#
#           Une version de TCL supérieure ou égale à 8.5 est requise           #
#              pour que ce script puisse fonctionner correctement.             #
#                                                                              #
#                    (c'est bien d'indiquer la raison ici)                     #
#------------------------------------------------------------------------------#
#                                 Installation                                 #
#------------------------------------------------------------------------------#
# Ajoutez à la fin du fichier 'eggdrop.conf' l'instruction suivante :          #
#  source -encoding utf-8 "chemin/vers/script.tcl"                             #
#                                                                              #
# Vous pouvez également modifier quelques options plus bas                     #
#------------------------------------------------------------------------------#
#                                                                              #
# [ TODO ]                                                                     #
#  - Faire un vrai script                                                      #
#  - Faire du café                                                             #
#  - Taper Artix                                                               #
#                                                                              #
# [ CHANGES ]                                                                  #
#  * 24/03/2010 v18.0                                                          #
#      Correction d'un bug qui empêchait de voter.                             #
#      Ajout d'une fonction pour remplir sa fiche d'imposition.                #
#                                                                              #
#  [...] (plein de truc mais on s'en fiche)                                    #
#                                                                              #
#  * 24/03/1992 v1.0                                                           #
#      Première version stable.                                                #
#                                                                              #
#  * 24/06/1992 v0.1                                                           #
#      Début du developpement.                                                 #
#                                                                              #
################################################################################



Bon vous êtes pas obliger de raconter votre vie comme je le fais, mais si votre script est conséquent c'est toujours bien de faire un truc informatif,
la longueur des tabulation et l'encodage ça aide vraiment les gens qui vous relisent, alors si vous connaissez ces infos hésitez pas à les mettre.

Bon ceci fait passons à un autre incontournable, j'ai nommé le namespace

II - Avant de commencer à penser à coder (en préparant le café)

Certaines personnes aiment bien nommer leurs procédure "script:action", outre le fait que ça fait un nom à rallonge, c'est pas très judicieux, en effet autant utiliser correctement les outils que nous avons à notre disposition.
Pour rappel, voici la page du manuel sur les namespace

Pour ça c'est très simple, il suffit de mettre son script entre :
tcl
namespace eval "script" {
	(ton script qui déchire grave)
}



Et magie tout fonctionne ! Ça va être très pratique quand nous aborderont la désinstallation du script Smile

En attendant, il faut juste penser à inclure le chemin complet des commandes dans les bind, exemple :
tcl
namespace eval "script" {
	proc irc_uneputaindeproc { s_nick s_uhost s_hand s_chan s_msg } {
		(un putain de code)
	}
 
	bind PUBM - "*" [namespace current]::irc_uneputaindeproc
	(ou)
	bind PUBM - "*" script::irc_uneputaindeproc
}



Si vous oubliez le [namespace current]:: ou le script:: ça risque pas de marcher, vous pouvez choisir l'un ou l'autre. Ma préférence va pour le [namespace current]:: ça facilite la vie si vous modifiez le nom du namespace.

III - Pendant que vous commencez à coder (en buvant le café)

Bon, vous aurrez remarqué que mes variable sont très étrangement préfixées d'un "s_" enigmatique. J'en arrive à une deuxième règle très importante à mes yeux puisqu'elle vous permet de remarque tout de suite l'erreur typique du débutant, à savoir appliquer des fonction de string sur des list et inversement.
Le principe est simple, si votre variable est destinée à contenir une chaîne de caractère (string) préfixez là d'un "s_", si elle doit contenir une liste d'un "l_", un tableau (array) "a_" un entier (integer) "i_" ou un dictionnaire "d_", rien de très sorcier et ça améliore vraiment la lisibilité de vos sources, c'est toujours bien d'en parler quelque part au début de votre script, afin que les lecteurs comprennent l'astuce et puisse en bnéficier eux aussi Smile
Exemple :
tcl
################################################################################
#                                  ATTENTION                                   #
#------------------------------------------------------------------------------#
# Vous n'avez pas besoin de modifier ce qui suit, à moins que vous ne          #
# souhaitiez changer le comportement de ce script.                             #
#------------------------------------------------------------------------------#
# Tcl étant un langage non typé, il a été choisi de préfixer chaque variable   #
# par le type de la valeur qu'elle contient  :                                 #
#  "s_" pour une chaîne de caractère (string)                                  #
#  "i_" pour un entier (integer)                                               #
#  "l_" pour une liste (list)                                                  #
#  "d_" pour un dictionnaire (dict)                                            #
#  "a_" pour un tableau (array)                                                #
################################################################################
namespace eval "script" {
	# C'est toujours bien de mettre ceci pour être sur que la version de TCL
	# soit bien égale ou supérieure à 8.5
	package require Tcl 8.5
 
	proc irc_uneputaindeproc { s_nick s_uhost s_hand s_chan s_msg } {
		set l_argument [split $s_msg " "]
		switch -exact -nocase -- [lindex $l_argument 0] {
			(des trucs pas interessants)
		}
}


tcl
namespace eval "script" {
	proc irc_uneputaindeproc { s_nick s_uhost s_hand s_chan s_msg } {
		switch -exact -nocase -- [lindex $s_msg 0] {
			# vous la voyez la grosse erreur sur la ligne juste au dessus ?
			# si non, voyez un opticien, une fonction dont le nom commence par
			# un "l" appliquée à une chaîne franchement c'est moche !
			(des trucs pas interessants)
		}
}



Bon j'ai la flemme de faire la suite donc ce sera pour plus tard, et puis j'ai fini mon café donc faut que j'aille m'en refaire.

La suite portera sur la désinstallation propre des scripts et sur quelques truc sympa à faire avec les namespace.
Après je consacrerais une petite partie sur les fonctions de traitement de chaîne et de liste.
Ensuite je pense faire un petit tuto sur les dictionnaires parce que c'est le bien (à mort les tableau !)
Et je terminerai peut être avec un putain de script d'exemple top moumoute pour voir comment on fait des API propres (ou pas).

Sur ce bonne nuit.
Répondre Avertir
#2
très bon tutos et très bonne initiative !! merci beaucoup
Répondre Avertir
#3
J'ajouterai quelques autres idées:

- Ne pas utiliser de tabulation, mais des espaces (4 caractères c'est une bonne norme)
- Ne pas trop commenter: les commentaires du genre:

tcl
# Cette proc sert à trier le texte
proc trier_texte {args} {
    # On tri le texte passé en paramètre
    ...
}



C'est pas vraiment utile.

Aussi Mareo pour un soucis de visiblité, plutot que de tout incorporer dans le namespace, je préfère, personellement:

tcl
namespace eval script {
    set version 0.1
    set author "ROXXOR"
}
 
proc script::do_job {...} {
   ...
}
 
proc script::blabla {args} {
   ...
}



Après, chacun ses gouts !

On attends la suite Mareo, à ton clavier Wink
Répondre Avertir
#4
Comme quoi les goûts et les couleurs Very Happy
Perso je préfère les tabulations justement parce que c'est fait pour indenter.

et je préfère inclure les définition des procs dans le namespace pour pas avoir à réécrire le nom du namespace à chaque fois (ce qui est assez chiant quand tu décide de changer le nom dudit namespace)

sinon je plussoie fortement l'idée de mettre la version du script et son/ses auteurs dans des variables au tout début !
Ça permet à d'autres scripts de savoir si celui-ci est chargé ou pas.
Sauf que moi j'aurai mis :
tcl
set s_version 0.1
set s_author "ROXXOR"



Mais bon après hein :p

La suite c'est pour demain :p
Répondre Avertir
#5
Tutorial super sympatique

Mais (oui il faut des remarques pour avancer) comme la dit Merwin il y a XX possibilités de faire ce que tu a fait et je pense que cela serais judicieux d'ajouter a ton tutorial au moin un code alternatif afin de montrer toutes les possibilités du TCL.
Répondre
#6
Bon tutoriel, je l'intègrerai dans le wiki quand il sera fini. Ou bien Mareo peut le faire aussi, dans la FAQ et avec un lien depuis cette page
Répondre
#7
Le problèmles des tabulations c'est que tu sais pas combien de caractères ça représente. Ca change d'un éditeur à l'autre (4, 8, etc). Du coup autant chez toi tu dépasses pas la limite des 80 chars, autant un gars il va ouvrir avec un autre éditeur et il sera à plus de 80 chars.

D'ou ma préférence pour les espaces Wink
Répondre Avertir
#8
heretoc a écrit :Tutorial super sympatique

Mais (oui il faut des remarques pour avancer) comme la dit Merwin il y a XX possibilités de faire ce que tu a fait et je pense que cela serais judicieux d'ajouter a ton tutorial au moin un code alternatif afin de montrer toutes les possibilités du TCL.

Je n'ai pas pour prétention de remplacer les page de manuels, mais j'en tiendrais compte Smile

CrazyCat a écrit :Bon tutoriel, je l'intègrerai dans le wiki quand il sera fini. Ou bien Mareo peut le faire aussi, dans la FAQ et avec un lien depuis cette page

J'avais prévu de wikifier le tuto quand il sera terminé Smile

Merwin a écrit :Le problèmles des tabulations c'est que tu sais pas combien de caractères ça représente. Ca change d'un éditeur à l'autre (4, 8, etc). Du coup autant chez toi tu dépasses pas la limite des 80 chars, autant un gars il va ouvrir avec un autre éditeur et il sera à plus de 80 chars.

D'ou ma préférence pour les espaces Wink

Le troll espace vs tabulations est vieux comme le monde, chacun ayant ses avantages et ses inconvénients \o
Je te répondrais juste que la taille des tabulations est configurable dans tout bon éditeur qui se respecte :p
Répondre Avertir
#9
http://www.eggdrop.fr/Coder_proprement
Voilà, j'y ai rajouté plus de truc sur le café et la partie sur les scripts qui se désinstallent tout seul (ou presque)
Sentez vous libre de corriger les erreurs qui ont pu se glisser Smile
Répondre Avertir
#10
J'ai rajouté une petite partie sur les 80 colonnes.
Je pense rajouter un autre article sur les désinstallations propre de scripts, histoire de ne pas trop encombrer le tuto.
(À moins que MenzAgitat ne s'y colle Very Happy)
Répondre Avertir
#11
J'aime l'idée de légèrement typé ses codes. Enfin comme tout le monde j'aime voir clair quand je lis.

Mareo, si je comprend bien et suis bien l'affaire.. J'ai une question !

Dans ton exemple tu utilise :
Code :
bind PUBM - "*"

et dans l'autre exemple tu utilise switch :
Code :
switch -exact -nocase -- [lindex $s_msg 0] {

Est que l'idée est de bind en PUBM et de gérer une liste de commande dans le switch dans l'idée a devoir unbind au uninstall le moins possible et rendre l'uninstall plus dynamique ?

Ou c'est un pur hasard ?

Parce que si je suis bien, il faut faire unbind de tout les bind realiser..

Mais si tu PUBM "*", tu récupère tout ce qui est dis et avec [lindex $s_msg 0] dans le pattern du switch tu doit verifier si la commande existe.. Vrai ?

Si oui, j'ai une autre question... si tu as 50 scripts qui font du PUBM, ce n'est pas moins optimiser que d'avoir finalement des bindings sur les commandes exact ?
Répondre Avertir
#12
mcdeffice a écrit :Mareo, si je comprend bien et suis bien l'affaire.. J'ai une question !

Dans ton exemple tu utilise :
Code :
bind PUBM - "*"

et dans l'autre exemple tu utilise switch :
Code :
switch -exact -nocase -- [lindex $s_msg 0] {

L'idée était plus de donner un exemple où le « typage » explicite des variable permet de repérer plus facilement des erreurs courantes, je ne pense pas qu'il faille en déduire quoi que ce soit d'autre. ;p
Mais parfois tu ne cherches pas à reconnaître une commande spécifique mais un motif particulier qui peut changer souvent au cours de l'execution du script, dans ce cas tu vas utiliser des constructions de ce genre. Dans un jeu de pendu, par exemple, tu peux vouloir tester si le texte qui passe sur le salon est lettre unique OU la réponse attendue.

mcdeffice a écrit :Est que l'idée est de bind en PUBM et de gérer une liste de commande dans le switch dans l'idée a devoir unbind au uninstall le moins possible et rendre l'uninstall plus dynamique ?

Parce que si je suis bien, il faut faire unbind de tout les bind realiser..

Je ne pense pas que ça rende la désinstallation plus dynamique, ça n'a jamais tué personne de mettre plein d'unbind. Wink

mcdeffice a écrit :Mais si tu PUBM "*", tu récupère tout ce qui est dis et avec [lindex $s_msg 0] dans le pattern du switch tu doit verifier si la commande existe.. Vrai ?

Tout à fait.

mcdeffice a écrit :Si oui, j'ai une autre question... si tu as 50 scripts qui font du PUBM, ce n'est pas moins optimiser que d'avoir finalement des bindings sur les commandes exact ?

Oui, dans l'absolu c'est moins efficace, même je suis pas certain qu'en pratique ça se ressente tant que ça.
Il est évident que si tu cherche à matcher une commande précise, il vaut mieux la binder directement que de s'imposer un niveau supplémentaire d'indirection avec un PUBM "*".
Répondre Avertir
#13
Mareo a écrit :
tcl
	proc irc_uneputaindeproc { s_nick s_uhost s_hand s_chan s_msg



# Tcl étant un langage non typé, il a été choisi de préfixer chaque variable #
# par le type de la valeur qu'elle contient : #
# "s_" pour une chaîne de caractère (string) #
# "i_" pour un entier (integer) #
# "l_" pour une liste (list) #
# "d_" pour un dictionnaire (dict) #
# "a_" pour un tableau (array) #
################################################################################

Une chose m'échappe dans l'exemple s_msg, si c'est composer de plusieurs strings c'est une liste ? et en general c'est le cas non ?

Si je suis la logique nous sommes mieux de mettre l_msg au lieu de s_msg non?

J'ai lu dans une doc ceci :
Citation :Tcl/Tk permet de tout considérer comme une liste, par exemple:
% set toto "il etait une bergere"
% llength $toto #fonction retournant le nombre d'éléments de la
#liste en argument
4
et dans l'exemple
Mareo a écrit :
tcl
namespace eval "script" {
	proc irc_uneputaindeproc { s_nick s_uhost s_hand s_chan s_msg } {
		switch -exact -nocase -- [lindex $s_msg 0] {
			# vous la voyez la grosse erreur sur la ligne juste au dessus ?
			# si non, voyez un opticien, une fonction dont le nom commence par
			# un "l" appliquée à une chaîne franchement c'est moche !
			(des trucs pas interessants)
		}
}


Si s_msg est juste, et, que la fonction lindex est faux dans l'exemple. Que faut-il utiliser ?

Au final quel est la correction que tu sous entend pour ton exemple ?
Répondre Avertir
#14
J'ai fait un petit ajout sur la page wiki, je retranscris ici:
Conventions de nommage a écrit :Une chose essentielle lorsqu'on développe, c'est que le code soit lisible et auto-suffisant. Cela passe par la mise en page (indentations propres), les commentaires judicieux (ni trop, ni trop peu) et un bon nommage de toutes les procédures et variables.

Il n'y a pas de règles définies, c'est à vous de vous les créer et de vous y tenir. Et surtout, soyez cohérents. J'ai vu des codes du genre :
tcl
proc verifuser { nick args } {
   set arg1 [lindex $arg 0]
   set arg2 [lindex $arg 1]
   if { $arg2 == "" } {
      putserv "PRIVMSG $nick :[::msgcat::mc m9]"
      return 0
   }
   return 1
}


Donc, on ne sait pas comment fonctionne cette procédure, est-ce que m9 est un message d'erreur, pourquoi $arg2 ne peut pas être vide, ...

Quitte à faire un code aussi peut parlant, pourquoi ne pas faire:
tcl
proc p19 {n a} {set b1 [lindex $a 0]; set b2 [lindex $a 1]; if {$b2==""} {putserv "PRIVMSG $n ::[::msgcat::mc m9]"; return 0;} return 1}



C'est vraiment le pire, non ?

Pour ma part, voici ce que je ferais:
tcl
proc verifUser { nick args } {
   set uIdent [lindex $arg 0]
   set uPass [lindex $arg 1]
   if { $uPass == "" } {
      putserv "PRIVMSG $nick :[::msgcat::mc errNoPass]"
      return 0
   }
   return 1
}


Ce n'est pas parfait, mais connaissant mes conventions de nommage, je sais que:
* uIdent et uPass sont des informations utilisateur (préfixe u) qui correspondent donc à un identifiant et un mot de passe
* Si le mot de passe est vide, j'utilise un message d'erreur (préfixe err) qui signale l'absence du password.
Répondre
#15
Merci pour le tuto Smile.
Je comprend mieux certaines choses maintenant.

Et l'explication en code et bien faite !
Répondre Avertir


Atteindre :


Utilisateur(s) parcourant ce sujet : 1 visiteur(s)
Tchat 100% gratuit -Discutez en toute liberté