Grammars and Constituency Structure

Michael BrenndoerferMay 4, 202546 min read

Learn how context-free grammars formalize constituency, parse trees, and ambiguity, then build a CKY parser and compare trees with PCFG scores.

Language AI HandbookAll chapters
Part 7 of 67Linguistic Form: Morphology and Syntax
Chapter 46Grammars and Constituency Structure
Reading the full chapter · 11 sections · 46 min11 sections · 46 min

All 11 sections · 46 min read

Grammars and Constituency StructureFull chapter · Sections in this chapter1 / 11 ↓
Reading tools Definitions, links & notes

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Notes & comments

Grammars and Constituency Structure

The string "the dog chased the cat with a telescope" can mean that the dog used a telescope to chase the cat, or that the dog chased a cat that happened to have a telescope. The words are identical. What changes is where "with a telescope" attaches: to the chase event or to the cat. A parser has to represent these different phrase groupings before it can rank the readings.

That grouping is the subject of this chapter. We will formalize constituency: the idea that words combine into nested phrases, phrases combine into larger phrases, and the whole sentence has a hierarchical structure. The central formalism is the context-free grammar, or CFG. CFGs give us a precise language for describing phrase structure, they connect naturally to parsing algorithms, and they expose both the power and the limits of symbolic approaches to syntax. A CFG is a compact specification. It does not list every sentence. Instead, it lists a finite set of rules that can generate an unbounded family of sentences and assign each generated sentence a hierarchical structure. That combination of finite description and infinite reach is what makes grammars useful.

This chapter builds directly on the sequence-labeling material from earlier. In Part-of-Speech Tagging, each token received a label such as noun or verb. In Chunking, we grouped adjacent tokens into shallow spans such as noun phrases. Constituency grammar goes further: it lets those spans nest recursively, so a noun phrase can contain a prepositional phrase, which contains another noun phrase, and so on. The move from tagging to chunking is already a move from labels to spans. The move from chunking to constituency is a move from flat spans to recursive structure. Recursive constituency can represent nested phrase groupings that flat chunk spans leave unspecified. Parsing now has to decide how those groupings fit together.

We will proceed in layers:

  • First, what constituency is and why linguists and NLP systems care about it.
  • Then the formal definition of a CFG and the trees it generates.
  • Then ambiguity, the central practical problem for parsing.
  • Then the CKY parsing algorithm and probabilistic CFGs.
  • Then a complete worked example and a small implementation.
  • Finally, the limitations of symbolic grammars and their contribution to parsing.

The order is deliberate. We begin with linguistic intuition, then move to formal objects, then to algorithms, and only then to implementation. Each layer answers a question raised by the previous one. Constituency tests provide evidence for phrases. CFGs tell us how to state phrase structure precisely. Parse trees make that structure visible. Ambiguity shows why a grammar alone is not enough. CKY shows how to search the space of trees efficiently. PCFGs show how to rank that space. The final discussion shows where the entire framework strains and why modern parsing looks the way it does.

Why Constituency?

A constituent is a span of words that behaves as a unit. Some groups of words can be replaced, moved, coordinated, or used as answers, while other groups cannot. In the continuous tree model used here, a constituent occupies a contiguous stretch of the sentence, with a particular order and boundaries. The phrase "the small dog" is a constituent because it occupies a continuous region and behaves as a single syntactic object. The words "the" and "dog" are also constituents, but they are smaller ones. Constituency is hierarchical because constituents can contain other constituents.

Consider the sentence:

The small dog barked.

The span "the small dog" behaves like a unit. It passes several constituency tests:

  • Replacement: It can be replaced with a pronoun: "It barked."
  • Coordination: It can be coordinated: "The small dog and the cat barked."
  • Clefting: It can be moved into a cleft construction: "It was the small dog that barked."
  • Answer: It can be used as an answer: "Who barked? The small dog."

By contrast, the span "small dog barked" does not behave like a unit in the same way. You cannot naturally replace it with a single pronoun or coordinate it with another span of the same kind. This suggests that "the small dog" is a constituent, while "small dog barked" is not. Constituency makes a testable claim about which spans pattern together under grammatical operations.

These constituency tests are useful diagnostics, but they are not mathematical proofs. Different tests can disagree, and some constructions are awkward. Replacement with a pronoun works cleanly for many noun phrases, but not for every phrase type. Some analyses allow coordination of nonconstituents, so use more than one diagnostic when arguing for a phrase structure. For NLP, constituency is a hypothesis about hierarchical organization, and a grammar is a compact way to state that hypothesis. The hypothesis may be refined or replaced, but it gives the parser something precise to compute with.

Constituent

In the continuous constituency representation used here, a constituent is a contiguous span of tokens that functions as a syntactic unit in a phrase structure tree. Other annotation formalisms can represent discontinuous constituents. In a tree, every nonterminal node corresponds to a constituent, and its yield is the sequence of tokens it dominates.

Constituency is not the only way to represent syntax. Dependency grammar, which we will meet in the next chapter, represents syntactic structure as links between words rather than nested phrases. The two views are related but not identical. A constituency tree emphasizes phrases and their internal composition. A dependency tree emphasizes head-dependent relations. For now, we focus on the phrase-structure view because it gives the cleanest introduction to formal grammars and chart parsing. It also makes recursion visually obvious: a phrase node can contain another phrase node of the same type, and that containment is exactly what a CFG rule describes.

It also helps to keep four things distinct:

  • Tokens are the surface words or word pieces, such as "the", "dog", and "chased".
  • Preterminal categories are labels attached to individual tokens, such as Det, N, and V. These are closely related to the POS tags from the previous part.
  • Phrasal categories are labels for constituents, such as NP, VP, and PP.
  • The sentence root is the largest constituent, usually S, which dominates the entire token sequence.

In the nonempty lexicalized examples here, a parse tree connects all four. Its leaves are tokens, its lowest internal nodes are preterminal categories, its higher internal nodes are phrases, and its root is the sentence category. This layering is important because it separates lexical information from structural information. The token "dog" is a string. The preterminal N says that this string is functioning as a noun here. The phrasal node NP says that a larger span is functioning as a noun phrase. The root S says that the whole span is a sentence. A complete constituency parse connects these layers; a parser may infer them jointly or take some, such as tokens and POS tags, as input.

Context-Free Grammars

A context-free grammar is a finite set of rules that recursively defines which strings belong to a language and how those strings are structured. The formal definition is compact.

Context-Free Grammar

A context-free grammar is a tuple

G=(N,Σ,R,S)G = (N, \Sigma, R, S)

where:

  • NN is a finite set of nonterminal symbols, such as S, NP, VP, and PP.
  • Σ\Sigma is a finite set of terminal symbols, such as the words in the language. NN and Σ\Sigma are disjoint.
  • RR is a finite set of production rules of the form A→αA \to \alpha, where A∈NA \in N and α∈(N∪Σ)∗\alpha \in (N \cup \Sigma)^*.
  • S∈NS \in N is the start symbol.

