Context-Free Grammars & Derivations

Context-Free Grammars (CFGs)

A context-free grammar (CFG) is a grammar in which every production rule has the form:

A → α

where A ∈ V is a single non-terminal, and α ∈ (V ∪ Σ)* is any string of terminals and/or non-terminals (including the empty string ε).

The key difference from regular grammars: the right-hand side can have any combination of terminals and non-terminals, including multiple non-terminals and nested structures. This is what enables CFGs to describe languages like {aⁿbⁿ} that require matching/counting.

The "context-free" name comes from the fact that the non-terminal A can be replaced by α regardless of what surrounds it (the context does not affect which rules can be applied).


Examples of CFGs

Example 1: L = {aⁿbⁿ | n ≥ 0}

G = ({S}, {a, b}, {S → aSb | ε}, S)

Derivation of aaabbb:

S ⟹ aSb ⟹ aaSbb ⟹ aaaSbbb ⟹ aaabbb

Example 2: Properly Matched Parentheses (Dyck Language)

S → SS | (S) | ε

Derivation of (()()):

S ⟹ (S) ⟹ (SS) ⟹ ((S)S) ⟹ (()S) ⟹ (()(S)) ⟹ (()())

Example 3: Arithmetic Expressions

E → E + T | T
T → T * F | F
F → (E) | id

This grammar generates all valid arithmetic expressions with +, *, parentheses, and identifiers (variables/numbers).

Example 4: Strings Where 1s Always Outnumber 0s

Language: { w ∈ {0,1}* | at every prefix of w, #1s > #0s } (Ballot sequences / Bertrand ballot problem variant).

S → 1A
A → 1A0A | 1A | ε

CFGs vs Regular Grammars

Regular grammars can only build strings by appending symbols to one end. CFGs can "wrap" structure around the middle, which is why they can handle balanced/nested constructs that are impossible for FAs.


Grammar Derivations

Sentential Forms

A sentential form is any string (of terminals and/or non-terminals) that can be derived from the start symbol S. Formally, α is a sentential form if S ⟹* α.

A string consisting entirely of terminals that is derivable from S is a sentence of the grammar (i.e., a string in L(G)).

Leftmost Derivation (LMD)

In a leftmost derivation, at each step we always expand the leftmost non-terminal in the current sentential form.

Example with E → E + T | T, T → T * F | F, F → (E) | id:

Leftmost derivation of id + id * id:

E ⟹ E + T           (expand leftmost E)
  ⟹ T + T           (expand leftmost E → T)
  ⟹ F + T           (expand leftmost T → F)
  ⟹ id + T          (expand F → id)
  ⟹ id + T * F      (expand T → T * F)
  ⟹ id + F * F      (expand T → F)
  ⟹ id + id * F     (expand F → id)
  ⟹ id + id * id    (expand F → id)

Rightmost Derivation (RMD)

In a rightmost derivation, we always expand the rightmost non-terminal.

Example: Same grammar, deriving id + id * id:

E ⟹ E + T
  ⟹ E + T * F
  ⟹ E + T * id
  ⟹ E + F * id
  ⟹ E + id * id
  ⟹ T + id * id
  ⟹ F + id * id
  ⟹ id + id * id

Why do derivation orders matter?

Both leftmost and rightmost derivations produce the same set of sentences. However, parsers in compilers work in a specific order — LL parsers use leftmost derivation, LR parsers use rightmost (in reverse). The choice of derivation order can also reveal ambiguity in a grammar.


Parse Trees

A parse tree is a hierarchical, visual representation of a derivation.

Structure: - The root is the start symbol S. - Internal nodes are non-terminals (V). - Leaf nodes are terminals (Σ) or ε. - The children of a node A in the parse tree correspond to the right-hand side of the rule applied to A in the derivation. - Reading the leaves from left to right gives the derived string (the yield of the tree).

Key Property

A parse tree captures the structure of a derivation without specifying the order in which rules were applied. Both the leftmost and rightmost derivations of the same string (with the same rule choices) give the same parse tree.

A grammar is unambiguous if and only if every string in L(G) has exactly one parse tree.

Example Parse Tree

Grammar: S → SS | (S) | ε
String: (())()

One derivation: S ⟹ SS ⟹ (S)(S) ⟹ ((S))(S) ⟹ (())(S) ⟹ (())().

Parse tree:

         S
        / \
       S   S
       |   |
      (S)  ()
       |
      (S)
       |
       ε

CFG Applications

Palindromes

CFGs can "remember" the first half of a string to match against the second half — something no FA can do:

S → aSa | bSb | a | b | ε

This generates all palindromes over {a, b}. The key is that the rules aSa and bSb allow the grammar to "wrap" matching symbols around the recursive structure.

Parenthesis Matching

The Dyck language (all properly matched parentheses) is a context-free language:

S → SS | (S) | ε

This language is not regular (you can prove this with the Pumping Lemma: the string (ⁿ)ⁿ cannot be pumped within the regular framework).