LL(1) Grammar Verification and Recursive Descent Parser Implementation

Consider the grammar $ G(S) $:

  1. $ S \rightarrow AB $
  2. $ A \rightarrow Da \mid \varepsilon $
  3. $ B \rightarrow cC $
  4. $ C \rightarrow aADC \mid \varepsilon $
  5. $ 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:

  1. $ E \rightarrow TE' $
  2. $ E' \rightarrow +TE' \mid \varepsilon $
  3. $ T \rightarrow FT' $
  4. $ T' \rightarrow *FT' \mid \varepsilon $
  5. $ 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.

Tags: compiler parsing LL(1) recursive-descent formal-grammar

Posted on Thu, 27 Aug 2026 16:45:50 +0000 by tofi84