The Timpson Lyceum seal

The Timpson Lyceum · Computation Theory

Lecture 9 (Computation Theory) Context-Free Grammars and Pushdown Automata

Overview

This lecture moves us up to the second level of computation: the context-free level. After reviewing the definition of a context-free grammar (CFG) and building grammars by hand, we learn Chomsky normal form (CNF) — a restricted but logically equivalent form that every CFG can be converted into — and walk through the conversion procedure. We then prove several closure properties of context-free languages (closed under union and concatenation, but not under intersection or complement). Finally we introduce the machine model that matches CFGs: the pushdown automaton (PDA), an NFA augmented with a stack, and give its formal six-tuple definition.

Key Concepts

Context-free grammar (CFG). A four-tuple G=(V,Σ,R,S)G = (V, \Sigma, R, S) where:

The defining feature: the left side of every rule is a single variable — it stands alone, with no surrounding context.

CFG vs. regular grammar. Regular grammars restrict the right side to a terminal followed by at most one variable, so only one variable ever appears as a string is generated. CFGs lift that restriction: the right side may mix any number of terminals and variables. This is what lets CFGs generate non-regular languages such as {0n1n}\{0^n 1^n\}.

Generating a string. Always start from SS and repeatedly apply rules, replacing a variable with the right side of one of its rules. When no variables remain, the string is generated. Use the shorthand A→0∣1A \to 0 \mid 1 for multiple rules on the same variable. Include S→εS \to \varepsilon if the empty string belongs to the language.

Chomsky normal form (CNF). A CFG is in CNF if every rule has one of these forms:

CNF grammars are a subset of CFGs (every CNF grammar is a CFG, not vice versa), but they generate exactly the same class of languages. They are valued because their rigid shape makes proofs about them easy. Any CFG can be converted to an equivalent CNF grammar — analogous to converting an NFA to a DFA.

Conversion to CNF — five steps, in order:

  1. New start variable. Add S0→SS_0 \to S so the start variable never appears on a right side.
  2. Remove ε\varepsilon-rules. Working bottom-up, delete each A→εA \to \varepsilon (except for the start variable) and patch every rule that used AA so the grammar still generates the same strings. If the language contains ε\varepsilon, a single S0→εS_0 \to \varepsilon survives.
  3. Remove unit rules. For any A→BA \to B (variable to single variable), replace it with A→A \to (everything BB could produce).
  4. Replace non-isolated terminals. Any terminal appearing alongside other symbols is swapped for a new variable UU, and the rule U→(that terminal)U \to (\text{that terminal}) is added.
  5. Break up long right sides. Any rule whose right side has more than two variables is split by introducing new variables for pairs until only two remain per rule.

Closure properties of context-free languages:

Definition of a context-free language (CFL). A language is context-free iff some CFG generates it. (Parallel to a regular language being defined as one a DFA recognizes.)

A canonical non-context-free language. {0n1n0n}\{0^n 1^n 0^n\} is not context-free (taken on faith here; proved later with a pumping lemma). It is the witness used to show intersection is not closed: {0m1n0n}∩{0n1n0m}={0n1n0n}\{0^m 1^n 0^n\} \cap \{0^n 1^n 0^m\} = \{0^n 1^n 0^n\}, an intersection of two CFLs that is not a CFL. A stack can match/count one pair of adjacent things, not two independent counts.

Pushdown automaton (PDA). An NFA equipped with a stack — a last-in/first-out store where you can only access the top symbol (the cafeteria plate-dispenser analogy). A special bottom marker \text{\}$ is pushed first so the machine can detect an empty stack. On each transition the machine may read an input symbol (or nothing), pop a stack symbol (or nothing), and push a stack symbol (or nothing). PDAs are non-deterministic, like NFAs.

Formal definition. A PDA is a six-tuple (Q,Σ,Γ,δ,q0,F)(Q, \Sigma, \Gamma, \delta, q_0, F):

The notation XεX_\varepsilon means X∪{ε}X \cup \{\varepsilon\}. The only difference from the NFA definition is the added stack alphabet Γ\Gamma and the stack-aware transition function. (A deterministic PDA drops the power set, giving one outcome per input — but we work almost exclusively with the non-deterministic version.) The transition-design power of PDAs is exactly the power of a single stack: an NFA (no stack) recognizes regular languages, a PDA (one stack) recognizes context-free languages, and adding a second stack yields the power of a Turing machine.

Worked Example

Build a CFG and a PDA for L={0n1n:n≥0}L = \{0^n 1^n : n \ge 0\}, then trace a derivation.

A grammar for LL:

S→0 S 1S→ε\begin{aligned} S &\to 0\,S\,1 \\ S &\to \varepsilon \end{aligned}

Generating the string 000111000111 — start from SS and apply S→0S1S \to 0S1 three times, then S→εS \to \varepsilon:

S⇒0 S 1⇒0 0 S 1 1⇒0 0 0 S 1 1 1⇒0 0 0 1 1 1\begin{aligned} S &\Rightarrow 0\,S\,1 \\ &\Rightarrow 0\,0\,S\,1\,1 \\ &\Rightarrow 0\,0\,0\,S\,1\,1\,1 \\ &\Rightarrow 0\,0\,0\,1\,1\,1 \end{aligned}

Matching PDA (the stack counts the zeros):

  1. From the start state, on ε\varepsilon read / pop nothing, push \text{\}$ (mark the bottom of the stack).
  2. Loop: on reading 00, pop nothing, push 00 — one stack symbol per zero.
  3. On ε\varepsilon, move to the popping state (guess the boundary between 00s and 11s).
  4. Loop: on reading 11, pop 00, push nothing — one zero removed per one.
  5. On ε\varepsilon, pop \text{\}$ → accept.

Trace on 001001 (a string it should reject): push \text{\},read, read 0(push(push0),read), read 0(push(push0),thenread), then read 1(popone(pop one0).Inputisnowexhaustedbuta). Input is now exhausted but a 0remainsonthestack;thereisnoremains on the stack; there is no\varepsilon−transitionthatpops-transition that pops \text{$}whileawhile a0sitsontop,sothemachinecannotreachtheacceptstate—correctlyrejected.Acceptancerequiresthatthestackbereducedtoexactlysits on top, so the machine cannot reach the accept state — correctly rejected. Acceptance requires that the stack be reduced to exactly\text{$}$ precisely when the input runs out.

Summary