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 toIβ." - 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.
- shift β read the next terminal, push it onto the stack, and go to the next state it points to.
- 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.)
π‘
shiftandreduceβ 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 ')') andid(Factor β β’ id). In the transition table we hadGOTO(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
Expris{ $, '+', ')' }β so those three cells get'+'= r2,')'= r2,$= r2 - The
Term β Term β’ '*' Factorsitting 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