Productions & Derivations
A derivation is the proof that a token sequence is in the language. Leftmost and rightmost derivations are the two canonical orders, and they are exactly the orders that top-down and bottom-up parsers reconstruct.
What does it actually mean to say a parser "derives" a program, and why does the order of the rewrites matter?
A *sentential form*: a mixed sequence of terminals and nonterminals that sits between the start symbol and the finished token sequence. It is the intermediate state of the membership proof, and it exists to answer "how far along is this derivation, and what must still be expanded?" A parser never materialises it as a data structure, but every parsing algorithm is reconstructing one.
A rewrite step is legal only if the symbol being replaced is a nonterminal that appears in the current sentential form, and the production used has exactly that nonterminal on its left-hand side. Nothing about the surrounding symbols may influence the choice — that restriction is what makes the grammar context-free, and it is what lets a parser expand a nonterminal without knowing where it sits.
Key points
- A derivation step replaces one nonterminal using one of its productions; a derivation is a sequence of such steps from the start symbol.
- A sentential form is the intermediate mixed sequence; a sentence is a sentential form with no nonterminals left.
- Leftmost and rightmost derivations are canonical because they correspond to top-down and bottom-up parsing respectively.
- Many derivations share one parse tree; the tree forgets the order of expansion, which is the information a derivation adds.
- Two different parse trees for one input, not two different derivations, is what ambiguity means.
- LR reductions are rightmost derivation steps run backwards — that single fact makes shift-reduce traces readable.
One step at a time
A derivation step picks a nonterminal in the current sentential form and replaces it with the right-hand side of one of its productions. A derivation is a sequence of such steps starting at the start symbol. If the sequence ends with a form containing no nonterminals, that form is a *sentence* of the language, and the derivation is the proof.
Two orders are singled out because they correspond to real algorithms. A leftmost derivation always expands the leftmost nonterminal; a rightmost derivation always expands the rightmost one. Neither is more correct; both reach the same sentence when the grammar is unambiguous, and they build the same tree. The reason to care is that a top-down parser such as [[recursive-descent]] or an LL table produces a leftmost derivation as it goes, while an LR parser produces a rightmost derivation *in reverse* — every reduction it performs is a rightmost derivation step run backwards, which is why [[shift-reduce]] looks nothing like the grammar until you know this.
The grammar below is the layered expression grammar that the rest of this module keeps returning to. Its layering — expression, term, factor — is precedence encoded structurally, which is the subject of [[operator-precedence]]. Here it is just a grammar with enough rules that the derivation is interesting.
1 + 2 * 3| expression | ::= | expression "+" term | term | Left-recursive on purpose: it makes `+` group to the left. See `[[left-recursion]]` for what that costs a top-down parser. |
| term | ::= | term "*" factor | factor | A separate level for `*`, and it sits below `+`, so `*` binds tighter. |
| factor | ::= | number | "(" expression ")" | The parenthesised alternative is what lets an author override the layering. |
- 1.expressionapplying start symbol
- 2.expression "+" termapplying expression → expression "+" term
- 3.term "+" termapplying expression → term
- 4.factor "+" termapplying term → factor
- 5.number "+" termapplying factor → number
- 6.number "+" term "*" factorapplying term → term "*" factor
- 7.number "+" factor "*" factorapplying term → factor
- 8.number "+" number "*" factorapplying factor → number
- 9.number "+" number "*" numberapplying factor → number — no nonterminals remain, so this is a sentence
The derivation is not the tree
A derivation is a sequence; a parse tree is a structure. The tree records *which* production expanded *which* nonterminal, and deliberately forgets the order in which the expansions happened. That is exactly why several derivations can share one tree: expanding the left branch fully before touching the right branch, or alternating between them, produces the same parent-child relationships.
This is a useful diagnostic. If two derivations of the same input yield the same tree, the grammar is fine and you were merely walking it differently. If two derivations of the same input yield *different* trees, the grammar is ambiguous and the language has not been specified — see [[ambiguous-grammars]]. The formal statement is that an unambiguous grammar has exactly one leftmost derivation per sentence, which is why leftmost is the canonical one to compare.
The tree below is the one the derivation above produced. Notice that number appears three times as a leaf: in the tree these are distinct nodes with distinct spans, even though the derivation wrote the same symbol. Spans are how the parser keeps them apart, and where they came from is [[source-locations]].
Read it asCount the chain expression → term → factor → number on the left: four nodes for one digit. That is what precedence-by-layering costs in tree size, and it is the single strongest argument for [[parse-tree-vs-ast]]. The AST for this input is three nodes.
Why the direction shows up in real parsers
%define parse.error detailed reports expected token sets, and rustc's hand-written parser earns its diagnostics with a great deal of construct-specific code rather than by being top-down.A recursive-descent parser calls parseExpression, which calls parseTerm, which calls parseFactor, which consumes 1. Read the call stack at that moment: it is the left spine of the tree, which is the prefix of the leftmost derivation. The parser is *choosing* which production to apply before it has seen the input the production covers, which is why top-down parsing needs lookahead and why it cannot cope with left recursion.
An LR parser does the opposite. It shifts 1 onto a stack, then reduces it to factor, then to term, then to expression — committing to a production only after the entire right-hand side is on the stack. Read those reductions bottom to top and you have the rightmost derivation backwards. Because it decides late, it can handle left recursion and a strictly larger class of grammars, and it pays for that with a construction step that produces tables no human reads — see [[ll-vs-lr]].
| Top-down (LL, recursive descent) | Bottom-up (LR) | |
|---|---|---|
| Derivation produced | Leftmost, forwards | Rightmost, in reverse |
| When it commits to a production | Before consuming the right-hand side | After the whole right-hand side is on the stack |
| Left recursion | Non-terminating; must be eliminated first | Handled natively, and preferred |
| What the code looks liketypical | One function per nonterminal — readable | A state table — not readable |
| Error message qualitytypical | Names the construct being parsed | Names a state number unless effort is spent |
How it works
The steps, in the order the compiler takes them.
- Begin with the sentential form consisting of just the start symbol.
- Select a nonterminal in the current form — the leftmost one for a leftmost derivation, the rightmost for a rightmost one.
- Select a production whose left-hand side is that nonterminal, and textually substitute its right-hand side.
- Repeat until no nonterminal remains; the result is a sentence, and the sequence of productions used is the proof of membership.
- To build the parse tree instead of the sequence, record each substitution as a parent node with the right-hand side symbols as its children, in order.
- To recover the derivation from a finished parse, do a preorder walk for the leftmost derivation and a reverse postorder walk for the rightmost.
How it breaks
What the engineer observes when it goes wrong — not what goes wrong internally.
- A recursive-descent parser written directly from a left-recursive production recurses forever and the compiler dies with a stack overflow on the first expression it sees — no error message, just a crash.
- A hand-traced derivation is done rightmost while the parser under test is top-down, so the trace and the parser disagree at every step and hours go into a discrepancy that was never real.
- A parser generator reports a conflict on a grammar the author derived by hand successfully. The derivation existed; the *deterministic* one-token-lookahead choice did not, and those are different claims.
- The parse tree is built with the children in reverse because a bottom-up parser pops the stack without re-reversing, so
a - bevaluates asb - aand nothing errors.
When it helps
- Debugging a parser generator conflict: deriving the ambiguous input both ways shows precisely which two productions are competing.
- Reviewing a grammar change: deriving three or four representative inputs by hand catches structural mistakes before any code is generated.
- Reading an LR trace: knowing that reductions are reversed rightmost steps turns an opaque log into a legible derivation.
When it hurts
- Hand-derivation does not scale. Nobody derives a realistic statement in a real language by hand, and trying to is a sign the grammar needs shrinking rather than tracing.
- For an ambiguous grammar, finding one derivation proves nothing useful — the interesting question is how many trees exist, and derivation-by-hand is a bad way to answer it.
What it costs
Every one of these is paid by something.
- Encoding precedence as derivation layers makes the grammar unambiguous by construction and costs one nonterminal per precedence level, deeper parse trees, and an edit to several rules every time an operator is added.
- A leftmost, top-down reconstruction buys readable code and construct-aware diagnostics, and pays with an inability to handle left recursion and a smaller class of accepted grammars.
- A rightmost, bottom-up reconstruction buys grammar generality and native left recursion, and pays in generated tables nobody can inspect and error messages that must be reconstructed from state numbers.
What else you could do
What a different compiler or language does instead, and when that is better.
- Earley parsing derives all parses of any context-free grammar at once, in cubic time worst case, which is what you want for natural language or for a grammar you are not allowed to rewrite.
- GLR forks the parse at every conflict and prunes the branches that die, which is how tools that must accept real C++ without a preprocessing pass survive — see
[[parser-generators]]. - Pratt parsing abandons the nonterminal-per-level structure entirely, deriving expressions with a binding-power loop; the derivation still exists, but no production ever names
termorfactor— see[[pratt-parsing]].
See it for yourself
The flag, dump or tool that shows you this directly.
bison -v grammar.ywritesgrammar.output: every production numbered, every state, and every reduction — read it alongside a trace to see the reversed rightmost derivation directly.bison -Dparse.traceplusyydebug = 1prints each shift and reduce at run time, which is the derivation happening in front of you.- ANTLR's
grun MyGrammar expr -guirenders the parse tree for an input you type, including the one-child chains that layering produces. - Our own grammar deriver at
/compilers/grammarsteps a derivation forwards and backwards over a grammar you can edit.
Plausible wrong readings
Stated the way a confident engineer states them.
- "Leftmost and rightmost derivations give different trees." They give the same tree for an unambiguous grammar. Different trees mean the grammar is ambiguous, which is a different problem entirely.
- "The parser builds the derivation." The parser builds the tree; the derivation is a way of talking about the order it did so. Nothing stores a list of sentential forms.
- "If I can derive it, the parser will accept it." Only if the parser's algorithm can *find* that derivation deterministically with the lookahead it has. Derivability and parseability by a given algorithm are separate properties.
- "Bottom-up parsing means reading the input right to left." It reads left to right like everything else. The *derivation* it reconstructs is the rightmost one, reversed.
Misconceptions
The claim, and what is actually true.
Go deeper
The same idea at increasing depth. Stop wherever it stops being useful.
overview
Deriving means starting from the start symbol and replacing nonterminals with the right-hand sides of rules until only tokens remain. Do it always-leftmost and you are imitating a top-down parser; do it always-rightmost and reverse it and you are imitating a bottom-up one.
practical
The practical payoff is reading a Bison trace. Each Reducing stack by rule 7 line is one rightmost derivation step, backwards. Write the rules down in the order they were reduced, reverse the list, and you have the derivation — which tells you exactly where the parser thought it was when it went wrong, rather than which state number it was in.
advanced
The derivation order also decides what an action can see. In a bottom-up parser, a semantic action attached to a production runs when the production is reduced, so every child is already built and every attribute of every child is available — this is what makes synthesised attributes natural in LR and inherited attributes awkward. In a top-down parser the reverse holds: the parent is entered before its children exist, so inherited context flows down easily and synthesised results must be threaded back by return value. Attribute grammars formalise this, and it is the reason two parser families produce such different-looking semantic code for the same language.
How much this depends on
Nothing in this domain is true of every compiler. These say how much.
If you were asked this in an interview
- Give a leftmost derivation of
1 + 2 * 3for a grammar with expression, term and factor levels. - Why do LR parsers produce a reversed rightmost derivation, and what does that let them do that LL parsers cannot?
- Two derivations of one input reach the same sentence. Is the grammar ambiguous? What would prove that it is?