Pushdown Automata (PDA)

CS305 -- Formal Language Theory

_______________________________________________ | | | NFA + Stack = Pushdown Automaton | | | | Recognizes: Context-Free Languages (CFLs) | | Equivalent to: Context-Free Grammars (CFGs) | |_______________________________________________|

Use arrow keys or buttons to navigate. ~21 slides.

1 / 21

The Big Picture: PDA = NFA + Stack

A Pushdown Automaton takes everything an NFA can do and adds a stack -- an infinite, last-in-first-out memory.

  • DFA/NFA -- recognizes regular languages
  • PDA -- recognizes context-free languages
  • Turing Machine -- recognizes recursively enumerable languages

Key Idea

The stack lets the PDA "remember" unbounded amounts of information, but only in LIFO order. This is exactly the extra power needed to match things like nested parentheses and anbn.

The Chomsky Hierarchy ===================== +-------------------------------+ | Recursively Enumerable | | (Turing Machine) | | | | +-------------------------+ | | | Context-Free | | | | (PDA) <-- YOU ARE HERE | | | | | | | | +-------------------+ | | | | | Regular | | | | | | (DFA / NFA) | | | | | +-------------------+ | | | +-------------------------+ | +-------------------------------+
2 / 21

Why Do We Need a Stack?

A DFA has finite memory -- just its current state. It cannot count beyond a fixed bound.

Consider the language L = { anbn | n ≥ 0 }:

  • A DFA would need a separate state for each count of a's
  • But n can be any number -- we'd need infinitely many states
  • The Pumping Lemma proves no DFA can do this

Solution: Give the machine a stack! Push each a, then pop one for each b. Accept if stack is empty.

Analogy: Cafeteria Tray Stack

Think of a spring-loaded cafeteria tray dispenser. You can only see and grab the top tray. You push trays on and pop them off. You never reach into the middle. That's exactly how a PDA's stack works.

DFA vs PDA Race Demo

DFA (finite memory)

Only 3 counting states available:
q0
q1
q2
q3
Reading input:

PDA (stack memory)

Stack grows as needed:
Reading input:
3 / 21

The PDA Machine Model

A PDA has three components: an input tape (read left-to-right), a finite control (current state), and a stack (unbounded LIFO memory).

INPUT TAPE read FINITE CONTROL q0 push/pop STACK Press Step or Run to begin
Step 0 / 7
At each step the PDA: 1. Reads the current input symbol (or ε -- no read)   2. Peeks at the top of the stack   3. Based on (state, input_symbol, stack_top), transitions to a new state and replaces the stack top with a string (push, pop, or no-change)
4 / 21

Formal Definition of a PDA

A PDA is a 7-tuple:

M = (Q, Σ, Γ, δ, q0, Z0, F)
SymbolMeaning
QFinite set of states
ΣInput alphabet
ΓStack alphabet
δTransition function
q0Start state (q0 ∈ Q)
Z0Initial stack symbol (Z0 ∈ Γ)
FSet of accept (final) states

The Transition Function

δ : Q x (Σ ∪ {ε}) x Γ --> P(Q x Γ*) domain: range: (state, input_or_eps, finite subsets of stack_top) (new_state, string_to_push)

Key Idea

The range is a set of (state, string) pairs -- this makes PDAs nondeterministic. The PDA can "choose" among multiple possible moves. It accepts if any choice sequence leads to acceptance.

Watch Out

Σ is the input alphabet (e.g., {a, b}). Γ is the stack alphabet (e.g., {a, b, $}). They can overlap, but Γ often has extra symbols like the bottom marker $.

5 / 21

Reading PDA Transitions

Each transition is written as:

δ(q, a, X) contains (p, Y)

Read this as:

  • "In state q,"
  • "reading input symbol a (or ε for no read),"
  • "with X on top of stack,"
  • "move to state p,"
  • "and replace X with string Y on the stack."

Key Idea

The string Y is pushed so that its leftmost symbol ends up on top of the stack. If Y = ABC, then A is on top, B below it, C at the bottom of the pushed portion.

Transition Explorer

q click a transition above p
Before
After
Select a transition type above to see how the stack changes.
6 / 21

Push, Pop, and No-Change Operations

Every transition pops the stack top and pushes a replacement string. We encode push, pop, and no-change using this single mechanism.

PUSH (add B on top of X)

δ(q, a, X) = {(p, BX)} Before: After: +---+ +---+ | X | top | B | top (B pushed on top!) +---+ +---+ | . | | X | +---+ +---+ | . | "Replace X +---+ with BX"

POP (remove X)

δ(q, a, X) = {(p, ε)} Before: After: +---+ +---+ | X | top | . | top (X is gone!) +---+ +---+ | . | +---+ "Replace X with ε (nothing)"

NO-CHANGE (leave X on top)

