Shift-reduce parsing and handles
Bottom-up parsing as a rightmost derivation in reverse: the stack and input, the four actions shift, reduce, accept and error, handles and handle pruning, viable prefixes, and the shift-reduce and reduce-reduce conflicts that make a grammar unusable for a deterministic shift-reduce parser.
Why it exists
A top-down parser must choose a production before it has seen the input that production will derive. A bottom-up parser waits: it reads tokens, keeps them on a stack, and only when the symbols on top of the stack form the right side of a production does it replace them by the left side. It decides later, with more information, so it handles a larger class of grammars (left recursion included) than LL(1) does. Every practical parser generator (Yacc, Bison) builds a bottom-up parser.
Prerequisites
You need grammars, derivations and parse trees (ambiguity and grammar transformations) and the stack (stack ADT). Recall that in a rightmost derivation the rightmost nonterminal is replaced at every step, and that a sentential form produced by one is a right-sentential form.
Precise definitions
Reduction. The opposite of a derivation step: replace a substring that matches the right side of a production by . Parsing a sentence bottom-up means reducing it step by step to the start symbol.
Bottom-up parsing = rightmost derivation in reverse. If , the bottom-up parser finds the steps in the opposite order: , then , then ..., then . The parse tree is built from the leaves up.
Handle. In a right-sentential form , a handle is a substring together with a production such that replacing by gives the previous right-sentential form in some rightmost derivation. In other words, the handle is the right side that has to be reduced next, at its particular position (the same substring can occur twice and only one occurrence is the handle). For an unambiguous grammar every right-sentential form has exactly one handle. The handle of a form is its leftmost complete subtree of height one (a node whose children are all leaves of the current form).
Handle pruning. Reducing the handle, then the handle of the new form, and so on until the start symbol is reached, is called handle pruning; it gives the rightmost derivation backwards. The whole difficulty of bottom-up parsing is finding the handle.
The shift-reduce parser. It has a stack (symbols; the top is at the right) and the input buffer, which ends in $. Initially the stack is empty. The stack together with the unread input always spells a right-sentential form. The moves are:
- Shift: move the next input token on to the stack.
- Reduce: the top of the stack is the right side of a production (the handle): pop it and push .
- Accept: the stack holds only the start symbol and the input is
$. - Error: no valid move.
The key fact that makes a stack sufficient: the handle is always at the top of the stack, never inside it. So the parser never needs to search the stack: after a reduce, the new handle (if any) again ends at the top; otherwise it shifts.
Viable prefix. A prefix of a right-sentential form that does not extend past the right end of the rightmost handle. The stack contents of a correct shift-reduce parser are always viable prefixes, and an error is detected as soon as the stack plus the next token is no longer a viable prefix. The set of viable prefixes of a grammar is a regular language, recognised by the DFA of LR(0) items that is built on the next pages.
Conflicts. Given only the stack and the next token the parser may have more than one choice.
- Shift-reduce conflict: both a shift and a reduce are possible. Typical cause: ambiguity of operators or the dangling else.
- Reduce-reduce conflict: two different reductions are possible (two different handles end at the top). Typical cause: two nonterminals with the same right side.
A grammar for which a deterministic shift-reduce parser (with a given amount of lookahead and a given table-building method) makes no such choice is an LR grammar of the corresponding class (LR parsing). Parser generators resolve a shift-reduce conflict in favour of shift and a reduce-reduce conflict in favour of the production listed first, unless the grammar author gives precedence declarations. Every ambiguous grammar has at least one conflict in every LR table; this is why ambiguity must be removed or declared.
How it works
Step through the parse of with the layered expression grammar. The stack is on the left (top at the right), the unread input on the right, and the tree is built as a forest that merges into one tree. Watch two things. First, after each shift, either a reduce follows or another shift does. Second, the stack plus the input is always a right-sentential form, so the sequence of reductions, read from the end to the start, is a rightmost derivation: .
Shift-reduce parse of id + id * id: start
Parser run by a script, not typedShift-reduce parsing: the stack holds grammar symbols (top at the right); a reduce replaces the handle on top by its left side.
- 1E → E + T
- 2E → T
- 3T → T * F
- 4T → F
- 5F → ( E )
- 6F → id
Step 0
The stack is empty; the input is followed by $. A shift-reduce parser moves a token from the input onto the stack (shift) or replaces the symbols on top of the stack that form a handle by a nonterminal (reduce). The stack together with the unread input is always a right-sentential form.
(The parser in the animation decides between shift and reduce with the table explained on the next pages; here only the moves matter.) Notice that with the stack and the next token *, the parser must shift, not reduce . That is the multiplication binding tighter: reducing too early would produce . The choice looks at the next token, which is why one token of lookahead is part of the method.
Worked examples
EX 1Count the moves
EasyFor the animation above: the sentence has 5 tokens, so there are 5 shifts. Every nonterminal node of the parse tree is created by one reduce: three nodes, three nodes and two nodes, so there are 8 reduces. Together that is 13 moves, then the accept. In general the number of shifts is the number of tokens and the number of reduces is the number of nonterminal nodes of the parse tree (with productions counted as reduces of an empty handle).
EX 2Find the handle
EasyRight-sentential form of the layered grammar. Which substring is the handle? The handle is the leftmost complete subtree of height one: (it is a leaf child of an node still to be reduced), reduced by . The substring looks like a right side too, but reducing it would not lead back to the start symbol by a rightmost derivation, because is followed by and belongs to . So only is the handle. Reducing wrongly would block the parser.
EX 3A shift-reduce conflict in an ambiguous grammar
Medium. After the parser has read it has on the stack and the next token is . It may reduce to (grouping the sum first) or shift the (so that the product is formed first). Both lead to a valid parse: the two parse trees of the ambiguous grammar. A conflict is the parser-level view of ambiguity. Yacc would shift by default, which gives higher precedence than , as intended; with %left declarations the author sets this explicitly. For followed by the same question decides associativity: reduce means left associative, shift means right associative.
EX 4A reduce-reduce conflict
Medium, , . After shifting the stack holds and both and apply; the next token () does not help. Two reductions are possible, so there is a reduce-reduce conflict, and the grammar is ambiguous ( has two trees). Not every reduce-reduce conflict means ambiguity: for , , the conflict (after reading or , which of , ?) disappears with more precise lookahead; it is an artefact of the table-building method, see LALR.
EX 5Simulating by hand
GATE levelGrammar , . Parse . Stack and input: empty and id + id $. Shift id. The top is a handle of : reduce to . Reduce to (). Next token +: shift. Shift id. Reduce to . The stack is with input $: reduce by . The stack is , input $: accept. The moves: shift, reduce, reduce, shift, shift, reduce, reduce, accept. A question may ask for "the sequence of reductions": , , , . Check by reading it backwards: , a rightmost derivation (the rightmost nonterminal was replaced every time).
Cheat box
Cheat box
- Bottom-up = rightmost derivation in reverse; tree built from the leaves.
- Moves: shift (next token to stack), reduce (handle on top replaced by its left side), accept, error.
- Handle: the right side to reduce next, leftmost complete height-one subtree; always at the top of the stack.
- Stack plus remaining input is always a right-sentential form; the stack is a viable prefix.
- Shift-reduce conflict: shift or reduce? Reduce-reduce: which production? Ambiguity always causes a conflict.
- Number of shifts = number of tokens; number of reduces = number of internal nodes of the tree.
- Yacc: shift wins a shift-reduce conflict, the earlier production wins a reduce-reduce conflict.
Traps and GATE patterns
Connections
Across subjects
Shift-reduce parsers are made deterministic by LR parsing, whose tables decide between shift and reduce. The finite automaton of viable prefixes is built from items in LR(0) and SLR. The top-down alternative is LL(1) parsing; the two are compared in the parser hierarchy. Ambiguity and its removal are in grammar transformations.
PYQs
6 previous year questions from your book. I re-solved each one myself (2 by running or simulating, 4 by reasoning only); the book's key is shown next to my answer. All PYQs
Which of the following is/are Bottom-Up Parser(s)
- (a)Shift-reduce Parser
- (b)Predictive Parser
- (c)LL(1) Parser
- (d)LR Parser
Consider the following grammar. S →aSB| d B →b The number of reduction steps taken by a bottom- up parser while accepting the string aaadbbb is _______.
Which one of the following is True at any valid state in shift-reduce parsing?
- (a)Viable prefixes appear only at the bottom of the stack and not inside
- (b)Viable prefixes appear only at the top of the stack and not inside
- (c)The stack contains only a set of viable prefixes
- (d)The stack never contains viable prefixes
What is the maximum number of reduce moves that can be taken by a bottom-up parser for a grammar with no epsilon-and unit-production (i.e. of type A → ε and A → a) to parse a string with n tokens?
- (a)n/2
- (b)n–1
- (c)2n–1
- (d)2n
Which of the following describes a handle (as applicable to LR-parsing) appropriately?
- (a)It is the position in a sentential form where the next shift or reduce operation will occur
- (b)It is non-terminal whose production will be used for reduction in the next step
- (c)It is a production that may be used for reduction in a future step along with a position in the sentential form where the next shift or reduce operation will occur
- (d)It is the production p that will be used for reduction in the next step along with a position in the sentential form where the right hand side of the production may be found
Consider the grammar
E → E + n | E × n | n
For a sentence n + n × n, the handles in the right-
sentential form of the reduction are- (a)n, E + n and E + n × n
- (b)n, E + n and E + E × n
- (c)n, n + n and n + n × n
- (d)n, E + n and E × n
Self-check
Question 1
Where in the stack is the handle of a shift-reduce parser, always?
Show answerHide
At the top. The parser never needs to look inside the stack.
Question 2
The sentence has how many shifts and reduces under , ?
Show answerHide
Three tokens (id, +, id) so 3 shifts; reduces: , , , , so 4 reduces.
Question 3
With on the stack and next token in , does reducing give left or right associativity?
Show answerHide
Left associativity: ; shifting instead would give right associativity.
Question 4
Name the kind of conflict in each: two reductions possible, or a shift and a reduction possible.
Show answerHide
Reduce-reduce, and shift-reduce respectively.
Revision card
One-screen revision
- Bottom-up: reduce handles until the start symbol; reductions reversed = rightmost derivation.
- Stack (top at right) plus input = right-sentential form; handle always on top.
- Shift, reduce, accept, error; conflicts: shift-reduce, reduce-reduce (ambiguity implies conflict).
- Shifts = tokens, reduces = internal nodes.
References
- ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools (2nd edition), Chapter 4 Syntax Analysis (4.5 bottom-up parsing: reductions, handle pruning, shift-reduce parsing, conflicts)
- ·Cooper and Torczon, Engineering a Compiler, Chapter 3 Parsers (bottom-up parsing)
Verification
The animation is a run of a real shift-reduce parser (the moves come from the SLR table of the expression grammar, scripts/gen-cd-traces.py). scripts/enum-checks/cd_lr.py checks, for seven sentences, that the reductions read in reverse order are a rightmost derivation of the input (each step rewrites the rightmost nonterminal), and tests/cd-traces.test.ts repeats this in TypeScript. The conflict examples were found by building the LR tables of the grammars quoted. Definitions of handle and viable prefix follow the Dragon book; some books define the handle for ambiguous grammars slightly differently. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (token counts, FIRST and FOLLOW sets, LL(1) tables and moves, LR item sets, state counts and parses, operator-precedence values, attribute evaluation and printing schemes, activation-record counts and access links, parameter-passing results, three-address code, jumping and backpatched code, leaders and blocks, value numbering and DAGs, gen/kill and data flow sets, Sethi-Ullman labels and live ranges); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)