GATE/Compiler Design/Syntax Analysis — LL & LR Parsing
Hard22 min readCompiler Design

Syntax Analysis — LL & LR Parsing

Syntax analysis (parsing) checks that token sequences conform to the grammar and builds a parse tree. GATE tests FIRST/FOLLOW sets, LL(1) parse tables, and LR parsing (SLR, CLR, LALR).

Key Points

  • ·Top-down parsing: builds tree from root; LL parsers, recursive descent
  • ·Bottom-up parsing: builds tree from leaves; LR parsers; more powerful
  • ·FIRST(α): set of terminals that begin strings derivable from α; includes ε if α ⟹* ε
  • ·FOLLOW(A): set of terminals that can follow A in any sentential form; $ for start symbol
  • ·LL(1): leftmost derivation, 1 token lookahead; parse table has no conflicts
  • ·Left recursion: A → Aα | β — must be eliminated for LL parsing
  • ·Left factoring: A → αβ | αγ → A → αA', A' → β | γ — required for LL(1)
  • ·LR(0), SLR(1), CLR(1), LALR(1) — increasing power and table size
  • ·Shift-reduce conflict and reduce-reduce conflict indicate ambiguity or grammar not in class

What is Parsing?

Analogy: Reading a sentence and identifying its grammatical structure. "The cat sat on the mat." → Subject: "The cat", Verb: "sat", Prepositional phrase: "on the mat"

A parser does the same for code:

x = a + b * c;
→ Assignment: x = (Expression)
  Expression: a + (Multiplication)
    Multiplication: b * c

FIRST and FOLLOW Sets — The Foundation

These sets tell us which token to expect at each point, allowing us to make deterministic parsing decisions.

FIRST(X) — "What can X start with?"

Rules:
1. If X is a terminal: FIRST(X) = {X}
2. If X → ε: add ε to FIRST(X)
3. If X → Y₁Y₂...Yₖ:
   - Add FIRST(Y₁) - {ε} to FIRST(X)
   - If ε ∈ FIRST(Y₁): also add FIRST(Y₂) - {ε}
   - If ε ∈ FIRST(Y₁) AND ε ∈ FIRST(Y₂): also add FIRST(Y₃) - {ε}
   - ... continue while ε keeps appearing
   - If ε ∈ FIRST(all Yᵢ): add ε to FIRST(X)

Worked Example:

Grammar:
  E → TE'
  E'→ +TE' | ε
  T → FT'
  T'→ *FT' | ε
  F → (E) | id

