Role of Shift-Reduce Parsing (with a Worked Example)
shift-reduce parsing is a bottom-up technique that recognizes strings by repeatedly applying two operations: shift and reduce. The parser maintains a stack and an input pointer; it continues until it reaches an accept state or a reject/error.
In practical parser families, shift-reduce parsing is often implemented as LR parsing (e.g., SLR/LALR/Canonical LR). In that setting, a parsing table chooses an action for the current state and current lookahead token: typically “shift to a new state,” “reduce by production ,” or “accept.” Even when the table is not shown explicitly, the conceptual role is the same: shift builds candidate “handles,” and reduce collapses them into higher-level constructs.
Why shift-reduce matters
- It performs parsing without recursion and in a single left-to-right pass over the input (with stack-based backtracking-free construction).
- It directly follows the grammar’s bottom-up structure: start from terminals, then reduce to nonterminals, until the start symbol is formed.
Key terms (role-focused):
- handle
- viable prefix
- lookahead
Important: The parser’s correctness hinges on using the right reduce rules (and when to reduce). In LR parsing, this decision is guided by the automaton state and lookahead, preventing incorrect premature reductions.
Shift-Reduce Parsing (LR) Explanation
Example: Shift-Reduce Parsing for a Small Expression Grammar
We’ll illustrate the mechanics of shift-reduce parsing using a simple grammar for expressions without operator precedence complications:
Grammar :
Goal: parse the token sequence:
$$(id)\ +\ (id)\ id + id$.
Assumption about actions (how a trace is possible)
To produce a concrete shift/reduce trace, we must know (or assume) the parser’s decision policy (typically from an LR parsing table). For instructional clarity, we’ll show a valid shift-reduce sequence consistent with bottom-up parsing:
- reduce each to ,
- then reduce to where appropriate,
- and finally reduce to .
This demonstrates the role of shift and reduce, even if we do not display the full LR table.
Notation for the trace
- Stack shown as a sequence of grammar symbols (left to right).
- Input shown as unread tokens.
- Actions:
- Shift: move next token from input to stack
- Reduce: replace RHS symbols on top of stack with LHS nonterminal
Shift-Reduce Trace for Parsing $id + id$
- 1Step 1
Stack is empty. Input is . The parser will repeatedly choose a shift or reduce based on the current configuration.
- 2Step 2
Push the first onto the stack. Stack: []. Input: .
- 3Step 3
Match the top-of-stack handle to RHS of production , replacing it with . Stack: []. Input: .
- 4Step 4
Now the stack top matches RHS of . Replace by . Stack: []. Input: .
- 5Step 5
Shift the token onto the stack. Stack: []. Input: [].
- 6Step 6
Push the next . Stack: []. Input: (empty).
- 7Step 7
Replace with . Stack: []. Input: empty.
- 8Step 8
Replace handle on stack with . Stack: []. Input: empty.
- 9Step 9
Since the input is consumed and the stack contains the start symbol , the parser accepts.
What this trace teaches about the role of shift-reduce
- Shift postpones commitment: the parser collects enough symbols on the stack to potentially form a right-hand side (a handle).
- Reduce performs commitment: once the handle matches a production, it collapses the recognized structure into a nonterminal. This is how the parser builds larger syntactic constituents from smaller ones.
- The alternation continues until there is nothing left to read and the stack represents the whole sentence as the start symbol.
Mermaid summary of the role:
type="tip" title="Pro Tip" content="When doing manual shift-reduce traces, always check whether the top of the stack matches the RHS of some production. If it does, you can reduce; otherwise you must shift (assuming your grammar/parser permits that configuration)."
type="warning" title="Warning: Not every grammar supports every naive reduce choice" content="In general, the parser must avoid wrong reductions. LR parsers prevent incorrect reduce choices by using state information and lookahead tokens (via a parsing table), rather than reducing based only on stack contents."
Conceptual Lifecycle of Shift-Reduce Parsing
Shift phase
1Read the next input token and push it to the stack to extend the current viable prefix."
Reduce phase
2When the stack top matches a production RHS, replace it with the corresponding LHS nonterminal."
Decision loop
3Repeat shift/reduce decisions until accept or error."
Accept / error
4Accept when the start symbol is on the stack and input is fully consumed; otherwise reject."
Action/State viewpoint (how LR connects to shift-reduce)
In an LR parser, the “role” of shift-reduce is realized by a deterministic choice:
- Shift occurs when the parsing table says to shift for the current (state, lookahead).
- Reduce occurs when it says to reduce by a specific production for that (state, lookahead).
- Accept occurs when it indicates the completed parse of the start symbol.
This makes shift-reduce parsing scalable: instead of guessing reductions, the parser uses precomputed automaton logic (parsing table plus states).
Shift vs Reduce in the Worked Example (Counts)
How many shift and reduce operations were applied during the trace of parsing .
Common Questions About Shift-Reduce Role
Knowledge Check
In shift-reduce parsing, what does a 'reduce' action do?