This grammar of the aⁿbⁿ language in Prolog's Definite Clause Grammar
syntactic sugar:
s --> a,b.
s --> a,s,b.
a --> [a].
b --> [b].
s is a non-terminal, a and b are preterminals, [a] and [b] are terminals and
"-->" can be read as "expands to". The syntax is the same as BNF and the
grammar is a Prolog program that is directly executable as both a recogniser
or a generator, depending on instantiation pattern at call time.
How does the grammar work? It must accept, or generate, a string of equal
numbers of a's and b's, but the grammar is not keeping track of the length.
There is nothing to count how many a's have been consumed or produced so far.
How does it know?
s is the start symbol of the grammar. The first production of s, which is also
the terminating condition for the recursion, accepts or produces one a
followed by one b. The second production of s accepts an a, followed by an
s-string, followed by a b.
Suppose we executed the grammar as a generator. In the first step, the output
would be the string S₁ = ab. In the second step, the output would be the
string S₂ = aS₁b. In the n'th step the output would be aSₙb.
So the grammar would always add exactly one a at the start, and one be at the
end of its output, recursively.
And it would always generate the same number of a's as b's.
Similar for when it runs as an acceptor.
You can visualise the first couple of steps as follows:
S
,-------|-------.
| S |
A / \ B
| A B |
a | | b
a b
How does the grammar work? It must accept, or generate, a string of equal numbers of a's and b's, but the grammar is not keeping track of the length. There is nothing to count how many a's have been consumed or produced so far. How does it know?
s is the start symbol of the grammar. The first production of s, which is also the terminating condition for the recursion, accepts or produces one a followed by one b. The second production of s accepts an a, followed by an s-string, followed by a b.
Suppose we executed the grammar as a generator. In the first step, the output would be the string S₁ = ab. In the second step, the output would be the string S₂ = aS₁b. In the n'th step the output would be aSₙb.
So the grammar would always add exactly one a at the start, and one be at the end of its output, recursively.
And it would always generate the same number of a's as b's.
Similar for when it runs as an acceptor.
You can visualise the first couple of steps as follows: