Consider the grammar $ G(S) $:
- $ S \rightarrow AB $
- $ A \rightarrow Da \mid \varepsilon $
- $ B \rightarrow cC $
- $ C \rightarrow aADC \mid \varepsilon $
- $ D \rightarrow b \mid \varepsilon $
To determine if this grammar is LL(1), compute the necessary FIRST, FOLLOW, and SELECT sets:
- $ \text{FIRST}(Da) = {b, a} $
- $ \text{FIRST}(\varepsilon) = {\varepsilon} $
- $ \text{FOLLOW}(A) = {c, b, a, #} $
- $ \text{SELECT}(A \rightarrow Da) = \text{FIRST}(Da) = {b, a} $
- $ \text{SELECT}(A \rightarrow \varepsilon) = (\text{FIRST}(\varepsilon) - {\varepsilon}) \cup \text{FOLLOW}(A) = \text{FOLLOW}(A) = {c, b, a, #} $
Since $ \text{SELECT}(A \rightarrow Da) \cap \text{SELECT}(A \rightarrow \varepsilon) = {b, a} \neq \emptyset $, the grammar is not LL(1).
Now consider the standard expression grammar after left-recursion elimination:
- $ E \rightarrow TE' $
- $ E' \rightarrow +TE' \mid \varepsilon $
- $ T \rightarrow FT' $
- $ T' \rightarrow *FT' \mid \varepsilon $
- $ F \rightarrow (E) \mid i $
Compute the relevant sets:
- $ \text{FIRST}(+TE') = {+} $
- $ \text{FIRST}(FT') = {} $
- $ \text{FIRST}((E)) = {(} $
- $ \text{FIRST}(i) = {i} $
- $ \text{FOLLOW}(E') = {), #} $
- $ \text{FOLLOW}(T') = {+, ), #} $
- $ \text{FOLLOW}(F) = {*, +, ), #} $
Corresponding SELECT sets:
- $ \text{SELECT}(E' \rightarrow +TE') = {+} $
- $ \text{SELECT}(E' \rightarrow \varepsilon) = {), #} $
- $ \text{SELECT}(T' \rightarrow FT') = {} $
- $ \text{SELECT}(T' \rightarrow \varepsilon) = {+, ), #} $
- $ \text{SELECT}(F \rightarrow (E)) = {(} $
- $ \text{SELECT}(F \rightarrow i) = {i} $
All alternative productions for each nonterminal have disjoint SELECT sets. Therefore, this grammar is LL(1).
Given that the grammar is LL(1), a recursive descent parser can be implemented as follows (assuming a global lookahead token and a match(token) function that consumes the current token and advances):
void parseE() {
switch (lookahead) {
case '(':
case 'i':
parseT();
parseEPrime();
break;
default:
fprintf(stderr, "syntax error\n");
exit(1);
}
}
void parseEPrime() {
switch (lookahead) {
case '+':
match('+');
parseT();
parseEPrime();
break;
case ')':
case '#':
break;
default:
fprintf(stderr, "syntax error\n");
exit(1);
}
}
void parseT() {
switch (lookahead) {
case '(':
case 'i':
parseF();
parseTPrime();
break;
default:
fprintf(stderr, "syntax error\n");
exit(1);
}
}
void parseTPrime() {
switch (lookahead) {
case '*':
match('*');
parseF();
parseTPrime();
break;
case '+':
case ')':
case '#':
break;
default:
fprintf(stderr, "syntax error\n");
exit(1);
}
}
void parseF() {
switch (lookahead) {
case '(':
match('(');
parseE();
match(')');
break;
case 'i':
match('i');
break;
default:
fprintf(stderr, "syntax error\n");
exit(1);
}
}
This parser, when integrated with a lexical analyzer that supplies tokens and maintains the lookahead symbol, can validate whether an input string conforms to the expression grammar.