Top-Down Parsing
Imagine a compiler front end reading the token stream `if ( x > 0 ) then y := 1 ;`. It must decide, instantly and without backtracking through the whole program, which grammar rule produced it. A parser that works top-down starts from the grammar's start symbol. It tries to rebuild the derivation that generated the input. It does this by expanding nonterminals left to right and matching terminals as it goes. This is the strategy behind the simplest hand-written parsers in real compilers. It also underlies why languages are deliberately designed to avoid certain grammatical shapes. Top-down parsing matters because it is the technique a human can implement directly. Each nonterminal becomes a procedure, each production becomes a case. Parsing becomes ordinary recursive function calls. Production compilers for languages like Pascal, early C variants, and many scripting languages use this strategy. So does nearly every hand-rolled parser for a configuration format or a small domain-specific language. They use a top-down strategy because it is transparent, debuggable, and fast. Parser generators such as ANTLR build on the same predictive core. The big picture is a search problem disguised as a mechanical procedure. Given a nonterminal `A` and a lookahead symbol, the parser must pick which production of `A` to expand. Ideally, it does this by looking at only a bounded number of upcoming tokens. This avoids trying every alternative and undoing wrong guesses. This is where the theory of FIRST and FOLLOW Sets and LL Parsing and Parse Tables comes in. They turn "guess and check" into "look up in a table." This article develops the top-down strategy in general. It covers what it means to build a parse tree root-first. It explains why naive top-down parsing can loop or backtrack expensively. It also covers what property (predictiveness) is required to make it linear-time. It sets up, without fully building, the two concrete realizations covered elsewhere. These are Recursive Descent Parsing as the hand-coded implementation, and table-driven LL Parsing and Parse Tables as the automatically generated one.
- Top-down parsing builds a parse tree from the start symbol downward, expanding leftmost nonterminals first.
- Each step must choose a production for the current nonterminal using a bounded lookahead of input tokens.
- Predictive top-down parsing (no backtracking) requires the grammar to be free of left recursion and left-factoring ambiguity.
- The core operation is: match terminals against input, and expand nonterminals by selecting a production based on lookahead.
- FIRST and FOLLOW sets formalize which lookahead symbols license which production, making the choice mechanical.
- When it works, top-down parsing runs in linear time in the input length and needs only a stack of pending symbols.
Top-down parsing derives the input by always expanding the leftmost pending nonterminal, turning "which rule produced this?" into a lookahead-driven choice that, done right, never backtracks.
The idea
A grammar defines a language by derivation: starting from a start symbol `S`, repeatedly replace a nonterminal by the right-hand side of one of its productions, until only terminals remain. A top-down parse of a string `w` reconstructs a specific derivation of `w` from `S` by simulating this replacement process directly, always expanding the leftmost nonterminal in the current sentential form. Because expansion proceeds leftmost-first, a top-down parser produces what is called a leftmost derivation, and the parse tree grows from the root (the start symbol) down toward the leaves (the terminals of the input), which is the origin of the name. The central difficulty is choice. At any point the parser holds a nonterminal `A` and must decide which of `A`'s productions, say `A → α₁ | α₂ | … | αₖ`, actually matches the remaining input. A naive top-down parser handles this by trying each alternative in turn and backtracking on failure — this always terminates for simple cases but can require exponential time on pathological grammars, and it interacts badly with side effects (you cannot easily backtrack after printing an error message or emitting code). A predictive top-down parser instead commits to exactly one alternative based on a fixed, small amount of lookahead — classically just the next unconsumed input token — and never backtracks. This is only possible when the grammar has the property that, for every nonterminal, the alternatives can be distinguished by their possible starting tokens; grammars with this property are called `LL(1)` (Left-to-right scan, Leftmost derivation, 1 token of lookahead), a concept fully developed in LL Parsing and Parse Tables. Two structural obstacles block predictiveness and must be removed before a grammar can be parsed top-down without backtracking. Left recursion, where a nonterminal can derive a string beginning with itself (`A → A α`), sends a naive top-down parser into infinite descent, since expanding `A` immediately re-poses the problem of expanding `A`. Left-factoring ambiguity arises when two productions of the same nonterminal share a common prefix (`A → α β₁ | α β₂`), so that seeing the start of `α` does not yet reveal which alternative applies. Both problems are grammar transformations, not parsing tricks: left recursion is eliminated by rewriting the grammar to use right recursion plus an auxiliary nonterminal, and common prefixes are factored out into a shared production followed by a choice point.
How it works
Conceptually, a top-down parser maintains a frontier of grammar symbols still to be matched against the remaining input — think of it as a stack, initialized with the start symbol. At each step it looks at the symbol on top of the stack and the next input token, and does one of three things: if the top is a terminal, it must equal the current input token (consume both, or fail); if the top is the end-marker and input is exhausted, parsing succeeds; if the top is a nonterminal, the parser uses the current input token as lookahead to select one production for that nonterminal, then replaces the nonterminal on the stack with the right-hand side of the chosen production, symbols in left-to-right order so the leftmost symbol ends up on top. The prediction step is the heart of the method. For each nonterminal `A` and each production `A → α`, the grammar's `FIRST(α)` set tells us which terminals can appear as the first symbol of some string derived from `α`. If `α` can derive the empty string, we also consult `FOLLOW(A)`, the set of terminals that can legally follow `A` in some derivation, because in that case `A` might need to vanish entirely and let whatever comes next decide. This information is precomputed once from the grammar and packaged into a parsing table `M[A, a]`, indexed by nonterminal and lookahead terminal, giving the production to use (or an error). Building the table is exactly the concern of LL Parsing and Parse Tables; here we treat the table lookup as a black box `select(A, a)`. ``` // Generic predictive (LL) top-down parsing algorithm. // Grammar G with start symbol S, end-of-input marker ) // bottom-of-stack marker stack.push(S) // start symbol tokens ← input_tokens + [: if X = a: stack.pop() i ← i + 1 a ← tokens[i] else: report_error("expected " + X + " found " + a) return FAIL else: // X is a nonterminal production ← select(X, a) // table lookup M[X, a] if production = FAIL: report_error("no rule for " + X + " on lookahead " + a) return FAIL stack.pop() for symbol in reverse(production.rhs): if symbol ≠ ε: // skip epsilon productions stack.push(symbol) record_expansion(X, production) // grows the parse tree if a = $: return SUCCESS(parse_tree) else: report_error("extra input after end of parse") return FAIL ``` Two implementation styles realize this same abstract algorithm. In recursive descent, the explicit stack is replaced by the call stack: each nonterminal `A` becomes a function that inspects the lookahead token, decides which production applies (often via an `if`/`switch` on `FIRST` sets), and recursively calls functions for the nonterminals in the chosen production's right-hand side, in order. In table-driven predictive parsing, the algorithm above is run literally, with an explicit stack and an explicit two-dimensional table `M`, making the parser data-driven and easy to regenerate automatically when the grammar changes. Both are explored as dedicated techniques; the algorithm above is their shared skeleton.
Complexity & correctness
Time complexity. Each iteration of the main loop either pops a terminal and consumes exactly one input token, or expands one nonterminal into a fixed (grammar-dependent) number of stack symbols and consumes no input. The number of terminal-consuming steps is bounded by `n`, the input length, since each consumes a distinct token. The number of expansion steps is bounded because each expansion strictly decreases the "distance to termination" measured by summing, over the stack, the length of the shortest terminal string each symbol can still derive — and because the grammar is finite, only finitely many nonterminal expansions can occur before some terminal is consumed. Concretely, for a fixed grammar with maximum production length `k` and no infinite chains of epsilon-only expansions, the total number of stack operations is `O(n)`, and each operation (table lookup, push, pop) is `O(1)` with a properly indexed table. Hence the whole parse runs in `O(n)` time and `O(n)` space in the worst case (for the stack and the resulting parse tree, which has `O(n)` nodes for grammars without vacuous unbounded epsilon expansion chains). This linear bound is precisely what predictiveness buys you. A backtracking top-down parser without lookahead-based selection can, on adversarial grammars, explore an exponential number of candidate derivations before finding (or ruling out) a match, since each nonterminal with `k` alternatives multiplies the branching factor by `k` at every recursive call with no way to prune wrong guesses early.
Correctness. The algorithm is correct if it accepts exactly the strings derivable from `S`, and when it accepts, the tree it records is a valid parse tree for the input. Invariant. At every point in the loop, define the sentential form represented by the stack as the sequence of symbols currently on the stack (bottom to top) followed by nothing — call it `β`. The invariant maintained is:
that is, the start symbol has a leftmost derivation to (the already-consumed input) concatenated with the current stack contents. Initially `β = S` and no input is consumed, so the invariant holds trivially (`S ⇒ S`). Each step preserves it: matching a terminal moves one symbol from `β`'s front into the consumed prefix without changing the derivability claim; expanding a nonterminal `A` at the front of `β` using production `A → α` extends the leftmost derivation by exactly one step, `A ⇒ α`, replacing `A` with `α` in `β`. When the stack becomes just `$` and input is exhausted, `β` is empty and the consumed input equals `w`, giving `S ⇒lm w`, i.e., a full leftmost derivation — exactly the definition of "w is generated by G," witnessed constructively by the sequence of `record_expansion` calls, which is precisely a parse tree. Soundness of production selection. The invariant's correctness depends on `select(A, a)` never choosing a production that cannot lead to a successful match. This is guaranteed exactly when the grammar is `LL(1)`: for every nonterminal `A` with productions `A → α₁ | … | αₖ`, the sets `FIRST(αᵢ)` (extended to include `FOLLOW(A)` when `αᵢ ⇒* ε`) are pairwise disjoint. Disjointness means the current lookahead token uniquely identifies the correct alternative — there is no ambiguity to backtrack over, so a single deterministic table lookup suffices and is provably safe. When the grammar fails this condition (has left recursion, requires left-factoring, or is inherently ambiguous), the table has a cell with two entries or none applicable, and top-down parsing without backtracking is not sound for that grammar; this is the boundary the grammar transformations in Recursive Descent Parsing and LL Parsing and Parse Tables exist to fix.
Honest tradeoffs. Top-down predictive parsing trades grammar generality for speed and simplicity: it handles only `LL(1)` (or, with `k`-token lookahead, `LL(k)`) grammars, which excludes many natural, unambiguous grammars — most notably any grammar with left recursion (the natural way to express left-associative operators) and grammars needing unbounded lookahead to disambiguate. Bottom-up methods such as `LR` parsing accept a strictly larger class of grammars at the cost of a less intuitive table-construction process and less direct correspondence between code structure and grammar structure.
Worked example
Consider the classic expression grammar, already left-factored and free of left recursion, used above: ``` E → T E′ E′ → + T E′ | ε T → F T′ T′ → * F T′ | ε F → id | ( E ) ``` The FIRST/FOLLOW sets needed to build the prediction table are:
| Nonterminal | FIRST | FOLLOW |
|---|---|---|
| `E` | `{ id, ( }` | `{ $, ) }` |
| `E′` | `{ +, ε }` | `{ $, ) }` |
| `T` | `{ id, ( }` | `{ +, $, ) }` |
| `T′` | `{ *, ε }` | `{ +, $, ) }` |
| `F` | `{ id, ( }` | `{ +, *, $, ) }` |
From these, the parsing table `M[A, a]` is fully determined (only nonterminal rows shown; blanks are errors):
| `id` | `+` | `*` | `(` | `)` | `$` | |
|---|---|---|---|---|---|---|
| `E` | `E→T E′` | `E→T E′` | ||||
| `E′` | `E′→+ T E′` | `E′→ε` | `E′→ε` | |||
| `T` | `T→F T′` | `T→F T′` | ||||
| `T′` | `T′→ε` | `T′→* F T′` | `T′→ε` | `T′→ε` | ||
| `F` | `F→id` | `F→( E )` |
Running `PARSE` on the token stream `id + id * id `, doing `O(1)` work per step — confirming the `O(n)` bound with `n = 5`.
Common pitfalls & traps
| Pitfall | Why it happens | Fix | ||
|---|---|---|---|---|
| Left recursion causes infinite loop | `A → A α` expands `A` before consuming input, re-invoking the same rule forever | Rewrite to right recursion: `A → β A′`, `A′ → α A′ \ | ε` | |
| Ambiguous prefix, table cell conflict | Two productions share a `FIRST` set, so lookahead can't distinguish them | Left-factor: `A → α β₁ \ | α β₂` becomes `A → α A′`, `A′ → β₁ \ | β₂` |
| Forgetting `FOLLOW` for nullable productions | Only checking `FIRST(α)`, missing that `A → ε` may apply when lookahead is in `FOLLOW(A)` | Always include `FOLLOW(A)` in the table row wherever some alternative derives `ε` | ||
| Off-by-one in stack vs. input pointer | Consuming a token without advancing lookahead, or vice versa | Advance `i` and re-read `a` in the same branch that pops the matched terminal | ||
| Treating epsilon as a real stack symbol | Pushing `ε` onto the stack, causing spurious "match ε against real token" failures | Skip `ε` symbols when pushing a production's right-hand side | ||
| Ignoring end-of-input marker `$` | Parser reports success on partial input, or crashes reading past the token stream | Always append `` under the start symbol on the stack |
Pioneers & history
Origins · 1 minJohn Backus and how this was discovered
Backus led the team that built Fortran, the first widely used high-level language, and later co-developed the notation — Backus–Naur Form — used to formally specify context-free grammars for programming languages. That notation is the substrate every top-down parsing algorithm operates on: without a precise grammar, there is no derivation to reconstruct.
Where this matters
Top-down parsing is the strategy of choice whenever a grammar is small, hand-tunable, or needs a parser that is easy to read and modify — which describes an enormous share of real software. Compilers for languages engineered with `LL(1)`-friendly grammars (Pascal historically, many configuration and markup languages, JSON parsers, and most hand-written expression evaluators) use recursive descent or table-driven prediction directly. Parser-generator ecosystems such as ANTLR, and the hand-written front ends inside tools like `clang`'s early parsing stages and many scripting-language interpreters, rely on the same predictive core described here. Interactive tools — IDEs offering syntax highlighting and incremental reparsing, linters, and configuration validators — favor top-down parsers because the direct correspondence between grammar rule and parsing function makes them easy to instrument with error recovery and precise diagnostics. Understanding the top-down strategy is also the necessary foundation for its two concrete children: Recursive Descent Parsing, where nonterminals become functions, and LL Parsing and Parse Tables, where the whole prediction process is compiled into a lookup table, both of which depend on the derivation and ambiguity concepts from Derivations, Parse Trees, and Ambiguity to even define what "correct" means.
Ask the tutor: How do I compute FIRST and FOLLOW sets systematically for an arbitrary context-free grammar?
What is the difference between top-down and bottom-up parsing?
Top-down parsing starts from the grammar's start symbol and expands nonterminals to match the input, building the parse tree root-first via a leftmost derivation. Bottom-up parsing starts from the input tokens themselves and repeatedly combines them into larger constituents, building the tree leaves-first, effectively discovering a rightmost derivation in reverse. Top-down parsers are typically simpler to write by hand and correspond directly to the grammar's structure, but accept a smaller class of grammars (LL). Bottom-up parsers, such as LR parsers, handle a strictly larger class of unambiguous grammars, including left-recursive ones, at the cost of a more opaque table-construction algorithm usually left to automated tools rather than hand-coding.
Why can't top-down parsers handle left-recursive grammars directly?
A predictive top-down parser decides which production to use for a nonterminal `A` and then immediately tries to expand whatever comes first in the chosen right-hand side. If a production is `A → A α`, the very first symbol to expand is `A` itself, so the parser recurses into expanding `A` again before ever consuming an input token. This recursion never terminates, because nothing distinguishes "the first attempt to expand A" from "the second," and no input has been consumed to shrink the problem. The standard fix rewrites left recursion into right recursion using an auxiliary nonterminal, which preserves the language generated while ensuring every recursive expansion is preceded by consuming at least one token from a non-recursive alternative.
Does top-down parsing ever need more than one token of lookahead?
Yes — grammars requiring two or more tokens of lookahead to distinguish productions are called LL(k) for k > 1, and some languages are not LL(k) for any fixed k at all. Classic predictive parsing as taught with FIRST/FOLLOW sets targets LL(1), the practically dominant case, because it keeps the parsing table two-dimensional and the algorithm simple. Some parser generators, notably ANTLR, implement adaptive lookahead (effectively unbounded, computed on demand) to handle grammars that are not cleanly LL(1) but are still deterministic with enough foresight, trading some table-construction complexity for broader grammar coverage.
How does left-factoring interact with top-down parsing correctness?
Left-factoring is a grammar-preserving rewrite, not a parsing-time trick: it transforms `A → α β₁ | α β₂` into `A → α A′` with `A′ → β₁ | β₂`, generating exactly the same language while deferring the choice between `β₁` and `β₂` until after the shared prefix `α` has been consumed. This matters because before left-factoring, both original productions have overlapping FIRST sets (both start with whatever `α` starts with), so a single lookahead token cannot select between them, violating the LL(1) disjointness condition needed for the correctness proof's soundness step. After left-factoring, the choice point moves to `A′`, whose alternatives now typically have distinct FIRST sets, restoring deterministic selectability.
Can every unambiguous context-free grammar be parsed top-down?
No. Being unambiguous is necessary but not sufficient for LL(1) parsability; there exist unambiguous grammars — including some left-recursive ones that cannot be rewritten to eliminate recursion without changing associativity semantics in an implementation-relevant way, and grammars needing unbounded lookahead — that no fixed-lookahead top-down predictive parser can handle. In fact the LL grammars are a strict subset of the unambiguous context-free grammars, which are themselves a strict subset of all context-free grammars. Bottom-up LR parsing accepts a strictly larger class (all deterministic context-free grammars) precisely because it can defer the reduction decision until more right-context has been seen.
Why does the parsing table need a FAIL entry for some (nonterminal, terminal) pairs?
A FAIL entry in cell `M[A, a]` signals that no production of `A` can ever derive a string beginning with terminal `a` in a valid position, meaning the input is syntactically malformed at that point. This is precisely how the parser detects and reports syntax errors: rather than silently accepting garbage or looping, encountering a FAIL cell lets the parser immediately report "unexpected token `a` while parsing `A`" with the exact stack state as context. Good error-recovery strategies build on these FAIL cells by defining synchronizing tokens (often in `FOLLOW(A)`) at which the parser can skip input or pop the stack to resume parsing after an error, rather than aborting entirely.
Is a stack strictly necessary, or can top-down parsing be done without one?
The explicit stack in the table-driven algorithm can be replaced by the implicit call stack of a recursive-descent implementation, since each pending nonterminal on the parsing stack corresponds to a not-yet-returned function call. Both are the "same" algorithm at different levels of abstraction: the table-driven version makes the stack a first-class, inspectable data structure (useful for tooling, error recovery, and automatic generation from a grammar), while recursive descent makes each nonterminal's logic an ordinary, debuggable function. You cannot eliminate the stack conceptually, though, because bounded lookahead alone does not remember what to do after the current nonterminal finishes — that memory is exactly what the stack (explicit or as call frames) provides.