S6) Composer avec des conditions
Flags et parcours
Gardes, mutations et gestion d’échec
Une grammaire qui produit toujours la même chose offre peu de surprises. Les flags transforment une grammaire statique en parcours compositionnel — la structure évolue pendant qu’elle se dérive.
Où se situe cet article ?
On a vu en S2 les gardes [] et les opérateurs de flags (six de comparaison, trois de calcul). Ici on les assemble : comment écrire des compositions qui changent de direction pendant la dérivation, qui comptent, qui réagissent. Pour les fondements de ce mécanisme dans BP3, voir B4.
[guard] — la garde déclarative
Une garde entre crochets [] avant le LHS (côté gauche) teste un flag (variable entière globale) et détermine si une règle existe :
[phase==1] S -> Sa Re Ga Pa // active seulement si phase vaut 1
[phase==2] S -> Ga Pa Dha Ni // active seulement si phase vaut 2
[count>3] A -> B C // active si count dépasse 3
[tension!=0] A -> B C // active si tension est non nulle
Opérateurs de test disponibles : ==, !=, >, <, >=, <=.
Flag nu et garde multiple
Deux raccourcis simplifient les gardes courantes :
[Ideas] S -> A B C // flag nu : actif si Ideas != 0 (non nul)
[phase==1] [count<3] S -> A B // garde multiple : les deux conditions doivent être vraies
Le flag nu ([Ideas]) est un raccourci pour [Ideas!=0] — le flag est traité comme un booléen (vrai si non nul). La garde multiple (plusieurs [] consécutifs) combine les conditions par un et logique — la règle n’existe que si toutes les conditions sont satisfaites en même temps.
Le point crucial : la garde est déclarative, pas impérative. Ce n’est pas un if/else exécuté pas à pas : c’est une condition d’existence de la règle. La règle existe quand la condition est vraie ; elle n’existe pas quand la condition est fausse. Il n’y a pas de « sinon ».
Ça change la façon de penser : on ne programme pas des branchements, on déclare des mondes possibles. Quand phase vaut 1, le monde est celui de l’ālāp. Quand phase vaut 2, le monde change — et avec lui, toutes les règles disponibles.
Évolution envisagée : combinaisons booléennes explicites
Aujourd’hui, plusieurs gardes consécutives se combinent par un et implicite (la garde multiple ci-dessus), et le ou se contourne en écrivant plusieurs règles. Une évolution du langage est à l’étude pour rendre ces combinaisons explicites : des opérateurs booléens entre gardes — && (et), || (ou) — et des parenthèses () pour grouper et fixer la priorité.
[a] && [b] // et explicite (équivalent à la garde multiple actuelle)
[a] || [b] // ou : actif si l'une au moins des conditions est vraie
[a] && ([b] || [c]) // groupement par parenthèses
À l’étude : cette syntaxe n’est pas encore implémentée. Pour l’instant, seul le et par juxtaposition (
[a] [b]) est disponible en BP3 ; le ou passe par plusieurs règles distinctes.
Mutations — changer l’état pendant la dérivation
Les flags ne servent à rien s’ils ne changent jamais. Une mutation s’écrit avec [] en fin de règle, après le RHS. Elle est hors-temps : elle s’applique au déclenchement de la règle, pendant la dérivation — pas à un point de la séquence jouée. Sa position ne porte donc aucun sens temporel.
S -> A B [count-1] // applique count-1 quand cette règle est dérivée
S -> A B [phase=1] [count=2] // plusieurs mutations, en fin de règle
Côté BP3, ces mutations deviennent des marqueurs /…/ placés en fin de règle :
| BPscript | BP3 |
|---|---|
S -> A B [count-1] |
S --> A B /count-1/ |
S -> A B [phase=1] [count=2] |
S --> A B /phase=1/ /count=2/ |
Pas de mutation « au milieu » : on ne peut pas insérer une mutation entre deux éléments pour qu’elle prenne effet « après le 3ᵉ son, avant le 4ᵉ ». Elle agit au niveau de la règle, au moment de sa dérivation — jamais à un instant précis du flux sonore.
Trois opérateurs de mutation :
=— assigner :[phase=2]+— incrémenter :[count+1]-— décrémenter :[tension-1]
Attention à ne pas confondre avec l’opérateur ! : ! est exclusivement temporel (triggers et simultanéité, voir S7 (à venir)). Une mutation de flag ne s’écrit jamais !phase=2 — toujours [phase=2] en fin de règle. La distinction est syntaxique et nette :
Sa!dha→!suivi d’un symbole → trigger temporel[phase=2]→ crochets avecnom=valeur→ mutation de flag (état moteur)
Les flags peuvent aussi se comparer entre eux :
[flag1==flag2]— tester l’égalité de deux flags[flag1=flag2]— copier la valeur d’un flag dans un autre
Exemple 1 : un raga en trois phases
Une illustration : un raga indien structuré en trois phases (ālāp → jor → jhālā), avec transition automatique :
@alphabet.raga:supercollider
@tempo:60
// Gardes : chaque phase a ses propres règles
[phase==1] S -> alap S
[phase==2] S -> jor S
[phase==3] S -> jhala
// L'ālāp explore lentement ; dériver cette règle passe phase à 2
alap -> Sa _ Re _ Ga _ [phase=2]
// Le jor accélère ; dériver cette règle passe phase à 3
jor -> {Sa Re Ga Pa}[speed:2] [phase=3]
// Le jhālā conclut
jhala -> {Sa Re Ga Pa Dha Ni Sa}[speed:4]
Ce qui se passe :
- Au départ,
phasevaut 1 (par défaut, les flags valent 0 — ici il faut initialiser à 1) - La garde
[phase==1]est active →Sse dérive enalap S - Dériver la règle
alapproduitSa _ Re _ Ga _et passephaseà 2 — la mutation agit au niveau de la règle, pas après les sons - Au prochain cycle,
[phase==2]est active →Sse dérive enjor S - Dériver
jorproduit la séquence plus rapide ([speed:2]) et passephaseà 3 [phase==3]→jhalaconclut (pas deSrécursif → la dérivation s’arrête)
Trois phases, trois ambiances, zéro logique impérative. La structure se parcourt.
Exemple 2 : un compteur cyclique
Les flags permettent de construire des séquences cycliques — chaque dérivation du même non-terminal produit un résultat différent :
[count==0] A -> Sa Re [count+1]
[count==1] A -> Ga Pa [count+1]
[count==2] A -> Dha Ni [count=0] // reset → le cycle recommence
Trois dérivations successives de A donnent :
Sa Re(count passe à 1)Ga Pa(count passe à 2)Dha Ni(count revient à 0)Sa Re(le cycle repart)
C’est plus puissant que le mode lin (dérivation leftmost aléatoire — voir S5) : avec les flags, on peut avoir des conditions complexes, des sauts, des réinitialisations conditionnelles. On construit de vrais parcours compositionnels, pas juste des variations positionnelles.
Gestion d’échec : on_fail
Quand une dérivation échoue (aucune règle ne s’applique pour un non-terminal donné), BP3 dispose de mécanismes de contrôle de flux bas niveau (_goto, _failed). BPscript prévoit de les exposer sous une forme plus lisible, on_fail :
// Directive globale — s'applique à toute la grammaire
@on_fail:skip // ignorer la règle qui échoue
// Surcharge locale sur une règle
[on_fail:retry(3)] S -> A B C // réessayer 3 fois
[on_fail:fallback(B)] S -> A B C // basculer vers la sous-grammaire B
Trois stratégies disponibles :
| Stratégie | Syntaxe | Effet |
|---|---|---|
| skip | @on_fail:skip |
Sauter la règle qui échoue, continuer |
| retry | [on_fail:retry(3)] |
Réessayer N fois (utile avec @mode:random) |
| fallback | [on_fail:fallback(X)] |
Basculer vers la sous-grammaire X |
on_fail fait partie de la librairie @core et on_fail est une clé réservée du langage. L’objectif : un filet de sécurité, utile en live coding où les grammaires sont modifiées en temps réel et peuvent être temporairement incohérentes — la cible de compilation étant _goto/_failed.
État actuel : la syntaxe
on_fail(skip / retry / fallback) est définie et reconnue par le parseur, mais son comportement n’est pas encore opérationnel (voir S12). Les contrôles BP3 sous-jacents_gotoet_failed, eux, existent.
Flags vs backticks : qui gère la logique ?
Une question légitime : pourquoi utiliser des flags (limités à des entiers et des comparaisons simples) plutôt que des backticks Python avec de la vraie logique ?
La réponse est dans qui tient l’horloge :
| Mécanisme | Géré par | Visible pour BP3 | Temps |
|---|---|---|---|
| Flags | BP3 (le moteur) | Oui — BP3 sait quelles règles sont actives | Synchrone avec la dérivation |
| Backticks | Le runtime (SC, Python…) | Non — BP3 ne voit pas l’état du runtime | Asynchrone, évalué à l’exécution par le runtime |
Les flags sont dans le moteur de dérivation. Quand [phase=2] s’exécute, BP3 le sait immédiatement et active les nouvelles règles au prochain pas de dérivation. Un backtick Python qui modifierait une variable Python ne changerait rien à la dérivation — BP3 ne voit pas les variables Python.
Règle simple : si la condition affecte la structure (quelles règles s’appliquent), c’est un flag. Si la condition affecte le son (quel synthétiseur jouer, à quel volume), c’est un backtick.
Ce qu’il faut retenir
- Les gardes
[]sont déclaratives : la règle existe ou non — pas deif/else - Les mutations s’écrivent en fin de règle avec
[](hors-temps, au niveau de la règle) : assignation ([flag=N]), incrément ([flag+1]), décrément ([flag-1]) — jamais avec! - Les flags sont des entiers globaux gérés par BP3, pas par les runtimes
- On déclare des mondes possibles, pas des branchements — chaque valeur de flag définit un ensemble de règles actives
- on_fail gère les échecs de dérivation : skip, retry(N), fallback(X)
- Flags = structure (synchrone avec la dérivation), backticks = comportement (asynchrone, évalué par le runtime)
Glossaire
- Flag : Variable entière globale dans BP3, testable par
[flag_expr]et modifiable par[flag=valeur]— contrôle quelles règles de grammaire sont actives - Garde : Condition
[flag_expr]avant le LHS qui détermine l’existence d’une règle — la règle est active si la garde est vraie - Mutation : modification d’un flag via
[flag=valeur],[flag+1]ou[flag-1]en fin de règle — appliquée au déclenchement de la règle (hors-temps), pas à un point du flux joué - on_fail : Directive de gestion d’échec — que faire quand aucune règle ne s’applique (skip, retry, fallback)
- Parcours compositionnel : Séquence d’états de flags qui définit une trajectoire à travers la grammaire — chaque état active un ensemble de règles différent
- Ālāp : Phase d’ouverture d’un raga indien, non mesurée, exploratoire — paradigme du temps lisse
- Jor : Phase intermédiaire d’un raga, progressivement rythmée
- Jhālā : Phase conclusive d’un raga, rapide et virtuose
Liens dans la série
- S2 — Les gardes
[]et les opérateurs de flags - S5 — Modes de dérivation — l’interaction entre mode et flags
- S7 — L’opérateur
!— simultanéité temporelle (distinct des mutations de flag) - B4 — Flags et poids dans BP3 — les fondements
Prérequis : S2, B4
Temps de lecture : 12 min
Tags : #BPscript #flags #gardes #composition-conditionnelle #BP3
Prochain article : S7 — L’instant partagé : ! et <!