Skip to main content

5.3 SLR(1) Parsing: Follow Set Lookaheads & Conflict Resolution

πŸ“šModule 01SLR(1) Parsing & Conflict Resolution⏱️5 min read
🎯High-Yield For:Semester Exams β€’ GATE CSE β€’ Technical Interviews

πŸ’‘ Core Intuition: The 3-Second Mental Model​

🍳 The Everyday Analogy: The Discerning Passport Inspector​

In pure LR(0) parsing, the parser operates with total blindness during reductions:

  • Whenever a state contains a completed assembly (Aβ†’Ξ±β‹…A \to \alpha \cdot), it writes Reduce across every single terminal column in that table row.
  • This is like an immigration officer stamping every single traveler's passport with an entry visa without checking their boarding pass! If a traveler holds a ticket for another sovereign terminal, an unnecessary conflict or a false entry occurs.

Simple LR (SLR(1)) introduces a sharp, common-sense lookahead gate:

  • A reduction by Aβ†’Ξ±A \to \alpha should ONLY occur if the next incoming terminal token aa is a symbol that can legally appear immediately after AA in the language:

    a∈FOLLOW(A)a \in \text{FOLLOW}(A)
  • If aβˆ‰FOLLOW(A)a \notin \text{FOLLOW}(A), performing reduction Aβ†’Ξ±A \to \alpha is futile because non-terminal AA could never legally connect to aa in any valid derivation.

  • By placing reductions strictly under FOLLOW(A)\text{FOLLOW}(A) columns, SLR(1) eliminates the vast majority of false conflicts that paralyze LR(0).

πŸ’» Bridging to Computer Science: Restricting Reductions via FOLLOW Lookaheads​

In bottom-up syntax analysis:

  • SLR(1) retains the exact same DFA state machine and identical canonical collection of LR(0) items as an LR(0) parser.
  • However, during parsing table generation, each complete item [Aβ†’Ξ±β‹…][A \to \alpha \cdot] consults the global context of the grammar: FOLLOW(A)\text{FOLLOW}(A).
  • Lookahead token aa acts as a gatekeeper: if a∈FOLLOW(A)a \in \text{FOLLOW}(A), the reduction is scheduled; if aβˆ‰FOLLOW(A)a \notin \text{FOLLOW}(A), the cell remains blank.

πŸ—οΈ Architectural Foundations: The SLR(1) Reduction Filter​

Architecture Flow

The SLR(1) Reduction Gating Mechanism

Filtering out illegal reduction conflicts using grammar FOLLOW sets as gatekeepers

πŸ“₯Input

Lookahead Token (a)

Next unconsumed terminal token from input buffer.

β†’
Encountered
🎯State

Complete Item A β†’ Ξ± β€’

State contains a handle ready for grammatical reduction.

β†’
Checks Gate
πŸ›‘οΈGatekeeper

a ∈ FOLLOW(A)?

Can terminal 'a' legally appear immediately after non-terminal A?

β†’
Decision
βœ…Action

ACTION[i, a] = rk

If YES: write reduce action. If NO: reject reduction and avoid conflict.

βš–οΈ LR(0) vs SLR(1): Side-by-Side Comparison​

LR(0) vs SLR(1): The Lookahead Filter

Contrasting blind row-wide reductions against targeted FOLLOW-set filtering

Zero Lookahead

LR(0) Parser

πŸ™ˆ
Dominant Architecture / DomainUnconditional Reductions
  • β€’Places Reduce A -> Ξ± in ALL terminal columns in the state's row.
  • β€’Zero lookahead check; completely blind to incoming tokens.
  • β€’Generates spurious Shift/Reduce conflicts on almost all real grammars.
  • β€’State machine and table dimensions identical to SLR(1).
"Stamp reductions unconditionally across every single column."
1 Lookahead

SLR(1) Parser

🎯
Dominant Architecture / DomainFOLLOW-Guided Reductions
  • β€’Places Reduce A -> Ξ± ONLY under columns a ∈ FOLLOW(A).
  • β€’Uses 1 token of lookahead to filter out illegal reductions.
  • β€’Resolves massive numbers of LR(0) Shift/Reduce conflicts.
  • β€’Same number of states as LR(0); significantly more expressive.
"Only reduce when the next token can legally follow the produced variable."

πŸ“ Formal Mathematical Foundations: SLR(1) Table Construction​

SLR(1) uses the exact same canonical collection of LR(0) items and the exact same DFA states as LR(0). The difference lies entirely in step 2 of table construction:

