Compilation (@gram-lang/kitchen)
Si @gram-lang/parser dicte le vocabulaire, c’est @gram-lang/kitchen qui incarne la logique.
Le package Kitchen récupère l’AST (Arbre Syntaxique Abstrait) généré par le parser et le compile. Son job ? Simuler le déroulé de la recette de bout en bout, résoudre les variables, extraire les compteurs de temps et bootstrapper la liste de courses.
Responsabilités principales
Section intitulée « Responsabilités principales »Le processus de compilation (orchestré par core.ts) délègue à plusieurs sous-modules :
1. Scope structurel & traitement (processor.ts)
Section intitulée « 1. Scope structurel & traitement (processor.ts) »Le processeur boucle sur chaque section et chaque étape de l’AST pour bâtir la timeline d’exécution.
- Résolution des variables : Au moindre
->&pâte, la déclaration est enregistrée dans le Scope Global. À la moindre référence&pâte, il tisse le lien. - Diagnostics : Le processeur a pour mission d’intercepter les erreurs logiques. Plutôt que d’échouer brutalement, il empile des objets
Warningstructurés dansCompilationResult.warnings. L’objectif est de permettre à l’éditeur de toujours afficher un rendu, même partiel. Si vous invoquez&pâtesans la déclarer, une alerteUNDEFINED_REFERENCEest émise. En cas de cycle de dépendances (&a -> &b -> &a), l’algorithme DFS dansgraph.tsl’identifie et lève unCIRCULAR_REFERENCE. Si besoin, la commandegram check --strictbasculera ces avertissements en erreurs bloquantes. - Génération de la Chronologie (ALAP Scheduling) : Le moteur s’appuie sur un ordonnancement ALAP (As Late As Possible) (éclaté dans
src/schedule/pour être bétonné de tests unitaires). Quatre passes s’enchaînent pour accoucher d’une timeline chirurgicale :- Phase 1 (Forward Pass) : Le compilateur jauge les temps actifs et les tâches de fond pour chronométrer le temps de production des intermédiaires (
->&nom) de chaque section. - Phase 2 (Backward Pass) : Il remonte la recette à l’envers (
alap.ts). À la moindre consommation d’un intermédiaire (&nom), le moteur note l’échéance critique à laquelle il doit être prêt, tout en digérant les éventuelles ancres de rétro-planning de section (~{-1j}). L’étape productrice sera alors calée juste-à-temps (JIT). - Phase 3 (Serialization) : L’algorithme des « pistes nommées » (Named Tracks,
tracks.ts) entre en piste pour s’assurer que des timers passifs partageant un même nom (ex.~_four) ne se chevauchent pas matériellement. En cas de collision, les horaires de départ sont glissés chronologiquement. - Phase 4 (Positive Rebasing) : C’est l’étape d’ajustement final (
rebase.ts). Si des préparations ont basculé dans le négatif (ex: la veille du jour J), l’intégralité de la chronologie est translatée vers l’avant (de l’opposé du minimum absolu). La structuretimingsfinale ne contiendra donc que des temps absolus, toujours positifs, démarrant à un repère zéro, facilitant ainsi l’intégration front-end.
- Phase 1 (Forward Pass) : Le compilateur jauge les temps actifs et les tâches de fond pour chronométrer le temps de production des intermédiaires (
flowchart LR
AST["📄 AST<br/>(depuis le Parser)"] --> P1["Phase 1 : Forward Pass<br/><i>Estimation des durées</i>"]
P1 --> P2["Phase 2 : Backward Pass<br/><i>Rétro-planning ALAP</i>"]
P2 --> P3["Phase 3 : Serialization<br/><i>File d'attente Named Tracks</i>"]
P3 --> P4["Phase 4 : Rebasing<br/><i>Recalage de T-Zéro à 0</i>"]
P4 --> Result["⚙️ CompilationResult<br/><i>(Ligne du temps optimisée)</i>"]
::: tip
La flèche 👉 que vous voyez dans les recettes rendues (ex : 👉*pâte*) est une icône d’affichage ajoutée par @gram-lang/renderer, pas de la syntaxe Gram. Dans le code source .gram, un intermédiaire est consommé avec un simple &nom.
:::
2. Métriques de temps (metrics.ts / processor.ts)
Section intitulée « 2. Métriques de temps (metrics.ts / processor.ts) »La Kitchen calcule quatre métriques de temps, combinées dans core.ts :
- Temps Actif (
activeTime) : La somme de toutes les durées des minuteurs actifs, plus un défaut de 2 minutes pour toute étape qui ne déclare aucun minuteur. - Temps de Cuisson (
cookTime) : Le temps de fin absolu maximal de la chronologie de cuisson, en tenant compte de toute tâche de fond passive (comme faire reposer une pâte pendant 24 heures) qui se termine après la dernière étape active. - Temps de Préparation (
preparationTime) : Complètement indépendant des timers. C’est le temps forfaitaire de mise-en-place : 1 minute par ingrédient/matériel unique, plus 2 minutes additionnelles si une étape de préparation est exigée (ex :@oignon(épluché)). - Temps Total (
totalTime) :preparationTime + cookTime— le véritable investissement en temps pour ce plat (du fouinage dans les placards jusqu’au service).
3. Agrégation de la liste de courses (shopping.ts)
Section intitulée « 3. Agrégation de la liste de courses (shopping.ts) »La Kitchen construit la liste de base des ingrédients nécessaires pour cuisiner la recette.
- Fusion : Elle empile les mentions d’un ingrédient via son ID brut (le slug du texte) et son unité. Ainsi,
@beurre{50 g}dans la pâte et@beurre{20 g}dans le glaçage seront sommés arithmétiquement en une ligne unique de70 g. - Logique des Composites : Elle orchestre l’impitoyable logique MAX et SUM des Ingrédients Composites. La quantité réclamée pour les enfants passe à la moulinette du MAX (ex: le plus gros entre « zeste de 2 citrons » et « jus de 3 citrons » gagne). Ensuite, toute quantité du parent utilisée « telle quelle » est sommée (SUM) par-dessus.
- Agrégation Hybride : Pour ne rien casser, les Quantités Relatives (basées sur des formules) sont stockées à part de la masse numérique absolue. La liste de courses est ainsi garantie mathématiquement exacte en toute circonstance.
La sortie brute de @gram-lang/kitchen est l’objet CompilationResult (la « recette compilée »). Structurellement irréprochable et mathématiquement agrégé, ce JSON n’est néanmoins pas encore physiquement exact.
Par exemple, Kitchen sait que vous avez demandé 1 tasse de farine, mais ne sait toujours pas combien ça pèse. Cet enrichissement physique massif se fera à la prochaine étape : place à l’Analyseur (Analyzer).
Autres responsabilités
Section intitulée « Autres responsabilités »- Ajustement des proportions (Scaling) : Si
scaleFactorest injecté à la compilation, toutes les quantités sont redimensionnées proportionnellement. Sauf exceptions : les constantes préfixées par=(ex:@sel{=5g}, insensibles au volume de la recette) et les champs libres non quantifiables. - Validation du Pourcentage du Boulanger : Le compilateur s’assure qu’un et un seul ingrédient porte la couronne du modificateur de pourcentage du boulanger (
*). S’il y a des prétendants multiples, il sévira avec une belle erreur.