FIRST(F) = { (, id }
FIRST(T') = { *, ε }
FIRST(T) = FIRST(F) = { (, id }
  (Since ε ∉ FIRST(F), we stop)
FIRST(E') = { +, ε }
FIRST(E) = FIRST(T) = { (, id }

FOLLOW(A) — "What terminals can come after A?"

Rules:
1. If A is start symbol: add $ to FOLLOW(A)
2. If B → αAβ: add FIRST(β) - {ε} to FOLLOW(A)
3. If B → αA or B → αAβ where ε ∈ FIRST(β): add FOLLOW(B) to FOLLOW(A)

Worked Example (continuing above):

FOLLOW(E) = { ), $ }   (E appears inside (E), and is the start symbol)
FOLLOW(E') = FOLLOW(E) = { ), $ }  (E' is at end of E → TE')
FOLLOW(T) = FIRST(E') - {ε} ∪ FOLLOW(E') = {+} ∪ {), $} = {+, ), $}
FOLLOW(T') = FOLLOW(T) = {+, ), $}
FOLLOW(F) = FIRST(T') - {ε} ∪ FOLLOW(T') = {*} ∪ {+,),} = {*, +, ), $}

LL(1) Parsing — Top-Down, Left-to-Right, 1 Lookahead

Top-down = build tree from the root (start symbol) down. L = left-to-right scan; L = leftmost derivation; 1 = 1 token lookahead.

LL(1) Parse Table Construction:

For each production A → α:
  For each terminal a ∈ FIRST(α): put A→α in M[A, a]
  If ε ∈ FIRST(α):
    For each b ∈ FOLLOW(A): put A→α in M[A, b]

LL(1) condition: no cell has MORE THAN ONE entry
  Multiple entries → grammar is not LL(1)

Example parse table (expression grammar above):

       id    +    *    (    )    $
E:   E→TE' |    |    | E→TE' |    |
E':  |  E'→+TE'|    |    | E'→ε | E'→ε
T:   T→FT' |    |    | T→FT' |    |
T':  | T'→ε | T'→*FT'|   | T'→ε | T'→ε
F:   F→id  |    |    | F→(E) |    |

Obstacles to LL(1) Parsing

Left Recursion — Must Eliminate!

Left-recursive grammar: A → Aα | β
This LOOPS forever in top-down parsing!

Elimination:
A  → βA'
A' → αA' | ε

Example: E → E+T | T
→ E  → TE'
   E' → +TE' | ε

Left Factoring — Must Apply for Common Prefixes

A → αβ | αγ   (both start with α — ambiguous which rule to apply!)

Left factor:
A  → αA'
A' → β | γ

Example: stmt → if E then S else S | if E then S
→ stmt → if E then S stmt'
   stmt'→ else S | ε

LR Parsing — Bottom-Up, More Powerful

Bottom-up = start with tokens, reduce to the start symbol. More powerful than LL — can handle more grammars.

LR parser uses:
  STACK: stores states and grammar symbols
  ACTION table: shift (push token) or reduce (apply production)
  GOTO table: state transitions after reduce

Parsing loop:
  1. Look at top of stack and next input token
  2. Shift: push state for current token
  3. Reduce: pop RHS of some production, push LHS
  4. Accept: input consumed, start symbol on stack
  5. Error: no valid action

LR Variants (Increasing Power)

LR(0):   No lookahead. Weakest. May have many conflicts.
SLR(1):  Use FOLLOW sets to decide when to reduce.
         Reduces when input ∈ FOLLOW(A) for production A → α
LALR(1): Merge LR(1) states with same core.
         Practical: YACC/Bison use LALR(1). Most languages are LALR(1).
CLR(1) = LR(1): Most powerful. Exact 1-token lookahead per item.
         Largest tables. Every LALR(1) grammar is LR(1).

Power hierarchy:

LR(1) = CLR(1)  ← most powerful
    ↓ merge states
LALR(1)         ← practical choice (YACC)
    ↓ use FOLLOW instead of exact lookahead
SLR(1)
    ↓ no lookahead
LR(0)           ← weakest

Every LL(1) grammar is also LALR(1)

Conflicts in LR Parsing

Shift-Reduce conflict: should we shift the next token OR reduce current handle?
  Often caused by ambiguity (e.g., dangling else problem)
  Resolution: usually prefer SHIFT (implemented in most tools)

Reduce-Reduce conflict: two different productions applicable for reduction
  More serious — indicates grammar ambiguity
  Must resolve by rewriting grammar

Quick Check

Q1. Grammar: S → aA, A → bA | ε. Compute FIRST(A) and FOLLOW(A).

FIRST(A): A → bA → FIRST = {b}; A → ε → add ε; FIRST(A) = {b, ε}
FOLLOW(A): S → aA: nothing after A at end, so add FOLLOW(S) = {$}
           A → bA: after A comes end of A production → add FOLLOW(A) = {$}
           FOLLOW(A) = {$}

Q2. Why cannot left-recursive grammar be parsed by LL(1)? Answer: LL(1) is top-down and predictive. When parsing A → Aα, the parser tries to expand A, which requires expanding A again — infinite recursion with no progress through the input.

Q3. Is every LALR(1) grammar also LR(1)? Answer: Yes. LALR(1) is built by merging LR(1) states with the same core. Merging can only create conflicts, not resolve them. So if LALR(1) has no conflicts, LR(1) definitely does not either.

Key Formulas

  • FIRST rule: FIRST(αβ) = FIRST(α) if ε ∉ FIRST(α), else FIRST(α)-{ε} ∪ FIRST(β)
  • LL(1) table fill: M[A,a]: add A→α if a ∈ FIRST(α) or (ε ∈ FIRST(α) and a ∈ FOLLOW(A))

GATE Exam Tips

  • Left-recursive grammars CANNOT be parsed by LL parsers — always eliminate before LL table.
  • SLR uses FOLLOW sets for reduce decisions; LR(1) uses exact lookahead — SLR has more conflicts.
  • GATE often gives a grammar: compute FIRST/FOLLOW, check LL(1) condition — practice this flow.
  • An ambiguous grammar can NEVER be LR(k) for any k — must resolve ambiguity first.

Finished reading this topic?

Mark it complete to track your study progress.