S12) L’EBNF de BPscript
Grammaire formelle du langage
Les productions clés
Un langage en développement actif a des divergences entre la spec et le code. Cet article les documente honnêtement — ce qui est implémenté, ce qui est prévu, ce qui diverge.
Où se situe cet article ?
Référence technique. Pour la notation EBNF, voir L3. Pour l’EBNF de BP3, voir B10. Spécification complète : BPSCRIPT_EBNF.md (v0.6).
La racine
scene = { directive | actor_directive | declaration | cv_instance
| macro | backtick_orphan | comment }
, subgrammar+ , [ template_section ] ;
Directives
directive = "@" , directive_body ;
actor_directive = "@actor" , IDENT , actor_props+ ;
actor_props = IDENT , ":" , actor_value ;
actor_value = IDENT [ "(" , param_pairs , ")" ] ;
directive_body = IDENT (* @core *)
| "controls" (* @controls *)
| "mode" , ":" , MODE (* @mode:random *)
| IDENT , ":" , value (* @tempo:120 *)
;
value = INT | FLOAT | neg_number | IDENT | ratio ;
neg_number = "-" , ( INT | FLOAT ) ;
ratio = INT , "/" , INT ;
La directive @actor est le concept central (S3) :
@actor sitar alphabet:sargam scale:sargam_22shruti transport:webaudio
Propriétés connues d’un acteur : alphabet (requis), scale (gamme/degrés → hauteur via tempérament,
si l’acteur a des hauteurs), sounds (définitions par terminal : timbre, percussions, échantillons), transport (requis),eval (runtime des backticks ; si omis, les backticks de l’acteur ne sont pas évalués — défaut null).
Sous-grammaires et règles
subgrammar = rule+ , [ separator ] ;
separator = "-----" , { "-" } ;
mode_directive = "@mode:" , MODE , [ "(" , mode_modifier , { "," , mode_modifier } , ")" ] ;
MODE = "ord" | "random" | "lin" | "sub1" | "sub" | "tem" | "poslong" ;
rule = [ guard ] , { context } , lhs , arrow , rhs
, [ runtime_qualifier ] , { qualifier } ;
guard = "[" , flag_expr , "]" ;
flag_expr = IDENT | IDENT , comparator , value ;
arrow = "->" | "<-" | "<>" ;
Le mode est une directive de bloc @mode:X qui s’applique à la sous-grammaire suivante, jusqu’au
prochain séparateur -----. Il n’est pas un qualificateur inline de la règle.
Qualificateurs — [] moteur vs () runtime
engine_qualifier = "[" , engine_pair , { "," , engine_pair } , "]"
| "[" , tempo_op , "]" ;
engine_pair = ENGINE_KEY , ":" , raw_value | ENGINE_KEY ;
ENGINE_KEY = "mode" | "scan" | "speed" | "weight" | "on_fail"
| "tempo" | "meter" | "scale" | "retro" | "rotate"
| "keyxpand" | "repeat" | "failed" | "stop" | "goto"
| "striated" | "smooth" ;
runtime_qualifier = "(" , runtime_pair , { "," , runtime_pair } , ")" ;
runtime_pair = RUNTIME_KEY , ":" , value ;
[]= moteur BP3, clés strictement réservées (ENGINE_KEY)()= runtime/dispatcher (aval, hors dépôt), clés définies parcontrols.json[]comme()sont suffixe sur un élément du RHS — pas de qualificateur préfixe ([X]An’est pas supporté ; pour positionner un flag entre éléments, utiliser la forme instantanée![X])
Le RHS
rhs_element = symbol_call | rest | prolongation | undetermined_rest
| polymetric | backtick_inline | flag_mutation
| simultaneous | trigger_in | symbol_with_trigger_in
| capture | homomorphism_var
| context_positive | context_negative
| template_master | template_slave | nil_string ;
Simultanéité — syntaxe infixe
simultaneous = symbol_call , { "!" , sim_target } ;
sim_target = symbol | symbol_call ;
Sa!dha!spotlight → SimultaneousGroup (primaire + secondaires). Le ! est infixe, pas standalone,
et exclusivement temporel : un secondaire est toujours un symbole (jamais une mutation de flag).
Les flags vont dans les qualificateurs [] de la règle.
Trigger entrant — forme standalone et attachée
trigger_in = "<!" , IDENT ;
symbol_with_trigger_in = symbol_call , "<!" , IDENT ;
Sa<!sync → SymbolWithTriggerIn : un symbole qui attend un trigger entrant avant de se déclencher.
Lexèmes — pièges
- Tiret
-dans IDENT : autorisé dans les non-terminaux (LHS) uniquement. Pré-scan. #dans IDENT : altérations (C#4). Attention avec l’alphabet plat._dans IDENT : l’EBNF le permet mais BP3 rejette_dans l’alphabet. Point bloquant connu.
Divergences connues
| Sujet | Spec | Parser | Statut |
|---|---|---|---|
@+ |
Absent | Accepté (legacy @controls) | Legacy |
| INT négatif | FLOAT seul a - |
Parser détecte - avant INT |
Spec à corriger |
@mode → subgrammar |
Pas de lien formel | parseSubgrammars(initialMode) |
Spec à corriger |
Sa!dha |
! standalone |
Infixe SimultaneousGroup |
Spec à corriger |
on_fail, hooks, timeout |
Dans la spec | Pas implémenté | Prévu |
| Cross-rule braces | Absent | annotateUnbalancedBraces |
À documenter |
Deux divergences anciennes sont désormais résolues dans la spec : Sa<!sync (forme attachéeSymbolWithTriggerIn) et la cv_instance (env1(...) = lib.type(...)) sont aujourd’hui décrites
par l’EBNF.
Ce qu’il faut retenir
@actorest la directive centrale — lie alphabet, scale, sounds, transport[]= moteur (clés strictes),()= runtime (clés libres via controls.json)!est infixe (Sa!dha), pas standalone, et exclusivement temporel (pas de mutation de flag)@mode:Xest une directive de bloc, pas un qualificateur inline- Divergences documentées entre spec et parser — le langage évolue activement
Glossaire
- EBNF : Extended Backus-Naur Form — notation standard pour la syntaxe (ISO 14977)
- SimultaneousGroup : Nœud AST pour
Sa!dha!spotlight(primaire + secondaires, temporel) - SymbolWithTriggerIn : Nœud AST pour
Sa<!sync(symbole + trigger entrant attaché) - ENGINE_KEY : Clé réservée pour les qualificateurs moteur
[] - raw_value : Valeur brute (tout texte jusqu’au
,ou]) pour les clés moteur
Liens dans la série
- L3 — La notation EBNF
- B10 — L’EBNF de BP3
- S2 — Les tokens
- S3 — La directive @actor
- S10 — Le compilateur qui implémente cette grammaire
- S13 (à venir) — L’AST produit par le parser
Prérequis : S2, L3
Temps de lecture : 10 min
Tags : #BPscript #EBNF #grammaire-formelle #spécification
Prochain article : S13 — L’AST de BPscript