Initiation au Lua avec Scribunto/Mise au point d'un module
Dans le chapitre précédent, nous avons vu suffisamment de notions pour commencer à faire des petits modules faciles à utiliser. Bien souvent les modules ne seront pas nécessairement petits et faciles à utiliser. Leur mise au point risque d’être délicate à faire. Par conséquent, avant d'aller plus loin dans l'étude du Lua avec Scribunto, nous allons consacrer ce chapitre à l'étude des moyens dont nous disposons pour faciliter la mise au point des modules.
Augmenter la lisibilité du code Lua
[modifier | modifier le wikicode]Pour mettre au point un programme Lua, commencez par l'écrire de façon à ce qu'un humain puisse le relire :
- Nous-même, car même si l’on a l'impression, sur le moment, de savoir ce qu’il contient, il se peut que l’on soit amené à y revenir après plusieurs mois et là, on risque d’avoir du mal à retrouver comment il fonctionne.
- Les autres, car les modules écrits sur un des projets Wikimédia peuvent être améliorés par d'autres utilisateurs.
L'amélioration de la lisibilité d'un programme se base sur trois techniques :
L'indentation
[modifier | modifier le wikicode]C'est le fait de décaler vers la droite un bloc d'instructions pour le rendre plus lisible. On décalera vers la droite les instructions se trouvant à l'intérieur d'une fonction, d'une structure if condition then instruction end, et les autres structures de contrôle que nous verrons plus tard.
Par exemple, dans le Module:Traduction multilingue, si nous avions écrit :
local p = {}
function p.traduit(frame)
if frame.args[2] == "Anglais" then
if frame.args[1] == "Lundi" then return "Monday" end
if frame.args[1] == "Mardi" then return "Tuesday" end
if frame.args[1] == "Mercredi" then return "Wednesday" end
if frame.args[1] == "Jeudi" then return "Thursday" end
if frame.args[1] == "Vendredi" then return "Friday" end
if frame.args[1] == "Samedi" then return "Saturday" end
if frame.args[1] == "Dimanche" then return "Sunday" end
end
if frame.args[2] == "Espagnol" then
if frame.args[1] == "Lundi" then return "Lunes" end
if frame.args[1] == "Mardi" then return "Martes" end
if frame.args[1] == "Mercredi" then return "Miércoles" end
if frame.args[1] == "Jeudi" then return "Jueves" end
if frame.args[1] == "Vendredi" then return "Viernes" end
if frame.args[1] == "Samedi" then return "Sàbato" end
if frame.args[1] == "Dimanche" then return "Domingo" end
end
end
return p
Le programme aurait, tout de même, bien fonctionné mais aurait été moins lisible.
Les noms de variable explicites
[modifier | modifier le wikicode]Le Lua, ainsi que la plupart des langages de programmation, permettent d'écrire les variables en utilisant plusieurs caractères. On donnera donc aux variables un nom qui exprimera ce qu'elles contiennent. Par exemple, une variable destinée à mémoriser un salaire s'appellera salaire.
Les commentaires dans le programme
[modifier | modifier le wikicode]Il est possible et même fortement conseillé de rajouter des commentaires à l'intérieur même des programmes pour expliquer ce que chaque partie du programme fait.
Un petit commentaire tenant sur une ligne se fera en commençant par mettre un double tirets --. On peut même mettre un commentaire après une instruction.
Par exemple, dans le Module:Faire part, on aurait pu rajouter des commentaires sur ce que réalisent les fonctions :
local p = {}
function p.mariage(frame) -- Faire part de mariage
return "Nous sommes heureux de vous annoncer le mariage de " .. frame.args[1] .. " et " .. frame.args[2] .. "."
end
function p.naissance(frame) -- Faire part de naissance
return "Nous avons la joie de vous annoncer la naissance de " .. frame.args[1] .. "."
end
function p.ame_soeur(frame) -- Faire part de petites annonces
return "Petites annonces : " .. frame.args[1] .. " " .. frame.args[2] .. "."
end
return p
On peut aussi imaginer avoir besoin de plusieurs lignes pour faire un commentaire :
Pour cela, on commencera le commentaire par --[[ et on le terminera par ]]--.
Par exemple, pour Module:Exemple simple, on aurait pu écrire :
local p = {}
function p.Salutation() --[[mon commentaire ………………………………………………
……………………………………………………………………………………………………………………………………………………………………
……………………………………………………………………… de plusieurs lignes]]--
return "Coucou, c’est moi !"
end
return p
L'éditeur Scribunto
[modifier | modifier le wikicode]Étudions de plus près l'éditeur dont nous disposons dans l’extension Scribunto. Nous remarquons, tout d’abord, que chaque ligne est numérotée :
Lorsque nous écrivons une ligne, celle-ci se détache sur un fond légèrement grisé. Nous le voyons à la figure 1, ligne 16 où se trouve le curseur.
| Cochez « Activer la barre d’outils de modification » dans les préférences de modification pour afficher les numéros de ligne. |
then' attendu proche de 'return'.
L'éditeur dispose d'une première correction pour détecter les erreurs grossières dans la syntaxe des instructions. Si nous n'écrivons pas correctement une instruction, le numéro en début de ligne se retrouve précédé d'une croix ⊠ dans un carré rouge. Nous le voyons, par exemple, dans la figure 2, ligne 16.
Si nous regardons, de plus près la ligne 16, nous verrons que nous avons oublié de mettre le mot-clé then qui doit obligatoirement se trouver dans une structure if condition then instructions end.
Mieux que cela, si un carré rouge avec croix apparaît devant un numéro de ligne, nous pouvons avoir une indication sur le type d'erreur en promenant le curseur dessus (nous voulons dire par là, que nous pointons le carré rouge avec le curseur sans toutefois cliquer dessus). Dans notre exemple, figure 2, nous avons le message « [16:33] 'then' expected near 'return' », ce qui signifie que ligne 16, position 33, then est attendu avant return.
Nous remarquons aussi, en début de certaines lignes, un symbole ⌄ juste après le numéro de ligne. Ce caractère permet de masquer (réduire) un bloc d'instructions. Par exemple, si nous cliquons sur le ⌄ de la ligne 3 où est déclarée la fonction p.traduit, nous voyons disparaître toutes les instructions se trouvant entre cette déclaration et le end indiquant la fin de l'écriture du contenu de la fonction. Si nous cliquons sur le ⌄ de la ligne 4, nous verrons disparaître toutes les instructions du bloc if. L'utilité de cette fonctionnalité est double. On peut ainsi masquer certaines parties du programme sur lesquelles on n’est pas en train de travailler. On peut aussi, dans un programme, où il y a beaucoup de structures emboîtées et par conséquent beaucoup de end, s'assurer que l’on ne s'est pas « emmêlé les pinceaux » avec les end. Cliquez sur › pour développer le code temporairement caché.
Une autre particularité intéressante de l'éditeur est que lorsque l’on clique juste après une parenthèse, un crochet ou une accolade ouvrante ou fermante, nous voyons un léger encadrement sur la parenthèse, le crochet ou l'accolade fermante ou ouvrante correspondante. Cela peut être utile dans les expressions ayant beaucoup de parenthèses, crochets et accolades pour éviter les erreurs.
Intéressons-nous maintenant à ce qui apparaît sous le cadre de visualisation. Nous n'allons pas nous intéresser à ce qui est juste en dessous du cadre de visualisation, car il n'y a là rien de bien nouveau.
Nous allons nous intéresser à ce qui se trouve plus bas dans le cadre noté « Aperçu de la page avec ce modèle » (voir figure 3). En effet, nous allons pouvoir, avec cet aperçu, voir ce que va donner le module avant même de devoir l'enregistrer. Il est possible, grâce à ce cadre, de faire l'écriture et la mise au point complète du module sans faire une seule édition.
Pour cela, enregistrez tout d’abord dans une page — par exemple Bac à sable — la commande concernant votre module :
{{#invoke:nom du module|nom de la fonction|arguments}}
Dans le cadre « Aperçu de la page avec ce modèle » (voir ci-contre) :
- Écrivez le titre de la page où le module est invoqué (ici la page « Bac à sable »).
- Cliquez sur « Afficher l'aperçu ». L'aperçu de la page où vous avez invoqué votre module (ici l'aperçu de Bac à sable) s'affiche en haut de la page.
Si le résultat n’est pas correct, vous pouvez corriger le module et cliquer à nouveau sur « Afficher l'aperçu » autant de fois que vous voulez, jusqu’à ce que le module soit au point. - Une fois le module au point, vous pouvez cliquer sur le bouton « Enregistrer » et le module sera édité.
Le traitement des erreurs de script
[modifier | modifier le wikicode]Après avoir corrigé toutes les erreurs indiquées par l'éditeur, nous ne sommes peut-être pas au bout de nos peines. En essayant le programme, nous voyons apparaître le charmant message :
Erreur de script : vous devez spécifier une fonction à appeler.
Cela signifie que nous avons malgré tout fait une erreur que l'éditeur n'a pas décelée mais qui rend l'exécution du programme impossible.
Avec l'expérience, nous pouvons éviter les principales erreurs de script. En attendant d'acquérir cette expérience, nous nous contenterons d'énumérer les principales situations qui provoquent une erreur de script.
Voici une liste des erreurs les plus fréquentes :
- Utilisation d'une variable en croyant qu'elle contient un certain type de données, alors qu'elle en contient un autre.
Exemple : comparaison d'une variable contenant une chaîne de caractères avec un nombre. - Utilisation d'une instruction en dehors du contexte où elle devrait être normalement utilisée.
Par exemple, emploi deframe.args[1]en dehors de la fonction qui devrait normalement recueillir l'argument. - La fonction appelée n'existe pas. Vous avez, peut-être, fait une faute d'orthographe en écrivant son nom ou simplement oublié
p.en début de nom. - Opération avec une variable, qui est bien du bon type, mais que l’on n'a pas initialisée et qui est donc vide au moment où on l'utilise.
- Peut éventuellement être produit par l'oubli de l'instruction
returndans une fonction (selon comment est utilisée la fonction).
Lorsqu'une erreur de script se produit, vous pouvez avoir une première indication sur la provenance de cette erreur en cliquant sur le message rouge et non bleu :
Erreur de script : vous devez spécifier une fonction à appeler.
L'indication vous permettra, peut-être, de corriger rapidement l'erreur.
Si, malgré tout, l'erreur de script continue à apparaître et que vous ne voyez pas d'où elle provient, vous pouvez utiliser l'astuce suivante :
Vous mettez -- progressivement au début des lignes, en commençant par celles qui paraissent les plus douteuses, jusqu'à ce que l'erreur de script disparaisse. Ces lignes commençant par -- seront alors interprétées comme étant des commentaires et ne pourront plus provoquer d'erreur de script. Vous pourrez ainsi repérer la ligne qui provoque l'erreur de script.
La recherche d'une erreur dans le programme
[modifier | modifier le wikicode]Vous avez écrit un module. L'éditeur n'a pas détecté d'erreur et lorsque vous lancez l'exécution, vous n'avez pas le message : Erreur de script.
Le problème, c’est que ce que vous fournit le programme n’est pas conforme à votre attente. Vous avez commis une erreur en écrivant le programme ! Vous essayez donc, dans un premier temps, de relire ce que vous avez écrit pour essayer de comprendre pourquoi cela ne marche pas. Au bout d'un certain temps de réflexion, vous vous rendez à l'évidence, vous n'arrivez pas à comprendre pourquoi cela ne marche pas. Nous allons donc étudier, dans ce paragraphe, des moyens dont nous disposons pour faciliter la recherche de l'erreur.
Introduction d'une variable espion
[modifier | modifier le wikicode]Nous avons d’abord une technique simple qui consiste à introduire dans le programme une variable supplémentaire, que l’on appellera rapport par exemple, dans laquelle vous allez, en certains points du programme, concaténer le contenu d'autres variables. À la fin de la fonction, au lieu de retourner la variable prévue, on retournera la variable rapport qui nous fournira ainsi une information sur le contenu des variables en certains points du programme et nous permettra de localiser plus précisément dans quelle partie se trouve l'erreur. Une fois que nous avons localisé de façon plus précise la partie du programme défaillante, nous pouvons recommencer en concaténant, dans notre variable rapport, plus d'informations sur la partie fautive. Et ainsi de suite jusqu'à repérer l'instruction qui est la cause de nos soucis.
Console de débogage
[modifier | modifier le wikicode]Présentation
[modifier | modifier le wikicode]Lorsque nous sommes en mode modification dans un module, nous avons vu que nous avions un certain nombre de possibilités. Si nous continuons à descendre dans la page, tout en bas, nous découvrons un encadré noté Console de débogage représenté ci-dessous :
Nous allons étudier comment cela fonctionne.
Calculatrice
[modifier | modifier le wikicode]Utilisons la console de débogage comme calculatrice.
En effet, si l’on rentre : =2+3 et que l'on valide par la touche Entrée : ↲
=2+3
Elle nous répond en seconde ligne par le nombre 5 :
5
En marge gauche, la numérotation des lignes n’apparaît pas dans la fenêtre de la console Lua. Elle sert à distinguer la commande surlignée en jaune (à copier‑coller puis valider) du résultat qui suit.
Salutation
[modifier | modifier le wikicode]Nous commencerons avec le premier exemple dans le premier chapitre, c'est-à-dire la fonction p.Salutation.
- Cliquez sur le wikilink Module:Exemple simple ;
- Cliquez sur le menu
Modifier le wikicode; - Scoller en fin de page jusqu'à la console de débogage ;
- Tapez à l'intérieur de sa zone de saisie l'appel de la fonction de salutation :
=p.Salutation() -- le signe égal et les parenthèses sont importants
puis appuyer sur la touche « Entrée ». Nous voyons alors que ce que l’on a écrit remonte au-dessus de la zone de saisie.
Après un léger temps d'attente, le résultat de la fonction apparaît, toujours au-dessus de la zone grisée :
Coucou, c’est moi !
Nous avons donc pu tester notre programme. À ce niveau, si quelque chose s'était mal passé, nous aurions eu un message d'erreur nous indiquant la nature de l'erreur et la ligne où l'erreur s'est produite.

Si nous avions tapé :
=p.Salutation -- sans les parenthèses
Nous aurions eu comme réponse :
function
Nous pouvons avoir ainsi la nature (le type) des fonctions ou des variables se trouvant dans le programme.
Il est préférable de coller la fonction print() en indiquant ce que l’on souhaite afficher comme paramètre :
print(p.Salutation()) -- l'appel de la fonction p.Salutation est imbriqué dans l’appel de la fonction print
Elle est équivalente au raccourci =p.Salutation().
Traces mw.log
[modifier | modifier le wikicode]Faisons maintenant une petite expérience dans la console d'apprentissage du langage Lua :
mw.log("Il fait beau !") -- la fonction identité renvoie son paramètre d'entrée
Il fait beau !
Rajoutons la ligne : mw.log("Il fait beau !") dans notre programme ainsi :
local p = {}
function p.Salutation()
mw.log("Il fait beau !")
return "Coucou, c’est moi !"
end
return p
Dans la console de débogage tapons à nouveau :
=p.Salutation()
Après nous avoir prévenu que nous avons modifié le programme nous obtenons :
Il fait beau !
Coucou, c’est moi !
mw.log est une commande qui nous permet de transmettre des messages à la console de débogage.
L'intérêt de la fonction mw.log sur l'instruction return est que la fonction mw.log ne nous fait pas sortir du programme comme return lorsqu'elle est utilisée. On va donc pouvoir utiliser la fonction mw.log en plusieurs points du programme pour ramener plusieurs informations visibles sur la console de débogage. On peut ainsi construire tout un rapport d'exécution du programme qui apparaîtra sur la console de débogage et nous permettra ainsi de mettre au point le programme.
Ci-dessous, nous représentons la console de débogage après avoir tapé toutes les opérations décrites ci-dessus :
Le programme Salutation est un programme sans paramètre entre ses parenthèses.
- Cliquez sur
Annuler; - Fermer Module:Exemple simple.
Programme avec un paramètre
[modifier | modifier le wikicode]Étudions maintenant la fonction p.traduit se trouvant dans le Module:Autre exemple.
- Cliquez sur
Modifier le wikicode; - Scrollez en fin de page.
La fonction de traduction, pour fonctionner, doit recevoir en argument un jour de la semaine. Pour parvenir à transmettre cet argument, nous devons copier-coller dans la console de débogage :
frame = mw.getCurrentFrame()
puis « Entrée ». Le message collé remonte au dessus de la zone grisée.
Nous collons ensuite le paramètre d'entrée de la fonction :
newFrame = frame:newChild{ args = { 'Jeudi' }}
puis « Entrée ».
Le second message collé remonte au dessus de la zone de saisie.
Nous collons et validons alors l’appel de la fonction de traduction :
=p.traduit( newFrame )
Le troisième message collé remonte au dessus de la zone grisée mais, cette fois, apparaît en plus :
Thursday
C'est bien la traduction de "Jeudi" en anglais.
Ci-dessous, nous représentons la console de débogage après avoir tapé toutes les opérations décrites ci-dessus :
Si vous modifiez le programme pour faire des essais comme l'introduction d'une fonction de trace mw.log par exemple, privilégiez le groupement des commandes de mise au point en une seule commande avec le séparateur ; d'instructions Lua :
frame = mw.getCurrentFrame(); frame.args[1] = "Jeudi"; print(p.traduit(frame))
print(p.traduit({args={"Vendredi"}})) -- tables imbriquées
Validez les deux commandes Lua en une fois par Entrée (↲) :
Thursday
Friday
frame et mw.log
[modifier | modifier le wikicode]Instrumentons la fonction p.alerte2 se trouvant dans le Module:Balance.
Supposons que le programme ne marche pas (c'est pas vrai ! mais on fait semblant). Pour essayer de comprendre pourquoi le programme ne marche pas, nous allons visualiser sur la console de débogage le contenu de toutes les variables se trouvant dans le programme (en fait, ici, il n'y en a que deux).
Insérez deux traces mw.log pour visualiser les contenus des variables poids et reponse sans sauvegarder le module. Le programme sera ainsi complété :
local p = {}
function p.alerte2(frame)
local poids = tonumber(frame.args[1])
mw.log("Le poids rentré est ", poids)
local reponse = "Votre poids est acceptable"
if poids > 54 then
reponse = "Attention, vous commencez à grossir !"
end
mw.log("Le contenu de la variable reponse est : ", reponse)
return reponse
end
return p
Dans la console de débogage, nous ferons un copier-coller des trois commandes groupées en une seule ligne :
frame = mw.getCurrentFrame(); frame.args[1] = "55"; print(p.alerte2(frame))
Après validation, la console de débogage peut se présenter ainsi :
où nous voyons clairement apparaître le contenu des variables poids et reponse, ce qui nous permettra éventuellement de mieux comprendre d'où provient l'erreur (s'il y en avait une).
Texte Html
[modifier | modifier le wikicode]L'interprétation du Html dépend de l'environnement dans lequel Lua est exécuté.
Html dans la console de débogage
[modifier | modifier le wikicode]La console de débogage n'interprète pas le code Html rendu par Lua. Si vous codez en Lua :
reponse = reponse.."Le carré du nombre 2 est "..'4'.."<br />"
reponse = reponse.."Le carré du nombre 3 est "..'9'.."<br />"
alors vous verrez à l'écran :
Le carré du nombre 2 est 4<br />Le carré du nombre 3 est 9<br />Pour interpréter le <br /> il faut le remplacer provisoirement par le caractère de passage à la ligne '\n' dans le code Lua, soit :
reponse = reponse.."Le carré du nombre 2 est "..'4'.."\n"
reponse = reponse.."Le carré du nombre 3 est "..'9'.."\n"
Ce qui donnera dans la console :
Le carré du nombre 2 est 4
Le carré du nombre 3 est 9Exercice 3-2 : Module:Boucle > Modifier le wikicode > Console de débogage :
frame = mw.getCurrentFrame(); print(p.carre(frame))
<u>Nombres premiers élevés aux carrés</u> <br />Le carré du nombre 2 est 4<br />Le carré du nombre 3 est 9<br />Le carré du nombre 5 est 25<br />Le carré du nombre 7 est 49<br />Le carré du nombre 11 est 121<br />Le carré du nombre 13 est 169<br />Le carré du nombre 17 est 289<br />Le carré du nombre 19 est 361<br />Le carré du nombre 23 est 529<br />Le carré du nombre 29 est 841<br />Le carré du nombre 31 est 961<br />Le carré du nombre 37 est 1369<br />Le carré du nombre 41 est 1681<br />
result = p.carre(mw.getCurrentFrame()); print((result:gsub('[<][bB][rR][^>]*[>]', '\n'))) -- regex
<u>Nombres premiers élevés aux carrés</u>
Le carré du nombre 2 est 4
Le carré du nombre 3 est 9
Le carré du nombre 5 est 25
Le carré du nombre 7 est 49
Le carré du nombre 11 est 121
Le carré du nombre 13 est 169
Le carré du nombre 17 est 289
Le carré du nombre 19 est 361
Le carré du nombre 23 est 529
Le carré du nombre 29 est 841
Le carré du nombre 31 est 961
Le carré du nombre 37 est 1369
Le carré du nombre 41 est 1681
- Clic Effacer ;
- Clic Annuler.
Html dans le navigateur au retour du #invoke
[modifier | modifier le wikicode]A l'inverse, au retour du #invoke, si vous avez laissé '\n' dans le code Lua, celui-ci ne sera pas interprété et le navigateur affichera une espace à la place :
Le carré du nombre 2 est 4 Le carré du nombre 3 est 9Il vous faudra alors remettre la balise <br /> d'origine, dans le code Lua pour obtenir dans le navigateur :
Le carré du nombre 2 est 4
Le carré du nombre 3 est 9
Console d'apprentissage de Lua
[modifier | modifier le wikicode]- Cliquez sur Module:Balance pour son équilibre ;
- Cliquez sur
Modifier le wikicode; - Scrollez en fin de page.
print(mw.getCurrentFrame():callParserFunction('#expr', '37 + 5')) -- {{#expr: 37 + 5 }}
42
Oui, la fonction identité renvoie son paramètre d'entrée "Yes" :
local yesno = require('Module:Yesno')
print(mw.getCurrentFrame():callParserFunction('#if', yesno("Yes"), "Yes", 'no'))
print(mw.getCurrentFrame():callParserFunction('#if', yesno('no'), "Yes", 'no'))
Yes
no
Sélectionnez par index comme dans un tableau :
print(select(2, 'Bienvenue dans', _VERSION)) -- Sélectionne le second item
Lua 5.1
Collez la déclaration de votre fonction et son appel en une fois. Validez l’ensemble :
local function factorial(nbr) local res = 1 for ind = 2, nbr do res = res * ind end return res end
print(factorial(5)) -- le séparateur ";" d'instruction est optionnel quand il n'y a pas d'ambiguïté
120
Ce n’est pas des mathématiques :
faites l’effort de choisir au moins trois lettres par variable :
function factorial(nbr, res) if nbr <= 1 then return res or 1 end return factorial(nbr - 1, (res or 1) * nbr) end
print(factorial(5)) -- sans res en second paramètre, c'est nil. Et nil or 1 est vraiment 1.
120
Transformer une liste de formats candidats, nombre et chemins URL :
local frame = mw.getCurrentFrame()
local candidates = {
{"formatnum", "12345"}, -- formate les nombres avec séparateur de milliers
{"localurl", "Main Page"}, -- renvoie le chemin relatif d'une URL locale d'une wiki page
{"fullurl", "Main Page"}, -- renvoie une URL avec le domaine
{"canonicalurl", "Category:Top level"}, -- renvoie une URL complète
}
for _, pair in ipairs(candidates) do -- _ = index ignoré, pair = valeur; ipairs itère en séquence les éléments numériques
local fn, arg = pair[1], pair[2] -- nom de la fonction et argument
local ok, res = pcall(function() return frame:callParserFunction(fn, arg or "") end) -- appel sécurisé
local out = ok and tostring(res) or ("<error>") -- conversion en chaîne ou marquer l'erreur
print(string.format('%s(%q) -> %s', fn, arg or "", out)) -- affiche la transformation dans la console Lua
end
formatnum("12345") -> 12,345
localurl("Main Page") -> /wiki/Main_Page
fullurl("Main Page") -> //fr.wikiversity.org/wiki/Main_Page
canonicalurl("Category:Top level") -> https://fr.wikiversity.org/wiki/Cat%C3%A9gorie:Top_level

