Copyright (C) 2015, H. Conrad Cunningham
Acknowledgements: MS student Eli Allen assisted in preparation of these notes. These lecture notes are for use with Chapter 7 of the textbook: Peter Linz. Introduction to Formal Languages and Automata, Fifth Edition, Jones and Bartlett Learning, 2012. The terminology and notation used in these notes are similar to those used in the Linz textbook. This document uses several figures from the Linz textbook.
Advisory: The HTML version of this document requires use of a browser that supports the display of MathML. A good choice as of December 2015 seems to be a recent version of Firefox from Mozilla.
Finite automata cannot recognize all context-free languages.
To recognize language , an automaton must do more than just verify that all 's precede all 's; it must count an unbounded number of symbols. This cannot be done with the finite memory of a dfa.
To recognize language , an automaton must do more than just count; it must remember a sequence of symbols and compare it to another sequence in reverse order.
Thus, to recognize context-free languages, an automaton needs unbounded memory. The second example above suggests using a stack as the "memory".
Hence, the class of machines we examine in this chapter are called pushdown automata.
In this chapter, we examine the connection between context-free languages and pushdown automata.
Unlike the situation with finite automata, deterministic and nondeterministic pushdown automata differ in the languages they accept.
Nondeterministic pushdown automata (npda) accept precisely the class of context-free languages.
Deterministic pushdown automata (dpda) just accept a subset--the deterministic context-free languages.
Linz Figure 7.1 illustrates a pushdown automaton.
On each move, the control unit reads a symbol from the input file. Based on the input symbol and symbol on the top of the stack, the control unit changes the content of the stack and enters a new state.
Linz Definition 7.1 (Nondeterministic Pushdown Accepter): A nondeterministic pushdown accepter (npda) is defined by the tuple
,
where
is a finite set of internal states of the control unit,
is the input alphabet,
is a finite set of symbols called the stack alphabet,
finite subsets of is the transition function,
is the initial state of the control unit,
is the stack start symbol,
is the set of final states.
Note that the input and stack alphabets may differ and that start stack symbol must be an element of
Consider the transition function finite subsets of .
1st argument from is the current state.
2nd argument from is either the next input symbol or for a move that does not consume input.
3rd argument from is the current symbol at top of stack. (The stack cannot be empty! The stack start symbol represents the empty stack.)
The machine is nondeterministic, but there are only a finite number of possible results for each step.
Suppose the set of transition rules of an npda contains
.
A possible change in the state of the automaton from to is shown in following diagram.
This transition function can be drawn as a transition graph as shown below. The triple marking the edges represent the input symbol, the symbol at the top of the stack, and the string pushed back on the stack. Here we use "/" to separate the elements of the triple on the edges; the book uses commas for this purpose.
Consider an npda where
with the initial state and the transition function defined as follows:
There are no transitions defined for final state or in the cases
, , , , , and .
These are dead configurations of the npda. In these cases, maps the arguments to the empty set.
The machine executes as follows.
Acceptance or rejection?
The machine accepts:
Other strings will always end in dead configurations. For example:
Thus, it is not difficult to see that .
Linz Figure 7.2 shows a transition graph for this npda. The triples marking the edges represent the input symbol, the symbol at the top of the stack, and the string pushed back on the stack.
Transition graphs are useful for describing pushdown automata, but they are not useful in formal reasoning about them. For that, we use a concise notation for describing configurations of pushdown automata with tuples.
The triple where
is the control unit state
is the unread input string
is the stack as string, beginning with the top symbol
is called an instantaneous description of a pushdown automaton.
We introduce the symbol to denote a move from one instantaneous description to another such that
is possible if and only if
.
We also introduce the notation to denote an arbitrary number of steps of the machine.
Linz Definition 7.2 (Language Accepted by a Pushdown Automaton): Let be a nondeterministic pushdown automaton. The language accepted by is the set
.
In words, the language accepted by is the set of all strings that can put into a final state at the end of the string. The stack content is irrelevant to this definition of acceptance.
Construct an npda for the language
.
We must count 's and 's, but their relative order is not important.
The basic idea is:
But, what if at some point?
We must allow a "negative" count. So we modify the above solution as follows:
on "",
if top is or 0
push 0
else if top is 1
pop 1
on "",
if top is or 1
push 1
else if top is 0
pop 0
So the solution is an npda.
, with given as
Linz Figure 7.3 shows a transition graph for this npda.
In processing the string , the npda makes the following moves (as indicated by transition rule number):
(3) | ||
(6) | ||
(2) | ||
(5) | ||
(1) |
Hence, the string is accepted.
Construct an npda for accepting the language ,
The basic idea is:
push symbols from on stack from left to right
pop symbols from stack for (which is right-to-left)
Problem: How do we find the middle?
Solution: Use nondeterminism!
Each symbol could be at middle.
Automaton "guesses" when to switch.
For , a solution to the problem is given by , where:
-- which is plus the staack start symbol
The transition function can be visualized as having several parts.
a set of transitions to push on the stack (one for each element of ):
a set of transitions to guess the middle of the string, where the npda switches from state to (any position is potentially the middle):
a set of transitions to match against the contents of the stack:
a transition to recognize a successful match:
Remember that, to be accepted, a final state must be reached with no unprocessed input remaining.
The sequence of moves accepting is as follows, where the number in parenthesis gives the transition rule applied:
(5) | ||
(2) | ||
(8) | ||
(10) | ||
(9) | ||
(11) |
Underlying idea: Given a context-free language, construct an npda that simulates a leftmost derivation for any string in the language.
We assume the context-free language is represented as grammar in Greibach Normal Form, as defined in Linz Chapter 6. We did not cover that chapter, but the definition and key theorem are shown below.
Greibach Normal Form restricts the positions at which terminals and variables can appear in a grammar's productions.
Linz Definition 6.5 (Greibach Normal Form): A context-free grammar is said to be in Greibach Normal Form if all productions have the form
,
where and .
The structure of a grammar in Greibach Normal Form is similar to that of an s-grammar except that, unlike s-grammars, the grammar does not restrict pairs to single occurrences within the set of productions.
Linz Theorem 6.7 (Existence of Greibach Normal Form Grammars): For every context-free grammar with , there exists an equivalent grammar in Greibach normal form.
Underlying idea, continued: Consider a sentential form, for example,
where are the terminals read from the input and are the variables on the stack.
Consider a production .
If variable is on top of stack and terminal is the next input symbol, then remove from the stack and push back .
An npda transition function for must be defined to have the move
for some state , input string suffix , and stack . This, we define such that
.
Construct a pda to accept the language generated by grammar with productions
.
First, we transform this grammar into Greibach Normal Form:
We define the pda to have three states -- an initial state , a final state , and an intermediate state .
We define the initial transition rule to push the start symbol on the stack:
We simulate the production with a transition that reads from the input and replaces on the top of the stack by .
Similarly, we simulate the production with a transition that reads while simply removing from the top of the stack. We represent these two productions in the pda as the nondeterministic transition rule:
Doing the same for the other productions, we get transition rules:
When the stack start symbol appears at the top of the stack, the derivation is complete. We define a transition rule to move the pda into its final state:
Linz Theorem 7.1 (Existence of NPDA for Context-Free Language): For any context-free language , there exists an npda such that .
Proof: The proof partly follows from the following construction (algorithm).
Algorithm to construct an npda for a context-free grammar
Let be a grammar for in Greibach Normal Form.
Define transition rule to initialize the stack.
For every in , define transition rules
that read , pop , and push . (Note the possible nondeterminism.)
Define transition rule to detect the end of processing.
Consider the grammar:
This grammar is already in Greibach Normal Form. So we can apply the algorithm above directly.
In addition to the transition rules for the startup and shutdown, i.e.,
the npda has the following transition rules for the productions:
The sequence of moves made by in processing is is
(1) | ||
(3) | ||
(4a) | ||
(4b) | ||
(6) | ||
(7) | ||
(2) |
This corresponds to the derivation
The previous construction assumed Greibach Normal Form. This is not necessary, but the needed construction technique is more complex, as sketched below.
e.g.,
etc.
Linz Theorem 7.2 (Existence of a Context-Free Language for an NPDA): If for some npda , then is a context-free language.
Basic idea: To construct a context-free grammar from an npda, reverse the previous construction.
That is, construct a grammar to simulate npda moves:
The stack content becomes the variable part of the grammar.
The processed input becomes the terminal prefix of sentential form.
This leads to a relatively complicated construction. This is described in the Linz textbook in more detail, but we will not cover it in this course.
A deterministic pushdown accepter (dpda) is a pushdown automaton that never has a choice in its move.
Linz Definition 7.3 (Deterministic Pushdown Automaton): A pushdown automaton is deterministic if it is an automaton as defined in Linz Definition 7.1, subject to the restrictions that, for every , and ,
contains at most one element,
if is not empty, then must be empty for every .
Restriction 1 requires that for, any given input symbol and any stack top, at most one move can be made.
Restriction 2 requires that, when a -move is possible for some configuration, no input-consuming alternative is available.
Consider the difference between this dpda definition and the dfa definition:
A dpda allows -moves, but the moves are deterministic.
A dpda may have dead configurations.
Linz Definition 7.4 (Deterministic Context-Free Language): A language is a deterministic context-free language if and only if there exists a dpda such that .
The language is a deterministic context-free language.
The pda with transition rules
accepts the given language. This grammar satisfies the conditions of Linz Definition 7.4. Therefore, it is deterministic.
Consider language
and machine
where:
The transition function can be visualized as having several parts:
Restriction 2 violation
Restriction 2 violation
This machines violates Restriciton 2 of Linz Definition 7.3 (Deterministic Pushdown Automaton) as indicated above. Thus, it is not deterministic.
Moreover, is itself not deterministic (which is not proven here).
Deterministic context-free languages are important because they can be parsed efficiently.
The dpda essentially defines a parsing machine.
Because it is deterministic, there is no backtracking involved.
We can thus easily write a reasonably efficient computer program to implement the parser.
Thus deterministic context-free languages are important in the theory and design of compilers for programming languages.
An LL-grammar is a generalization of the concept of s-grammar. This family of grammars generates the deterministic context-free languages.
Compilers for practical programming languages may use top-down parsers based on LL-grammars to parse the languages efficiently.