Une proc qui gere l'exploration des sous commandes par les namespaces
#1
Introduction
Pour faire un résumé assez court; Voici une procédure a placer dans votre namespace parent, et le faire appeler
Quote:create_sub_procs [namespace current]

create_sub_procs va créer a partir du namespace parents des procédures pour explorer les sous namespaces et leurs sous commandes/procédures.

Fonctionnement en bref
  • En appelants un namespace comme une procédure, le code va vous dire qu'il nécessite une sous-commande
  • En appelants une procédure qui n'existe pas dans un sous-namespace, le code va vous dire que vous essayer d'appeler une procédure inconnue ou que votre commande est ambigu en fournissant la listes des  sous-procédures existants
  • En appelants un sous(-sous..)-namespace comme une procédure, le code va vous dire qu'il nécessite une sous(-sous..)-commande
  • Les sous(-sous..)-namespaces ne nécessite plus de nom complets du style "::monscript::user:add::group <user> <group>" dans votre namespace ::monscript vous pouvez appeler comme ceci : [user add group ZarTeK Developpeur] par exemple.

Exemple
Voici un exemple de namespaces avec des sous namespaces et des sous procedures; la procedure; et des tests dans cette environnement; tout cela commenter

tcl
# Creation d'un namespace nommer ::example pour faire des tests
namespace eval ::example {
    # on cree dedans text (::example::text)
    namespace eval text {
    # dedans on crée 3 proc pour test (::example::text::a, ::example::text::b et ::example::text::c)
        proc a { text } { puts $text }
        proc B {} {}
        proc c {} {}
    }
    # on crée dedans un deuxième (::example::text2)
    namespace eval text2 {
    # on crée également des procs dans (::example::text) qui seront (::example::text::e,::example::text::f, ::example::text::g )
        proc e { text } { puts $text }
        proc f {} {}
        proc g {} {}
    #dans le namespace (::example::text2) on créer un sous namespace text3 (::example::text2::text3)
        namespace eval text3 {
    # dedans on creer une proc 123 (::example::text2::text3::123)
            proc 123 { text } { puts $text }
        }
    }
    ::example::text::a "premier test"
    # retourne : premier test
    ::example::text2::e "test2"
    # retourne : test2
 
    # jusque ici, tout est normal on appelle les procs avec leurs namespaces en préfix
 
    #creation des procs dans les namespaces
    proc create_sub_procs { namespace } {
    # Boucle sur les namespaces enfants de $namespace (::example) retourne -> ::example::text; ::example::text2
        foreach child_name [namespace children ${namespace}] {
    # Création de procédure portant le nom des namespaces enfants ::example::text, ::example::text2
            proc ${child_name} { {subcommand ""} args } {
    # tout ce qui fais partie ici, sera executé lors de l'exécution de la proc créer
    # les variables, commandes
    # le  proc_path contient chemin de la proc (lors de sont exécution et non maintenant donc)
                set proc_path [lindex [info level 0] 0]
    # Si la proc est apeller sans subcommand, nous signalons qu'elle nécessite une
                if { ${subcommand} == "" } {
                    return  -code error \
                        "wrong # args: should be \"${proc_path} subcommand ?arg ...?\""
                }
    # Si la subcommand n'existe pas dans les procs enfants, ont prévois de retourner la liste des procs existante dans le namespace courant (celle de la proc appelé )
                if { [info commands ${proc_path}::${subcommand}] == "" } {
                    set subcommands_list  [join [string map "${proc_path}:: \"\"" [info procs ${proc_path}::*]] ", "]
                    return  -code error \
                        "wrong ${proc_path} unknown or ambiguous subcommand \"${subcommand}\": must be ${subcommands_list}"
                }
    # si la subcommand existe, on l'execute avec les valeurs fournis
                ${proc_path}::${subcommand} {*}${args}
            }
    # ici nous sommes sorties de la creation de la proc, et de retour dans la boucle enfant, nous allons exporté les proc
            namespace export *
    # Nous allons répéter ces opérations dans le niveau inferieur/enfants (dans ::example::text et ::example::text2)
            create_sub_procs ${child_name}
        }
    # fin de la boucle
    }
    # Nous exécutons la recette dans notre namespace courant 
    create_sub_procs [namespace current]
}
# maintenant quelque petit tests:
 
