FOLLOW ยท Definition and derivation
๐ This is the advanced track ยท theory.
Now that you've finished the FIRST track, here comes its partner, FOLLOW.
If FIRST was about "what does it start with," FOLLOW is about "what comes after it."
On this page we'll look at the definition and at deriving it directly from the definition. Then on โ Computation rules โ Implementation.
๐ Where it lives ยท engine
FirstFollowAnalyzerยท moduleJanglim.FrontEndโ Layer 2 (the tier below the parse table)
Here's the example grammar we keep using.
This time the start symbol matters, so let me mark it โ the Expr at the very top is the start symbol.
Expr : Expr '+' Term | Term ; โ start symbol Term : Term '*' Factor | Factor ; Factor : '(' Expr ')' | id ;
The answer we worked out by hand in the basic track was this.
FOLLOW(Expr) = { $, '+', ')' } FOLLOW(Term) = { $, '+', ')', '*' } FOLLOW(Factor) = { $, '+', ')', '*' }
Let's go through this again, carefully, starting from the definition.
Definition โ what FOLLOW is
Let me pin it down in one line first.
FOLLOW(B) = the set of terminals that can appear immediately after the nonterminal
Bsomewhere in a valid sentence.
(And ifBcan come at the very end of a sentence, we also include$, which stands for the end of input.)
Lining it up with FIRST as a pair: FIRST is the terminal that can come first when you derive that symbol, and FOLLOW is the terminal that can come right after it.
Seeing "right behind" through derivation
Back in the FIRST definition we defined derivation (โ) โ swapping a nonterminal for the right-hand side of a production.
FOLLOW is about deriving outward from the start symbol Expr and watching which terminal ends up attached right behind B.
Expr โ* โฆ B a โฆ โ a came right after B โ a โ FOLLOW(B)
Written tightly in symbols (S = start symbol, T = the set of terminals):
FOLLOW(B) = { a โ T | S โ* โฆ B a โฆ } โช ( { $ } if S โ* โฆ B )
๐ One thing that's crucially different from FIRST.
With FIRST you only had to look at that one symbol, but for FOLLOW you have to scan everywhereBis used across the whole grammar. What comes afterBdiffers at every spot whereBappears.
By the definition โ deriving it directly
Let's apply the definition directly and work out "gather the terminals that come right after B" by hand.
FOLLOW(Expr) โ it comes out cleanly
Expr is the start symbol โ that is, the entire input is one big Expr.
So once you've read that Expr all the way to the end, what comes right after?
The end of input, with nothing left to read.
And the imaginary token that marks the end of input was $.
So in effect $ comes right after Expr โ $ โ FOLLOW(Expr).
Now let's hunt across the whole grammar for what comes right after Expr.
Expr โ Expr '+' Term right after the leading Expr : '+' Factor โ '(' Expr ')' right after Expr : ')'
What can come after Expr is '+' and ')', plus $ at the very end. Gathering them all:
FOLLOW(Expr) = { $, '+', ')' }
FOLLOW(Term) โ when it sits at the end, it inherits
Let's hunt the grammar for what's right after Term.
Term โ Term '*' Factor right after the leading Term : '*' โ add '*' Expr โ Expr '+' Term Term is at the end of the production โฆ โ nothing after it? Expr โ Term Term is at the end of the production โฆ โ again, nothing after
'*' goes straight in.
But the last two lines are curious โ Term sits at the very end of the production, so there's no terminal right behind it.
So then what comes after Term?
Look at Expr โ Term: Term is all of Expr โ that is, Expr and Term end at the exact same spot.
Words are abstract, so let's see it as one scene.
Picture the fragment ( Expr ) (Factor โ '(' Expr ')'). Here ) comes right after Expr.
But if that Expr is just a single Term (Expr โ Term), then the same spot becomes ( Term ).
( Expr ) โ ) after Expr ( Term ) โ now ) after Term (same spot!)
Look โ the ) that used to be after Expr is now sitting right after Term.
Because Term inherited the end-spot of Expr as is.
๐ก This is the core rule of FOLLOW.
When a nonterminal comes at the very end of a production, it inherits the entire FOLLOW of that production's LHS (the left-hand nonterminal).
Here it'sExpr โ Term, so โFOLLOW(Expr)flows straight intoFOLLOW(Term).
FOLLOW(Term) = { '*' } โช FOLLOW(Expr) = { '*' } โช { $, '+', ')' } = { $, '+', ')', '*' }
When ฮต is involved โ with a small grammar
Up to now, the ฮฒ after B has always been a terminal ('+', ')', '*'), so it was simple. But if ฮฒ is a nonterminal that can disappear, one more layer appears. Our expr grammar has no such nonterminal, so here's the grammar we'll use for this section:
S โ A B A โ a | ฮต B โ b | ฮต
A and B can each disappear into ฮต. Look at FOLLOW(A) by the definition: in S โ A B, right after A comes B.
- if
Bdoesn't disappear: the first terminal afterAisB's first terminalb. โb โ FOLLOW(A). (TheฮตinFIRST(B)isn't a terminal, so we drop it and add onlyFIRST(B) โ ฮต.) - if
Bdoes disappear:S โ A B โ A(B is ฮต), soAbecomes the very end ofS, and what's afterAis then what's afterSโ it inheritsFOLLOW(S). SinceFOLLOW(S) = { $ }(start symbol),$ โ FOLLOW(A).
FOLLOW(A) = ( FIRST(B) โ ฮต ) โช FOLLOW(S) = { b } โช { $ } = { b, $ }
ฮฒ (here B) can disappear, which is why $ flowed in too. These two things we just saw โ taking FIRST(ฮฒ) with only ฮต removed and inheriting the LHS's FOLLOW when ฮฒ disappears โ are exactly rules 2 and 3 of the summary below.
Why it doesn't finish in one pass โ the FOLLOW you'd inherit may not be settled yet
As we just saw, when B comes at the very end of a production, you need the FOLLOW of that production's left-hand nonterminal (LHS).
But that LHS's FOLLOW may itself still be filling in. (Exactly the same situation as when recursion blocked us with FIRST.)
So just following the definition step by step won't finish in a single pass.
That's why on the next page we'll โ tidy this process into a handful of rules and solve it, like with FIRST, by iterating until nothing changes.
Summary
Following the definition, FOLLOW ended up being filled in three ways.
- The start symbol's FOLLOW gets
$. (Because it can come at the very end of a sentence.)
$ โ FOLLOW(start symbol) - If something comes right after
B, the first terminal of that something goes into FOLLOW(B).
ForA โ ฮฑ B ฮฒ, addFIRST(ฮฒ) โ ฮตtoFOLLOW(B) - If
Bcomes at the very end of a production, it inherits the FOLLOW of that production's LHS.
ForA โ ฮฑ B, inheritFOLLOW(A)intoFOLLOW(B)
Next โ turning this process into rules
Next up is tidying these three into computation rules that work for any grammar, and solving them by iteration.
๐ FOLLOW ยท Computation rules
๐ Back to the basic concepts: FIRST / FOLLOW