Démarrage

Créer un ScriptEngine

YScript.ScriptEngine est le point d'entrée public du moteur. Il dérive de Runtime.ScriptState, qui porte l'essentiel de l'API (chargement, exécution, pile de valeurs...) : tout ce qui est décrit dans cette section de la documentation s'appelle donc soit sur ScriptEngine directement, soit via l'interface IScript qu'il implémente.

// Configuration et hôte par défaut
using var engine = new ScriptEngine();

// Options explicites, hôte par défaut
using var engine2 = new ScriptEngine(new ScriptOptions { MaxStackSize = 500_000 });

// Hôte personnalisé (voir hote.md) et options par défaut
using var engine3 = new ScriptEngine(myHost, null);

ScriptEngine implémente IDisposable : le disposer libère les ressources retenues par le moteur (pile, objets gérés par le ramasse-miettes du moteur). Dans une application qui crée un moteur par requête/session plutôt qu'un moteur unique pour toute la durée de vie du process, encadrez-le d'un using.

Options (ScriptOptions)

Propriété Défaut Rôle
MinStackSize 24 Taille initiale de la pile de valeurs du moteur
MaxStackSize 1 000 000 Taille maximale de la pile (protège contre une récursion excessive)
IndexChainMaxSize 200 Profondeur maximale suivie pour une chaîne de métatables __index
MaxCalls 200 Profondeur maximale de la pile d'appels
Culture CultureInfo.CurrentCulture Culture utilisée pour les conversions nombre/texte et l'interop .NET
FloatToIntMode Equal Comportement de la coercition flottant → entier (Equal refuse une valeur non entière, Floor/Ceil arrondissent)

Ouvrir les bibliothèques standard

Un ScriptEngine fraîchement créé n'a aucune bibliothèque chargée — même print n'existe pas tant que base n'a pas été ouverte. Les bibliothèques s'ouvrent via les méthodes d'extension de YScript.Libraries.LibrariesExtensions :

using YScript.Libraries;

engine
    .OpenLibs()      // base, coroutine, package, table, math, string, regex, debug
    .OpenIOLib()      // io.*  — nécessite un IScriptHost qui gère fichiers/flux (voir hote.md)
    .OpenOSLib();     // os.*  — nécessite un IScriptHost qui gère horloge/process (voir hote.md)

OpenLibs() regroupe les bibliothèques qui ne dépendent que du moteur lui-même. io et os en sont volontairement exclues : elles ont besoin d'un IScriptHost qui implémente réellement l'accès fichier, l'horloge et le contrôle de processus, ce que tout hôte n'a pas forcément à offrir — elles s'ouvrent donc explicitement, à la carte, avec OpenIOLib()/OpenOSLib().

dotnet (accès réflexif aux types et assemblies .NET depuis un script) suit la même logique mais pour une raison de sécurité plutôt que de capacité de l'hôte : elle donne à un script les moyens de charger des assemblies et d'instancier/inspecter n'importe quel type .NET accessible depuis le process hôte. Ne l'ouvrez qu'avec OpenDotnetLib(), et seulement si les scripts exécutés sont dignes de confiance — voir Interop .NET.

Chaque bibliothèque peut aussi s'ouvrir individuellement (OpenBaseLib(), OpenMathLib(), ...) si vous voulez composer un sous-ensemble précis plutôt que tout OpenLibs().

Exécuter du code

L'API de bas niveau (Load, Call, PCall...) manipule une pile de valeurs, dans le même esprit que l'API C de Lua : les arguments et le résultat d'une opération transitent par des valeurs poussées/dépilées sur la pile du script plutôt que passés comme paramètres C# ordinaires. Pour la plupart des usages d'un hôte (exécuter un script, appeler une fonction globale simple), les méthodes d'aide ci-dessous suffisent sans avoir à manipuler la pile directement.

// Charger et exécuter du code source, en une fois
ScriptStatus status = engine.DoString("return 1 + 1", "mon-script");

// Charger et exécuter un fichier
ScriptStatus status2 = engine.DoFile("scripts/init.ys");

if (status2 != ScriptStatus.Ok)
{
    // Le message d'erreur est au sommet de la pile (voir erreurs-avertissements.md)
    Console.Error.WriteLine(engine.ToString(-1));
    engine.Pop();
}

DoString/DoFile combinent Load/LoadFile (compilation) et PCall (appel protégé) — voir Erreurs et avertissements pour le détail de ce que chacun retourne et comment récupérer un résultat plutôt que l'ignorer.

Pour charger sans exécuter immédiatement (par exemple pour appeler la fonction plusieurs fois, ou avec des arguments), utilisez Load/LoadFile puis Call/PCall :

if (engine.Load("return ...", "greet") == ScriptStatus.Ok)
{
    engine.PushString("monde");
    var error = engine.PCall(1, out int nResults);
    if (error == null && nResults > 0)
        Console.WriteLine(engine.ToString(-1));   // "monde"
    engine.Pop(nResults);
}

Exposer une fonction C# à un script

Register pousse une fonction hôte (ExternalFunction, signature int Method(IScript script)) et la publie comme variable globale, sans passer par le système de modules :

engine.Register(script =>
{
    double a = script.CheckNumber(1);
    double b = script.CheckNumber(2);
    script.PushFloat(a + b);
    return 1;   // nombre de valeurs de retour poussées
}, "add");
print(add(2, 3))   --> 5

Pour exposer une surface plus large (plusieurs fonctions regroupées sous un nom, chargées via require), voir Modules externes. Pour exposer directement des objets/types .NET existants, voir Interop .NET.

Le REPL (ScriptReplEngine)

YScript.Repl.ScriptReplEngine implémente la logique d'une boucle lecture-évaluation-affichage (multi-ligne, historique, invite personnalisable via une fonction/globale prompt/_PROMPT/_PROMPT2 côté script) au-dessus d'un IScript déjà configuré. Il ne lit pas lui-même l'entrée clavier : c'est à l'hôte de lui fournir chaque ligne saisie et d'afficher les invites (l'interpréteur en ligne de commande, voir L'interpréteur, fournit un exemple complet avec édition de ligne et historique).

var repl = new ScriptReplEngine(engine) { DebugMode = false };
repl.StartSession();
while (true)
{
    string line = Console.ReadLine();
    if (line == null) break;
    repl.CurrentLine = line;
    if (repl.RunCurrentLine() == ReplStatus.Exit) break;
}

RunCurrentLine détecte une ligne incomplète (ex. if sans end) et renvoie ReplStatus.IncompletedLine pour demander la suite à l'hôte plutôt que de lever une erreur de syntaxe prématurée.