Given canonical collection C={I0,I1,…,In}\mathcal{C} = \{ I_0, I_1, \dots, I_n \} for augmented grammar Gβ€²G':

  1. Shift Actions: If [Aβ†’Ξ±β‹…aΞ²]∈Ii[A \to \alpha \cdot a \beta] \in I_i where a∈Σa \in \Sigma is a terminal, and GOTO(Ii,a)=Ij\text{GOTO}(I_i, a) = I_j:

    ACTION[i,a]=ShiftΒ j(writtenΒ sj)\text{ACTION}[i, a] = \text{Shift } j \quad (\text{written } s_j)
  2. SLR(1) Reduce Actions (The Targeted Filter): If [Aβ†’Ξ±β‹…]∈Ii[A \to \alpha \cdot] \in I_i where Aβ‰ Sβ€²A \neq S' and Aβ†’Ξ±A \to \alpha is production rule kk:

    ACTION[i,a]=Reduce k(written rk)for all a∈FOLLOW(A)\text{ACTION}[i, a] = \text{Reduce } k \quad (\text{written } r_k) \quad \text{\bf for all } a \in \text{FOLLOW}(A)

    (All terminal columns bβˆ‰FOLLOW(A)b \notin \text{FOLLOW}(A) remain empty error cells!).

  3. Accept Action: If [Sβ€²β†’Sβ‹…]∈Ii[S' \to S \cdot] \in I_i:

    ACTION[i,$]=Accept\text{ACTION}[i, \$] = \text{Accept}
  4. GOTO Transitions: If GOTO(Ii,A)=Ij\text{GOTO}(I_i, A) = I_j for non-terminal A∈VA \in V:

    GOTO[i,A]=j\text{GOTO}[i, A] = j

🀼 SLR(1) Conflict Conditions​

An SLR(1) parsing table exhibits a conflict if and only if one of the following conditions is satisfied:

βš”οΈ Shift/Reduce Conflict in SLR(1)​

A state IiI_i contains a shift item for terminal aa (A→α⋅aβA \to \alpha \cdot a \beta) AND a completed reduce item (B→γ⋅B \to \gamma \cdot) such that:

a∈FOLLOW(B)a \in \text{FOLLOW}(B)

(If aβˆ‰FOLLOW(B)a \notin \text{FOLLOW}(B), the conflict is successfully eliminated by the SLR(1) lookahead filter!).

πŸ’₯ Reduce/Reduce Conflict in SLR(1)​

A state IiI_i contains two distinct completed reduce items A→α⋅A \to \alpha \cdot and B→β⋅B \to \beta \cdot such that their follow sets share at least one common terminal:

FOLLOW(A)∩FOLLOW(B)β‰ βˆ…\text{FOLLOW}(A) \cap \text{FOLLOW}(B) \neq \emptyset

🏭 Real-World Production Context: How Industry Builds This​

While pure SLR(1) parsers are rarely used directly in modern production compilers (having been superseded by LALR(1) in tools like GNU Bison, Yacc, and Menhir):

Understanding why SLR(1) fails on unambiguous constructs (such as pointer dereference assignment statements) is the core intellectual foundation of bottom-up parsing theory:

  • SLR(1) computes FOLLOW sets globally across the entire grammar.
  • Global FOLLOW sets merge lookaheads from independent derivation branches, polluting the lookahead set with tokens that can never legally appear in the current derivation context.
  • This limitation directly inspired Canonical LR(1) (context-specific lookaheads) and LALR(1) (merging LR(1) states with identical cores).

Because SLR(1) uses the exact same DFA states as LR(0):

  • Memory footprint in the runtime executable is identical to LR(0).
  • Parser performance is O(n)O(n) with zero backtracking.

πŸ“ Numerical Applications & Worked Examples​

Example 1: Resolving an LR(0) Shift/Reduce Conflict via SLR(1)​

Recall the arithmetic expression grammar from Module 05 Topic 2 that suffered a Shift/Reduce conflict in LR(0):

Sβ†’EEβ†’E+T∣TTβ†’id\begin{aligned} S &\to E \\ E &\to E + T \mid T \\ T &\to \text{id} \end{aligned}
  1. Augment the grammar and compute FOLLOW sets.
  2. Evaluate state I1I_1 and prove that SLR(1) completely eliminates the Shift/Reduce conflict.

Step-by-Step Analytical Solution:

Step 1: Augmented Grammar & Numbered Productions

  • (0) Sβ€²β†’SS' \to S
  • (1) Sβ†’ES \to E
  • (2) Eβ†’E+TE \to E + T
  • (3) Eβ†’TE \to T
  • (4) Tβ†’idT \to \text{id}

Step 2: Compute FOLLOW Sets

  • For Start Symbol SS:
FOLLOW(S)={$}\text{FOLLOW}(S) = \{ \$ \}
  • For EE:
    • In Sβ†’ES \to E: EE is at the end:
FOLLOW(S)βŠ†FOLLOW(E)β€…β€ŠβŸΉβ€…β€Š$∈FOLLOW(E)\text{FOLLOW}(S) \subseteq \text{FOLLOW}(E) \implies \$ \in \text{FOLLOW}(E)
  • In Eβ†’E+TE \to E + T: EE is followed by +:
+∈FOLLOW(E)β€…β€ŠβŸΉβ€…β€ŠFOLLOW(E)={+,$}+ \in \text{FOLLOW}(E) \implies \text{FOLLOW}(E) = \{ +, \$ \}
  • For TT:
    • In Eβ†’E+TE \to E + T and Eβ†’TE \to T: TT is at the end:
FOLLOW(E)βŠ†FOLLOW(T)β€…β€ŠβŸΉβ€…β€ŠFOLLOW(T)={+,$}\text{FOLLOW}(E) \subseteq \text{FOLLOW}(T) \implies \text{FOLLOW}(T) = \{ +, \$ \}

Step 3: Evaluate State I1I_1 From initial state I0=CLOSURE({Sβ€²β†’β‹…S})I_0 = \text{CLOSURE}(\{ S' \to \cdot S \}), transitioning across non-terminal EE yields state I1I_1:

I1=GOTO(I0,E)={S→E⋅,E→E⋅+T}I_1 = \text{GOTO}(I_0, E) = \{ S \to E \cdot, \quad E \to E \cdot + T \}

In state I1I_1:

  1. Item E→E⋅+TE \to E \cdot + T is a shift item on terminal +. It requests a Shift on column +.
  2. Item S→E⋅S \to E \cdot is a completed reduce item (production 1: S→ES \to E).
    • In LR(0): r1r_1 was written into both columns + and $, creating a fatal conflict on +!
    • In SLR(1): r1r_1 is written ONLY under columns in FOLLOW(S)\text{FOLLOW}(S):
FOLLOW(S)={$}\text{FOLLOW}(S) = \{ \$ \}
  • Crucially, +βˆ‰FOLLOW(S)+ \notin \text{FOLLOW}(S)!

Table Population for Row 1:

  • Column +: Contains Shift to state GOTO(I1,+)\text{GOTO}(I_1, +).
  • Column $: Contains Reduce 1 (Sβ†’ES \to E).

The conflict has vanished! Every cell contains at most one action. The grammar is strictly SLR(1)!


Example 2: The Canonical Assignment Grammar That Fails SLR(1)​

Consider the unambiguous pointer-assignment grammar:

Sβ†’L=R∣RLβ†’βˆ—R∣idRβ†’L\begin{aligned} S &\to L = R \mid R \\ L &\to * R \mid \text{id} \\ R &\to L \end{aligned}

Prove analytically that this grammar is NOT SLR(1) by identifying the exact state where an unresolved Shift/Reduce conflict occurs.

Step-by-Step Analytical Proof:

Step 1: Compute FOLLOW Sets

  • SS is start symbol:
FOLLOW(S)={$}\text{FOLLOW}(S) = \{ \$ \}
  • In Sβ†’L=RS \to L = R: LL is followed by = β€…β€ŠβŸΉβ€…β€Š=∈FOLLOW(L)\implies = \in \text{FOLLOW}(L).
  • In Sβ†’RS \to R: RR is at the end:
FOLLOW(S)βŠ†FOLLOW(R)β€…β€ŠβŸΉβ€…β€Š$∈FOLLOW(R)\text{FOLLOW}(S) \subseteq \text{FOLLOW}(R) \implies \$ \in \text{FOLLOW}(R)
  • In Lβ†’βˆ—RL \to * R: RR is at the end β€…β€ŠβŸΉβ€…β€ŠFOLLOW(L)βŠ†FOLLOW(R)\implies \text{FOLLOW}(L) \subseteq \text{FOLLOW}(R).
  • In Rβ†’LR \to L: LL is at the end β€…β€ŠβŸΉβ€…β€ŠFOLLOW(R)βŠ†FOLLOW(L)\implies \text{FOLLOW}(R) \subseteq \text{FOLLOW}(L).
  • Therefore, the follow sets of LL and RR merge:
FOLLOW(R)=FOLLOW(L)={=,$}\text{FOLLOW}(R) = \text{FOLLOW}(L) = \{ =, \$ \}

Step 2: Construct State I2I_2 (Transition on symbol LL) Starting from I0=CLOSURE({Sβ€²β†’β‹…S})I_0 = \text{CLOSURE}(\{ S' \to \cdot S \}):

  • Includes Sβ€²β†’β‹…SS' \to \cdot S
  • Includes Sβ†’β‹…L=RS \to \cdot L = R and Sβ†’β‹…RS \to \cdot R
  • Includes Lβ†’β‹…βˆ—RL \to \cdot * R and Lβ†’β‹…idL \to \cdot \text{id}
  • Includes Rβ†’β‹…LR \to \cdot L

Compute GOTO(I0,L)=I2\text{GOTO}(I_0, L) = \mathbf{I_2}:

  • Advancing dot over LL in Sβ†’β‹…L=RS \to \cdot L = R yields: Sβ†’Lβ‹…=RS \to L \cdot = R
  • Advancing dot over LL in Rβ†’β‹…LR \to \cdot L yields: Rβ†’Lβ‹…R \to L \cdot
  • I2={Sβ†’Lβ‹…=R,Rβ†’Lβ‹…}\mathbf{I_2} = \{ S \to L \cdot = R, \quad R \to L \cdot \}

Step 3: Analyze Conflict in State I2I_2 In state I2I_2:

  1. Item S→L⋅=RS \to L \cdot = R is a shift item on terminal =. It demands Shift on column =.
  2. Item R→L⋅R \to L \cdot is a complete reduce item for production R→LR \to L.
    • In SLR(1), reduce Rβ†’LR \to L is placed in all columns belonging to FOLLOW(R)\text{FOLLOW}(R):
FOLLOW(R)={=,$}\text{FOLLOW}(R) = \{ =, \$ \}
  • Because = belongs to FOLLOW(R)\text{FOLLOW}(R), the reduction Rβ†’LR \to L MUST be placed in column =!

Table Population for Cell ACTION[I2,=]\text{ACTION}[I_2, =]:

ACTION[I2,=]={Shift,Reduce R→L}\text{ACTION}[I_2, =] = \{ \text{Shift}, \quad \text{Reduce } R \to L \}

An unresolved Shift/Reduce Conflict exists in cell [I2,=][I_2, =]!
Even though this grammar is completely unambiguous, it is NOT SLR(1).

Root Cause of SLR(1) Failure: FOLLOW(R)\text{FOLLOW}(R) contains = because of the valid rightmost derivation S⇒L=R⇒R=RS \Rightarrow L = R \Rightarrow R = R. However, in state I2I_2, the stack has observed prefix LL. In this specific context, an = can only be legally followed if LL remains on the left side of the assignment; an reduction R→LR \to L at this position would create an invalid prefix R=R =. Because SLR(1) uses global follow sets rather than context-specific lookaheads, it cannot see this distinction.


🎯 Exam & Interview Pitfall Check​

Core Conceptual Questions

Question 1: "Does SLR(1) have more states or a larger parsing table than LR(0)?"
Key Focus Points:

  • Strict Rule: No. The number of states and the dimensions of the parsing table in SLR(1) are identically equal to those in LR(0).
  • Reason: Both parsers use the exact same canonical collection of LR(0) items. SLR(1) differs solely in filtering out which cells receive reduction entries.

Question 2: "If a grammar has an Ο΅\epsilon-production, can it be SLR(1)?"
Key Focus Points:

  • Yes. Grammars with Ο΅\epsilon-productions can certainly be SLR(1), provided that the empty reduction item Aβ†’β‹…A \to \cdot does not conflict with shift items or other reduce items under lookaheads in FOLLOW(A)\text{FOLLOW}(A).
Common Interview Traps: Architectural Trade-Offs

Trap 1: Assuming that an unambiguous grammar is always SLR(1).
Answer: Classic trap. Unambiguity does not guarantee SLR(1) compatibility. The assignment grammar (Sβ†’L=R∣RS \to L = R \mid R) is unambiguous and has a unique parse tree for every valid string, yet it fails SLR(1) because global FOLLOW sets are too coarse.

Trap 2: Claiming that SLR(1) eliminates all Shift/Reduce conflicts.
Answer: False. SLR(1) eliminates an S/R conflict if and only if the shift terminal aa does NOT belong to the FOLLOW set of the reducing non-terminal (aβˆ‰FOLLOW(B)a \notin \text{FOLLOW}(B)). If a∈FOLLOW(B)a \in \text{FOLLOW}(B), the S/R conflict survives.


πŸ’¬

Discussion & Doubts