B10) The EBNF of BP3
Formal Grammar of a Musical Language
In L3, we learned to read EBNF grammars. In B1 to B6, we discovered BP3’s constructions one by one. This article brings it all together: the complete grammar of the BP3 language in ~83 productions, documented in a systematic reference format.
Where does this article fit in?
This article is the formal convergence point of the B series. Each construction explored individually — probabilities (B1), alphabets (B2), derivation (B3), flags (B4), polymetry (B5), homomorphisms (B6) — finds its exact place here in a single formal grammar.
It is also a direct application of L3: where L3 presented EBNF notation as an abstract tool, B10 uses it to specify a real language. Readers who master the EBNF conventions from L3 can read the productions in this article without further explanation.
B10 prepares B11 (BP3’s AST): each EBNF production corresponds to one or more node types in the abstract syntax tree. The EBNF defines what can be written; the AST defines how it is represented in memory.
Why is this important?
BP3 (Bol Processor 3, cf. I2) has existed since 1990, but its syntax has never been formalized in a complete EBNF grammar. Bernard Bel’s historical documentation describes constructions through examples and C source code — a pragmatic approach but insufficient for:
- Implementing an alternative parser: without a formal grammar, one must read the C source code to understand what is syntactically valid.
- Comparing BP3 with other languages: without specification, one cannot precisely measure BP3’s syntactic expressiveness against LilyPond, ABC notation, or TidalCycles.
- Classifying BP3 in Chomsky’s hierarchy: the question “at what level of the hierarchy is BP3?” requires distinguishing the grammar of BP3 (its syntax) from the grammars within BP3 (what the user writes).
The EBNF we present here is, to our knowledge, the first complete formal specification of BP3 — and one of the few published EBNF grammars for an algorithmic composition language.
The idea in one sentence
BP3’s syntax is described in ~83 EBNF productions organized into 8 layers — global structure, grammar blocks, rules, 23 element types, special functions, templates, lexemes, and reference files — documented here in a systematic reference format (EBNFdoc).
Reading Convention (EBNFdoc format)
This article uses a systematic documentation format inspired by API references (Javadoc, MDN, Rust docs), adapted for EBNF productions. For each production:
- Production — the EBNF code (ISO 14977)
- Components — each terminal and non-terminal documented individually:
– Exact name as it appears in the production
– Type (INT, IDENT, non-terminal, literal)
– Cardinality: required, optional ([ ]), repeatable ({ }, +)
– Precise description
- Constraints — what EBNF cannot express (invariants, inter-production consistency)
- Source — reference to BP3’s C code when relevant (
file:lines) - Example — taken from test files (
bp3-ctests/) when available
EBNF Notation Used (ISO 14977):
| Notation | Meaning |
|---|---|
= |
Definition |
, |
Concatenation |
| $\vert$ | Alternative |
[ ... ] |
Optional (0 or 1) |
{ ... } |
Repetition (0 or more) |
... + |
Repetition (1 or more) — common extension |
"..." |
Literal terminal |
(* ... *) |
Comment |
Layer 1 — Global Structure
bp_file
bp_file = { header } , grammar_section+ , [ template_section ] ;
- #
header(0..n) — headers preceding grammar blocks (comments, file references, initialization directives). - #
grammar_section(1..n) — grammar blocks containing production rules. At least one block required. - #
template_section(optional) — block of structural templates. Present only if at least one sub-grammar usesTEMmode.
Constraint: template_section can only appear if hasTEMP == TRUE (CompileGrammar.c:190).
header
header = comment | file_ref | init_directive ;
A header is exactly one of the following three types:
- #
comment— textual comment ignored by the parser. - #
file_ref— reference to an external file (alphabet, settings, etc.). - #
init_directive— initialization directive.
comment
comment = "//" , TEXT ;
"//"— comment start marker.TEXT— free content until end of line. Ignored by the parser.
file_ref
file_ref = "-" , prefix , "." , name ;
"-"— file reference marker.prefix:IDENT— prefix identifying the file type (see table below)."."— separator.name:IDENT— name of the referenced file (without extension).
Source: -BP3main.h:386-401 (FilePrefix[], FileExtension[], DocumentTypeName[])
Documented Prefixes (10):
| Prefix | DocumentTypeName |
Content | Test Occurrences |
|---|---|---|---|
gr |
Grammar | Main grammar file (implicit) | 52 |
se |
Settings | Tempo, MIDI channels, global configuration | 72 |
al |
Alphabet | Terminals and MIDI or SynthDef mappings | 12 |
ho |
Homomorphisms | Note → note substitution tables | 1 |
cs |
Csound | Csound scores (external format) | — |
to |
Time-objects | References to time objects (sound, video, robot) | — |
tb |
Tables/talas | Predefined rhythmic structures | 20 |
so |
Sound objects | Sound-object definitions | 7 |
sc/+sc |
Scripts | External control scripts | 3 |
da |
Data/items | Internal data (items) | 26 |
Internal Prefixes (6):
| Prefix | DocumentTypeName |
Content | Source |
|---|---|---|---|
kb |
Keyboard | Physical keyboard mapping to BP3 functions | -BP3main.h:398 |
gl |
Glossary | Glossary of terms and abbreviations | -BP3main.h:398 |
in |
Interaction | Interactive K parameters (dynamic weights <K1>, <K1=5>) |
-BP3main.h:398 |
md |
MIDI driver | MIDI driver configuration (ports, channels, timing) | -BP3main.h:398 |
tr |
Trace | Debug log (derivations, rule applications) | -BP3main.h:398 |
wg |
Weights | Predefined weight tables for RND/LIN modes | -BP3main.h:398 |
Example:
-se.Tihai (* loads the settings file "Tihai" *)
-al.Tabla (* loads the alphabet "Tabla" *)
init_directive
init_directive = "INIT:" , TEXT ;
"INIT:"— initialization block marker.TEXT— script command executed before derivation to initialize the environment (MIDI instrument, tempo, file reading, etc.). The content is passed toExecScriptLine(CompileGrammar.c:222).
Constraint: certain script commands are forbidden in INIT: (e.g., opening a new project — ScriptUtils.c:102).
Examples:
INIT: MIDI program 110 (* selects MIDI instrument 110 *)
INIT: Play I46 (* plays interaction file I46 before derivation *)
Complete global structure example:
// Simple Tihai in ORD mode ← comment
-se.Tihai ← file_ref (prefix=se, name=Tihai)
-al.Tabla ← file_ref (prefix=al, name=Tabla)
ORD[1] ← start grammar_section
gram#1[1] S --> |x| - |x| - |x| ← rule
gram#1[2] |x| --> dha tirakita dhin ← rule
Layer 2 — Grammar Blocks
grammar_section
grammar_section = mode_line , [ preamble ] , [ separator ] , rule+ ;
- #
mode_line— required. Declaration of the derivation mode and block index. - #
preamble(optional) — special functions applied globally to the entire block before any rules. - #
separator(optional) — line of dashes separating the preamble from the rules. - #
rule(1..n) — production rules of the block. At least one rule required.
mode_line
mode_line = MODE , "[" , INT , "]" , [ IDENT ] ;
- #
MODE— required. Derivation mode of the block. "[","]"— index delimiters.- #
INT— required. Numerical index of the block (≥ 1). Identifies the block ingram#Nreferences. - #
IDENT(optional) — free label attached to the block. Does not affect derivation.
Constraint: The INT in mode_line must correspond to the N in the rule_prefix (gram#N) of the rules contained within this block.
Example: RND[2] MyBlock → RND mode, index 2, label MyBlock.
MODE
MODE = "ORD" | "RND" | "LIN" | "SUB" | "SUB1" | "TEM" | "POSLONG" ;
7 possible values:
| Value | Full Name | Rule Selection Strategy | Article |
|---|---|---|---|
ORD |
Ordered | Sequential, in order of declaration | B3 |
RND |
Random | Weighted random according to rules’ <N> weights |
B1, B3 |
LIN |
Linear | Cyclic (wrap-around: returns to first rule after last) | B3 |
SUB |
Substitute | Simultaneous substitution of all occurrences of a non-terminal | B3 |
SUB1 |
Substitute first | Substitution of the first occurrence only (leftmost) | B3 |
TEM |
Templates | Sub-grammar intended for matching via the TEMPLATES: block |
B3 |
POSLONG |
Positional longest | Like SUB1, but selects the longest derivation (leftmost-longest match) |
B3 |
Source: -BP3main.h:125 (SubgramType[]), -BP3.h:875-881 (RNDtype=0 to POSLONGtype=6)
Constraints:
SUB,SUB1, andPOSLONGmodes prohibit/flag/flags,_gotodirectives, and the “Produce all items” command (CompileGrammar.c:613-695,ProduceItems.c:757).TEMmode has no specific selection logic in the computation engine (TEMtypedefined but not referenced inCompute.c) — behavior is determined by theTEMPdirection.
preamble
preamble = special_fn+ ;
- #
special_fn(1..n) — special functions applied globally before the block’s rules (e.g.,_mm(120),_ins(Tabla)).
separator
separator = "----" , { "-" } ;
"----"— minimum 4 dashes."-"(0..n) — optional additional dashes (free length).
Layer 3 — Rules
rule
rule = rule_prefix , [ weight ] , [ DERIVATION_MODE ] , { flag } , lhs , ARROW , rhs , [ comment ] ;
- #
rule_prefix— required. Unique rule identifier (gram#N[M]). - #
weight(optional) — probabilistic weight of the rule. - #
DERIVATION_MODE(optional) — traversal direction for this rule (default:RND). - #
flag(0..n) — conditions and variable modifications. - #
lhs— required. Left-hand side (what is rewritten). - #
ARROW— required. Arrow type (rule direction). - #
rhs— required. Right-hand side (result of rewriting). Can be empty. - #
comment(optional) — end-of-line comment.
Component Order: the order is strict. The DERIVATION_MODE comes after the weight and before the flags.
Source: Encode.c:103-725 (rule encoding)
Example: gram#3[1] <100> LEFT /x=1/ M16 <-> V16 // comment
rule_prefix
rule_prefix = "gram#" , INT , "[" , INT , "]" ;
"gram#"— rule marker.- #
INT(1st) — required. Grammar number (N). Identifies the parent block. "[","]"— delimiters.- #
INT(2nd) — required. Rule number within the block (M).
Constraint: the 1st INT must correspond to the index of the parent grammar_section (that of mode_line).
Example: gram#3[47] → 47th rule of the 3rd block.
ARROW
ARROW = "-->" | "<->" | "<--" ;
3 possible values:
| Value | Name | Direction | Active in PROD | Active in ANAL |
|---|---|---|---|---|
--> |
Production | Left → right | yes | no |
<-> |
Bidirectional | Both | yes | yes |
<-- |
Analysis | Right → left | no | yes |
Source: -BP3main.h:114 (MAXARROW=3), -BP3.h:886 (ARROWLENGTH=4)
Constraint: <-> is the most common value (217 occurrences in bp3-ctests). <-- does not appear in any test file — its actual use is an open question (cf. §13, Q4).
See also: B8 (PROD, ANAL, TEMP directions)
DERIVATION_MODE
DERIVATION_MODE = "LEFT" | "RIGHT" | "RND" ;
3 possible values. This mode is distinct from the block’s MODE (Layer 2):
MODE(block) = what selection strategy? (ORD, RND, LIN, SUB, SUB1, TEM, POSLONG)DERIVATION_MODE(rule) = in what direction to traverse? (LEFT, RIGHT, RND)
| Value | Traversal Direction |
|---|---|
LEFT |
Left to right (leftmost) |
RIGHT |
Right to left (rightmost) |
RND |
Random position (default if omitted) |
Source: Encode.c:478-508, -gr.dhati:49, -gr.look-and-say
Example: gram#3[1] <100> LEFT M16 <-> V16 → left-to-right derivation.
weight
weight = "<" , weight_expr , ">" ;
weight_expr = INT , [ "-" , INT ]
| "K" , IDENT , [ "=" , INT ] ;
"<",">": literals — weight delimiters.weight_expr: one of the following forms:
| Form | Components | Semantics | Example |
|---|---|---|---|
INT |
simple weight | Fixed weight. Shortcut for INT-0. |
<3> |
INT - INT |
decremented weight | 1st = initial value, 2nd = decrement per use. | <50-12> |
K IDENT |
bare K | Interactive weight — user chooses dynamically. | <K1> |
K IDENT = INT |
K with value | Rule active only if K-parameter IDENT equals INT. |
<K1=5> |
Source: -gr.Mozart:34-44 (K with and without value)
See also: B4
flag
flag = "/" , IDENT , [ FLAG_OP , flag_value ] , "/" ;
FLAG_OP = "=" | "+" | "-" | ">" | "<"
| "≥" | "≤" | "≠" ;
flag_value = INT | FLOAT
| "K" , IDENT
| IDENT ;
"/"— flag delimiters.- #
IDENT— required. Variable (flag) name. FLAG_OP(optional): operator. Absent = boolean switch.flag_value(optional, required ifFLAG_OPpresent): operation value.
Operators:
FLAG_OP |
Semantics |
|---|---|
= |
Assignment or equality test |
+ |
Increment |
- |
Decrement |
>, < |
Strict comparison |
≥, ≤, ≠ |
Comparison (Unicode) |
Forms of flag_value:
| Form | Type | Semantics | Example |
|---|---|---|---|
INT |
INT |
Literal integer value | /x=5/ |
FLOAT |
FLOAT |
Literal floating-point value | /tempo=0.8/ |
K IDENT |
"K" + IDENT |
Reference to an interactive K-parameter | /a=Kb/ |
IDENT |
IDENT |
Direct reference to another flag (flag→flag) | /a=otherflag/ |
Source: Encode.c:478-508 (flag-to-flag assignment with needsK and needsflag)
Examples:
/stop/— boolean switch (no operator)/section=1/— test: rule applies only ifsectionequals 1/count+1/— increment:countincreases by 1 with each application/a=Kb/— cross-assignment via K-parameter/a=otherflag/— direct flag→flag assignment
See also: B4
Example combining block, rule, and control:
RND[2] ← MODE=RND, INT=2
gram#2[1] <50-10> /section=1/ A --> do ré mi fa
gram#2[2] <30> /section>2/ A --> sol la si do5
gram#2[3] <20> A --> - - - -
- Block in
RNDmode (weighted random selection, Layer 2). - Each
ruleusesARROW=-->(production only). - Rule 1:
weight=<50-10>(initial 50, decrement 10),flag=/section=1/(active ifsection== 1). - Rule 2:
weight=<30>(fixed),flag=/section>2/(active ifsection> 2). - Rule 3:
weight=<20>(fixed), no flag (always available).
Layer 4 — LHS and RHS Elements
lhs
lhs = lhs_element+ ;
lhs_element = non_terminal | variable | wildcard | context_marker ;
lhs_element(1..n) — the left-hand side must contain at least one element. Each element is a #non_terminal, #variable, #wildcardor #context_marker.
Constraint: when lhs contains more than one element, the rule becomes context-sensitive (Chomsky Type 1). The first element is the rewritten symbol, the following ones are the required context.
Context-sensitive example:
gram#3[47] |o| |miny| --> |o1| |miny|
|o| is rewritten to |o1| only if |miny| is adjacent. The |miny| in the RHS is a pass-through — it is transmitted without modification. This mechanism pushes BP3 beyond Type 2 (L1).
rhs
rhs = rhs_element* ;
- #
rhs_element(0..n) — the right-hand side can be empty (*), which corresponds to the erasure of a symbol (ε-production).
rhs_element
rhs_element = note | rest | prolongation | undetermined_rest
| non_terminal | variable | wildcard
| polymetric | special_fn | nil_string
| pattern_expr | time_sig | annotation
| quoted_symbol | tie
| context_marker | goto_directive
| out_time_object | sync_tag
| speed_ratio | negation
| beat_separator | start_symbol ;
23 possible types, organized into 4 families:
![[BP3_RHS_Elements.png|700]]
Figure 1 — The 23 types of BP3 RHS elements, organized into 4 families.
4.1 Terminals
note
note = NOTE_NAME , [ OCTAVE ] ;
- #
NOTE_NAME— required. Note name in one of the 4 notation systems. - #
OCTAVE(optional) — digit indicating the octave (0–9). Default: octave defined in settings.
4 notation systems (-BP3.h:478-483):
| System | Examples | Alterations |
|---|---|---|
| French | do, ré, mi, fa, sol, la, si |
# (sharp), b (flat) |
| Anglo-Saxon | C, D, E, F, G, A, B |
#, b |
| Indian | sa, re, ga, ma, pa, dha, ni |
p (komal), d (tivra) |
| Keys (MIDI) | 60, 72, 48 |
��� (direct MIDI numbers 0-127) |
BP3 supports up to 100 custom scales (MAXCONVENTIONS=100) via _scale().
Constraint: Anglo-Saxon notes (C4, A8) are syntactically identical to non-terminals (UPPER_IDENT). The distinction is resolved at emission: undefined uppercase identifiers that match the pattern [A-G][#b]?\d are treated as notes. This is a syntactic ambiguity resolved by semantic context.
rest
rest = "-" ;
"-"— determined rest. Its duration is calculated by the temporal context.
Source: token T3/1 (BP2-info.txt)
prolongation
prolongation = "_" ;
"_"— prolongation. Extends the previous event without a new attack. The sound continues, no silence. Internal code:kobj=0inFillPhaseDiagram.c.
Source: token T3/0 (BP2-info.txt)
undetermined_rest
undetermined_rest = "..." ;
"..."— undetermined rest. Duration calculated by the PolyMake algorithm to balance polymetric voices.
Source: token T3/0 (BP2-info.txt)
See also: B5, B13
non_terminal
non_terminal = UPPER_IDENT ;
- #
UPPER_IDENT— required. Identifier starting with an uppercase letter (S,Phrase,Tihai,A8).
4.2 Context-sensitive Extensions
variable
variable = "|" , IDENT , "|" ;
"|"— delimiters.- #
IDENT— required. Variable name.
When |x| appears multiple times in the RHS, each occurrence is replaced by the same value — synchronized copy. This is the mechanism that pushes BP3 beyond Type 2 (B6).
wildcard
wildcard = "?" , [ INT ] ;
"?"— wildcard marker.- #
INT(optional) — named capture index. Absent = anonymous wildcard.
| Form | PROD Mode | ANAL Mode |
|---|---|---|
? |
Anonymous placeholder | Accepts any terminal |
?1 |
Named capture — generates a pattern | Captures a pattern and checks its consistency |
?1 ... ?1 |
Replication of the captured pattern | Checks that the same pattern appears at both positions |
See also: B6, B8
nil_string
nil_string = "lambda" | "nil" | "empty" | "null" ;
4 equivalent forms for the empty production ε (erasure of a non-terminal).
Source: -BP3main.h:112
pattern_expr
pattern_expr = "(=" , rhs_element+ , ")"
| "(:" , rhs_element+ , ")"
| HOMO_REF ;
(= ...)— master: derives its content normally and freezes the result for linked slaves.(: ...)— slave: copies the already derived result from the closest(= ...)master to the left whose syntactic content is identical. If a*homomorphism is defined in the alphabet, the slave applies the transformation before copying (→ pseudo-repetition).HOMO_REF— reference to an external homomorphism file (-ho.).
Mechanism. The master/slave link is resolved at compilation by left-to-right linear search. Direct nesting is not supported — use intermediate non-terminals for nested patterns (e.g., chakkardar tihai).
Examples:
S --> (= A)(= B)(: A)(: B)(: A) (* tihai: produces aabba if A→a, B→b *)
S --> (= A) (: A) (* strict copy: produces {ww} *)
See also: B6
4.3 Structural Constructions
polymetric
polymetric = "{" , [ ratio , "," ] , voice , { "," , voice } , "}" ;
ratio = INT | INT "/" INT ;
voice = rhs_element+ ;
"{","}"— polymetric block delimiters.ratio(optional) — tempo factor. #INTalone = integer, #INT/#INT= fraction.voice(1..n) — sequence of #rhs_elementforming a voice. Multiple voices separated by",".
Examples:
{do ré mi, sol la si do5} (* 2 simultaneous voices, equal duration *)
{3, do ré mi fa sol la si} (* tempo ratio: 3× *)
{2/3, mi fa sol} (* fractional ratio *)
See also: B5, B12
time_sig
time_sig = TIME_SIG_PATTERN ;
- #
TIME_SIG_PATTERN— time signature.
annotation
annotation = "[" , TEXT , "]" ;
"[","]"— delimiters.TEXT— free content. Metadata attached to the musical stream.
quoted_symbol
quoted_symbol = "'" , TEXT , "'" ;
"'"— delimiters.TEXT— literal symbol transmitted as-is.
tie
tie = note , "&" | "&" , note ;
note &: tie start — the note is prolonged.& note: tie continuation — the note is connected to the previous one.
Equivalent to the tie in Western musical notation.
4.4 Flow Control
context_marker
context_marker = "LEFT" ;
LEFT: forces the parser to consider only the left context.
Correction
The original version of this article included
(|symbol|),#({)and#(})as extendedcontext_marker`. These constructions do not exist in BP3’s source code. They have been removed.
goto_directive
goto_directive = "_goto(" , INT , "," , INT , ")" ;
- #
INT(1st) — required. Target block number (N). - #
INT(2nd) — required. Target rule number (M).
Imperative jump to rule M of block N. Imperative flow control mechanism within a declarative formalism.
Constraint: forbidden in SUB, SUB1, and POSLONG blocks (Encode.c:363-365).
Source: 8 occurrences in bp3-ctests.
out_time_object
out_time_object = "<<" , ( note | IDENT ) , ">>" ;
"<<",">>"— out-of-time object delimiters.- #
note— MIDI note played immediately out of the temporal flow (encoded askey + 16384). - #
IDENT— reference to a sound-object in the alphabet (encoded as index).
Source: Encode.c:574-725 (token T7). The threshold 16384 distinguishes notes (x ≥ 16384 → x - 16384 = MIDI key) from sound-objects (x < 16384 → index).
sync_tag
sync_tag = "<<" , "W" , INT , ">>" ;
"<<",">>"— delimiters."W"— synchronization marker.- #
INT— required. Synchronization point index.
Rendezvous mechanism between polymetric voices: <<W1>> in one voice waits for <<W1>> in another voice before continuing.
Source: token T8 (BP2-info.txt)
speed_ratio
speed_ratio = "/" , INT , "/"
| "*" , INT
| "**" , INT
| "\" , INT ;
4 speed operators:
| Form | Token | Semantics |
|---|---|---|
/N/ |
T0/11 | Speed up — acceleration by factor N |
*N |
T0/21 | Scale up — increase |
**N |
T0/24 | Scale down — decrease |
\N |
T0/25 | Speed down — deceleration by factor N |
Source: BP2-info.txt:22,32,35, Encode.c:103-118
negation
negation = "#" ;
"#"— negation operator.
beat_separator
beat_separator = "." ;
"."— beat separator.
Source: token T0/7 (BP2-info.txt:18)
start_symbol
start_symbol = "S" ;
"S"— grammar start symbol.
Source: token T0/10 (BP2-info.txt:21)
~~concatenation~~ — REMOVED
Correction
The
+is not a concatenation operator between symbols in the RHS. It only exists in the context of flags (/flag+1/), where it serves as an increment. It is not part ofrhs_element. Misinterpretation of token T0/3 in the original version of this article.
Layer 5 — Special Functions
special_fn
special_fn = "_" , IDENT , [ "(" , args , ")" ] ;
args = arg , { "," , arg } ;
arg = INT | FLOAT | IDENT | STRING ;
"_"— mandatory prefix distinguishing special functions from grammatical elements.- #
IDENT— required. Function name. args(optional) — list of arguments separated by",".arg— a typed argument (#INT, #FLOAT, #IDENTorSTRING).
BP3 defines 68 performance controls (tokens T10-T46 and T12/0-30) and 17 grammar procedures. They fall into 8 categories:
| Category | Main Functions | Num args | Layer* |
|---|---|---|---|
| Pitch | _transpose(N), _scale(name,base), _pitchbend(N), _pitchrange(N), _keyxpand(N1,N2) |
1-2 | 4 |
| Velocity | _vel(N), _volume(N), _rndvel(N) |
1 | 3 |
| Articulation | _staccato(N), _legato(N) |
1 | 3 |
| Timing | _mm(N), _tempo(ratio), _rndtime(N), _striated, _smooth |
0-1 | 5 |
| Instruments | _ins(name), _script(name), _chan(N), _part(N), _capture(N) |
1 | 2 |
| MIDI | _mod(N), _pitchbend(N), _press(N), _pan(N), _switchon(cc,value), _switchoff(cc,value) |
1-2 | 2 |
| Structure | _retro, _rotate(N), _randomize, _destru, _srand(N) |
0-1 | 4 |
| Derivation | _goto(N,M), _failed(N,M), _repeat(N), _stop |
0-2 | 5 |
*Layer numbers refer to the musical abstraction model (M12).
Source: StringLists.h:265-335 (function declarations with number of arguments)
Constraint: each continuous control (velocity, modulation, pitch bend, etc.) exists in 3 variants: _xxxstep (step), _xxxcont (interpolation), _xxxfixed (fixed value). These variants are MIDI implementation details.
Example:
ORD[1]
_mm(120) ← preamble: global tempo 120 BPM
_ins(Tabla) ← preamble: Tabla instrument
gram#1[1] S --> _vel(80) Tihai ← _vel(80) applies locally
Layer 6 — Templates
template_section
template_section = "TEMPLATES:" , template_def+ ;
"TEMPLATES:"— section start marker.- #
template_def(1..n) — template definitions.
Constraint: appears only if at least one sub-grammar has TEM mode and if hasTEMP == TRUE (CompileGrammar.c:190).
template_def
template_def = "[" , INT , "]" , template_pattern ;
"[","]"— index delimiters.- #
INT— required. Template index. - #
template_pattern— the structural skeleton.
template_pattern
template_pattern = { time_sig | speed_ratio | placeholder
| "(" , "@" , INT , template_body , ")"
| rhs_element } ;
- #
time_sig(0..n) — time signature. - #
speed_ratio(0..n) — speed ratio (*1/1,*1/2). - #
placeholder(0..n) — substitution position. (@N ...)(0..n) — group referencing the sub-grammar of indexN. #INT= sub-grammar index, #template_body= content.- #
rhs_element(0..n) — ordinary RHS elements.
placeholder
placeholder = "_"+ ;
"_"(1..n) : one or more consecutive underscores. In the context of templates,_retains its prolongation semantics (T3/0,kobj=0) — the same semantics as everywhere else in the language. There is no double semantics.
The
TEMPLATES:section is ignored by the compiler (CompileGrammar.c:437-443). It serves as structural documentation, not executable code.
template_body
template_body = { placeholder | rhs_element } ;
Source: -gr.checktemplates, -gr.dhin-- (bp3-ctests)
Example:
TEMPLATES:
[1] *1/1 __*1/2 _
[5] *1/1 (@0 _)(@1 )
[1] 4+4*1/6 (@0 (@0 (@0 ______) * (@1 ))(@2 ))____ * (@3 )____
Layer 7 — Lexemes
IDENT
IDENT = letter , { letter | digit | "_" } ;
letter: lowercase or uppercase letter.digit: digit0–9."_": underscore allowed in the body.
UPPER_IDENT
UPPER_IDENT = upper_letter , { letter | digit | "_" } ;
upper_letter: uppercase letter (A–Z). First position must be uppercase.
Used for non-terminals.
INT
INT = digit+ ;
digit(1..n) : one or more digits0–9.
FLOAT
FLOAT = [ "-" ] , digit+ , "." , digit+ ;
"-"(optional) : negative sign.digit+: integer part.".": decimal separator.digit+: fractional part.
NOTE_NAME
NOTE_NAME = FRENCH_NOTE | ANGLO_NOTE | INDIAN_NOTE | KEY_NOTE ;
KEY_NOTE = INT ;
FRENCH_NOTE = ("do"|"ré"|"re"|"mi"|"fa"|"sol"|"la"|"si") , [alteration] ;
ANGLO_NOTE = ("C"|"D"|"E"|"F"|"G"|"A"|"B") , [alteration] ;
INDIAN_NOTE = ("sa"|"re"|"ga"|"ma"|"pa"|"dha"|"ni") , [alteration_in] ;
alteration = "#" | "b" ;
alteration_in = "p" | "d" ;
4 notation systems that produce the same AST node type (Note). The parser must recognize 4 musical alphabets simultaneously.
OCTAVE
OCTAVE = digit ;
digit: a single digit0–9.
TIME_SIG_PATTERN
TIME_SIG_PATTERN = INT , { "+" , INT } , "/" , INT ;
- #
INT(1..n) — numerator(s). Multiple numerators added together separated by"+". "/"— separator.- #
INT— denominator.
Example: 4+4*1/6 → composite time signature.
Layer 8 — Reference Files
Preliminary formalization of the 5 most used formats. These EBNFs are derived from the analysis of the C source code and the 213 test files — they have not been exhaustively validated.
settings_file (-se.)
settings_file = { settings_line } ;
settings_line = key , ":" , value ;
key = "tempo" | "channel" | "velocity" | "transpose"
| "pitchbend_range" | IDENT ;
value = INT | FLOAT | STRING ;
Key-value pairs configuring global parameters (tempo, MIDI channel, initial velocity, transposition).
alphabet_file (-al.)
alphabet_file = { alphabet_entry } ;
alphabet_entry = terminal , mapping ;
terminal = NOTE_NAME | IDENT ;
mapping = midi_mapping | synthdef_mapping ;
midi_mapping = INT ;
synthdef_mapping = "SynthDef(" , STRING , ")" ;
Correspondence between terminals and sound events (MIDI or SuperCollider SynthDefs).
homo_file (-ho.)
homo_file = { homo_entry } ;
homo_entry = source_note , "-->" , target_note ;
source_note = NOTE_NAME ;
target_note = NOTE_NAME ;
Note → note substitution tables (B6).
csound_file (-cs.)
csound_file = CSOUND_SCORE ;
External Csound format — transmitted directly to the synthesis engine, not parsed by BP3.
timeobj_file (-to.)
timeobj_file = { timeobj_entry } ;
timeobj_entry = IDENT , ":" , resource_ref ;
resource_ref = file_path | url | device_ref ;
Correspondence between identifiers and external resources (audio files, videos, robotic commands). See B9.
Note: the formalization of reference files constitutes an original contribution: no published document provides a formal grammar for these auxiliary formats. These EBNFs are preliminary.
Meta — Grammar of BP3 vs. Grammars within BP3
The EBNF above (~83 productions) is the grammar of the BP3 language — it describes what a well-formed BP3 file can syntactically contain. It is a context-free grammar (Chomsky Type 2, L1).
BP3 files themselves contain grammars — the gram#N[M] rules written by the user. These internal grammars, thanks to variables, wildcards, homomorphisms, and multi-symbol LHS rules, are more powerful than Type 2: they reach the MCSL level (Mildly Context-Sensitive Languages — L9).
![[BP3_Grammar_Levels.png|700]]
Figure 2 — Two levels of grammar: the grammar of BP3 (meta-level, Type 2) and the grammars within BP3 (object-level, Type 2+).
Limitations of EBNF
An EBNF grammar specifies the syntax — what constitutes a well-formed BP3 file. It does not cover:
Semantics. EBNF states that <50-12> is a valid weight, but not what “decrement by 12” means. See L6 (operational semantics), L7 (denotational semantics).
Derivation strategy. EBNF states that ORD and RND are valid modes, but not how the engine selects and applies rules. See B3.
Inter-production constraints. EBNF cannot express that the INT of rule_prefix must correspond to the index of the parent grammar_section. These invariants fall under AST specification (B11).
EBNF is a necessary but not sufficient condition: a file can be syntactically correct yet invalid (inconsistent numbers, undefined terminals, unbound variables). A real parser needs a validation phase after parsing — the 4th phase of the pipeline (B7).
Summary
| Layer | Productions | Content | Articles |
|---|---|---|---|
| 1. Global Structure | 5 | File, headers, 16 prefixes, directives | B7 |
| 2. Grammar Blocks | 5 | 7 modes, preamble, separator | B3 |
| 3. Rules | 8 | 3 arrows, 3 derivation modes, weights (K), flags (Unicode) | B1, B4 |
| 4. LHS/RHS Elements | 23 | Notes, rests, polymetry, variables, wildcards, out-time, sync, T0 ops | B2 to B6 |
| 5. Special Functions | 3 | 68 perf controls + 17 grammar procedures in 8 categories | M12 |
| 6. Templates | 5 | TEMPLATES: section, placeholders, groups (@N) | B8 |
| 7. Lexemes | ~14 | Identifiers, 4 note systems, numbers | B2 |
| 8. Reference Files | ~10 | Settings, alphabet, homo, Csound, time-objects | B9 |
| Total | ~83 |
Key Takeaways
- BP3 is described in ~83 EBNF productions organized into 8 layers: global structure, grammar blocks, rules, LHS/RHS elements, special functions, templates, lexemes, reference files.
- The EBNFdoc format systematically documents each production: typed components, cardinality, constraints, C source, examples.
- 7 sub-grammar modes (ORD, RND, LIN, SUB, SUB1, TEM, POSLONG) determine the rule selection strategy at the block level.
- 3 arrows (
-->,<->,<--) control in which directions (PROD, ANAL) each rule is active. - 3 derivation modes per rule (LEFT, RIGHT, RND) control the traversal direction — a distinct level from the block mode.
- 23 types of RHS elements give the language its flexibility: from notes and rests to context-sensitive variables, polymetry, sync tags, and special functions.
- 68 performance controls and 17 grammar procedures form a performative layer that operates on 4 levels of abstraction.
- The grammar of BP3 (this EBNF) is context-free (Type 2). The grammars within BP3 (written by the user) are Type 2+ (MCSL) thanks to variables, wildcards, and context-sensitive rules.
- EBNF is a necessary but not sufficient condition: a real parser needs additional invariants (B11) and a validation phase (B7).
What’s Next?
EBNF is the architect’s blueprint — it tells what parts exist and how they fit together. The next step is to understand what the parser builds from this blueprint: the Abstract Syntax Tree (AST), with its 29 node types, its invariants, and its production-by-production correspondence with the EBNF. This is the subject of B11.
Glossary
- ARROW: Type of arrow in a BP3 rule (
-->,<->,<--). Determines in which directions (PROD, ANAL) the rule is active. - DERIVATION_MODE: Traversal direction for an individual rule (
LEFT,RIGHT,RND). Distinct from the block’sMODE. - EBNFdoc: Systematic documentation format for EBNF productions, inspired by API references (Javadoc, MDN). Each production is documented with its typed components, constraints, C source, and examples.
- Flag: Conditional variable attached to a rule (
/name=value/). Controls activation and modifies the derivation state. - grammar_section: Grammar block containing a mode, an optional preamble, and rules.
- K-parameter: Interactive weight (
<K1>,<K1=5>) allowing the user to dynamically control rule selection. - LHS (Left-Hand Side): Left side of a rule — what is rewritten. Must contain at least one element.
- MCSL (Mildly Context-Sensitive Languages): Class of languages between Type 2 (context-free) and Type 1 (context-sensitive). Position of grammars written within BP3.
- MODE: Derivation mode of a grammar block (ORD, RND, LIN, SUB, SUB1, TEM, POSLONG). Determines the rule selection strategy.
- Out-time object:
<<...>>element played immediately, outside the normal temporal flow. Can be a MIDI note or a sound-object. - Performance control: Special function (
_name(args)) controlling sound rendering (pitch, velocity, timing, etc.). 68 functions defined in BP3. - Weight: Numerical value
<N>or<N-M>controlling the probability of rule selection in RND mode. - RHS (Right-Hand Side): Right side of a rule — the result of rewriting. Can be empty (ε-production). 23 possible element types.
- rule_prefix: Unique identifier of a rule (
gram#N[M]).N= block index,M= rule index within the block. - Sync tag: Synchronization point
<<WN>>between polymetric voices. Rendezvous mechanism. - Template: Structural skeleton pre-calculated by the TEMP direction. Stored in the
TEMPLATES:block.
Links
Prerequisites
- L3
- B1 to B6
Direct Continuation
- B11
Connections
- B7 (complete pipeline)
- B8 (PROD, ANAL, TEMP)
- B9 (
-to.files) - B12 (symbolic time and
_tempo()) - L1 (classification)
- L9 (MCSL)
- M12 (abstraction layers)
- R2 (detailed technical version)
Back
- Series Index
- Glossary
References
[Bel1990] Bel, B. (1990). Bol Processor BP1 and its music features. GRTC Research Report, Marseille.
[Bel1992] Bel, B. (1992). Symbolic and Sonic Representations of Sound-Object Structures. In M. Balaban, K. Ebcioğlu & O. Laske (Eds.), Understanding Music with AI, AAAI Press.
[Bel1998] Bel, B. (1998). Migrating musical concepts: an overview of the Bol Processor. Computer Music Journal, 22(2), 56-64.
[Bel2001] Bel, B. (2001). Rationalizing Musical Time: Syntactic and Symbolic-Numeric Approaches. In C. Music (Ed.), The Ratio Book, Den Haag: Royal Conservatory.
[ISO14977] ISO/IEC 14977:1996. Information technology — Syntactic metalanguage — Extended BNF.
[Chomsky1956] Chomsky, N. (1956). Three Models for the Description of Language. IRE Transactions on Information Theory, 2(3), 113-124.
[Kippen1992] Kippen, J. & Bel, B. (1992). Modelling Music with Grammars: Formal Language Representation in the Bol Processor. In A. Marsden & A. Pople (Eds.), Computer Representations and Models in Music, Academic Press.
[AhoUllman1972] Aho, A. V. & Ullman, J. D. (1972). The Theory of Parsing, Translation, and Compiling. Prentice-Hall.
Prerequisites: L3 (EBNF), B1 to B6 (BP3 constructions)
Reading time: 20 min
Tags: #EBNF #BP3 #specification #reference #EBNFdoc #formal-grammar
← Back to index