The notation (N∪Σ)∗(N \cup \Sigma)^* means all finite sequences of terminals and nonterminals, including the empty sequence ε\varepsilon. A rule may therefore replace AA with several symbols, one symbol, or no symbols. Similarly, Σ∗\Sigma^* contains all finite terminal strings.

The grammar is context-free because every rule has exactly one nonterminal on the left-hand side. A rule says that AA can be rewritten as α\alpha regardless of what surrounds AA.

The phrase "regardless of context" is the key. A context-sensitive rule could say "rewrite A as a nonempty sequence α\alpha only when it appears between B and C." A context-free rule cannot require that surrounding context. For CFGs, we can use the span-based parsing method developed below. The bare rule S→NP VPS \to NP\ VP does not encode subject-verb agreement because its categories contain no number features. A CFG can enforce finite agreement distinctions by refining these categories. Context-free does not mean that a grammar cannot constrain lexical environments; it means that each production applies to its left-hand-side category without inspecting surrounding symbols. The context-free restriction is therefore both a simplification and a limitation.

Here is a small grammar for a fragment of English:

S→NP VPNP→Det N∣NP PPVP→V NP∣VP PPPP→P NPDet→the∣aN→dog∣cat∣telescopeV→chasedP→with\begin{aligned} S &\to NP\ VP\\ NP &\to Det\ N \mid NP\ PP\\ VP &\to V\ NP \mid VP\ PP\\ PP &\to P\ NP\\ Det &\to \text{the} \mid \text{a}\\ N &\to \text{dog} \mid \text{cat} \mid \text{telescope}\\ V &\to \text{chased}\\ P &\to \text{with} \end{aligned}

The vertical bar means "or". So NPNP can be either Det NDet\ N or NP PPNP\ PP. The symbols S, NP, VP, PP, Det, N, V, and P are nonterminals. The words "the", "a", "dog", "cat", "telescope", "chased", and "with" are terminals. The grammar is tiny, but it already supports recursion. An NP can contain a PP, and that PP contains another NP. A VP can contain a PP, and that PP contains another NP. This is enough to generate an unbounded family of structures from a finite set of rules.

A derivation shows how the start symbol rewrites step by step into a string of terminals. For example:

S⇒NP VP⇒Det N VP⇒the N VP⇒the dog VPS \Rightarrow NP\ VP \Rightarrow Det\ N\ VP \Rightarrow \text{the}\ N\ VP \Rightarrow \text{the dog}\ VP

Continuing, we can derive:

the dog chased the cat\text{the dog chased the cat}

At each step, we choose a nonterminal and replace it using one of its rules. The sequence of rewrites is a derivation. The final string contains only terminals. Different choices about which nonterminal to rewrite next can produce different derivations of the same string. That is why derivations and parse trees are not in one-to-one correspondence unless we fix a convention, such as always rewriting the leftmost nonterminal first. The tree, rather than the linear derivation, is usually the cleaner object for representing constituency.

The language generated by a grammar is the set of all terminal strings that can be derived from the start symbol:

L(G)={ w∈Σ∗:S⇒∗w }L(G) = \{\, w \in \Sigma^* : S \Rightarrow^* w \,\}

The star on ⇒∗\Rightarrow^* means "derives in zero or more steps." A string is grammatical with respect to GG if it belongs to L(G)L(G). A string is ungrammatical if it does not. This notion of grammaticality is relative to a grammar. A sentence that is ungrammatical under one grammar may be grammatical under another, richer grammar. The formal definition does not tell us whether the grammar is linguistically correct. It only tells us what the grammar generates.

This definition is abstract, and that is deliberate. A CFG does not say how to find a derivation. It only defines which derivations exist. A parser is the algorithm that searches for those derivations. The grammar is the specification; the parser is the implementation, a distinction also illustrated in the NLTK book's syntax chapter. This separation is useful because the same grammar can be used by many parsers, and the same parser can be used with many grammars. It also clarifies what a parsing failure means. If a parser fails, either the sentence is outside the grammar's language, or the parser failed to find a derivation that exists. Those are different problems.

Parse Trees

Derivations are linear sequences of rewrites. Parse trees are the same information in hierarchical form. The tree makes constituency visible. It also removes the irrelevant order in which independent rewrites happened. Two derivations that differ only in the order of rewriting unrelated nonterminals can correspond to the same tree.

Parse Tree

A parse tree for a CFG G=(N,Σ,R,S)G = (N, \Sigma, R, S) is a finite rooted ordered tree in which:

  • The root is labeled SS.
  • Every node is labeled with a symbol in N∪ΣN \cup \Sigma.
  • Terminal nodes have no children.
  • A nonterminal node labeled AA has ordered child labels X1,X2,…,XkX_1, X_2, \ldots, X_k exactly when the corresponding production A→X1X2…XkA \to X_1 X_2 \ldots X_k is used. An empty production A→εA \to \varepsilon gives AA zero children.

The yield of a tree is the sequence of its terminal leaf labels read from left to right. A childless nonterminal expanded by an empty production contributes the empty string. An optional drawn ε\varepsilon is only a placeholder for that empty yield, not a terminal or an extra child in the formal tree.

For the sentence "the dog chased the cat", one parse tree has the following bracketed form:

Code
CollapseExpand
(S
  (NP (Det the) (N dog))
  (VP
    (V chased)
    (NP (Det the) (N cat))))

The same tree can be written as a bracketed string:

Code
CollapseExpand
[S [NP [Det the] [N dog]] [VP [V chased] [NP [Det the] [N cat]]]]

Both notations say the same thing. The sentence splits into a subject NP and a predicate VP. The subject NP splits into a determiner and a noun. The VP splits into a verb and an object NP. The object NP splits into a determiner and a noun. Each local split corresponds to a grammar rule. The root rule is S→NP VPS \to NP\ VP. The subject rule is NP→Det NNP \to Det\ N. The predicate rule is VP→V NPVP \to V\ NP. The object rule is another NP→Det NNP \to Det\ N.

The leaves are the tokens. The internal nodes are constituents. The root S dominates the whole sentence. The NP "the dog" dominates the tokens "the" and "dog". The VP dominates "chased the cat". This is the sense in which a CFG captures constituency: every nonterminal node corresponds to a span, and the rules determine how spans can combine. The tree is not a decoration added after parsing. It is the object that the grammar licenses, and the yield is the sentence that the tree derives.

Context-Free Power

CFGs are more powerful than regular expressions in an important way. A regular language can be recognized by a finite-state automaton, which has no memory beyond a finite set of states. A CFG can generate languages with unbounded nested structure. The difference is not about the number of rules. It is about the kind of memory the formalism can use. A finite-state automaton has a fixed amount of memory encoded in its states. A CFG can use recursion to keep track of nested structure without a fixed bound.

