B11) L’AST de BP3
Anatomie d’un code intermédiaire musical
29 types de nœuds, 3 niveaux d’invariants
Un fichier de grammaire BP3 est du texte. Pour le transformer en musique, il faut d’abord le transformer en arbre. Cet arbre — l’AST — a 29 types de nœuds. Chacun capture un aspect du langage BP3.
Où se situe cet article ?
L4 a introduit les AST en général. B10 a défini la grammaire formelle de BP3 (~50 productions EBNF). B7 a montré comment le transpileur traduit BP3 en SuperCollider. Ici on ouvre la boîte entre les deux : l’AST est la représentation intermédiaire qui connecte le parsing (lecture du texte) à l’émission (génération du code SC).
Vue d’ensemble : 29 nœuds en 11 catégories
L’AST de BP3 capture tout ce qu’un fichier de grammaire peut exprimer. Les 29 types de nœuds se répartissent en 11 catégories :
graph TD
BPFile --> Headers
BPFile --> GrammarBlock
BPFile --> TemplateDef
Headers --> Comment
Headers --> FileRef
Headers --> InitDirective
GrammarBlock --> Rule
Rule --> Weight
Rule --> Flag
Rule --> RHS["RHSElement (19 types)"]
RHS --> Note
RHS --> Rest
RHS --> NonTerminal
RHS --> Variable
RHS --> Wildcard
RHS --> Polymetric
RHS --> SpecialFn
RHS --> Lambda
RHS --> HomoApply
RHS --> Tie
RHS --> Others["+ 9 autres"]
style BPFile fill:#4a7ab5,stroke:#3a6aa5,color:#fff
style GrammarBlock fill:#4a9a8a,stroke:#3a8a7a,color:#fff
style Rule fill:#c8842a,stroke:#b8742a,color:#fff
style RHS fill:#8a5ab5,stroke:#7a4aa5,color:#fff
Figure 1 — Hiérarchie de l’AST BP3.
BPFileest la racine. ChaqueGrammarBlockcontient desRule, chaqueRulecontient desRHSElement(19 types possibles).
Les conteneurs : la structure de l’arbre
BPFile — la racine
BPFile(
headers: list[Header], # commentaires, -al., -se., INIT:
grammars: list[GrammarBlock], # au moins 1 bloc de grammaire
templates: list[TemplateDef] # optionnel — mode TEM
)
Invariant : len(grammars) >= 1 — un fichier BP3 sans grammaire n’a pas de sens.
GrammarBlock — un bloc de sous-grammaire
GrammarBlock(
mode: str, # "ORD", "RND", "LIN", "SUB", "SUB1", "TEM", "POSLONG"
index: int, # numéro du bloc (gram#1, gram#2...)
rules: list[Rule] # les règles de ce bloc
)
Chaque bloc a son propre mode de dérivation (B3). Les blocs sont séparés par ----- et dérivés séquentiellement : le moteur épuise le bloc N avant de passer au bloc N+1.
Rule — une règle de production
Rule(
grammar_num: int, # numéro du bloc
rule_num: int, # numéro dans le bloc
weight: Weight, # poids (optionnel)
arrow: str, # "-->" ou "<-->" ou "<--"
flags: list[Flag], # conditions et mutations
lhs: list[RHSElement], # côté gauche (1+ éléments)
rhs: list[RHSElement] # côté droit (0+ éléments)
)
Invariants : arrow ∈ {"-->", "<-->", "<--"}, len(lhs) >= 1.
Les terminaux : les feuilles de l’arbre
| Nœud | Champs | Exemple BP3 | Rôle |
|---|---|---|---|
| Note | name, octave |
C4, do3, Sa |
Note musicale |
| Rest | determined: bool |
- (déterminé), _ (prolongation) |
Silence |
| UndeterminedRest | (aucun) | ... |
Repos calculé par PolyMake |
| NonTerminal | name |
S, Phrase, Tihai |
Symbole à réécrire |
Rappel (B2) : les non-terminaux commencent par une majuscule et sont des noms simples dans BP3 (pas de pipes
|S|). Les terminaux sont définis dans le fichier d’alphabet (-al.).
Le contrôle : poids et flags
Weight — le poids d’une règle
Weight(
value: int, # poids numérique (ex: 50)
decrement: int, # décrémentation (ex: 12 dans <50-12>)
k_name: str, # K-paramètre (ex: "K1")
k_value: int # valeur initiale K (ex: 5 dans <K1=5>)
)
Quatre formes : <3> (fixe), <50-12> (décrémental), <K1> (paramétrique), <K1=5> (paramétrique initialisé). Voir B1 et B4.
Flag — condition et mutation
Flag(
name: str, # "avart", "phase"
op: str, # "", "=", "+", "-", ">", "<"
value: str # "4", "1", None (test simple)
)
op="" = test de non-nullité (/avart/). op="=" = assignation (/avart=4/). Voir B4.
Les extensions context-sensitive : ce qui dépasse Type 2
Ces nœuds donnent à BP3 une expressivité au-delà des grammaires hors-contexte (L1) :
| Nœud | Syntaxe BP3 | Rôle | Article |
|---|---|---|---|
| Variable | \|x\| |
Capture et réplique un symbole | B6 |
| Wildcard | ?1, ?2 |
Pattern matching numéroté | B6 |
| HomoApply | (=X), (:X) |
Master (=) et slave (:) d’un motif | B6 |
| ContextMarker | LEFT, # |
Contexte de dérivation | B6 |
| Polymetric | {v1, v2} |
Superposition de voix | B5 |
| SpecialFn | _tempo(2) |
30+ fonctions spéciales | B7 |
| Lambda | lambda |
Production vide (ε) | — |
Le nœud HomoApply a trois variantes (kind) :
MASTER:(= X)— première occurrence, définit le motifSLAVE:(: X)— occurrences suivantes, doivent être identiquesREF: référence simple
Les extensions langage complet
Ces nœuds existent dans le langage BP3 complet mais ne sont pas tous couverts par le transpileur BP2SC :
| Nœud | Syntaxe BP3 | Rôle |
|---|---|---|
| OutTimeObject | <<C4>>, <<chik>> |
Objet hors-temps (trigger) |
| SpeedRatio | /2, *3, **3, \2 |
4 opérateurs de vitesse/échelle |
| BeatSeparator | . |
Séparateur de période |
| TemplateDef | [1] *1/1 __*1/2 _ |
Définition de template structurel |
| Tie | C4&, &C4 |
Liaison (notes liées) |
| GotoDirective | _goto(N,M) |
Saut impératif vers une règle |
| TimeSig | 4+4+4+4/4 |
Signature temporelle additive |
Les invariants : ce que l’AST garantit
Trois niveaux de garanties :
Invariants structurels (vérifiables au parsing)
- Un fichier a au moins un bloc de grammaire
- Chaque mode est dans
{ORD, RND, LIN, SUB, SUB1, TEM, POSLONG} - Chaque flèche est dans
{-->, <-->, <--} - Les poids sont non-négatifs, les décréments positifs
- Les wildcards ont un index ≥ 0
Invariants sémantiques (vérifiables à la compilation)
- Les terminaux existent dans l’alphabet (
-al.) - Les variables sont liées (chaque
|x|a une définition) - Les HomoApply SLAVE référencent un MASTER existant
Invariants inter-nœuds (vérifiables à l’exécution)
- Les flags référencés dans les gardes sont initialisés quelque part
- Les poids K-paramétriques sont cohérents entre les blocs
- Les numéros
gram#Nsont séquentiels
Le triple mapping : EBNF → AST → SC
Pour comprendre comment un motif traverse les 3 représentations, suivons <50-12> S --> A B C :
| Étape | Représentation |
|---|---|
| Texte BP3 | gram#1[1] <50-12> S --> A B C |
| EBNF (B10) | Production rule → weight, lhs, "-->", rhs |
| AST | Rule(weight=Weight(50,12), arrow="-->", lhs=[NonTerminal("S")], rhs=[NonTerminal("A"), NonTerminal("B"), NonTerminal("C")]) |
| SC (B7) | Pdef(\S, Prand([Pseq([Pdef(\A), Pdef(\B), Pdef(\C)])], inf)) avec gestion des poids |
Chaque couche a sa propre préoccupation : le texte est lisible, l’EBNF est vérifiable, l’AST est traitable, le SC est jouable.
Les limites : pourquoi cet AST n’est pas une IR générique
L’AST de BP3 est un code intermédiaire BP3-spécifique :
| Propriété | AST BP3 (29 nœuds) | IR musicale générique |
|---|---|---|
| Nœuds | BPFile, GrammarBlock, Rule | Section, Voice, Note, Tempo |
| Dépend de | Sémantique BP3 | Aucune notation source |
| Réutilisable | Non (BP3 seulement) | Oui (N sources → M cibles) |
| Couverture | Grammaires Type 2+ | Toute musique |
Pour parser du MusicXML, du LilyPond ou du MIDI, il faudrait un AST différent. C’est le constat qui motive la recherche d’une représentation intermédiaire musicale universelle — un sujet pour un autre article.
Ce qu’il faut retenir
- 29 types de nœuds en 11 catégories — de BPFile (racine) à BeatSeparator (feuille)
- 3 conteneurs : BPFile → GrammarBlock → Rule — la colonne vertébrale de l’arbre
- 19 types de RHSElement — le cœur de l’expressivité de BP3
- 3 niveaux d’invariants : structurels (parsing), sémantiques (compilation), inter-nœuds (exécution)
- Le triple mapping EBNF → AST → SC montre comment chaque motif traverse les représentations
- Cet AST est BP3-spécifique — il ne peut pas servir d’IR musicale générique
Pour aller plus loin
- Aho, A.V. et al. (1986) : Compilers: Principles, Techniques, and Tools — la référence sur les AST et la compilation. DOI:10.5555/6448
- Bel, B. & Kippen, J. (1992) : « Modelling Music with Grammars » — le formalisme sous-jacent à ces 29 nœuds
Glossaire
- AST : Abstract Syntax Tree — représentation arborescente d’un programme après parsing
- RHSElement : Union type des 19 types de nœuds pouvant apparaître dans le côté droit d’une règle
- NonTerminal : Symbole pouvant être réécrit (commence par une majuscule en BP3)
- HomoApply : Nœud d’homomorphisme — MASTER (=X) définit, SLAVE (:X) réplique
- SpecialFn : Fonction spéciale BP3 (
_tempo,_smooth,_legato, etc.) — 30+ fonctions - Lambda : Production vide (ε) — le non-terminal disparaît
- OutTimeObject : Objet hors-temps
<<symbole>>— déclenché sans occuper de durée - SpeedRatio : Opérateur de vitesse/échelle (
/2,*3,**3,\2) - IR : Intermediate Representation — représentation intermédiaire entre le source et la cible
Liens dans la série
- L4 — Qu’est-ce qu’un AST — les concepts généraux
- B10 — L’EBNF de BP3 — la grammaire qui définit ce que l’AST peut contenir
- B7 — Le transpileur — comment l’AST est traduit en SuperCollider
- B3 — Les 7 modes — le champ
modede GrammarBlock - B4 — Flags et poids — les nœuds Weight et Flag
- B5 — Polymétrie — le nœud Polymetric
- B6 — Homomorphismes — Variable, Wildcard, HomoApply
Prérequis : L4, B7, B10
Temps de lecture : 16 min
Tags : #ast #bp3 #code-intermédiaire #représentation #nœuds