Context-Free Grammars
Consider the language of balanced parentheses: strings like `(())` and `()()` are valid. Strings like `)(` and `(()` are not. No finite automaton can recognize this language. Doing so would require counting unboundedly many nested parentheses. Proving Languages Nonregular shows exactly why counting defeats finite memory. Yet balanced parentheses, arithmetic expressions, and the nested structure of `if`-`then`-`else` blocks are everywhere in real software. We need a more expressive way to describe such languages precisely. Context-free grammars (CFGs) are that tool. A CFG describes a language by giving a small set of rewriting rules. These rules — productions — generate every string in the language by repeated substitution. This generative view differs from the recognizer view of automata. Instead of scanning an input and deciding accept or reject, a grammar builds strings from a start symbol outward. That shift in perspective turns out to be exactly what is needed. It captures nested, recursive structure. CFGs matter because they are the backbone of nearly every parser ever written. Programming language syntax, markup languages like HTML and JSON, and even natural-language syntax models are specified with context-free grammars or close variants. The theory here is not an academic curiosity. It is the mathematical foundation underneath every compiler front end. It underlies every configuration-file parser, and every "grammar" in a parser-generator tool like Yacc or ANTLR. This article gives the precise formal definition of a context-free grammar. That includes variables, terminals, productions, and the start symbol. It defines rigorously what language a grammar generates. It builds the vocabulary needed before tackling Derivations and Parse Trees. It also prepares for the normalization technique Chomsky Normal Form. Finally, it introduces the automaton model — the pushdown automaton. This automaton recognizes exactly the context-free languages.
- A context-free grammar (CFG) is a 4-tuple : variables, terminals, production rules, and a start symbol.
- Productions have the form , where is a single variable and is any string of variables and terminals — this "single variable on the left" restriction is what makes a grammar context-free.
- The language generated by a grammar is the set of all terminal strings reachable from the start symbol via repeated rule applications (derivations).
- CFGs can express nested and recursive structure — like balanced parentheses and arithmetic expressions — that no finite automaton or regular expression can.
- Every regular language is context-free, but the reverse fails: context-free grammars are strictly more expressive than regular expressions.
- The class of languages generated by CFGs is exactly the class recognized by pushdown automata, a fact proved via a direct grammar-to-automaton construction.
A context-free grammar generates strings by rewriting one variable at a time, regardless of the surrounding context — and that single restriction is precisely what lets recursion, and hence nesting, be captured cleanly.
The idea
A context-free grammar is a formal system for generating strings through repeated substitution. Formally, it is a 4-tuple
where is a finite set of variables (also called nonterminals), is a finite set of terminals disjoint from , is a finite set of productions (or rules), and is the designated start symbol. Each production has the form
The left side of every production is a single variable, and the right side can be any finite string built from variables and terminals — including the empty string . This "one variable on the left" restriction is exactly what the word context-free refers to: a rule for rewriting may be applied wherever appears, irrespective of what surrounds it. (Contrast this with context-sensitive grammars, where the left side may include surrounding symbols that constrain when a rule fires.) Generation proceeds by derivation. Starting from the string consisting of just , at each step we pick some variable occurring in the current string, choose a production from , and replace that occurrence of with . We write for one such step, and for zero or more steps (the reflexive-transitive closure of ). A string is derivable from if . The language generated by , written , is the set of all such strings:
A language is context-free if there exists some CFG with . Note carefully: contains only strings entirely over (no leftover variables) — a derivation that stalls with variables still present has not produced a member of the language. Grammars are usually written compactly, listing productions with the same left-hand side on one line separated by `|` (read "or"). For example,
is shorthand for four productions: , , , . This particular grammar generates — precisely the kind of matched-count language that defeats finite automata, because each application of adds one `0` on the left and its matching `1` on the right, and only 's rules can end the recursion.
How it works
Constructing a CFG for a target language is a design exercise: you must find variables that capture the recursive substructure of the language, and productions that build every valid string while excluding every invalid one. A useful mental discipline is to give each variable a language it stands for, then check that its productions are exactly the ways to build members of that sub-language from smaller pieces.
Example 1 — balanced parentheses. Let and define one variable standing for "a balanced string of parentheses." The grammar
works because every balanced string is either empty, a balanced string wrapped in one more pair of parentheses, or the concatenation of two balanced strings. Those three cases are exhaustive and mutually exploitable via recursion.
Example 2 — arithmetic expressions. With terminals , a grammar reflecting operator precedence uses three tiers of variables:
Here (expression) handles addition at the loosest binding, (term) handles multiplication, and (factor) handles atoms and parenthesization — a layered design that simultaneously encodes the language of valid expressions and their precedence structure, which is why compiler grammars are typically written this way. The following pseudocode gives an explicit, generic derivation procedure: given a grammar and a target string, it performs a bounded, systematic search for a derivation (this is the naive membership-testing idea that motivates smarter parsing algorithms such as CYK, covered alongside Chomsky Normal Form). ``` function DERIVES(G = (V, Σ, R, S), target w, maxSteps):
# Breadth-first search over sentential forms (strings over V ∪ Σ) frontier ← { S } # a set of sentential forms, start with just S visited ← { S } for step in 1 .. maxSteps: nextFrontier ← ∅ for each sentential form α in frontier: if α == w: return TRUE # found a derivation S ⇒ w if α has no variables and α != w: continue # dead end: fully terminal but wrong if length(α) > length(w): continue # prune: can only grow via non-erasing rules for each position i where α[i] is a variable A: for each production (A → β) in R: α' ← α with α[i] replaced by β if α' not in visited: add α' to visited add α' to nextFrontier frontier ← nextFrontier if frontier is empty: return FALSE return FALSE # exceeded maxSteps without finding w (only a heuristic bound) ``` The pruning step `length(α) > length(w)` is valid only when every production is non-shrinking (no rule with , i.e. no -productions); grammars with -productions need a variant that also prunes on other grounds (e.g. removing -productions from first via a standard transformation) — this is one reason real parsers convert grammars to a normal form before parsing. This brute-force search is presented for conceptual completeness — it illustrates precisely what "derivation" means as a search process — but it is not how real parsers work; it can loop or blow up on ambiguous or highly recursive grammars. Efficient parsing (CYK, Earley, LL, LR) restricts either the grammar shape or the search strategy to guarantee polynomial time, and is developed once Chomsky Normal Form and pushdown automata are on the table.
Complexity & correctness
Membership testing (worst case, general CFGs). The classic algorithm for deciding whether is the CYK algorithm, which requires the grammar first be converted to Chomsky Normal Form (every rule of the shape or ). CYK runs in
time and space, where and is the size of the grammar (number of productions), by dynamic programming over substrings — this bound, and its derivation, belongs properly to the CYK/Chomsky Normal Form discussion, but it is worth previewing here: context-freeness buys polynomial-time parsing, in contrast to the undecidability that looms for more powerful grammar classes.
Space for grammar representation. A CFG with variables and productions, each of bounded length , is described in space — grammars are typically tiny (tens of rules) even when the languages they generate are infinite, which is the entire point of a generative, finite description of an infinite set.
Correctness of " is well-defined." The relation is defined on sentential forms (strings in ); is its reflexive-transitive closure, a standard closure operation, so is a well-defined set for every grammar — no ambiguity in what a grammar "means" as a language, even though (as the pitfalls section shows) a string can have more than one derivation.
Correctness of a specific grammar (proof technique). To prove a grammar generates exactly a target language , show both inclusions: 1. (soundness). Prove by induction on the number of derivation steps that every string derivable from any variable satisfies the invariant intended for that variable (e.g., "every string derivable from is balanced"). The base case covers productions with only terminals/ on the right; the inductive step covers productions that introduce recursive variables, using the inductive hypothesis on the shorter sub-derivations of those variables. 2. (completeness). Prove by induction on the structure of an arbitrary string in (e.g., on parenthesis nesting depth, or on string length) that it can be built by some production, reducing to strings that are shorter/simpler and hence covered by the inductive hypothesis. For the balanced-parentheses grammar : soundness follows because is balanced, wrapping a balanced string in parentheses preserves balance, and concatenating two balanced strings is balanced. Completeness follows because any nonempty balanced string either has matching outer parentheses around a balanced substring ( case) or splits at its first point of "returning to depth zero" into two nonempty balanced pieces ( case) — an argument entirely mirrored by the recursive definition itself, which is exactly why the grammar is correct.
Expressive tradeoff. Context-freeness is a genuine restriction: languages like (three matched counts, not two) are not context-free, provable via the pumping lemma for context-free languages — a cousin of the technique from Proving Languages Nonregular, but with pumping applied to two separated locations in a parse tree rather than one contiguous substring. So CFGs sit in a strict expressiveness band: strictly more powerful than regular languages, strictly less powerful than context-sensitive or unrestricted (Turing-recognizable) grammars.
Worked example
Trace a full derivation of the string `id + id × id` using the arithmetic grammar
using leftmost derivation (always rewrite the leftmost remaining variable — a standard canonical choice that removes spurious ordering ambiguity when tracing steps).
| Step | Sentential form | Rule applied |
|---|---|---|
| 0 | — (start) | |
| 1 | ||
| 2 | ||
| 3 | ||
| 4 | ||
| 5 | ||
| 6 | ||
| 7 | ||
| 8 |
Reading off step 8: the fully terminal string is `id + id × id`, matching the target, so , confirming membership in . Notice the structural payoff: because nests below in the derivation, the parse tree (shown in the figure above) groups the multiplication tighter than the addition — the grammar has encoded operator precedence purely through its layered variables, with no extra "priority" mechanism needed.
A second, contrasting trace — showing failure. Attempt to derive the malformed string `id +` (a dangling operator) from the same grammar. Any derivation must eventually replace every variable with terminals; in particular the rightmost symbol of any string generated via must eventually expand into a nonempty terminal string, because every production for and its descendants (, ) produces at least one terminal symbol before halting — no production ever erases a variable to in this grammar. Hence no derivation can end with a dangling `+`, and `id +` , exactly matching the informal expectation that a plus sign always needs a right operand.
Common pitfalls & traps
| Pitfall | Why it happens | Fix |
|---|---|---|
| Confusing "generates" with "recognizes" | Beginners think of a grammar as scanning input like an automaton | Remember a grammar builds strings top-down from ; recognition (parsing) is a separate, derived problem |
| Forgetting excludes sentential forms with leftover variables | A derivation that "gets stuck" with a variable present looks like progress | Only fully-terminal strings () count as members of |
| Assuming every string has a unique derivation | Confusing derivation order (leftmost vs. rightmost vs. arbitrary) with genuine structural ambiguity | Distinguish order-ambiguity (harmless, resolved by leftmost-derivation convention) from true ambiguity (two different parse trees for the same string) |
| Writing left-recursive rules without checking termination | style rules can be correct but easy to reason about wrong | Trace small derivations by hand; verify a base case (non-recursive alternative) exists and is reachable |
| Treating CFGs as closed under intersection and complement | Regular languages have those closures, so people assume CFGs do too | Context-free languages are closed under union, concatenation, and Kleene star, but not in general under intersection or complement |
| Ignoring -productions when reasoning about string length | silently shrinks a sentential form, breaking naive length-based pruning | Handle -productions explicitly, or eliminate them first via a standard transformation before length-based arguments |
Pioneers & history
The formal apparatus of context-free grammars grew from two independent traditions arriving at essentially the same object: rewriting-rule generative systems in theoretical linguistics, and BNF-style syntax specification in early programming-language design.
Origins · 1 minNoam Chomsky and how this was discovered
Where this matters
Context-free grammars are the specification language for essentially every parser in production software. Compiler front ends for languages like C, Python, and Java are built from CFGs processed by parser generators such as Yacc, Bison, and ANTLR, which convert a grammar directly into working parsing code. Data formats — JSON, XML, YAML, and even most configuration-file syntaxes — are defined with grammars, and the parsing libraries that read them implement context-free (or near-context-free) recognition algorithms under the hood. Beyond compilers, CFGs underlie network protocol parsers (where malformed input handling is a security-critical concern), template engines, regular-expression-engine syntax itself (regex syntax is context-free even though the matched language may be regular), and computational linguistics, where probabilistic context-free grammars model natural-language syntax for parsing and machine translation pipelines. The theoretical guarantee that context-free membership testing is solvable in polynomial time — a fact whose machinery is built out in Chomsky Normal Form and realized operationally by Pushdown Automata — is precisely why "write a grammar, generate a parser" is a reliable engineering workflow rather than a computationally risky gamble. Understanding Derivations and Parse Trees is the natural next step, since it makes precise how a grammar's abstract generative process turns into the concrete tree structure that every real parser actually builds.
Ask the tutor: How does converting a context-free grammar to Chomsky Normal Form actually work, and why does it preserve the generated language exactly?
Is every regular language also context-free?
Yes. Any regular language can be generated by a CFG whose productions mirror the transitions of a finite automaton or the structure of a regular expression: for each state with a transition to state on symbol , add a production (where are variables corresponding to states), and mark accepting states with . This "right-linear grammar" construction shows regular languages sit strictly inside context-free languages. The containment is strict because languages like are context-free but demonstrably not regular, as shown via the pumping lemma for regular languages.
What's the difference between a sentential form and a string in the language?
A sentential form is any string reachable from the start symbol during a derivation — it may still contain variables. A string in must be a sentential form consisting only of terminals, with for that all-terminal . Intermediate sentential forms like or in the worked derivation are not themselves members of the language; they are waypoints. This distinction matters because algorithms reasoning about derivations, like the CYK algorithm, explicitly track terminal-only substrings as the base case of their recursion.
Can every context-free language be generated by an unambiguous grammar?
No — some context-free languages are inherently ambiguous, meaning every grammar generating them has at least one string with multiple parse trees. A classical example combines two overlapping matched-count patterns, such as the union of and , whose overlap region () forces any generating grammar to derive some strings two structurally different ways. This is a property of the language itself, not a fixable flaw in a particular grammar, and it has real consequences for programming-language design, since language designers deliberately avoid inherently ambiguous constructs.
How is a CFG different from a regular expression?
A regular expression describes a language using concatenation, union, and Kleene star applied to symbols, with no mechanism for matching nested structure across arbitrary distances. A CFG allows recursive variables that can reference themselves, enabling constructs like that pair up symbols no matter how deeply nested. Every regular expression can be converted to an equivalent (non-recursive-in-a-certain-sense) CFG, but the reverse fails: grammars with genuine self-embedding recursion, such as , generate languages no regular expression can match.
Do CFGs always terminate when generating a string?
Any single derivation step is trivial and always terminates, but a derivation sequence only "finishes" when every variable has been rewritten away, leaving a pure terminal string — and nothing forces this to happen quickly, or ever, along an arbitrary sequence of choices. Rules like or are essential "escape hatches" that let a derivation stop expanding. A grammar with no such escape hatch reachable from some variable can derive infinite sentential forms without ever producing a terminal string from that variable, effectively contributing nothing usable to .
Why do grammars for arithmetic expressions use three variables (E, T, F) instead of one?
Layering variables by precedence level encodes operator precedence directly into the grammar's structure without any extra bookkeeping. A single flat rule like generates the right language but is ambiguous — `id + id × id` would have two structurally different parse trees, one where `+` binds tighter and one where `×` does. Splitting into (sums), (products), (atoms) forces multiplication to always nest below addition in any parse tree, resolving the ambiguity and matching the intended arithmetic semantics.
How does grammar size relate to the complexity of parsing algorithms?
Grammar size (number of productions, ) enters parsing time linearly or with a small polynomial factor, separate from the input length . The CYK algorithm's bound, for instance, is cubic in the string length but only linear in the number of grammar rules (after Chomsky Normal Form conversion, where each rule contributes a bounded amount of work per substring pair). In practice grammars for real programming languages have on the order of dozens to a few hundred productions, so the term dominates for anything but the shortest inputs — one reason production parsers use restricted grammar classes (LL, LR) achieving linear time instead of general CYK.