The classic example is:

S→a S b∣εS \to a\ S\ b \mid \varepsilon

This grammar generates strings like abab, aabbaabb, aaabbbaaabbb, and so on. The language is {anbn:n≥0}\{a^n b^n : n \ge 0\}. No finite-state automaton can recognize this language, because it would need to count arbitrarily many aa's and then check that the same number of bb's follow. A CFG does it with recursion. Each recursive expansion of SS adds one aa on the left and one bb on the right, so the two counts stay synchronized. The grammar does not count in the arithmetic sense. It encodes the matching dependency in the recursive structure itself.

Natural language has similar nested dependencies. Relative clauses can nest:

the cat that chased the rat that ate the cheese

The phrase "that chased the rat that ate the cheese" modifies "cat", and inside it another relative clause modifies "rat". Each relative clause can contain another. In principle, the nesting can continue indefinitely. A CFG expresses this recursive phrase analysis directly. The displayed right-branching chain alone does not prove that its string language is nonregular: a regular grammar can also generate unbounded right-branching sequences. The earlier anbna^n b^n example establishes the formal separation by requiring an unbounded match between two counts. Tree depth and the class of generated strings are different questions.

This does not mean that every aspect of natural language is context-free. It means that context-free recursion is a useful and surprisingly powerful approximation. Later we will see where that approximation strains. For now, the important point is that CFGs give us a way to describe nested structure without enumerating every possible depth. A finite grammar can describe an infinite set of nested sentences.

Phrase Structure and Tree Geometry

A parse tree is an ordered tree, which means the left-to-right order of children matters. That order is what connects the tree to the original token sequence: the yield of a parent is the concatenation of its children's yields in order. Reversing two children can therefore change the token sequence even when their labels and dominance relations remain the same. Order is part of the representation, not an afterthought.

Several structural relations are useful when discussing trees:

  • Dominance: A node AA dominates a node BB if BB is a descendant of AA, including AA itself in the reflexive case. The root dominates every node.
  • Immediate dominance: AA immediately dominates BB if BB is a child of AA.
  • Precedence: A node AA precedes a node BB if every leaf dominated by AA comes before every leaf dominated by BB in the yield.
  • Yield: The yield of a node is the sequence of terminal leaves it dominates. In a constituency tree, the yield of every node is a contiguous span of the sentence.
  • Head: Under a head-selection convention, many phrase types have a distinguished head child. The parent's lexical head is inherited from that child: usually a noun in an NP and a verb in a VP. In NP→NP PPNP \to NP\ PP, for example, the head child can be the inner NP while the headword lies deeper in the tree. For ordinary compositional phrases, heads contribute category and lexical meaning.

Under the reflexive definition above, dominance is a partial order. For nodes with nonempty yields, precedence is a strict partial order: it is transitive and irreflexive, so a node does not precede itself. They let us talk about structure without constantly referring to the original token positions. Immediate dominance is the local relation that grammar rules license. If a node labeled VP immediately dominates a V and an NP, then the rule VP→V NPVP \to V\ NP must be in the grammar. Precedence tells us how sister constituents are ordered. The yield connects the abstract tree back to the concrete sentence.

The head relation is not built into the bare definition of a CFG. A CFG rule such as NP→Det NNP \to Det\ N does not formally say that N is the head. Linguists often annotate headedness separately. But headedness is why constituency and dependency grammar are not as far apart as they first appear. A dependency tree makes the head-dependent relations explicit. A bare constituency tree does not identify head-dependent links by itself. Given additional head-selection conventions, you can derive a dependency representation from many constituency analyses.

An ordinary ordered CFG tree gives each node a contiguous substring as its yield. It therefore does not directly represent a constituent whose words are separated by material outside that constituent. Some linguistic analyses use discontinuous constituents; treebanks may instead introduce empty categories and links to represent displaced material. This continuity property should not be confused with dependency projectivity, which concerns head-dependent links over token positions and will be defined in the next chapter. Head conventions and treatment of empty categories matter when converting between the representations.

A parse tree has a yield, a root, nonterminal nodes, and production rules that license each local configuration. Parsing searches for a tree whose root is the start symbol, whose terminal leaves are the input tokens in order, and whose local configurations are licensed by the grammar.

Ambiguity

Ambiguity is a central practical problem in parsing. A grammar can assign more than one parse tree to the same token sequence. When that happens, the grammar is ambiguous for that sentence. Ambiguity is a property of the mapping between strings and structures. A grammar designed to preserve distinct structural readings can license alternative trees. Preserving those analyses is stronger than simply covering the same set of strings: two grammars can generate the same language while assigning different trees.

There are two broad kinds of ambiguity.

Lexical category ambiguity occurs when a word can belong to more than one syntactic category. The word "duck" can be a noun or a verb. The financial-institution and river-side senses of "bank" illustrate word-sense ambiguity, another form of lexical ambiguity: both can have the same noun category, so a bare N-to-bank rule does not distinguish them. In a CFG, category ambiguity appears as multiple preterminal rules for the same terminal:

N→duck,V→duckN \to \text{duck}, \qquad V \to \text{duck}

A sentence like "I saw her duck" can then have multiple parses. In the noun reading, "duck" heads the noun phrase "her duck"; in the verb reading, it describes her action. This is closely related to the POS tagging problem from Part-of-Speech Tagging, but here the category analysis affects the larger phrase structure, not just the label of one token. Choosing a lexical category restricts which grammar rules and phrase structures the parser can use.

Structural ambiguity occurs when the same words and the same word categories can combine into different phrase structures. The classic example is prepositional phrase attachment:

the dog chased the cat with a telescope

The phrase "with a telescope" can attach to the object noun phrase "the cat":

Code
CollapseExpand
[S
  [NP [Det the] [N dog]]
  [VP
    [V chased]
    [NP
      [NP [Det the] [N cat]]
      [PP [P with] [NP [Det a] [N telescope]]]]]]

Or it can attach to the verb phrase "chased the cat":

Code
CollapseExpand
[S
  [NP [Det the] [N dog]]
  [VP
    [VP [V chased] [NP [Det the] [N cat]]]
    [PP [P with] [NP [Det a] [N telescope]]]]]

Both trees yield the same string. The first says the cat has a telescope. The second says the dog used a telescope to chase the cat. The grammar alone does not choose between them. Contextual information and plausibility judgments can guide a reader. To rank the alternatives, a parser can use a scoring model, a preference heuristic, or a probabilistic grammar. The structural difference is small in terms of the number of nodes, but it is large in terms of meaning. This is why attachment ambiguity is such a central test case for parsing models.

Coordination also creates structural ambiguity. The phrase "old men and women" can mean "old men and old women" or "old men and women who are not necessarily old". The string does not tell you which grouping is intended. A grammar can enumerate these structures, but it cannot decide which reading a speaker intends without extra information.