#.tcl ::example::text
# > error: wrong # args: should be "::example::text subcommand ?arg ...?"
# ici dessus, nous appelons un namespace, le code accepte, mais retourne une erreur pour dire qu'une sous-commande doit être fournis
 
#.tcl ::example::text subcommand123
# > error: wrong ::example::text unknown or ambiguous subcommand "subcommand123": must be a, B, c
# ici dessus, nous avons donner une sous-commande qui n'existe pas, il nous retourne une erreur pour ce dire qu'on c'est trompé et fournis la liste des sous-commandes disponibles
 
# .tcl ::example::text a
# > error: wrong # args: should be "::example::text::a text"
# ci dessus, nous voyons qu'il nous dis que ::example::text::a nécessite un argument "text".
# a noter que nous avons utiliser "::example::text a" qui est en réalité "::example::text::a"
 
# .tcl ::example::text a coucou
# > coucou
# ici il a bien exécuter la proc a dans ::example::text et l'argument text vaut coucou, et il nous l'affiche bien "coucou"
 
#.tcl ::example::text2
# > error: wrong # args: should be "::example::text2 subcommand ?arg ...?"
# rien de nouveau avec le text2 a comparer au text pour l'instant
 
#.tcl ::example::text2 lala
# > error: wrong ::example::text2 unknown or ambiguous subcommand "lala": must be text3, e, f, g
# il nous retourne une erreur comme nous l'avions avec text, sauf que cette fois ci, il nous annonce que le sous namespace text3 est une sous commande !
 
# .tcl ::example::text2 e coucou2
# > coucou2
# rien de special, il nous affiche bien notre coucou2
 
# essayons maintenant avec la sous commandes text3 qu'il nous propose et qui est une namespace qu'on avais déclarer
# .tcl ::example::text2 text3
# > error: wrong # args: should be "::example::text2::text3 subcommand ?arg ...?"
# ils nous annonce qui a des sous commandes !
 
# .tcl ::example::text2 text3 coucou3
# > error: wrong ::example::text2::text3 unknown or ambiguous subcommand "coucou3": must be 123
# Il nous informe que coucou3 n'ai pas une sous commandes, mais qu'il existe 123 comme sous commande
 
# .tcl ::example::text2 text3 123
# > error: wrong # args: should be "::example::text2::text3::123 text"
# il nous informe que la sous commandes text3 nécessite un argument text, on va lui fournir
#.tcl ::example::text2 text3 123 "voilà !!!"
# > voilà !!!

Le code
Et voici la procédure en elle-même :

tcl
  proc create_sub_procs { namespace } {
    # Boucle sur les namespaces enfants de $namespace (::example) retourne -> ::example::text; ::example::text2
        foreach child_name [namespace children ${namespace}] {
    # Création de procédure portant le nom des namespaces enfants ::example::text, ::example::text2
            proc ${child_name} { {subcommand ""} args } {
    # tout ce qui fais partie ici, sera exécuté lors de l'exécution de la proc créer
    # les variables, commandes
    # le  proc_path contient chemin de la proc (lors de sont exécution et non maintenant donc)
                set proc_path [lindex [info level 0] 0]
    # Si la proc est appeler sans subcommand, nous signalons qu'elle nécessite une
                if { ${subcommand} == "" } {
                    return  -code error \
                        "wrong # args: should be \"${proc_path} subcommand ?arg ...?\""
                }
    # Si la subcommand n'existe pas dans les procs enfants, ont prévois de retourner la liste des procs existante dans le namespace courant (celle de la proc appelé )
                if { [info commands ${proc_path}::${subcommand}] == "" } {
                    set subcommands_list  [join [string map "${proc_path}:: \"\"" [info procs ${proc_path}::*]] ", "]
                    return  -code error \
                        "wrong ${proc_path} unknown or ambiguous subcommand \"${subcommand}\": must be ${subcommands_list}"
                }
    # si la subcommand existe, on l'execute avec les valeurs fournis
                ${proc_path}::${subcommand} {*}${args}
            }
    # ici nous sommes sorties de la création de la proc, et de retour dans la boucle enfant, nous allons exporté les proc
            namespace export *
    # Nous allons répéter ces opérations dans le niveau inferieur/enfants (dans ::example::text et ::example::text2)
            create_sub_procs ${child_name}
        }
    # fin de la boucle
    }
    # Nous exécutons la recette dans notre namespace courant 
    create_sub_procs [namespace current]

