Skip to content
roomi fields

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 * 4 equals 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: G=(V,Σ,R,S)G = (V, \Sigma, R, S)

A context-free grammar is defined by four elements, traditionally denoted G=(V,Σ,R,S)G = (V, \Sigma, R, S):

SymbolNameRoleExample
VVariables (non-terminals)Abstract symbols that will be replacedExpr, Term, Factor
ΣAlphabet (terminals)Concrete symbols of the final language+, *, (, ), 0-9
RProduction rulesHow to replace variablesExpr→Expr+TermExpr \to Expr + Term
SStart symbolWhere the derivation beginsExpr

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, Expr→Expr+Term∣TermExpr \to Expr + Term \mid Term 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 A→xyA \to xy, we can always replace AA with xyxy, regardless of what comes before or after AA. 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 TypeDerivation UsedExample Tool
LL (top-down)LeftmostANTLR (ANother Tool for Language Recognition), recursive descent parsers
LR (bottom-up)RightmostBison, 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
ExprExprTermeFacteur2+TermeTermeFacteur3£Facteur4

Figure 1 — Derivation tree for 2 + 3 * 4 with 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:

ExprExpr2+ExprExpr3£Expr4=146=ExprExprExpr2+Expr3£Expr4=20

Figure 2 — Ambiguity: two derivation trees for 2 + 3 * 4 with 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:

  1. Rewrite the grammar to eliminate ambiguity (this is what we did in our first example with Term and Factor).
  2. Disambiguation rules: operator precedence and associativity.
  3. 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 + Term

An Expr contains another Expr! 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:

LimitationFormal NotationConcrete Example
Triple counting equalityaⁿ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: S
  • rhs: 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

  1. G=(V,Σ,R,S)G = (V, \Sigma, R, S): Variables, Terminals, Rules, Start Symbol. These four elements completely define a grammar.
  2. “Context-free” means that rules apply independently of the surrounding context. This is what makes analysis efficient.
  3. Derivation is the process of successively replacing variables until a string of terminals is obtained.
  4. The derivation tree captures the hierarchical structure of the sentence, which determines its interpretation.
  5. Ambiguity is the enemy: an ambiguous grammar produces multiple possible interpretations. It is resolved by careful rule design.
  6. 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

TermDefinition
AmbiguityA 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 treeA 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.
CFGContext-Free Grammar. A grammar where each rule replaces a single non-terminal, independently of its context. Type 2 in Chomsky’s hierarchy.
DerivationA sequence of rule applications transforming the start symbol into a string of terminals. The central process of how a grammar works.
Chomsky HierarchyA 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-terminalAn abstract symbol (variable) that will be replaced by other symbols according to the rules. Generally denoted in uppercase or brackets.
ParserA program that analyzes a string of characters according to a grammar and constructs its structure (derivation tree or AST). Synonym: syntactic analyzer.
ProductionSynonym for “rewrite rule”. Defines how a non-terminal can be replaced.
RecursionA property of a rule where a non-terminal appears in its own definition (e.g., Expr→Expr+TermExpr \to Expr + Term). Allows expressing structures of arbitrary depth.
Σ (Sigma)Greek letter traditionally used to denote the alphabet (set of terminal symbols) of a grammar.
TerminalA 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


← Back to index