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:

  1. Production — the EBNF code (ISO 14977)
  2. 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

  1. Constraints — what EBNF cannot express (invariants, inter-production consistency)
  2. Source — reference to BP3’s C code when relevant (file:lines)
  3. 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 uses TEM mode.

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 to ExecScriptLine (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 in gram#N references.
  • #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] MyBlockRND 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, and POSLONG modes prohibit /flag/ flags, _goto directives, and the “Produce all items” command (CompileGrammar.c:613-695, ProduceItems.c:757).
  • TEM mode has no specific selection logic in the computation engine (TEMtype defined but not referenced in Compute.c) — behavior is determined by the TEMP direction.

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 if FLAG_OP present): 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 if section equals 1
  • /count+1/ — increment: count increases 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 RND mode (weighted random selection, Layer 2).
  • Each rule uses ARROW = --> (production only).
  • Rule 1: weight = <50-10> (initial 50, decrement 10), flag = /section=1/ (active if section == 1).
  • Rule 2: weight = <30> (fixed), flag = /section>2/ (active if section > 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, #wildcard or #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 (09). Default: octave defined in settings.

4 notation systems (-BP3.h:478-483):

System Examples Alterations
French do, , 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=0 in FillPhaseDiagram.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. #INT alone = integer, #INT/#INT = fraction.
  • voice (1..n) — sequence of #rhs_element forming 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 extended context_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 as key + 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 of rhs_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, #IDENT or STRING).

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 index N. #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 : digit 09.
  • "_" : underscore allowed in the body.

UPPER_IDENT

 

UPPER_IDENT = upper_letter , { letter | digit | "_" } ;

 

  • upper_letter : uppercase letter (AZ). First position must be uppercase.

Used for non-terminals.

INT

 

INT = digit+ ;

 

  • digit (1..n) : one or more digits 09.

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 digit 09.

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’s MODE.
  • 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


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