Outroduction

Voilà; personnellement je l'ai créer pour mon propre toolkit et ses fonctions. Si ca peut vous servir tant mieux  Cool

Je laisse libre droits sur mon thread a tout modérateurs de eggdrop.fr, vous pouvez l'éditer, supprimer, déplacer, critiqué etc a volonté …  Razz
Retrouvez les dernières modifications de mes scripts TCL (versions Alpha/Bêta) ainsi que d'autres de mes réalisations sur ma page GitHub et les versions stables dans la section scripts de ce site  8-)
  Reply
#2
Et en pratique, à part remplacer [::user::add::group ZarTeK Developpeur] par [user add group ZarTeK Developpeur], quel est l'utilité ?
De manière tout à fait personnelle, je préfère la première écriture (en terme de lisibilité) car elle permet de savoir où s'arrête la procédure et où commencent les arguments.
zeolia: tchat gratuit, sans inscription ni publicité
Merci de ne pas demander d'aide en MP
Away
  Reply
#3
(07/07/2022, 14:19)CrazyCat Wrote: quel est l'utilité ?

Pour toi apparemment aucune, pour ceux qui en vois une, celle qu'elle vois.
Pour moi personnellement, cela me permet que mon toolkit agis telle qu'un package sans me casser le cul.
Car si on appelle un sous namespace, il agit comme une procédure et avec les erreurs qui vont avec telle que ceci:


tcl
.tcl return [string]
Error: wrong # args: should be "string subcommand ?arg ...?" - 0.024 ms
.tcl return [string a]
Error: unknown or ambiguous subcommand "a": must be bytelength, cat, compare, equal, first, index, is, last, length, map, match, range, repeat, replace, reverse, tolower, totitle, toupper, trim, trimleft, trimright, wordend, or wordstart - 0.035 ms
.tcl return [string toupper]
Error: wrong # args: should be "string toupper string ?first? ?last?" - 0.045 ms

Retrouvez les dernières modifications de mes scripts TCL (versions Alpha/Bêta) ainsi que d'autres de mes réalisations sur ma page GitHub et les versions stables dans la section scripts de ce site  8-)
  Reply
#4
Je ne suis pas certain de bien comprendre la démonstration, as-tu un exemple concret et simple ?

Et oui, si je demande l'utilité, c'est que je ne la vois pas. Mais comme je disais sur le canal: "Au premier abord, je ne vois pas l'intérêt de ça, mais si ça existe ça doit en avoir un". Je ne pense pas que des personnes aient développé cette fonctionnalité pour rien, je ne comprends juste pas à quoi ça peut me servir.
zeolia: tchat gratuit, sans inscription ni publicité
Merci de ne pas demander d'aide en MP
Away
  Reply
#5
CrazyCat n'a pas émis une critique, il se demandait seulement à quoi ça pouvait servir dans la pratique, n'ayant jamais utilisé cette fonctionnalité.

Edit : ah il a répondu avant moi.

Edit : fruit de ma réflexion :
Quote:[15:10:18] <MenzAgitat> CrazyCat > je pense avoir compris l'intérêt de la chose en lisant https://wiki.tcl-lang.org/page/namespace+ensemble
[15:10:23] <Boole> Titre de l'URL de MenzAgitat : namespace ensemble
[15:10:27] <MenzAgitat> regarde à "Simple example"
[15:11:00] <MenzAgitat> ça permet de créer des commandes avec des sous-commandes en plus des arguments
[15:12:05] <CrazyEgg> (CrazyCat@Zeolia) ben encore une fois, je ne vois pas la différence entre glovar setit et glovar::setit
[15:12:10] <CrazyEgg> (CrazyCat@Zeolia) je dois être con
[15:12:28] <MenzAgitat> ça permet d'avoir une aide à la syntaxe automatiquement gérée par Tcl (sous-commande manquante, sous-commande invalide et proposition des sous-commandes valides)
[15:13:13] <MenzAgitat> ben setit définit une variable et getit la retourne
[15:14:32] <MenzAgitat> !café CrazyCat
[15:15:13] <MenzAgitat> mais ce qui est intéressant dans l'exemple c'est la façon dont Tcl gère et affiche les erreurs d'utilisation de la commande
[15:17:01] <CrazyEgg> (CrazyCat@Zeolia) Oki, je viens de comparer, je comprends mieux
[15:17:06] <MenzAgitat> tu vois par exemple dans Motus j'ai une commande stats qui accepte plusieurs sous-commandes
[15:17:31] <MenzAgitat> read.stats, write.stats, rename.player, ....
[15:17:50] <MenzAgitat> ç'aurait été plus simple et plus propre en utilisant namespace ensemble
Toute l'actualité de mes scripts ici     (dernière mise à jour le 22/04/2020)

