A language A ⊆ Σ* is PDA-recognizable if there exist state and stack alphabet types
together with a pushdown automaton P = (Q, Σ, Γ, δ, q₀, F) whose accepted language equals A.
Instances For
States of the PDA constructed from a CFG. The PDA has four kinds of states:
start— initial state; pushes the start variable and the bottom-of-stack marker;loop— main loop that either pops a terminal matching the input, applies a rule by replacing a nonterminal on the stack, or transitions toacceptwhen the bottom-of-stack marker is seen;pushing L— intermediate states used to push a listLof grammar symbols onto the stack one symbol per step;accept— the unique accepting state.
- start {T : Type u_1} {N : Type} : QState T N
- loop {T : Type u_1} {N : Type} : QState T N
- pushing {T : Type u_1} {N : Type} : List (Symbol T N) → QState T N
- accept {T : Type u_1} {N : Type} : QState T N
Instances For
The standard PDA construction from a CFG g (Sipser, Lecture 4, "Converting CFGs to PDAs").
The stack alphabet is Option (Symbol T g.NT), where none is the bottom-of-stack marker
and some s carries a grammar symbol s. The PDA:
- From
start, pushes the bottom marker and moves intopushing [S]whereS = g.initial. - In
loop, pops a nonterminalAand nondeterministically replaces it by the right-hand side of any ruleA → r.output(using thepushingstates to push symbols one at a time). - In
loop, pops a terminaltfrom the stack only if the next input symbol matchest. - When the bottom marker is exposed in
loop, moves toaccept.
Instances For
From state pushing L on stack stack, the PDA can reach state loop having pushed
all symbols of L onto the stack (resulting in L.reverse.map some ++ stack), without
consuming any input.
In loop, the PDA can pop a terminal t from the top of the stack while
consuming a matching input symbol t.
In loop, if a nonterminal r.input is on top of the stack and r ∈ g.rules is a
production with that left-hand side, the PDA can replace the nonterminal by r.output
(reaching state loop with r.output.map some ++ stack on the stack).
DerivesIn' g n γ δ means γ ⇒ⁿ δ in g: there is a derivation of length exactly
n from γ to δ using the rules of g.
- refl {T : Type u_pda} {g : ContextFreeGrammar T} (γ : List (Symbol T g.NT)) : DerivesIn' g 0 γ γ
- step {T : Type u_pda} {g : ContextFreeGrammar T} {n : ℕ} {γ δ τ : List (Symbol T g.NT)} : g.Produces γ δ → DerivesIn' g n δ τ → DerivesIn' g (n + 1) γ τ
Instances For
A zero-step derivation forces the start and end sentential forms to be equal.
Append a single derivation step at the end: if γ ⇒ⁿ mid and mid ⇒ δ,
then γ ⇒^{n+1} δ.
Any derivation γ ⇒* δ can be witnessed by a derivation of some explicit finite length n.
If a single derivation step is applied to (terminal t) :: γ', the leading terminal
is preserved: the result is (terminal t) :: δ' where γ' ⇒ δ'.
Iterated version of produces_cons_terminal: in an n-step derivation starting from
(terminal t) :: γ', the leading terminal is preserved throughout, and the suffix admits
a matching n-step derivation.
Reordering derivations to apply the leftmost nonterminal first.
If A :: γ' ⇒^{n+1} target and target is a string of terminals, then some rule
r ∈ g.rules with r.input = A is applied (effectively first) and r.output ++ γ' ⇒^m target
for some m ≤ n.
Forward simulation. If the sentential form γ derives the terminal string w in
n steps, then starting in state loop with γ (encoded on the stack above an arbitrary
suffix extra) and input w, the PDA can reach state loop having consumed all input
and exposed extra on top of the stack.
The accept state of the constructed PDA has no outgoing transitions:
no Step originates from a configuration whose state is accept.
The accept state is a sink: any configuration reachable from (accept, w', stk')
is (accept, w', stk') itself.
StepCount M n c₁ c₂ means the PDA M reaches configuration c₂ from c₁ in
exactly n steps. This is the step-counted refinement of M.Reaches.
- refl {T : Type u_pda} {σ : Type u_1} {γ₁ : Type u_2} {M : PDA T σ γ₁} (c : PDA.Config) : StepCount M 0 c c
- step {T : Type u_pda} {σ : Type u_1} {γ₁ : Type u_2} {M : PDA T σ γ₁} {n : ℕ} {c1 c2 c3 : PDA.Config} : M.Step c1 c2 → StepCount M n c2 c3 → StepCount M (n + 1) c1 c3
Instances For
Append a single step at the end of a StepCount sequence:
if c₁ →ⁿ c₂ and c₂ → c₃ then c₁ →^{n+1} c₃.
Inversion lemma for a single PDA step: any transition out of (q_src, w_src, stk_src)
arises from some choice of next state q', optional consumed input symbol a, optional
popped stack symbol pop, optional pushed stack symbol push, and tails inp, stk
of the input and stack respectively, with (q', push) ∈ δ q_src a pop.
Pushing a list L of length |L| consumes exactly |L| + 1 steps (one push per symbol
plus one final transition from pushing [] to loop). If a StepCount from pushing L
already takes at least |L| + 1 steps, then after that prefix the PDA is in
(loop, w_arg, L.reverse.map some ++ stk_arg), and the remaining n - (|L|+1) steps
reach the same target.
Lower bound on the time to leave the pushing family of states. If fewer than
|L| + 1 steps have been taken starting from pushing L, the PDA is still in some
pushing L' state.
Backward simulation (loop-to-loop). If from (loop, w, γ.map some ++ [⊥]) the PDA
reaches (loop, [], [⊥]) in exactly n steps (where ⊥ = none is the bottom-of-stack
marker), then in the grammar γ ⇒* w (with w viewed as a sequence of terminals).
This is the core invariant for the converse direction: a successful run of the PDA from
loop back to loop while consuming w and shrinking the stack from γ to empty
corresponds to a derivation γ ⇒* w.
Stack-shape invariant for one step. As long as the PDA is not transitioning into
or out of the special start / accept states, a stack of the form L.map some ++ [none]
(a list of grammar symbols on top of the bottom marker) is preserved as L'.map some ++ [none]
after one step, for some new list L'.
No transition in the constructed PDA targets the start state:
once the PDA has left start, it never returns.
Reachable-version of stack_invariant_step: along any run between two non-accept
configurations (where the start configuration is also not start), the stack shape
L.map some ++ [⊥] is preserved, possibly with a different list L'.
Backward direction, auxiliary form. Any accepting run of the constructed PDA on w
— starting in (start, w, []) and ending in (accept, [], stk) for some final stack stk
— yields a CFG derivation [S] ⇒* w from the start variable S = g.initial.
Backward direction. If the PDA toPDA g accepts w, then g derives w from
its start variable, i.e. w ∈ L(g).
Correctness of the CFG → PDA construction. The PDA produced by toPDA g
recognizes exactly the language of g: L(toPDA g) = L(g).
Theorem (Converting CFGs to PDAs), Sipser Lecture 4.
If A is a context-free language, then some pushdown automaton recognizes A.