δ(q, a, X) = {(p, X)} Before: After: +---+ +---+ | X | top | X | top (same!) +---+ +---+ | . | | . | +---+ +---+ "Replace X with X (itself)"

PUSH MULTIPLE (replace X with ABC)

δ(q, a, X) = {(p, ABC)} Before: After: +---+ +---+ | X | top | A | top (leftmost = top) +---+ +---+ | . | | B | +---+ +---+ | C | +---+ | . | +---+
7 / 21

Example 1: PDA for { anbn | n ≥ 0 }

Strategy

  1. Start with bottom marker $ on the stack
  2. For each a read: push an a onto the stack
  3. Switch to "b-reading" mode
  4. For each b read: pop one a from the stack
  5. Accept if stack has only $ (all a's matched)

Key Idea

The stack counts the a's. Each b cancels one a by popping. If counts match, the stack returns to just $, and we accept.

Watch Out

The empty string ε is in this language (n=0). We must accept it! That's why q0 transitions directly to accept on seeing $.

State Diagram

a, $ / a$ b, a / ε a, a / aa +----------+ +----------+ | | | | v | v | ---> ( q0 ) ----> ( q1 ) ----> ( q2 ) | ε,$/$ | b,a/ε | | | | | ε, $ / $ ε, $ / $ | | | +-----+----+---------+----+ | | v v (( q_accept )) States: q0 : start (reading a's, pushing) q1 : reading b's (popping a's) q_accept : accept state

Formal Transitions

δ(q0, a, $) = {(q0, a$)} -- first a δ(q0, a, a) = {(q0, aa)} -- more a's δ(q0, ε, $) = {(q_acc, $)} -- accept ε δ(q0, b, a) = {(q1, ε)} -- first b δ(q1, b, a) = {(q1, ε)} -- more b's δ(q1, ε, $) = {(q_acc, $)} -- done!
8 / 21

Interactive Simulator: PDA for { anbn | n ≥ 0 }

Try it yourself! Enter a string of a's and b's and step through the PDA execution.

q0 q1 q_acc a,$/a$ | a,a/aa b, a/ε b, a/ε ε,$/$ ε,$/$
Stack:
$
Tape:
Step Log:
9 / 21

Acceptance: Final State vs. Empty Stack

There are two equivalent ways a PDA can accept a string.

1. Acceptance by Final State

Accept if: - ALL input has been consumed - PDA is in a state q ∈ F (Stack contents don't matter!) +-----+ | | <-- could have stuff +-----+ on the stack, | | we don't care +-----+ State: q ∈ F --> ACCEPT

This is the more common definition in textbooks. The accept states F determine acceptance.

2. Acceptance by Empty Stack

Accept if: - ALL input has been consumed - Stack is completely EMPTY (Current state doesn't matter!) (empty) <-- stack is empty that's all we check State: any --> ACCEPT

Here, F is ignored (or F = Q). The PDA accepts purely by emptying its stack after reading all input.

Key Idea

These two acceptance modes are equivalent in power. Any language accepted by a PDA using final states can also be accepted by some (different) PDA using empty stack, and vice versa. We can mechanically convert between the two.

10 / 21

Converting Between Acceptance Modes

Final State --> Empty Stack

Idea: When original PDA reaches a final state, drain the entire stack. ε, Z0' / Z0' (q0') -----------> (q0) new original start start For each q ∈ F: ε, any / ε (q) ---------> (q_drain) | | ε, any / ε (loop: pop everything) v (q_drain) Steps: 1. Add new start state q0', push new bottom marker Z0' under Z0 2. From every final state, add ε-transition to q_drain 3. q_drain pops everything

Empty Stack --> Final State

Idea: When stack empties, transition to a new accept state. ε, Z0' / Z0 Z0' (q0') ----------------> (q0) new original start start For each state q: ε, Z0' / ε (q) ---------> (( q_f )) new final state Steps: 1. Add new start state q0', push new bottom marker Z0' under Z0 2. From EVERY state, add ε-transition to q_f when Z0' is on top (= original stack is empty) 3. q_f is the only accept state

Watch Out

The new bottom marker Z0' is essential! Without it, the original PDA might empty its stack prematurely (not at a final state), and the conversion would accept strings it shouldn't.

11 / 21

Example 2: PDA for Balanced Parentheses

Language: { w ∈ {(, )}* | w has properly nested, balanced parentheses }

Strategy

  • Push a marker for each (
  • Pop a marker for each )
  • Accept if stack returns to just $
  • If we see ) but stack has only $ -- reject (unmatched close)

State Diagram

(, $ / L$ ), L / ε (, L / LL +--------+ +--------+ | | | | v | v | ---> ( q0 ) ---------> ( q0 ) | | | ε, $ / $ | +--------+---------+ | v (( q_acc ))

Note: We can use a single state q0! The transitions for ( and ) just loop on q0.

Formal Transitions

δ(q0, (, $) = {(q0, L$)} push L δ(q0, (, L) = {(q0, LL)} push L δ(q0, ), L) = {(q0, ε)} pop L δ(q0, ε, $) = {(q_acc, $)} accept L = marker for open paren

Try It: Interactive Simulator

q0 (,$/L$ | (,L/LL | ),L/ε ε,$/$ q_acc
Stack:
$
12 / 21

Example 3: PDA for { wwR | w ∈ {0,1}* }

Even-length palindromes! The string's second half is the reverse of the first.

The Problem

Consider 01 10: the first half is 01, the second half 10 is 01 reversed.

But how does the PDA know where the middle is?

Key Idea: Nondeterminism!

The PDA guesses the midpoint! At every position, it nondeterministically branches into two choices:

  1. "I haven't reached the middle yet" -- keep pushing
  2. "This is the middle!" -- switch to popping mode

It accepts if any guess leads to acceptance. The "correct" guess (at the true midpoint) will succeed.

Analogy

Imagine cloning yourself at every step. One clone keeps pushing, the other switches to matching. Most clones die. The one who guessed right survives.

Try It: Palindrome Simulator

q0 0,1 push ε (guess mid!) q1 match & pop ε,$/$ ε,$/$ q_acc
Stack:
13 / 21

PDA vs. DPDA: Nondeterminism Matters!

Recall: DFA vs NFA

For finite automata, deterministic = nondeterministic in power.

DFA = NFA (same languages: regular)

For PDAs: NOT the same!

DPDA < PDA (strict subset!) +---------------------------+ | Context-Free Languages | | (PDA) | | | | +---------------------+ | | | Deterministic CFLs | | | | (DPDA) | | | | | | | | +---------------+ | | | | | Regular | | | | | | (DFA) | | | | | +---------------+ | | | +---------------------+ | +---------------------------+

What is a DPDA?

A deterministic PDA has at most one move at each step. Formally: for every (state, input, stack_top), there is at most one transition, and if there's an ε-transition, there are no other transitions for that (state, stack_top).

Critical Difference

The language { wwR | w ∈ {0,1}* } is context-free but NOT deterministic context-free. No DPDA can recognize it -- the "guess the middle" trick requires nondeterminism.

Key Idea

Languages recognized by DPDAs are called Deterministic CFLs. They are important in practice because most programming languages are designed to be deterministic CFLs (so parsers are efficient).

Examples

LanguageDPDA?PDA?
anbnYesYes
wwRNoYes
wcwR (with center marker)YesYes
14 / 21

CFG to PDA Conversion

Given any CFG G, we can build a PDA M such that L(M) = L(G). This uses a top-down parsing strategy.

The Construction (3 states!)

ε, $ / S$ (q_start) ----------> (q_loop) -------> ((q_acc)) | ^ ε, $ / $ | | +--+ For each rule A -> w: ε, A / w For each terminal a: a, a / ε

The PDA has essentially one working state (qloop) that does all the work.

How It Works

  1. Start: Push start variable S onto stack
  2. Loop: Repeat until stack is empty:
    • If top of stack is a variable A: nondeterministically pick a rule A → w and replace A with w
    • If top of stack is a terminal a: match it against the next input symbol (pop a, consume input a)
  3. Accept: Stack is empty (just $) and all input consumed

Key Idea

The stack holds the "prediction" of what the rest of the input should look like. Variables get expanded (replaced by rule right-hand sides). Terminals get matched and consumed. This is a leftmost derivation simulation.

15 / 21

CFG to PDA: Worked Example

Grammar G

S → aSb | ε

This generates { anbn | n ≥ 0 }.

Resulting PDA Transitions

From the construction: Start: δ(q_start, ε, $) = {(q_loop, S$)} Variable rules (replace on stack): δ(q_loop, ε, S) = {(q_loop, aSb), (q_loop, ε)} Terminal matching: δ(q_loop, a, a) = {(q_loop, ε)} δ(q_loop, b, b) = {(q_loop, ε)} Accept: δ(q_loop, ε, $) = {(q_acc, $)}

Step-Through: Parsing "aabb"

Step 0 / 9
Stack:
$
Input remaining: aabb
Start: stack=[$], input="aabb"
16 / 21

PDA to CFG Conversion

The harder direction: given a PDA, build an equivalent CFG. This proves every PDA language is context-free.

The Main Idea

Create a variable Apq for every pair of states (p, q). This variable generates exactly the strings that take the PDA:

  • From state p to state q
  • Starting and ending with the same stack (net effect: nothing added or removed)
A_pq generates all strings w such that: (p, w, empty) |-*-> (q, ε, empty) state p state q | ^ | reading w | | stack: push/pop/... | | but NET stack change | +-------- = 0 ------------+

Construction Rules

Rule 1: A_pp → ε (for all states p) "Zero-length path, no net stack change" Rule 2: A_pq → A_pr A_rq (for all states p, r, q) "Concatenate two net-zero paths" Rule 3: If δ(p,a,X) contains (r,Y) and δ(s,b,Y) contains (q,ε): A_pq → a A_rs b "Push X, do stuff, pop X"

Watch Out

This construction can produce a huge number of rules (|Q|3 rules just from Rule 2). Many rules may be useless (generating no terminal strings), but the grammar is correct.

Key Idea

The start variable is Aq0,qf for accept states qf. The grammar works because every computation that starts with an empty stack and ends with an empty stack can be decomposed into paired push/pop segments.

17 / 21

The Big Equivalence: PDA = CFG

+------------------------------+ | | | Context-Free Languages | | | | +--------+ +--------+ | | | CFG |<==>| PDA | | | +--------+ +--------+ | | | | Both describe EXACTLY the | | same class of languages. | +------------------------------+ CFG ---> PDA : Top-down parsing (Slide 15-16) PDA ---> CFG : A_pq construction (Slide 17)

Key Idea

This is one of the fundamental theorems of formal language theory. It says that the generative model (grammars deriving strings) and the recognition model (automata accepting strings) agree perfectly for context-free languages.

The Complete Picture

Language Class Generator Recognizer +-----------------+-----------+------------+ | Regular | Regular | DFA / NFA | | | Expr / RG | | +-----------------+-----------+------------+ | Det. CF | LR(1) | DPDA | | | Grammar | | +-----------------+-----------+------------+ | Context-Free | CFG | PDA | +-----------------+-----------+------------+ | Context- | CSG | Linear | | Sensitive | | Bounded | +-----------------+-----------+------------+ | Recursively | Unrestr. | Turing | | Enumerable | Grammar | Machine | +-----------------+-----------+------------+

Analogy

Think of CFGs as blueprints (how to build strings) and PDAs as inspectors (how to verify strings). The theorem says: anything one can build, the other can check -- and vice versa. They're two views of the same reality.

18 / 21

Limitations: What PDAs Cannot Do

Languages Beyond PDAs

LanguagePDA?Why Not
{ anbncn }NoCan't count 3 things
{ ww } (copy)NoCan't match forward
{ anbncndn }NoSame reason
{ aibjck | iNoTwo independent counts

Why anbncn Fails

Push a's: +---+ | a | +---+ Count of a's on stack | a | +---+ | $ | +---+ Pop for b's: Stack tracks a=b. Good. But now the stack is EMPTY. How do we check c count = n too? The stack was "used up" matching a's and b's. No memory left for c's!

Why ww (Copy Language) Fails

Input: 0 1 1 | 0 1 1 first second (copy of first) half half After pushing first half: +---+ | 1 | top +---+ | 1 | +---+ | 0 | +---+ | $ | +---+ But popping gives REVERSE: 1,1,0 We need FORWARD match: 0,1,1 Stack is LIFO -- it reverses! That's why ww^R works but ww doesn't.

Key Idea

A stack is LIFO (last-in, first-out). It naturally handles reversal (palindromes, nesting) but cannot handle copying or three-way matching. These require a Turing machine's tape.

Proving Non-CFL

Use the Pumping Lemma for CFLs to formally prove a language is not context-free (and hence no PDA can recognize it).

19 / 21

Summary & Cheat Sheet

PDA at a Glance

PDA = NFA + Stack 7-tuple: (Q, Σ, Γ, δ, q0, Z0, F) Transition: δ(q, a, X) = {(p, Y)} "state q, read a, pop X, push Y, goto p" Push: replace X with BX Pop: replace X with ε No-change: replace X with X

Acceptance Modes

Final State: end in q ∈ F, input consumed Empty Stack: stack empty, input consumed (Equivalent in power!)

Key Equivalence

CFG <====> PDA Both describe Context-Free Languages. CFG → PDA: top-down parsing (3 states) PDA → CFG: A_pq variable construction

What PDAs Can and Can't Do

LanguageRegular?CFL?
a*b*YesYes
anbnNoYes
balanced parensNoYes
wwRNoYes (nondet)
wcwRNoYes (det)
anbncnNoNo
ww (copy)NoNo

PDA vs DPDA

Remember

DPDA < PDA (unlike DFA = NFA). Some CFLs fundamentally require nondeterminism. Example: wwR (even palindromes) needs nondeterministic "guess the middle."

Mental Model

DFA = person with a notecard (finite notes).
PDA = person with a stack of plates (LIFO memory).
TM = person with a scroll (random-access memory).

20 / 21

Challenge Quiz: Test Your PDA Knowledge

3 random questions from a pool of 6. Pick the correct answer!

21 / 21