Tout programme comporte au moins un bug et pourrait être raccourci d'au moins une instruction, de quoi l'on peut déduire que tout programme peut être réduit à une seule instruction qui ne fonctionne pas.
  Reply
#6
Ok, après une petite explication de MenzAgitat sur le canal, j'ai compris.

Fonctionnement sans :

tcl
% namespace eval glovar {
   variable value {}
   proc getit {} { variable value; return $value; }
   proc setit newvalue { variable value; set value $newvalue; }
}
% ::glovar
invalid command name "::glovar"
% ::glovar junk
invalid command name "::glovar"
% ::glovar::setit abc
abc
% ::glovar::getit
abc


Fonctionnement avec:

tcl
% namespace eval glovar { namespace export getit setit; namespace ensemble create; }
% ::glovar
wrong # args: should be "::glovar subcommand ?arg ...?"
% ::glovar junk
unknown or ambiguous subcommand "junk": must be getit, or setit
% ::glovar setit 1234
1234
% ::glovar::getit
1234
% ::glovar getit
1234

Le gros intérêt est donc dans le retour lorsque l'appel est incomplet qui est beaucoup plus explicite qu'un simple "invalid command". Et ça n'empèche en rien l'utilisation des appels "normaux"
zeolia: tchat gratuit, sans inscription ni publicité
Merci de ne pas demander d'aide en MP
Away
  Reply
#7
(07/07/2022, 14:55)CrazyCat Wrote: as-tu un exemple concret et simple ?
Et oui, si je demande l'utilité, c'est que je ne la vois pas.
J'ai commenter mon code, mis un exemple pourtant. je te donne un autre si cela peut t'aider:

tcl
namespace eval ::ZCT::TXT { }
namespace eval ::ZCT::TXT::visuals {}
# permet de centrer du text sur une longueur donné
proc ::ZCT::TXT::visuals::espace { text length } {
    set text			[string trim ${text}]
    set text_length		[string length ${text}];
    set espace_length	[expr (${length} - ${text_length})/2.0]
    set ESPACE_TMP		[split ${espace_length} .]
    set ESPACE_ENTIER	[lindex ${ESPACE_TMP} 0]
    set ESPACE_DECIMAL	[lindex ${ESPACE_TMP} 1]
    if { ${ESPACE_DECIMAL} == 0 } {
        set espace_one			[string repeat " " ${ESPACE_ENTIER}];
        set espace_two			[string repeat " " ${ESPACE_ENTIER}];
        return "${espace_one}${text}${espace_two}"
    } else {
        set espace_one			[string repeat " " ${ESPACE_ENTIER}];
        set espace_two			[string repeat " " [expr (${ESPACE_ENTIER}+1)]];
        return "${espace_one}${text}${espace_two}"
    }
 
}
##############################################################################
### Substitution des symboles couleur/gras/soulignement/...
###############################################################################
# Modification de la fonction de MenzAgitat
# <cXX> : Ajouter un Couleur avec le code XX : <c01>; <c02,01>
# </c>  : Enlever la Couleur (refermer la denier déclaration <cXX>) : </c>
# <b>   : Ajouter le style Bold/gras
# </b>  : Enlever le style Bold/gras
# <u>   : Ajouter le style Underline/souligner
# </u>  : Enlever le style Underline/souligner
# <i>   : Ajouter le style Italic/Italique
# <s>   : Enlever les styles précèdent
proc ::ZCT::TXT::visuals::apply { data } {
    regsub -all -nocase {<c([0-9]{0,2}(,[0-9]{0,2})?)?>|</c([0-9]{0,2}(,[0-9]{0,2})?)?>} ${data} "\003\\1" data
    regsub -all -nocase {<b>|</b>} ${data} "\002" data
    regsub -all -nocase {<u>|</u>} ${data} "\037" data
    regsub -all -nocase {<i>|</i>} ${data} "\026" data
    return [regsub -all -nocase {<s>} ${data} "\017"]
}
# Enleve les style visuals
proc ::ZCT::TXT::visuals::remove { data } {
    regsub -all -nocase {<c([0-9]{0,2}(,[0-9]{0,2})?)?>|</c([0-9]{0,2}(,[0-9]{0,2})?)?>} ${data} "" data
    regsub -all -nocase {<b>|</b>} ${data} "" data
    regsub -all -nocase {<u>|</u>} ${data} "" data
    regsub -all -nocase {<i>|</i>} ${data} "" data
    return [regsub -all -nocase {<s>} ${data} ""]
}


