Modules

Un module YScript est simplement un fichier source (.yes) chargé par require, qui construit et retourne une table rassemblant ses fonctions et valeurs publiques. Cette page documente, côté auteur d'un module, comment écrire ce fichier : le patron standard, ce que le chunk reçoit au chargement, où le placer sur le disque, et comment le pré-enregistrer en mémoire sans fichier.

Elle complète stdlib/package.md, qui documente require/package.* côté consommateur d'un module (recherche de fichier, cache, searchers), et se distingue d'un module externe, qui package des fonctions C# dans une assembly séparée plutôt que du code YScript dans un fichier .yes.

Patron standard

Un module est un fichier .yes ordinaire, exécuté comme n'importe quel chunk, à ceci près qu'on attend de lui qu'il retourne une valeur (typiquement une table) : c'est cette valeur que require renvoie à l'appelant et met en cache.

-- stack.yes
local M = {}

function M.push(t, v)
    t[#t + 1] = v
end

function M.pop(t)
    local v = t[#t]
    t[#t] = nil
    return v
end

return M
-- utilisation
local stack = require("stack")

local t = {}
stack.push(t, 1)
stack.push(t, 2)
print(stack.pop(t))   --> 2

Construire une table locale, y attacher les fonctions publiques du module, puis la return en fin de fichier : c'est le seul contrat que require impose. Tout le reste (variables locales privées au module, fonctions internes non exposées via M, etc.) reste un chunk YScript ordinaire.

Ce que reçoit le chunk au chargement

Le fichier du module est chargé et appelé comme une fonction variadique ; require lui passe exactement deux arguments, récupérables via ... :

  1. le nom du module tel que passé à require (ex. "stack", "yeg.module1") ;
  2. une donnée de chargement associée au searcher qui a trouvé le module — pour un module fichier (searcher « script »), c'est le nom du fichier trouvé sur le disque.
-- stack.yes
local name, filename = ...
-- name     == "stack"
-- filename == "/chemin/vers/stack.yes"

local M = {}
...
return M

Un module peut ignorer ces arguments (cas le plus courant) ou s'en servir, par exemple pour du diagnostic ou pour localiser des fichiers annexes relatifs à filename.

Si le fichier ne retourne aucune valeur (pas de return, ou return sans expression), require utilise true comme résultat — ce true est ce qui est mis en cache dans package.loaded[nom] et ce que retournent tous les appels ultérieurs à require pour ce nom. Un module qui n'a besoin d'aucune valeur exportée (il s'exécute uniquement pour son effet de bord, ex. enregistrer des métatables globales) peut donc légitimement ne rien retourner.

Emplacement du fichier : ?.yes / ?/init.yes

require("a.b.c") cherche un fichier en substituant le nom (avec ses . convertis en /) dans chaque gabarit de package.path. Par défaut, chaque racine de recherche fournit deux gabarits :

La forme dossier convient à un module qui se découpe en plusieurs fichiers internes (que init.yes charge via des require relatifs, des dofile, etc., et rassemble dans la table qu'il retourne), sans changer le nom sous lequel le module est consommé. Le détail exact du gabarit par défaut (racines, suffixe de version, variables d'environnement) est documenté dans stdlib/package.md#packagepath--packagedotnetpath.

package.preload : enregistrer un module sans fichier

Quand un module est déjà construit en mémoire — typiquement déposé par l'application hôte .NET au démarrage — package.preload permet de l'enregistrer sous un nom, pour qu'un require(nom) côté script le récupère sans jamais toucher au système de fichiers. Le searcher « preload » est le premier consulté par require, avant toute recherche par chemin.

Côté auteur/hôte, il suffit de déposer dans package.preload une fonction de chargement portant le même nom que le module — cette fonction joue exactement le rôle du chunk d'un fichier .yes : elle reçoit (nom, donnée) et sa valeur de retour est celle que require renverra :

package.preload["stack"] = function(name, data)
    local M = {}
    function M.push(t, v) t[#t + 1] = v end
    function M.pop(t) local v = t[#t]; t[#t] = nil; return v end
    return M
end

local stack = require("stack")   -- exécute la fonction ci-dessus, sans lire aucun fichier

Voir stdlib/package.md#packagepreload pour le détail de la table package.preload elle-même.

Cache

Comme tout module, un fichier .yes chargé par require n'est exécuté qu'une seule fois : les appels suivants avec le même nom retournent la valeur mise en cache — voir stdlib/package.md#requiremodname pour le détail.