The number of possible structures can grow extremely quickly. For n≥1n \ge 1 tokens, the number of ordered full binary bracketings, before a grammar constrains which ones are licensed, is the Catalan number:

Cn−1=1n(2n−2n−1)C_{n-1} = \frac{1}{n}\binom{2n-2}{n-1}

Equivalently, using the standard indexing Cm=1m+1(2mm)C_m = \frac{1}{m+1}\binom{2m}{m}, the number of ordered full binary tree shapes with nn leaves is Cn−1C_{n-1}. This counts possible bracketings before grammar labels and productions restrict them; it is not the number of parses licensed by every binary grammar.

The growth is exponential with a power-law correction. For large Catalan index mm, the standard asymptotic estimate is

Cm∼4mπ m3/2,m→∞.C_m \sim \frac{4^m}{\sqrt{\pi}\,m^{3/2}}, \qquad m \to \infty.

Here m=n−1m=n-1 for a sentence with nn tokens, π\pi is the circle constant, and ∼\sim means that the ratio of the two expressions tends to one. The numerator supplies exponential growth; the denominator reduces it by a power of mm. This is an approximation for large mm, not an exact count for a finite sentence. In particular, the exact identity Cm+1/Cm=(4m+2)/(m+2)C_{m+1}/C_m=(4m+2)/(m+2) tends to 4 from below. Calling the counts faster than every exponential would therefore be wrong.

For n=20n = 20, that is already C19=1,767,263,190C_{19} = 1{,}767{,}263{,}190 possible binary bracketings. A grammar may rule out many of these shapes, but unrestricted enumeration still faces this worst-case growth. Chart parsers address it by sharing substructure. Pruning can further restrict which candidates are explored, although discarding candidates can lose a valid or best parse. Probabilistic ranking alone does not remove the need for efficient search: a parser that scores every complete tree still has to construct those trees.

The growth is easier to see on a log scale. The rising counts explain why chart parsers share subproblems: compact recognition avoids listing every complete tree, while explicit enumeration remains output-sensitive.

PythonIn [3]
CollapseExpand
import math

import numpy as np

