Formal languages · L2 · Course
Context-Free Grammars
Discover the fundamental rules that allow computers to interpret the structure of any language, from Python to JSON.
Published 8 February 2026 · updated 15 February 2026 · Lire en français
The Foundation of Everything
How can a computer understand that
2 + 3 * 4equals 14 and not 20? The answer lies in three letters: CFG.
Where does this article fit in?
This article is part of the Formal Languages (L) series. It delves into Type 2 of Chomsky’s hierarchy (see L1): context-free grammars, the foundation of most parsers and compilers (programs that translate a source language into a target language).
Why is it important?
Every time you write Python code, JSON, or even a formula in Excel, a program needs to understand the structure of what you’ve written. This understanding doesn’t happen by magic: it relies on precise rules that describe how elements fit together.
Context-Free Grammars (CFG) are the fundamental tool for describing these rules. They are at the heart of all compilers, all parsers, all tools that transform structured text into something usable. Without them, no Python, no JavaScript, no HTML… and no BP3 (Bol Processor 3, a musical grammar language — see I2), the musical notation we transpile (translate from one source language to another language of the same level) to SuperCollider (an audio programming environment — see I3).
Understanding CFGs means understanding how languages are defined and how machines interpret them.
The idea in one sentence
A context-free grammar is a set of rewrite rules that describe how to construct all valid sentences of a language, without considering the context.
Let’s explain step by step
The structure of a grammar:
A context-free grammar is defined by four elements, traditionally denoted :
| Symbol | Name | Role | Example |
|---|---|---|---|
| V | Variables (non-terminals) | Abstract symbols that will be replaced | Expr, Term, Factor |
| Σ | Alphabet (terminals) | Concrete symbols of the final language | +, *, (, ), 0-9 |
| R | Production rules | How to replace variables | |
| S | Start symbol | Where the derivation begins | Expr |
Why these Greek letters?
- Σ (capital sigma) represents the alphabet, by mathematical tradition
- The arrow
→(or-->) reads “rewrites to” or “can be replaced by”- The vertical bar
|means “or” (alternative between several possibilities)Thus, reads: “An expression can be rewritten either as ‘expression + term’, or as ‘term’ alone.”
Why “context-free”?
The term “context-free” means that each rule can be applied independently of what surrounds the symbol. If we have the rule , we can always replace with , regardless of what comes before or after . This property is what makes CFGs efficiently parsable.
Example 1: Arithmetic expressions
Let’s build a grammar for expressions like 2 + 3 * 4 or (1 + 2) * 3.
The four components:
V = { Expr, Term, Factor } -- Variables
Σ = { +, *, (, ), 0, 1, ..., 9 } -- Terminals
S = Expr -- Start symbol
R = { -- Rules
Expr → Expr + Term | Term
Term → Term * Factor | Factor
Factor → ( Expr ) | number
}
Derivation of 2 + 3 * 4:
Let’s start from the start symbol and apply the rules:
Expr
→ Expr + Term (rule 1)
→ Term + Term (rule 1, "Term" case)
→ Factor + Term (rule 2, "Factor" case)
→ 2 + Term (rule 3, "number" case)
→ 2 + Term * Factor (rule 2)
→ 2 + Factor * Factor (rule 2, "Factor" case)
→ 2 + 3 * Factor (rule 3)
→ 2 + 3 * 4 (rule 3)
Why does it give 14 and not 20?
The structure of the grammar encodes operator precedence. Multiplication is “deeper” in the tree (it appears in Term), so it is evaluated first. The grammar forces the interpretation 2 + (3 * 4) and not (2 + 3) * 4.
This is the magic of CFGs: the grammatical structure captures the semantics.
The derivation process
A derivation is a sequence of replacements starting from the start symbol and resulting in a string of terminals (the final text). This is the core of how a grammar works.
Analogy: derivation as a cooking recipe
Imagine a recipe: “Dessert → Pie | Cake”, “Pie → Crust + Filling”, etc.
Derivation is following the steps: starting from “Dessert”, replacing it with “Pie”, then “Pie” with “Crust + Filling”, until you get the concrete ingredients (terminals).
Types of derivation:
- Leftmost derivation: at each step, the leftmost non-terminal is replaced.
- Rightmost derivation: at each step, the rightmost non-terminal is replaced.
Both strategies lead to the same final result, but the order of intermediate steps differs. This distinction is important because different types of parsers use one strategy or the other:
| Parser Type | Derivation Used | Example Tool |
|---|---|---|
| LL (top-down) | Leftmost | ANTLR (ANother Tool for Language Recognition), recursive descent parsers |
| LR (bottom-up) | Rightmost | Bison, Yacc (Yet Another Compiler-Compiler) |
Derivation tree:
Each derivation can be represented as a tree, where:
- The root is the start symbol
- Internal nodes are non-terminals
- Leaves are terminals
- Each level represents the application of a rule
Figure 1 — Derivation tree for
2 + 3 * 4with the unambiguous grammar. Multiplication, being deeper in the tree, is evaluated first.
Example 2: A simplified musical grammar
Let’s imagine a minimalist musical notation:
V = { Phrase, Motif, Note }
Σ = { do, ré, mi, fa, sol, -, ( , ) }
S = Phrase
R = {
Phrase → Motif | Phrase Motif
Motif → Note | ( Phrase )
Note → do | ré | mi | fa | sol | -
}
This grammar can generate:
do ré mi(three notes)do - ré(note, rest, note)( do ré ) ( mi fa )(two grouped motifs)( do ( ré mi ) fa )(nesting)
Derivation of do ( ré mi ) fa:
Phrase
→ Phrase Motif -- add a motif
→ Phrase Motif Motif -- add another
→ Phrase Motif fa -- Note → fa
→ Phrase ( Phrase ) fa -- Motif → ( Phrase )
→ Phrase ( Phrase Motif ) fa -- Phrase → Phrase Motif
→ Phrase ( Motif Motif ) fa -- Phrase → Motif
→ Phrase ( ré Motif ) fa -- Note → ré
→ Phrase ( ré mi ) fa -- Note → mi
→ Motif ( ré mi ) fa -- Phrase → Motif
→ do ( ré mi ) fa -- Note → do
The problem of ambiguity
A grammar is ambiguous if the same string can be derived in several different ways (multiple derivation trees).
Example of an ambiguous grammar:
Expr → Expr + Expr | Expr * Expr | nombre
For 2 + 3 * 4, two trees are possible:
Figure 2 — Ambiguity: two derivation trees for
2 + 3 * 4with the ambiguous grammar. The left tree yields 14, the right one yields 20.
The first gives 2 + (3 * 4) = 14, the second gives (2 + 3) * 4 = 20.
Why is this serious?
Ambiguity means the parser doesn’t know which interpretation to choose. For a programming language, this is unacceptable: the code must have a unique meaning.
Solutions:
- Rewrite the grammar to eliminate ambiguity (this is what we did in our first example with
TermandFactor). - Disambiguation rules: operator precedence and associativity.
- Accept ambiguity and resolve it semantically (rare).
Properties of CFGs
What CFGs can describe:
- Nested structures (parentheses, HTML/XML tags)
- Recursion (definitions that refer to themselves)
- The syntax of most programming languages
What is recursion?
A definition is recursive when it refers to itself. For example:
Expr → Expr + TermAn
Exprcontains anotherExpr! It’s like Russian dolls, or mirrors facing each other.Recursion allows expressing structures of arbitrary depth:
1+2+3+4+...with a single rule.
What CFGs CANNOT describe:
| Limitation | Formal Notation | Concrete Example |
|---|---|---|
| Triple counting equality | aⁿbⁿcⁿ | “as many a’s, then b’s, then c’s” |
| Declaration before use | - | Checking that a variable is declared before being used |
| Long-distance agreement | - | Subject-verb agreement with interpolated words |
Reading
aⁿbⁿcⁿThis mathematical notation means: “n repetitions of ‘a’, followed by n repetitions of ‘b’, followed by n repetitions of ‘c’”.
Valid examples:
abc(n=1),aabbcc(n=2),aaabbbccc(n=3)A CFG cannot guarantee that the three groups have exactly the same number of elements.
These limitations motivate the existence of context-sensitive grammars (Type 1 of Chomsky’s hierarchy — see L1) and additional semantic analyses performed after parsing.
How CFGs apply to BP3
The syntax of BP3, the musical grammar language, is itself described by a CFG. Here is a simplified excerpt:
bp_file → header* grammar_section+
grammar_section → mode_line rule_line+
rule_line → weight? lhs "-->" rhs
lhs → nonterminal+
rhs → element*
element → note | rest | nonterminal | polymetric | ...
A BP3 rule like:
<50> S --> A B C
Breaks down into:
weight:<50>lhs:Srhs:A B C
The parser of the BP2SC transpiler (BP3-to-SuperCollider, see B7) uses this structure to build an AST (Abstract Syntax Tree — see L4), which will then be translated into SuperCollider code.
Key takeaways
- : Variables, Terminals, Rules, Start Symbol. These four elements completely define a grammar.
- “Context-free” means that rules apply independently of the surrounding context. This is what makes analysis efficient.
- Derivation is the process of successively replacing variables until a string of terminals is obtained.
- The derivation tree captures the hierarchical structure of the sentence, which determines its interpretation.
- Ambiguity is the enemy: an ambiguous grammar produces multiple possible interpretations. It is resolved by careful rule design.
- CFGs are at the heart of compilation: the syntax of Python, Java, JSON, HTML, and BP3 is described by CFGs.
To go further
- Reference book: Aho, Lam, Sethi & Ullman, Compilers: Principles, Techniques, and Tools (the “Dragon Book”)
- Online course: Coursera, Automata Theory by Jeffrey Ullman (Stanford)
- Practical tool: JFLAP to visualize CFGs and their derivations
- Formal standard: ISO 14977 for EBNF notation
Glossary
| Term | Definition |
|---|---|
| Ambiguity | A property of a grammar allowing multiple derivation trees for the same string. An ambiguous grammar poses a problem because the parser doesn’t know which interpretation to choose. |
| Derivation tree | A graphical representation of a derivation in the form of a tree. The root is the start symbol, the leaves are the terminals, and each internal node represents the application of a rule. |
| CFG | Context-Free Grammar. A grammar where each rule replaces a single non-terminal, independently of its context. Type 2 in Chomsky’s hierarchy. |
| Derivation | A sequence of rule applications transforming the start symbol into a string of terminals. The central process of how a grammar works. |
| Chomsky Hierarchy | A classification of formal grammars into 4 types (0 to 3) according to their expressive power, proposed by Noam Chomsky in 1956. CFGs are Type 2. |
| Non-terminal | An abstract symbol (variable) that will be replaced by other symbols according to the rules. Generally denoted in uppercase or brackets. |
| Parser | A program that analyzes a string of characters according to a grammar and constructs its structure (derivation tree or AST). Synonym: syntactic analyzer. |
| Production | Synonym for “rewrite rule”. Defines how a non-terminal can be replaced. |
| Recursion | A property of a rule where a non-terminal appears in its own definition (e.g., ). Allows expressing structures of arbitrary depth. |
| Σ (Sigma) | Greek letter traditionally used to denote the alphabet (set of terminal symbols) of a grammar. |
| Terminal | A concrete symbol of the final language, which cannot be replaced. These are the “words” of the vocabulary: operators, keywords, literal values. |
Prerequisite: L1 — Chomsky Hierarchy Reading time: 12 min Tags: #grammars #cfg #context-free #parsing #compilation
Next article: L3 — EBNF: the language for describing languages