Push-Down Automaton (PDA) is an automaton that can test a context-free language. Just like how NFA can test a regular language. PDA is the device we later will use for LR grammar parsing.
Components of PDA
PDA is an e-NFA with an extra stack. To formulate it more precisely, it contains,
- State set Q={qs,q1,q2,⋯,qn}
- A vocabulary set V
- A stack vocabulary set S
- A start state {qs}
- A stack start character z0∈S
- A transition table T(q,v,z)={(qi′,Zi′),…},Zi′∈S∗
The T takes a state, an input vocabulary, and an stack symbol, and gives out a set of possible transitions, including he next state and a stack sentence. PDA will pop the top element, based on the transition table, the input and the current state, transit to a possible state and push every new element into the new stack elements.
We use the empty-stack acceptance. That is, when the stack becomes empty, we accept the input. Some other books may use accept state set like we did in NFA. They have the same level of expressiveness.
The graph to describe PDA is similar to that of e-NFA, just that on each edge, besides the input character, we also note z/Z′ as the top element and the sentence to push after transition.
For example, we know that 0n1n is not a regular language, but we can build a PDA that accepts it.
The x in the stack acts as count for number zero.
This is equivalent to the CFG,
S→0X1X→0X1∣01∣λ
Instant Description
For convenience, we use instant description sometimes instead of writing, T.
For FA, we use,
(q,aX)⊢(q′,X′)
To represent,
T(q,aX)=T(q′,X)
For,
q′∈T(q,a)
Or for DFA,
q′=T(q,a)
Where a is a terminal symbol, X is the sentence to test.
For PDA, we use,
(q,aX,Z)⊢(q′,X′,Z′)
Where Z represents the elements in the stack. The left side is the stack top, the right side is the stack bottom.
Again, consider this PDA,
Instead of drawing graphs like we used to, we can use the following instant description. Assume we want it to test 0011.
(qs,0011,z0)⊢(qs,011,xz0)⊢(qs,11,xxz0)⊢(q1,1,xz0)⊢(q1,1,z0)⊢(qf,λ,z0)
CFL is the Language of PDA
Consider a CNF CFG, that every CFG can be converted into. Every grammar should be either,
A→BCD→d
First we define,
T(qs,λ,z0)={(qf,S)}
Then, for each rule like,
A→B0C0∣BiCi
Convert this into,
T(q1,λ,A)={(qf,B0C0),(qf,BiCi)}
And lastly, for,
D→d
We add,
T(qf,d,D)={(qf,λ)}
So that, from the start state, the PDA can perform arbitrary operation based on the rules (using leftmost derivation since stack is last in first out). Then, the PDA pops every symbol from the stack, and convert them to the corresponding, terminals- and during the step, consuming all the string.
Let's consider this rule,
S→0X1X→0X1∣01∣λ
We can convert it into CNF. Firstly, the above grammar equlas,
S→0S1∣λ
We can convert into CNF with the following steps
S→AS1∣λA→0
S→AB∣λA→0B→S1
S→AB∣λB→SCC→1A→0
It's safe to ignore the empty generation, S→λ, since it only adds empty sentence to the language, thus, the CNF of the original CFG is,
S→ABB→SCC→1A→0
Now we can construct the T.
We first have,
T(qf,λ,S)={(qf,S)}
And for the two rules,
S→ABB→SC
We have,
T(qf,λ,S)={(qf,AB)}T(qf,λ,B)={(qf,SC)}
For,
C→1A→0
We have,
T(qf,1,C)={(qf,λ)}T(qf,0,A)={(qf,λ)}
We can draw this PDA,
For example, to test 0011, we have the process,
(qf,0011,z0)⊢(qf,0011,S)⊢(qf,0011,AB)⊢(qf,011,B)⊢(qf,011,SC)⊢(qf,011,BCC)⊢(qf,11,CC)⊢(qf,1,C)⊢(qf,λ,λ)
We don't actually have to go through the whole CNF process. We can convert the CFG into,
A→W∗B→bWhere W∗ is a string of non-terminals. This conversion is obviously valid.
Then, we define,
T(qs,λ,A)={(qs,W∗)}T(qs,b,B)={(qs,λ)}
PDA tests CFL
To convert PDA into CFG, we construct variables look likes this,
A=[qAp]
This is a non-terminal. This variable refers to an edge in the PDA, that state q with stack top A can reach p with an empty stack.
I'll continue by showing how to convert a PDA to a CFG, using simple notations.
Converting PDA to CFG
For a PDA, we can construct variables in our CFG of the form:
[qiAqj]
Where this variable represents "starting in state qi with A on top of stack, we can reach state qj with empty stack.
The key idea is that we can construct grammar rules based on PDA transitions-
If a transition is in form of,
(q′,Z′)∈T(q,a,Z)
That is, when a transition (q′,Z1Z2…Zk)∈T(q,a,Z) exists (where Z′=Z1Z2…Zk is the sequence pushed onto the stack), we generate productions in the CFG to model the effect of this transition on the stack and states. Specifically:
-
For transitions pushing k≥1 symbols:
For every possible sequence of intermediate states q1,q2,…,qk−1, add the rule:
[qZq′′]→a[q′Z1q1][q1Z2q2]⋯[qk−1Zkq′′]
Here, a is the input symbol consumed (or omitted if the transition is on ε). This rule encodes that after popping Z and pushing Z1Z2…Zk, the PDA processes each Zi sequentially, transitioning through states q1,…,qk−1 until the stack empties at q′′.
-
For transitions pushing nothing (k=0):
Add the production:
[qZq′]→a
This directly represents popping Z and moving to q′ while consuming a, leaving the stack empty.
-
Start symbol:
The CFG’s start symbol S derives all variables corresponding to the PDA’s initial configuration:
S→[q0Z0q]for all q∈Q,
where q0 is the PDA’s initial state and Z0 is the initial stack symbol. This ensures all valid computations starting from q0 with Z0 are captured. The q here is the possible state that the PDA is in when the stack is empty. If there exists multiple such states, use a symbol to generate all of them at first.
For example, considering our first PDA,
We have,
T(qs,0,x)={(qs,xx)}T(qs,0,z0)={(qs,xz0)}T(qs,1,x)={(q1,λ)}T(q1,1,x)={(q1,λ)}T(q1,z0,z0)={(qf,λ)}
We should construct the grammar with the following step,
The start variable is,
[qsz0qf]
This represents computations starting in qs with z0 on the stack and ending in qf with an empty stack.
We derive productions from each PDA transition:
(a) Transition (qs,0,x)→(qs,xx)
Pushes two x symbols. For all states q1,q′′∈{qs,q1,qf}:
[qsxq′′]→0[qsxq1][q1xq′′]
This encodes that after reading 0
, the first x is processed from qs→q1, and the second x from q1→q′′.
(b) Transition (qs,0,z0)→(qs,xz0)
Pushes x followed by z0. For all q1,q′′∈{qs,q1,qf}:
[qsz0q′′]→0[qsxq1][q1z0q′′]
This encodes processing x from qs→q1, then z0 from q1→q′′.
(c) Transition (qs,1,x)→(q1,λ)
Pops x. Directly generates 1:
[qsxq1]→1
(d) Transition (q1,1,x)→(q1,λ)
Pops x. Directly generates 1:
[q1xq1]→1
(e) Transition (q1,ϵ,z0)→(qf,λ)
Pops z0 without consuming input. Generates λ:
[q1z0qf]→λ
For the string 0011:
- Start with S→[qsz0qf].
- Use production (b):
[qsz0qf]→0[qsxq1][q1z0qf].
- Expand [qsxq1] using production (a):
[qsxq1]→0[qsxq1][q1xq1].
- Apply [qsxq1]→1 (production c) and [q1xq1]→1 (production d):
011
- Combine with [q1z0qf]→λ:
0011
The complete CFG is:
S[qsxq′′][qsz0q′′][qsxq1][q1xq1][q1z0qf]→[qsz0qf]→0[qsxq1][q1xq′′]∀q1,q′′∈{qs,q1,qf}→0[qsxq1][q1z0q′′]∀q1,q′′∈{qs,q1,qf}→1→1→λ