G27
Self-checkedTier 1 11 min readcd.parsing-shift-reduce

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 A→βA \to \beta by AA. Parsing a sentence bottom-up means reducing it step by step to the start symbol.

Bottom-up parsing = rightmost derivation in reverse. If S⇒rmγ1⇒rm⋯⇒rmwS \Rightarrow_{rm} \gamma_1 \Rightarrow_{rm} \dots \Rightarrow_{rm} w, the bottom-up parser finds the steps in the opposite order: ww, then γn−1\gamma_{n-1}, then ..., then SS. The parse tree is built from the leaves up.

Handle. In a right-sentential form γ\gamma, a handle is a substring β\beta together with a production A→βA \to \beta such that replacing β\beta by AA 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:

  1. Shift: move the next input token on to the stack.
  2. Reduce: the top of the stack is the right side β\beta of a production A→βA \to \beta (the handle): pop it and push AA.
  3. Accept: the stack holds only the start symbol and the input is $.
  4. 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 id+id∗id\text{id} + \text{id} * \text{id} 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: E⇒E+T⇒E+T∗F⇒E+T∗id⇒E+F∗id⇒E+id∗id⇒T+id∗id⇒F+id∗id⇒id+id∗idE \Rightarrow E + T \Rightarrow E + T * F \Rightarrow E + T * \text{id} \Rightarrow E + F * \text{id} \Rightarrow E + \text{id} * \text{id} \Rightarrow T + \text{id} * \text{id} \Rightarrow F + \text{id} * \text{id} \Rightarrow \text{id} + \text{id} * \text{id}.

Shift-reduce parse of id + id * id: start

Parser run by a script, not typed

Shift-reduce parsing: the stack holds grammar symbols (top at the right); a reduce replaces the handle on top by its left side.

  1. 1E → E + T
  2. 2E → T
  3. 3T → T * F
  4. 4T → F
  5. 5F → ( E )
  6. 6F → id
stack (top at the right)
(empty)
input
id+id*id$
action start

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.

0 / 14
computed by script: the algorithm is executed, one move per step. python3 scripts/gen-cd-traces.py

(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 E+TE + T and the next token *, the parser must shift, not reduce E→E+TE \to E + T. That is the multiplication binding tighter: reducing too early would produce (E+T)∗id(E + T) * \text{id}. 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

Easy

For 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 FF nodes, three TT nodes and two EE 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 ε\varepsilon productions counted as reduces of an empty handle).

EX 2Find the handle

Easy

Right-sentential form E+T∗idE + T * \text{id} of the layered grammar. Which substring is the handle? The handle is the leftmost complete subtree of height one: id\text{id} (it is a leaf child of an FF node still to be reduced), reduced by F→idF \to \text{id}. The substring E+TE + T looks like a right side too, but reducing it would not lead back to the start symbol by a rightmost derivation, because TT is followed by ∗* and belongs to T∗FT * F. So only id\text{id} is the handle. Reducing wrongly would block the parser.

EX 3A shift-reduce conflict in an ambiguous grammar

Medium

E→E+E∣E∗E∣idE \to E + E \mid E * E \mid \text{id}. After the parser has read id+id\text{id} + \text{id} it has E+EE + E on the stack and the next token is ∗*. It may reduce E+EE + E to EE (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 E+EE + E followed by ++ the same question decides associativity: reduce means left associative, shift means right associative.

EX 4A reduce-reduce conflict

Medium

S→A∣BS \to A \mid B, A→aA \to a, B→aB \to a. After shifting aa the stack holds aa and both A→aA \to a and B→aB \to a apply; the next token (\mathdollar\mathdollar) does not help. Two reductions are possible, so there is a reduce-reduce conflict, and the grammar is ambiguous (aa has two trees). Not every reduce-reduce conflict means ambiguity: for S→aAd∣bBd∣aBe∣bAeS \to a A d \mid b B d \mid a B e \mid b A e, A→cA \to c, B→cB \to c the conflict (after reading aca c or bcb c, which of A→cA \to c, B→cB \to c?) disappears with more precise lookahead; it is an artefact of the table-building method, see LALR.

EX 5Simulating by hand

GATE level

Grammar E→E+T∣TE \to E + T \mid T, T→idT \to \text{id}. Parse id+id\text{id} + \text{id}. Stack and input: empty and id + id $. Shift id. The top id\text{id} is a handle of T→idT \to \text{id}: reduce to TT. Reduce TT to EE (E→TE \to T). Next token +: shift. Shift id. Reduce id\text{id} to TT. The stack is E+TE + T with input $: reduce by E→E+TE \to E + T. The stack is EE, input $: accept. The moves: shift, reduce, reduce, shift, shift, reduce, reduce, accept. A question may ask for "the sequence of reductions": T→idT \to \text{id}, E→TE \to T, T→idT \to \text{id}, E→E+TE \to E + T. Check by reading it backwards: E⇒E+T⇒E+id⇒T+id⇒id+idE \Rightarrow E + T \Rightarrow E + \text{id} \Rightarrow T + \text{id} \Rightarrow \text{id} + \text{id}, 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

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

GATE 2024 · Set 1 · MSQ · 1 markQ7 · page 257 of the book
Which of the following is/are Bottom-Up Parser(s)
  • (a)Shift-reduce Parser
  • (b)Predictive Parser
  • (c)LL(1) Parser
  • (d)LR Parser
GATE 2020 · NAT · 1 markQ13 · page 258 of the book
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
_______.
GATE 2015 · MCQQ23 · page 260 of the book
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
GATE 2013 · MCQQ30 · page 261 of the book
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
GATE 2008 · MCQ · 1 markQ36 · page 263 of the book
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
year not read yetQ13 · page 275 of the book
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 answer

At the top. The parser never needs to look inside the stack.

Question 2

The sentence id+id\text{id} + \text{id} has how many shifts and reduces under E→E+T∣TE \to E + T \mid T, T→idT \to \text{id}?

Show answer

Three tokens (id, +, id) so 3 shifts; reduces: T→idT \to \text{id}, E→TE \to T, T→idT \to \text{id}, E→E+TE \to E + T, so 4 reduces.

Question 3

With E+EE + E on the stack and next token ++ in E→E+E∣idE \to E + E \mid \text{id}, does reducing give left or right associativity?

Show answer

Left associativity: (id+id)+…(\text{id} + \text{id}) + \dots; 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 answer

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