span_sizes = np.arange(2, 21)
catalan_counts = np.array(
    [math.comb(2 * (n - 1), n - 1) // n for n in span_sizes]
)
VisualizationOut [4]
CollapseExpand
Line plot of Catalan numbers rising sharply on a log scale.
Number of ordered full binary bracketings for input lengths from 2 to 20 tokens, shown on a logarithmic vertical axis. The count increases from 1 at 2 tokens to 1,767,263,190 at 20 tokens. These are possible tree shapes before a grammar constrains their labels and combinations.

A probabilistic context-free grammar, or PCFG, adds probabilities to rules and gives each tree a probability. We will define PCFGs shortly. A probabilistic model can assign different weights to the permitted analyses. A probabilistic parser can return a highest-probability tree or represent the remaining uncertainty. This shifts the question from which structures are possible to how their probabilities compare.

Parsing with CKY

A parser analyzes a token sequence using a grammar; on a successful parse, it can return one or more licensed trees. For CFGs, the most important teaching algorithm is the Cocke-Kasami-Younger algorithm, usually called CKY. It is a dynamic programming algorithm that fills a table of spans. The table is often called a chart, and each cell records the nonterminals that can derive a particular substring. Because subspans are reused many times, CKY avoids the combinatorial explosion of enumerating full trees independently.

CKY requires the grammar to be in Chomsky Normal Form.

Chomsky Normal Form

A context-free grammar is in Chomsky Normal Form (CNF) if every rule has one of these forms:

  • A→B CA \to B\ C, where BB and CC are nonterminals.
  • A→wA \to w, where ww is a terminal.
  • Optionally, S→εS \to \varepsilon if the empty string is in the language and SS does not appear on the right-hand side of any rule.

Apart from the optional empty start rule, every non-lexical rule in CNF combines exactly two nonterminals.

The grammar from earlier is already in CNF. Every rule is either a binary nonterminal rule, such as S→NP VPS \to NP\ VP, or a lexical rule, such as Det→theDet \to \text{the}. There are no rules with three or more symbols on the right-hand side, and no empty productions. This makes the CKY recurrence especially simple: every span longer than one token must be split into exactly two smaller spans.

A CFG can contain rules outside these forms. For example:

VP→V NP PPVP \to V\ NP\ PP

has three children. It can be binarized by introducing a fresh nonterminal:

VP→V X1,X1→NP PPVP \to V\ X_1, \qquad X_1 \to NP\ PP

The new nonterminal X1X_1 is an artificial intermediate constituent introduced by binarization. It is absent from the original ternary analysis and lets the parser decompose that rule into two binary steps. The original tree can be recovered by collapsing the artificial nodes after parsing. This is a common pattern in parsing: introduce auxiliary symbols to satisfy a normal form, then undo the auxiliary structure when presenting the final analysis.

Converting an arbitrary CFG to CNF also involves eliminating empty productions other than the permitted start-to-empty exception, and unit productions such as A→BA \to B. These conversions can change the shape of the parse tree. To recover original trees, retain a mapping from transformed rules back to the original grammar. Any CFG can be converted to an equivalent CNF grammar under the stated empty-string convention, possibly with a larger set of nonterminals. The equivalence is in the generated language, not necessarily in the exact tree shapes. A parser that needs linguistically faithful trees must therefore reconstruct the original rule applications.

The CKY Recurrence

Suppose the input tokens are w0,w1,…,wn−1w_0, w_1, \ldots, w_{n-1}. We use half-open spans [i,j)[i, j), where ii is the start index and jj is one past the end. The span [i,j)[i, j) covers tokens wiw_i through wj−1w_{j-1}. Half-open intervals are convenient because adjacent spans meet cleanly: the left span [i,k)[i, k) and the right span [k,j)[k, j) share the boundary kk without overlapping.

The following chart recurrence assumes n≥1n \geq 1. For empty input, accept exactly when the CNF grammar contains S→εS \to \varepsilon; if a tree is requested, return the start node expanded by that empty production. Our later toy grammar has no empty production and therefore rejects empty input.

Let chart[i][j]\text{chart}[i][j] be the set of nonterminals that can derive the span [i,j)[i, j). The base case handles single tokens:

A∈chart[i][i+1]if(A→wi)∈RA \in \text{chart}[i][i+1] \quad \text{if} \quad (A \to w_i) \in R

The recursive case handles longer spans. A nonterminal AA derives [i,j)[i, j) if there is a split point kk and a binary rule A→B CA \to B\ C such that BB derives the left span [i,k)[i, k) and CC derives the right span [k,j)[k, j):

A∈chart[i][j]if∃k, i<k<j, (A→B C)∈R, B∈chart[i][k], C∈chart[k][j]A \in \text{chart}[i][j] \quad \text{if} \quad \exists k,\ i < k < j,\ (A \to B\ C) \in R,\ B \in \text{chart}[i][k],\ C \in \text{chart}[k][j]

The sentence is accepted if the start symbol SS is in chart[0][n]\text{chart}[0][n].

This recurrence is the heart of CKY. It is also the reason CNF matters. With binary rules, every span is split into exactly two parts. If rules could have three children, the recurrence would need to consider two split points at once, which is more complex. Binarization removes that complexity by turning one ternary rule into two binary rules. The parser then only needs to consider one split point per span.

The algorithm fills spans in increasing order of length. First all one-token spans, then all two-token spans, then all three-token spans, and so on. By the time we need a longer span, all of its subspans have already been computed. This is the same dynamic programming principle you saw with the Viterbi algorithm in Part VI, Ch 6, but here the table is indexed by spans rather than by time steps. In HMM Viterbi, a subproblem scores a prefix ending at a particular state. In CKY, it concerns a contiguous substring and its possible root category. The principle is the same: solve small problems once, store the results, and combine them to solve larger problems.

The fill order is triangular. Each cell is completed only after the smaller spans on both sides of its split have been completed.

PythonIn [5]
CollapseExpand
import numpy as np

n_cky = 8
cky_span_order = np.full((n_cky, n_cky), np.nan)
for i in range(n_cky):
    for j in range(i + 1, n_cky + 1):
        cky_span_order[i, j - 1] = j - i
VisualizationOut [6]
CollapseExpand
Heatmap of CKY chart cells colored by span length.
CKY chart cells are filled in increasing span length, so every split uses completed subspans.

The time complexity is:

O(n3∣R∣)O(n^3 |R|)

where nn is the number of tokens and ∣R∣|R| is the number of grammar rules. For a fixed grammar, this is O(n3)O(n^3). For the recognition chart, which stores nonterminal membership rather than every tree, space is O(n2∣N∣)O(n^2 |N|), where ∣N∣|N| is the number of nonterminals. With a fixed grammar it is O(n2)O(n^2). Explicitly enumerating all parse trees adds output-sensitive work and storage beyond these bounds. The cubic bound grows quickly with input length; actual runtime also depends on the grammar and implementation. Binarization enables the binary recurrence, neural scores can rank structures, and pruning can reduce work by discarding candidates, with a possible loss of exactness. Where a grammar and input produce a sparse chart, a parser can also avoid work on absent constituents. Sparsity depends on the grammar and sentence; it is not guaranteed.

CKY is not the only CFG parsing algorithm. The Earley parser works on arbitrary CFGs without requiring CNF, and it has an O(n2)O(n^2) time bound for unambiguous grammars and a linear bound for more restricted classes such as fixed LR(kk) grammars. For the distinctions among Earley grammar classes and runtime bounds, see the discussion in Opedal et al. (2023). Generalized LR parsers are another family used in compiler construction. But CKY is the cleanest algorithm for understanding the span-based dynamic programming that underlies many statistical parsers. Once you understand CKY, the inside algorithm, Viterbi parsing, and many neural chart parsers become much easier to read.

Probabilistic CFGs

A plain CFG says which trees are possible. A probabilistic context-free grammar says how likely each tree is. A PCFG assigns a probability to every rule with a given left-hand side:

P(A→α∣A)P(A \to \alpha \mid A)

For each nonterminal AA, the probabilities of all rules with left-hand side AA sum to 1:

∑α:(A→α)∈RP(A→α∣A)=1\sum_{\alpha : (A \to \alpha) \in R} P(A \to \alpha \mid A) = 1

When treating these weights as a probability distribution over finite sentences, we also assume that generation terminates with probability 1. Local rule normalization alone does not guarantee this: a recursive grammar can put some probability on infinite derivations.

The probability of a finite parse tree is the product of the probabilities of all rule occurrences used in the tree:

P(T)=∏(A→α)∈TP(A→α∣A)P(T) = \prod_{(A \to \alpha) \in T} P(A \to \alpha \mid A)

The probability of a sentence is the sum of the probabilities of all its parse trees:

P(w)=∑T:yield(T)=wP(T)P(w) = \sum_{T : \text{yield}(T) = w} P(T)

A PCFG parser often returns the most likely tree:

T∗=arg⁡max⁡T: yield(T)=wP(T)T^* = \arg\max_{T:\,\mathrm{yield}(T)=w} P(T)

Here ww is the observed input token sequence, and the maximization ranges only over trees whose yield is ww. This can be computed with a variant of CKY that stores scores instead of just sets of nonterminals. For each span and nonterminal, we keep the best score and a backpointer to the split point and children that achieved it. The backpointers let us reconstruct the best tree after the chart is filled. This is exactly the same idea as Viterbi decoding in an HMM, generalized from sequence positions to spans.

The inside algorithm computes the total probability of all parses of a span. Let β[i][j][A]\beta[i][j][A] be the total probability that nonterminal AA derives the span [i,j)[i, j). The base case is lexical:

β[i][i+1][A]=P(A→wi∣A)\beta[i][i+1][A] = P(A \to w_i \mid A)

if that lexical rule exists. The recursive case sums over split points and binary rules:

β[i][j][A]=∑k=i+1j−1∑A→B CP(A→B C∣A) β[i][k][B] β[k][j][C]\beta[i][j][A] = \sum_{k=i+1}^{j-1} \sum_{A \to B\ C} P(A \to B\ C \mid A)\, \beta[i][k][B]\, \beta[k][j][C]

This is the PCFG analogue of the forward algorithm for HMMs. The root value β[0][n][S]\beta[0][n][S] gives the sentence likelihood. The Viterbi variant replaces the sum with a max and stores backpointers. Inside values also support training and constituent uncertainty calculations, but an inside value alone is not the posterior probability that a span is a constituent in the observed sentence. That posterior also needs an outside contribution for the surrounding tree and normalization by the sentence probability, assumed positive.

In a PCFG, the probability of a rule depends only on its left-hand-side category. With coarse categories such as NP and VP, all occurrences of the same category share one rule distribution. The model can express a preference for an attachment rule, but cannot change that rule's probability directly in response to a surrounding word or discourse context. Richer categories can encode additional conditioning information. Some modern constituency parsers replace local rule probabilities with neural scores while retaining chart decoding. We will see that story in Constituency and Dependency Parsing.

Worked Example

Let us work through the ambiguous sentence with the grammar from earlier:

the dog chased the cat with a telescope

The tokens are:

Code
CollapseExpand
the  dog  chased  the  cat  with  a  telescope

The grammar allows two complete S trees.

Parse A: the prepositional phrase modifies the object noun phrase.

Code
CollapseExpand
[S
  [NP [Det the] [N dog]]
  [VP
    [V chased]
    [NP
      [NP [Det the] [N cat]]
      [PP [P with] [NP [Det a] [N telescope]]]]]]

In this parse, "the cat with a telescope" is a single NP. The cat is the one with the telescope. The VP is "chased the cat with a telescope", where the object is the entire NP. The attachment is noun phrase attachment because the PP is a sister of the inner NP "the cat" inside the larger object NP.

Parse B: the prepositional phrase modifies the verb phrase.

Code
CollapseExpand
[S
  [NP [Det the] [N dog]]
  [VP
    [VP [V chased] [NP [Det the] [N cat]]]
    [PP [P with] [NP [Det a] [N telescope]]]]]

In this parse, "with a telescope" is a sister of the VP "chased the cat". The dog used a telescope to chase the cat. The object NP is just "the cat". The attachment is verb phrase attachment because the PP combines with the VP rather than with the object NP.

Both trees have the same yield:

Code
CollapseExpand
the dog chased the cat with a telescope

The grammar does not distinguish them. A PCFG can rank them if its rule probabilities favor one attachment pattern. For example, assuming the shared rule factors have positive probability, if the grammar assigns a higher probability to VP→VP PPVP \to VP\ PP than to NP→NP PPNP \to NP\ PP, Parse B will score higher. When a PCFG is estimated from a treebank, its preferences come from those observed rule uses. In our code example below, we instead assign probabilities by hand to illustrate the scoring calculation. The worked example therefore shows both the strength and the weakness of CFGs. The grammar precisely defines the space of possible structures, but it does not by itself tell us which structure is intended.

The grammar gives us a precise space of possible structures. Ambiguity means that space contains more than one tree for the same string. Scoring can rank the alternatives; identifying a speaker's intended reading may require contextual information.

Code Implementation

We will build a small CKY parser from scratch. The grammar is already in CNF, so we can use it directly. We will then add a simple PCFG scorer and show how the number of possible binary bracketings grows. The implementation is deliberately small. It is meant to make the recurrence concrete, not to compete with a production parser. The core parsing and scoring operations follow the definitions from the previous sections.

The parser and scorer use the Python standard library; NumPy and Matplotlib support the figures. No corpus or model downloads are needed. This keeps the algorithm visible while the small grammar makes every result inspectable.

Representing Trees

We start with a small Tree class. A tree has a label and a list of children. A child is either another Tree or a terminal string. The class provides three useful views: the yield, an indented pretty print, and a bracketed string. These views correspond to the three ways we have been talking about trees: as token sequences, as hierarchical diagrams, and as compact structural notation.

PythonIn [7]
CollapseExpand
import math
from dataclasses import dataclass, field
from typing import Dict, List, Tuple, Union


@dataclass
class Tree:
    label: str
    children: List[Union["Tree", str]] = field(default_factory=list)

    def leaf_tokens(self) -> List[str]:
        tokens = []
        for child in self.children:
            if isinstance(child, Tree):
                tokens.extend(child.leaf_tokens())
            else:
                tokens.append(child)
        return tokens

    def pretty(self, indent: int = 0) -> str:
        pad = "  " * indent
        lines = [f"{pad}{self.label}"]
        for child in self.children:
            if isinstance(child, Tree):
                lines.append(child.pretty(indent + 1))
            else:
                lines.append(f"{'  ' * (indent + 1)}{child}")
        return "\n".join(lines)

    def bracket(self) -> str:
        parts = []
        for child in self.children:
            parts.append(child.bracket() if isinstance(child, Tree) else child)
        return f"({self.label} {' '.join(parts)})"

The leaf_tokens method recovers the yield of a tree. The pretty method prints an indented tree. The bracket method produces the compact bracketed notation we used earlier. Keeping all three views in one class makes it easy to check that every tree has the expected yield.

Defining the Grammar

Each rule is a pair: a left-hand-side nonterminal and a tuple of right-hand-side symbols. Lexical rules have a single terminal on the right. This representation is simple, but it is enough to support the CKY recurrence. The grammar is small on purpose, so every rule can be inspected by eye.

PythonIn [8]
CollapseExpand
Rule = Tuple[str, Tuple[str, ...]]

rules: List[Rule] = [
    ("S", ("NP", "VP")),
    ("VP", ("V", "NP")),
    ("VP", ("VP", "PP")),
    ("NP", ("Det", "N")),
    ("NP", ("NP", "PP")),
    ("PP", ("P", "NP")),
    ("Det", ("the",)),
    ("Det", ("a",)),
    ("N", ("dog",)),
    ("N", ("cat",)),
    ("N", ("telescope",)),
    ("V", ("chased",)),
    ("P", ("with",)),
]

Let's check the size and shape of the grammar.

ConsoleOut [9]
CollapseExpand
Grammar has 13 rules
Binary nonterminal rules: 6
Lexical rules: 7

The grammar has 13 rules: 6 binary rules and 7 lexical rules. Every rule is in Chomsky Normal Form, so CKY can use it without any conversion step. The binary rules build phrases, and the lexical rules connect terminals to preterminal categories.

Implementing CKY

The parser fills a chart indexed by spans. Each chart cell maps a nonterminal to a list of trees for that span. Keeping the trees, rather than just the nonterminals, lets us inspect every parse. This version explicitly enumerates parse trees. Its time and storage can therefore grow exponentially with ambiguity, unlike the membership-only CKY bounds above. We use only a tiny sentence with two parses so the full trees remain practical to inspect.

PythonIn [10]
CollapseExpand
def cky_parse(words: List[str], grammar: List[Rule]):
    n = len(words)
    chart = [[{} for _ in range(n + 1)] for _ in range(n + 1)]

    nonterminals = {lhs for lhs, _ in grammar}
    lexical: Dict[str, List[str]] = {}
    binary: Dict[Tuple[str, str], List[str]] = {}

    for lhs, rhs in grammar:
        if len(rhs) == 1 and rhs[0] not in nonterminals:
            lexical.setdefault(rhs[0], []).append(lhs)
        elif len(rhs) == 2:
            binary.setdefault((rhs[0], rhs[1]), []).append(lhs)

    for i, word in enumerate(words):
        for lhs in lexical.get(word, []):
            chart[i][i + 1].setdefault(lhs, []).append(Tree(lhs, [word]))

    for span in range(2, n + 1):
        for i in range(n - span + 1):
            j = i + span
            cell = chart[i][j]
            for k in range(i + 1, j):
                left = chart[i][k]
                right = chart[k][j]
                for (b, c), lhs_list in binary.items():
                    if b not in left or c not in right:
                        continue
                    for left_tree in left[b]:
                        for right_tree in right[c]:
                            for a in lhs_list:
                                cell.setdefault(a, []).append(
                                    Tree(a, [left_tree, right_tree])
                                )

    return chart, chart[0][n].get("S", [])

The base case fills one-token spans from lexical rules. The recursive case loops over span lengths, start positions, split points, and binary rules. If the left and right subspans both contain the required nonterminals, the parser combines every pair of trees into a new tree for the parent nonterminal. The order of the loops matters. By processing spans from short to long, the parser guarantees that chart[i][k] and chart[k][j] are already complete when they are used.

Parsing the Ambiguous Sentence

Now we parse the sentence and count the complete S trees.

PythonIn [11]
CollapseExpand
sentence = "the dog chased the cat with a telescope"
tokens = sentence.split()
chart, parses = cky_parse(tokens, rules)
ConsoleOut [12]
CollapseExpand
Tokens: ['the', 'dog', 'chased', 'the', 'cat', 'with', 'a', 'telescope']
Number of S parses: 2

The parser finds two complete S trees. Both have the same yield, which is the original sentence. This confirms the ambiguity we analyzed by hand.

Let's print both trees and their bracketed forms.

ConsoleOut [13]
CollapseExpand
Parse 1
S
  NP
    Det
      the
    N
      dog
  VP
    V
      chased
    NP
      NP
        Det
          the
        N
          cat
      PP
        P
          with
        NP
          Det
            a
          N
            telescope
Yield: the dog chased the cat with a telescope
Bracketed: (S (NP (Det the) (N dog)) (VP (V chased) (NP (NP (Det the) (N cat)) (PP (P with) (NP (Det a) (N telescope))))))

Parse 2
S
  NP
    Det
      the
    N
      dog
  VP
    VP
      V
        chased
      NP
        Det
          the
        N
          cat
    PP
      P
        with
      NP
        Det
          a
        N
          telescope
Yield: the dog chased the cat with a telescope
Bracketed: (S (NP (Det the) (N dog)) (VP (VP (V chased) (NP (Det the) (N cat))) (PP (P with) (NP (Det a) (N telescope)))))

The output shows the two attachment patterns directly. In one tree, the PP is inside the object NP. In the other, the PP is a child of the VP. The yields are identical, which is exactly the point: the string does not determine the tree.

Scoring with a PCFG

A plain CFG treats both trees as equally possible. A PCFG can rank them. We assign illustrative probabilities to the rules. For each left-hand side, the probabilities sum to 1.

PythonIn [14]
CollapseExpand
rule_probs: Dict[Rule, float] = {
    ("S", ("NP", "VP")): 1.0,
    ("VP", ("V", "NP")): 0.7,
    ("VP", ("VP", "PP")): 0.3,
    ("NP", ("Det", "N")): 0.8,
    ("NP", ("NP", "PP")): 0.2,
    ("PP", ("P", "NP")): 1.0,
    ("Det", ("the",)): 0.8,
    ("Det", ("a",)): 0.2,
    ("N", ("dog",)): 0.4,
    ("N", ("cat",)): 0.4,
    ("N", ("telescope",)): 0.2,
    ("V", ("chased",)): 1.0,
    ("P", ("with",)): 1.0,
}


def tree_probability(tree: Tree, probs: Dict[Rule, float]) -> float:
    rhs = tuple(
        child.label if isinstance(child, Tree) else child
        for child in tree.children
    )
    probability = probs[(tree.label, rhs)]
    for child in tree.children:
        if isinstance(child, Tree):
            probability *= tree_probability(child, probs)
    return probability

The function walks the tree recursively. At each node, it looks up the rule probability and multiplies in the probabilities of all subtrees. The result is the product of rule probabilities for the entire tree.

ConsoleOut [15]
CollapseExpand
Rank 1: probability = 0.00044040
(S (NP (Det the) (N dog)) (VP (VP (V chased) (NP (Det the) (N cat))) (PP (P with) (NP (Det a) (N telescope)))))

Rank 2: probability = 0.00029360
(S (NP (Det the) (N dog)) (VP (V chased) (NP (NP (Det the) (N cat)) (PP (P with) (NP (Det a) (N telescope))))))

With these illustrative probabilities, the VP-attachment parse ranks higher. The absolute probabilities are tiny because many probabilities are multiplied together. What matters is the relative ranking. This ranking directly illustrates why a PCFG can resolve the ambiguity that the plain grammar left open: the grammar defines which structures are possible, and the hand-assigned rule probabilities express an illustrative attachment preference. A PCFG estimated from a treebank would use observed rule counts. Handling words absent from that treebank requires an additional lexical strategy, such as unknown-word rules or a model that reserves probability for unseen events.

A linear scale starting at zero shows the relative scores without exaggerating their difference. The longer bar marks the highest-scoring parse under these weights.

PythonIn [16]
CollapseExpand
import numpy as np

parse_prob_values = np.array([prob for prob, _ in scored])
parse_attachment_labels = [
    "VP attachment" if "(VP (VP" in tree.bracket() else "NP attachment"
    for _, tree in scored
]
VisualizationOut [17]
CollapseExpand
Horizontal bar chart comparing probabilities of two attachment parses.
Illustrative PCFG probabilities rank the verb-phrase attachment parse above the noun-phrase attachment parse.

How Fast Does Ambiguity Grow?

Finally, let's quantify the combinatorial explosion. The number of binary bracketings of nn tokens is the Catalan number Cn−1C_{n-1}.

PythonIn [18]
CollapseExpand
def catalan(n: int) -> int:
    return math.comb(2 * n, n) // (n + 1)
ConsoleOut [19]
CollapseExpand
5 tokens: 14 binary bracketings
10 tokens: 4,862 binary bracketings
15 tokens: 2,674,440 binary bracketings
20 tokens: 1,767,263,190 binary bracketings

The numbers grow rapidly. At 20 tokens, there are already more than 1.7 billion binary bracketings before any grammar constraints are applied. A grammar can exclude many of these shapes. Membership-only CKY shares subproblems without enumerating every tree, while the small tutorial implementation deliberately materializes its two complete trees. Unrestricted enumeration can therefore become impractical even for short inputs. Algorithms such as CKY share subproblems to avoid enumerating every complete tree.

Limitations & Impact

Context-free grammars are a major success of formal language theory. They give a precise definition of phrase structure, they support efficient parsing algorithms, and they connect syntax to recursion in a way that regular expressions cannot. CKY, Earley parsing, and probabilistic chart parsing all build on the CFG framework. The Penn Treebank paper describes the annotation of skeletal constituency structures used for syntactic analysis and parser research. The formalism also influenced compiler design, where context-free grammars describe programming language syntax and parser generators turn those grammars into working parsers.

But a CFG is a model, not a complete theory of human language. Its limitations become visible as soon as you try to cover real text. Our symbolic grammar lists its lexical rules explicitly. Broader coverage requires more entries or a strategy for unfamiliar words. A word may have multiple categories, and a fixed list cannot anticipate every new word. A grammar developed for one domain may under-cover text from another domain. Hand-written rules also encode assumptions about what is grammatical, and those assumptions can be brittle in the face of typos, fragments, code-switching, and informal syntax. Treebanks reduce the manual burden, but annotation is expensive and annotators disagree about borderline cases.

The formal power of CFGs is both a strength and a weakness. CFGs capture nested constituents and arbitrarily deep recursion. A small grammar with coarse categories may nevertheless omit agreement, case, or lexical valency constraints; finite category refinement can encode many such distinctions. The issue is the chosen grammar and the particular dependency pattern, not an inability to express any unbounded dependency. A rule like S→NP VPS \to NP\ VP does not require the subject and verb to agree in number. You can encode agreement by splitting categories into SgNPSgNP, PlNPPlNP, SgVPSgVP, and so on. This duplicates compatible productions; with several independent finite features, their category combinations can multiply. Feature-based grammars and unification-based formalisms add features and constraints to handle these dependencies more compactly. Shieber (1985) presents evidence for weak non-context-freeness from Swiss German cross-serial constructions, under the linguistic assumptions made in that argument. Tree-adjoining grammars are one way to extend context-free generative capacity while retaining polynomial parsing. Generative capacity and parsing complexity must be distinguished: even weakly equivalent formalisms can differ when grammar size is included in the analysis, as Kuhlmann, Satta, and Jonsson (2018) show for one CCG formalism versus TAG. Capacity and complexity claims require a specified formalism and explicit restrictions. The debate is subtle, and the practical lesson is not that CFGs are useless. It is that CFGs are a powerful approximation whose limits depend on which linguistic phenomena matter for the task.

Ambiguity is another fundamental limitation. A grammar can generate many trees for the same sentence, and the grammar alone cannot choose among them. PCFGs add probabilities, but their independence assumptions are strong. Rule probabilities depend only on the left-hand side, not on the surrounding lexical context or the larger discourse. A PCFG with coarse nonterminal categories shares rule probabilities across contexts that its category labels do not distinguish. Adding lexical or structural information to those labels can refine the model. Some neural chart-based constituency parsers use learned structural scores in place of local PCFG rule probabilities while retaining chart decoding. Other parsing approaches use different search procedures, so this architecture should not be attributed to every modern parser. The move from symbolic grammars to statistical and neural parsing is not a rejection of constituency. It is a change in how constituency is scored and induced.

The impact of CFGs is therefore double-edged. They gave NLP a formal backbone for syntax, a family of parsing algorithms, and a way to evaluate structural predictions. They also revealed the limits of pure symbolic description. A grammar can tell you that a sentence has two possible structures. It cannot tell you which one a speaker intended. Inferring an intended reading may require context, world knowledge, or clarification from the speaker. The next chapters take up these threads. Dependency syntax offers a different view of structure, constituency and dependency parsing turn grammars into practical predictors, and structured prediction and parsing evaluation give us the tools to measure whether those predictors are any good.

Summary

  • Constituency is the hierarchical grouping of words into phrases. Constituents can be identified with substitution, movement, coordination, and answer tests, though those tests are diagnostics rather than proofs.
  • A context-free grammar is a tuple (N,Σ,R,S)(N, \Sigma, R, S) with rules A→αA \to \alpha. It is context-free because each rule rewrites a single nonterminal regardless of surrounding symbols.
  • A parse tree records a derivation hierarchically. Its terminal leaves are tokens, its nonterminal nodes record licensed phrase expansions, and its yield is the terminal sequence.
  • Ambiguity arises when one token sequence has multiple parse trees. Lexical category ambiguity comes from words with multiple categories. Structural ambiguity comes from different phrase groupings, such as prepositional phrase attachment.
  • CKY parsing uses dynamic programming over spans. In Chomsky Normal Form, a span is built by splitting it into two subspans and applying a binary rule. For a fixed grammar, its compact recognition chart runs in O(n3)O(n^3) time; enumerating every parse adds output-sensitive work.
  • A PCFG assigns probabilities to rules and therefore to trees. The probability of a tree is the product of its rule probabilities. The inside algorithm sums over all parses of a span.
  • CFGs capture recursion and nested constituency, including some unbounded dependencies. A particular grammar may need refined categories or feature constraints for agreement and lexical selection. Some dependency patterns motivate richer formalisms, while neural constituency parsers can supply learned structural scores.
  • Symbolic grammars specify licensed trees. A parser can recognize or enumerate these structures without ranking them. A model or contextual information can then support the choice of a preferred or intended reading.

Quiz

Ready to test your understanding? Take this quick quiz to reinforce what you've learned about grammars and constituency structure.

Grammars and Constituency Structure

Question 1 of 80 of 8 completed
Which set of diagnostics does the chapter use to argue that a span such as 'the small dog' behaves as a constituent?
Join the discussionAsk a question or share what you learned.

Comments

Add to the conversation

A question, a correction, or an example from your own work.

No comments yet. Be the first to share your thoughts!

A different angle
All writing
Language AI Handbook · Vol. 3

Constituency and Dependency Parsing

Compare constituency and dependency parsing: CKY charts, Earley, transition-based and graph-based MST decoding, neural arc scoring, and key trade-offs.

48 min read
Language AI Handbook · Vol. 3

Dependency Syntax and Universal Dependencies

Learn how dependency syntax and Universal Dependencies represent grammatical relations as head-dependent arcs, with projectivity and CoNLL-U tree validation.

52 min read
Language AI Handbook · Vol. 3

Morphological Analysis and Generation

Explains how finite-state transducers and neural sequence-to-sequence models perform morphological analysis and generation.

46 min read

About the author

Reference

Citation details

Cite or share this article.

BIBTEXAcademic
@misc{brenndoerfer2025grammarsconstituency, author = {Michael Brenndoerfer}, title = {Grammars and Constituency Structure}, year = {2025}, url = {https://mbrenndoerfer.com/writing/grammars-constituency-structure-nlp}, organization = {mbrenndoerfer.com}, note = {Accessed: 2026-11-13} }
APAAcademic
Michael Brenndoerfer (2025). Grammars and Constituency Structure. Retrieved from https://mbrenndoerfer.com/writing/grammars-constituency-structure-nlp
MLAAcademic
Michael Brenndoerfer. "Grammars and Constituency Structure." 2026. Web. November 13, 2026. <https://mbrenndoerfer.com/writing/grammars-constituency-structure-nlp>.
CHICAGOAcademic
Michael Brenndoerfer. "Grammars and Constituency Structure." Accessed November 13, 2026. https://mbrenndoerfer.com/writing/grammars-constituency-structure-nlp.
HARVARDAcademic
Michael Brenndoerfer (2025) 'Grammars and Constituency Structure'. Available at: https://mbrenndoerfer.com/writing/grammars-constituency-structure-nlp (Accessed: November 13, 2026).
SimpleBasic
Michael Brenndoerfer (2025). Grammars and Constituency Structure. https://mbrenndoerfer.com/writing/grammars-constituency-structure-nlp
Newsletter

Stay up to date

Get new articles, book updates, and notes on AI and technology.

Unsubscribe at any time. Privacy policy