Open In App

Recursive Descent Parser

Last Updated : 05 Feb, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

A recursive descent parser is a top-down parser that processes input based on a set of recursive functions, where each function corresponds to a grammar rule. It parses the input from left to right, constructing a parse tree by matching the grammar's production rules. This parser is simple to implement and is suitable for LL(1) grammars, where decisions can be made based on a single lookahead token. While straightforward, recursive descent parsers struggle with left-recursive grammars and may require grammar transformations to handle such cases effectively.

Predictive Parser is a special case of Recursive Descent Parser, where no Back Tracking is required. 
By carefully writing a grammar, means eliminating left recursion and left factoring from it, the resulting grammar will be a grammar that can be parsed by a recursive descent parser.

By carefully writing a grammar means eliminating left recursion and left factoring from it, the resulting grammar will be a grammar that can be parsed by a recursive descent parser.

Example:

Before removing left recursionAfter removing left recursion
E –> E + T | T 
T –> T * F | F 
F –> ( E ) | id
E –> T E’ 
E’ –> + T E’ | e 
T –> F T’ 
T’ –> * F T’ | e 
F –> ( E ) | id

Algorithm for Recursive Descent Parser

S()
{ Choose any S production, S ->X1X2…..Xk;
for (i = 1 to k)
{
If ( Xi is a non-terminal)
Call procedure Xi();
else if ( Xi equals the current input, increment input)
Else /* error has occurred, backtrack and try another possibility */
}
}

Let's understand it better with an example:

The given grammar is:

E  → i E'
E' → + i E' | ε

Function E()

E()
{
if (input == 'i') { // If the input is 'i' (identifier)
input++; // Consume 'i'
}
E'(); // Call E' to check for further expressions
}
  • It checks for i (identifier).
  • If found, it moves the input pointer ahead.
  • Calls E'() to check if a + operation exists.

Function E'()

void E`() {
if (input == '+') {
input++; // Consume the '+'

if (input == 'i') {
input++; // Consume the 'i'
}

E`(); // Recursively process more additions
} else {
return; // If no '+', return (ε production)
}
}
  • It checks for + i.
  • If found, it consumes them and calls E'() recursively.
  • If no +, it returns (ε production).

Main Function

Main()
{
E(); // Start parsing from E
if (input == '$') // If we reach end of input
Parsing Successful;
}
  • Calls E() to start parsing.
  • Checks if the input ends with $, which indicates a successful parse.

Example Input Parsing

Let’s consider the example input:

i + i $

Processing step by step:

  1. E() starts → input == i, so consume i
  2. Call E'() → input == +, so consume +
  3. input == i, so consume i
  4. Call E'() again → no +, so return.
  5. Back to Main(), input == $ → Parsing Successful

Important points about recursive descent parsers

  1. Top-Down Parsing: It starts from the start symbol and recursively applies grammar rules to break down the input.
  2. One Function per Non-Terminal: Each grammar rule has a corresponding function in the parser, making the implementation straightforward.
  3. Uses Recursion: The parser calls functions within themselves to process different parts of the input, matching the recursive nature of grammar rules.
  4. Works Best with LL(1) Grammars: It’s most effective for grammars that can be parsed with a single token lookahead, typically simple, non-left-recursive grammars.
  5. Easy to Implement: The structure is easy to follow and implement, making it a good choice for small compilers or interpreters.
  6. Error Handling: It can detect syntax errors and report them, making it useful for debugging input strings.

Code Implementation of a Recursive Descent Parser

C
#include <stdio.h>
#include <string.h>

#define SUCCESS 1
#define FAILED 0

// Function prototypes
int E(), Edash(), T(), Tdash(), F();

const char *cursor;
char string[64];

int main()
{
    puts("Enter the string");
    scanf("%s", string); // Read input from the user
    cursor = string;
    puts("");
    puts("Input          Action");
    puts("--------------------------------");

    // Call the starting non-terminal E
    if (E() && *cursor == '\0')
    { // If parsing is successful and the cursor has reached the end
        puts("--------------------------------");
        puts("String is successfully parsed");
        return 0;
    }
    else
    {
        puts("--------------------------------");
        puts("Error in parsing String");
        return 1;
    }
}

// Grammar rule: E -> T E'
int E()
{
    printf("%-16s E -> T E'\n", cursor);

    if (T())
    { // Call non-terminal T
        if (Edash())
        { // Call non-terminal E'
            return SUCCESS;
        }
        else
        {
            return FAILED;
        }
    }
    else
    {
        return FAILED;
    }
}

// Grammar rule: E' -> + T E' | $
int Edash()
{
    if (*cursor == '+')
    {
        printf("%-16s E' -> + T E'\n", cursor);
        cursor++;

        if (T())
        { // Call non-terminal T
            if (Edash())
            { // Call non-terminal E'
                return SUCCESS;
            }
            else
            {
                return FAILED;
            }
        }
        else
        {
            return FAILED;
        }
    }
    else
    {
        printf("%-16s E' -> $\n", cursor);
        return SUCCESS;
    }
}

// Grammar rule: T -> F T'
int T()
{
    printf("%-16s T -> F T'\n", cursor);

    if (F())
    { // Call non-terminal F
        if (Tdash())
        { // Call non-terminal T'
            return SUCCESS;
        }
        else
        {
            return FAILED;
        }
    }
    else
    {
        return FAILED;
    }
}

// Grammar rule: T' -> * F T' | $
int Tdash()
{
    if (*cursor == '*')
    {
        printf("%-16s T' -> * F T'\n", cursor);
        cursor++;

        if (F())
        { // Call non-terminal F
            if (Tdash())
            { // Call non-terminal T'
                return SUCCESS;
            }
            else
            {
                return FAILED;
            }
        }
        else
        {
            return FAILED;
        }
    }
    else
    {
        printf("%-16s T' -> $\n", cursor);
        return SUCCESS;
    }
}

// Grammar rule: F -> ( E ) | i
int F()
{
    if (*cursor == '(')
    {
        printf("%-16s F -> ( E )\n", cursor);
        cursor++;

        if (E())
        { // Call non-terminal E
            if (*cursor == ')')
            {
                cursor++;
                return SUCCESS;
            }
            else
            {
                return FAILED;
            }
        }
        else
        {
            return FAILED;
        }
    }
    else if (*cursor == 'i')
    {
        printf("%-16s F -> i\n", cursor);
        cursor++;
        return SUCCESS;
    }
    else
    {
        return FAILED;
    }
}

Next Article
Article Tags :

Similar Reads