Avec ceci, plus ma procédure lancer sur ::ZCT, ce (morceau de) package réagis maintenant comme ceci :
Quote:.tcl return [::ZCT::TXT]
Error: wrong # args: should be "::ZCT::TXT subcommand ?arg ...?" - 0.102 ms
.tcl return [::ZCT::TXT a]
Error: wrong ::ZCT::TXT unknown or ambiguous subcommand "a": must be visuals - 0.05 ms
.tcl return [::ZCT::TXT visuals]
Error: wrong # args: should be "::ZCT::TXT::visuals subcommand ?arg ...?" - 0.113 ms
.tcl return [::ZCT::TXT visuals a]
Error: wrong ::ZCT::TXT::visuals unknown or ambiguous subcommand "a": must be espace, remove, apply - 0.062 ms
.tcl return [::ZCT::TXT visuals espace]
Error: wrong # args: should be "::ZCT::TXT::visuals::espace text length" - 0.145 ms

Il agis telle qu'une commande TCL intégrer, comme avec l'exemple de [string] qui n'a rien avoir avec mon code et pourtant donne le même résultat/erreur

J'ai juste a créer mes namespaces et les procédures, sans me soucier de créer une proc d'aide pour chaque cas d'erreur éventuelle dans l'utilisations des commandes de mon packages.
Cela normalise mon package, comme n'importe commande de TCL.

Tu commence à comprendre ?

Pour te donné encore une autre exemple qui risque de mieux te parler avec l'alltools que tu modifie: https://github.com/crazycatdevs/eggdrop/...ltools.tcl

Tu créer un namespace ::nbr
et au lieux de faire tes procs comme ceci:
Quote:proc number_to_number {number} {
proc isnumber {string} {
proc decround {number {dec 2}} {
proc ceilfloor {number dec {mode "ceil"}} {
proc decfloor {number {dec 2}} {
proc decceil {number {dec 2}} {
proc ordnumber {string} {

Tu crée plutôt tes procédures comme ceci:

tcl
namespace eval ::nbr {
proc is {string} {}
proc decround {number {dec 2}} {}
proc ceilfloor {number dec {mode "ceil"}} {}
proc decfloor {number {dec 2}} {}
proc decceil {number {dec 2}} {}
proc ord {string} {}
}


Ca regroupe toutes tes procédures pour agir sur les nombres dans ::nbr; maintenant avec ma procédure les erreurs sont différentes
Lors de l'appelle de [::nbr] ou [nbr] il va maintenant afficher:

tcl
wrong # args: should be "::nbr subcommand ?arg ...?"

au lieu de commande inccorect
et lors de l'appelle de [::nbr::nawak] ou [nbr nawak] il va afficher:

tcl
error: wrong ::nbr  unknown or ambiguous subcommand "nawak": must be is, decround, ceilfloor, decfloor, decceil, ord


Il réagis comme n'importe quelle commande qui aurais des sous commandes dans celle de TCL de base.

EDIT: j'étais occupé à composer cette réponse durant les deux derniers réponses.
Je ne sais pas si namespace ensemble fait également la même chose que ma procédure?
J'avais questionné sur son utilité dans le thread suivant : https://forum.eggdrop.fr/namespace-ensemble-t-1950.html
Retrouvez les dernières modifications de mes scripts TCL (versions Alpha/Bêta) ainsi que d'autres de mes réalisations sur ma page GitHub et les versions stables dans la section scripts de ce site  8-)
  Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Enregistrement du salon sous chanserv heretoc 1 2,915 11/05/2009, 09:32
Last Post: CrazyCat

Forum Jump:


Users browsing this thread: 1 Guest(s)