Table of Contents

The Parse Table Β· How to Build It

πŸŽ“ This is the Advanced track.
Over in the canonical collection, we gathered all 12 states and every transition between them.
Now we turn that β€” into a single table the parser actually reads and acts on. That table is the parse table.

πŸ“ Where it lives Β· LRParsingTable.CreateParsingTable Β· …/Parsers/Collections/…

What a parse table is

As it reads its input, the parser has to decide at every moment: "what do I do right now?" The table that writes down all of those answers in advance is the parse table.
In a word β€” it's the parser's instruction sheet. For each cell it spells out: "you're in this state (row), and the next input symbol is this (column) β†’ so do this action."

The table splits into two parts.

  • ACTION β€” the columns are terminals + $ (end of input). Each cell holds one of three actions.
    • shift β€” read the next terminal, push it onto the stack, and go to the next state it points to. s5 = "read it and go to Iβ‚…."
    • reduce β€” you've read the whole right-hand side of some production, so you fold it back into the nonterminal on its left. r2 = "reduce by production 2."
    • accept β€” the whole input matched the grammar. Done. acc.
  • GOTO β€” the columns are nonterminals. This is which state to go to after reducing a nonterminal. (It's just the nonterminal columns of the canonical collection's transition table, copied over.)

πŸ’‘ shift and reduce β€” where have you seen those? In the State chapter, with "if a terminal follows the dot, read more (shift); if the dot is at the end, reduce."
Writing that decision down in advance, as a table, for every state Γ— every input β€” that's the parse table.

The rules for filling it in

Filling in the table is just a matter of copying over the items of the states and the transitions we built in the canonical collection. There are only four rules.

First we number the productions. (reduce needs to point at which one to reduce by.)

   1:  Expr   β†’ Expr '+' Term
   2:  Expr   β†’ Term
   3:  Term   β†’ Term '*' Factor
   4:  Term   β†’ Factor
   5:  Factor β†’ '(' Expr ')'
   6:  Factor β†’ id

Now, for each state Iα΅’ β€”

β‘  shift β€” if there's an item with a terminal a right after the dot and GOTO(Iα΅’, a) = Iβ±Ό, then β†’ ACTION[Iα΅’][a] = sβ±Ό
β‘‘ goto β€” if a nonterminal A follows the dot so that GOTO(Iα΅’, A) = Iβ±Ό, then β†’ GOTO[Iα΅’][A] = j
β‘’ reduce β€” if there's a complete item A β†’ Ξ± β€’ (dot at the end), then β†’ for every terminal x in the FOLLOW of A, set ACTION[Iα΅’][x] = rN (N = that production's number)
β‘£ accept β€” if Accept β†’ Expr β€’ is present, then β†’ ACTION[Iα΅’][$] = acc

β‘ β‘‘ are just copying the transition table over (terminals into ACTION, nonterminals into GOTO); β‘’β‘£ come from complete items.

The heart of β‘’ is the "for every terminal in FOLLOW" part β€” we only reduce when the terminal is "a token that can legitimately come after we reduce by this rule." That's the promise from the State chapter: "reduce only on FOLLOW."
(This way of deciding the reduce cells by FOLLOW is called SLR β€” once we've met conflicts, we'll treat it properly in the SLR chapter.)

Filling it in yourself

Let's apply the rules to a few of our states by hand. (Keep the canonical collection's states and transition table beside you.)

β‘  ACTIONΒ·GOTO for Iβ‚€ β€” no complete items, so only β‘ β‘‘

Iβ‚€ has no complete items, so we only use β‘ β‘‘.

  • Terminals after the dot: '(' (Factor β†’ β€’ '(' Expr ')') and id (Factor β†’ β€’ id). In the transition table we had GOTO(Iβ‚€,'(') = Iβ‚„, GOTO(Iβ‚€,id) = Iβ‚… β†’ '(' = s4, id = s5
  • Nonterminals after the dot: Exprβ†’I₁, Termβ†’Iβ‚‚, Factorβ†’I₃ β†’ GOTO: Expr = 1, Term = 2, Factor = 3

β‘‘ reduce for Iβ‚‚ β€” r2 in the FOLLOW cells of the complete item

Iβ‚‚ has the complete item Expr β†’ Term β€’ (rule 2).

  • FOLLOW of Expr is { $, '+', ')' } β†’ so those three cells get '+' = r2, ')' = r2, $ = r2
  • The Term β†’ Term β€’ '*' Factor sitting alongside it also gives a shift: '*'β†’I₇ β†’ '*' = s7

β‘’ accept for I₁ β€” the $ cell accepts

I₁ has Accept β†’ Expr β€’, so β†’ $ = acc.
(And from Expr β†’ Expr β€’ '+' Term, '+'β†’I₆ β†’ '+' = s6.)

Fill in all 12 states this way β€” and the table is complete.

The finished SLR parse table

The left six columns (id ~ $) are ACTION, the right three columns (ExprΒ·TermΒ·Factor) are GOTO.

State id '+' '*' '(' ')' $ Expr Term Factor
Iβ‚€ s5 s4 1 2 3
I₁ s6 acc
Iβ‚‚ r2 s7 r2 r2
I₃ r4 r4 r4 r4
Iβ‚„ s5 s4 8 2 3
Iβ‚… r6 r6 r6 r6
I₆ s5 s4 9 3
I₇ s5 s4 10
Iβ‚ˆ s6 s11
I₉ r1 s7 r1 r1
I₁₀ r3 r3 r3 r3
I₁₁ r5 r5 r5 r5

How to read the table β€” sβ±Ό = read and go to Iβ±Ό (shift), rN = reduce by rule N, acc = accept, the number in a GOTO cell = the state to go to after reducing a nonterminal.
A blank cell means that input is an error here.

Next

The table is built. But two things are still hanging.

  • What if two actions land in one cell? (That's the conflict seed we planted back in the State chapter.)
  • This way of deciding reduce cells by FOLLOW (SLR) β€” what are its limits, and how can we make it more precise?

πŸ‘‰ The Parse Table Β· What Is a Conflict?


πŸ‘ˆ Previously: the canonical collection