They are typically used in LL parsers (from top to bottom) to check if a working parser has come across in any situation where there is more than one way to continue parsing.
If you have an alternative A | B A | B , and also there is FIRST(A) = {"a"} and FIRST(B) = {"b", "a"} , then you will have a FIRST / FIRST conflict, because when "a" appears at the input , you don’t know whether to extend A or B. (Assuming lookahead is 1).
On the other hand, if you have a Nonterminal that can be null, for example AOpt: ("a")? then you need to make sure that FOLLOW(AOpt) does not contain "a", because otherwise you would not know whether to extend AOpt or not like it here: S: AOpt "a" Either S or AOpt can consume "a" that gives us the FIRST / FOLLOW conflict.
FIRST sets can also be used during the parsing process for performance reasons. If you have a NULLNNN with a null value, you can expand it to find out if it can consume anything, or it can be faster to check if the FIRST(NullableNt) contains the next token, and if you don't just ignore it (reverse tracing against predictive parsing). Another performance improvement would be to provide an additional lexical scanner with the current FIRST set, so the scanner will not use all possible terminals, but only those that are currently permitted by the context. This conflicts with reserved terminals, but this is not always necessary.
There are different types of conflicts in paired analyzers, namely: reduction / decrease and shift / decrease. They also use elements to detect conflicts, not FIRST, FOLLOW.
Your grammar will not work with LL parsers because it contains left recursion. But the FIRST for E, T, and V will be {id} (assuming your T := T*V | T means T := T*V | V ).
source share