MIT 18.404 — Lecture 4

Context-Free Grammars and Pushdown Automata

§ Context-Free Grammars: URLs

Recall from Lecture 12 of 6.1020 (Software Construction) that the following is a context-free grammar (CFG).

url ::= 'http://' hostname (':' port)? '/' 
hostname ::= word '.' hostname | word '.' word
port ::= [0-9]+
word ::= [a-z]+

We might describe the components of this CFG like so:

The language corresponding to this CFG is the set of valid URLs.

Definition. A language A=L(G)A = L(G) is a context-free language (CFL) if it is the language of some CFG GG.

By replacing all variables with their productions, we can also describe our URL language in a single regular expression.

url ::= 'http://' ([a-z]+ '.')+ [a-z]+ (':' [0-9]+)? '/' 

Note that this cannot be done for all CFGs.

start ::= '0' start '1' | ''        // cannot be done
start ::= '0' start | ''            // can be done

The problem with the former of these two CFGs is self-embedding. In fact, it turns out that any CFG without self-embedding can be reduced to a regular expression. (This fact was not discussed in lecture; proof omitted.)

§ Context-Free Grammars: PEMDAS

Consider this example of a CFG G1:=(V,Σ,R,S)G_1 := (V, \Sigma, R, S).

Then the language L(G1)L(G_1) of G1G_1 contains words such as a+a×aa + a \times a, and (a+a)×a(a + a) \times a, and a+(a×a)a + (a \times a).

Each of these words has its own parse tree that describes its derivation from variables to terminals.

Notice how the structure of the parse tree, itself, encodes the fact that ×\times comes before ++.

§ Context-Free Grammars: Ambiguous PEMDAS

Now consider the following CFG G2:=(V,Σ,R,S)G_2 := (V, \Sigma, R, S) with the same language.

Then L(G2)L(G_2) also contains words such as a+a×aa + a \times a, and (a+a)×a(a + a) \times a, and a+(a×a)a + (a \times a).

But unlike G1G_1, the grammar G2G_2 is ambiguous, in that some words can have multiple derivations with distinct parse trees.

The ambiguity of this grammar means that this grammar does not inherently encode the fact that ×\times comes before ++.

§ Pushdown Automata

Informally, a pushdown automaton is just an NFA that is granted the additional ability to read and write memory to a stack.

Definition. A pushdown automaton (PDA) is a 6-tuple P=(Q,Σ,Γ,δ,q0,F)P = (Q, \Sigma, \Gamma, \delta, q_0, F) where:

Problem. Design a pushdown automaton that recognizes the (nonregular!) language A:={0k1k∣k≥0}A := \{\str{0}^k\str{1}^k \mid k \geq 0\}.

Solution: The algorithm, informally, works like so:

  1. So long as the input is 0\str{0}, push X\str{X} onto the stack.

  2. The first time the input is 1\str{1}, transition from the start state q0q_0 to the state q1q_1 without consuming that 1\str{1}.

  3. So long as the input is 1\str{1}, pop X\str{X} from the stack.

    • If at any point a 0\str{0} is read as input, move to a permanent REJECT\str{REJECT} state.

    • If at any point a 1\str{1} is read while the stack is empty, move to a permanent REJECT\str{REJECT} state.

  4. At the end of the input string, move to an ACCEPT\str{ACCEPT} state if the stack is empty; otherwise, REJECT\str{REJECT}.

Technically, the hardware of a PDA cannot explicitly “know” whether the stack is empty or not. Here's an easy fix: as a Step 0, push END\str{END} onto the stack; if the top of the stack is ever END\str{END}, the stack must be effectively empty.   ■\blacksquare

Problem. Design a pushdown automaton that recognizes the (nonregular!) language A:={wwR∣w∈{0,1}∗}A := \{ww^R \mid w \in \{\str{0}, \str{1}\}^*\}.

Solution: If we knew that ww had length 1010 (say), then all we would do is push the first 1010 input symbols onto the stack, then pop the stack 1010 times and compare the result of each pop against the next 1010 input symbols read.

Of course, we don't know the length of ww before we read it. But the nondeterminism of the NFA component of the PDA allows us to work around this in the obvious way.   ■\blacksquare

§ CFLs are PDA Languages

We'll finish lecture with a theorem that says exactly what you'd expect.

Theorem. (CFL   ⟹  \implies PDA) If AA is a context-free language, then it is recognized by some PDA.

Proof: Given any w∈Aw \in A, the PDA just uses the stack as scratch work to nondeterministically try to guess the derivation of ww.

  1. Initially, the stack contains the start variable of the CFG atop END\str{END}.

  2. Repeat the following until ACCEPT\str{ACCEPT} or REJECT\str{REJECT} is reached. Suppose the top of the stack is XX.

    • If XX is a variable, then (nondeterministically) replace XX with one of its productions.

    • If XX is a terminal symbol, then pop XX from the stack, and check if XX matches the input.

      • If it does match, then proceed to the next atom in the input.

      • If it doesn't match, then REJECT\str{REJECT}.

    • If X=ENDX = \str{END}, then ACCEPT\str{ACCEPT} if the input has been entirely read, and REJECT\str{REJECT} otherwise.

Remark. Interestingly, the states of the PDA are effectively unused; they only exist to move to REJECT\str{REJECT} and ACCEPT\str{ACCEPT}.

It turns out the converse is also true. In particular,

Theorem. (CFL == PDA) A language is context-free if and only if it is recognized by some PDA.

The proof of the converse is in the textbook, but we won't go over it in lecture.