Computer Science Related Others Courses AvailableThe Best Codder.blogspot.com
Posts

Recursive Descent Parser in compiler design

Recursive Descent Parser 

A Recursive Descent Parser is a type of top-down parser used in compiler design that uses a set of mutually recursive procedures to recognize a language by recursively descending through the input, following the production rules of the grammar.

Here are the basic steps involved in implementing a Recursive Descent Parser:


Define a set of parsing functions:

 Each parsing function corresponds to a non-terminal symbol in the grammar. The parsing function takes the input string and attempts to recognize the language by recursively calling other parsing functions for the production rules.


Use a scanner to tokenize the input string:

 Before parsing the input string, it needs to be tokenized into a sequence of tokens. A scanner can be used for this purpose, which reads the input string and converts it into a sequence of tokens based on the language's syntax.


Implement the parsing functions: 

Each parsing function should implement one or more of the production rules for the corresponding non-terminal symbol. The parsing function should first check if the next token in the input matches the expected terminal symbol for the production rule. If it matches, the parsing function should consume the token and recursively call the parsing functions for the production rules of the non-terminal symbols in the production rule. If it doesn't match, the parsing function should generate a syntax error.


Handle ambiguity: 

Ambiguity can occur when there are multiple ways to parse the same input string. In such cases, the parsing functions should be designed to use a predefined order of operations to resolve the ambiguity and choose the correct production rule.


Return the parse tree:

 If the input string is successfully parsed, a parse tree should be returned that represents the structure of the input string based on the grammar rules.


Recursive Descent Parser is simple and easy to implement, but it has some limitations. It can only handle LL(k) grammars and is not suitable for handling left recursion in grammars. However, it is still widely used in many compilers and interpreters, especially for simple and small-scale languages.

 Parsing is the process to determine whether the start symbol can derive the program or not. If the Parsing is successful then the program is a valid program otherwise the program is invalid. 

There are generally two types of Parsers: 

  1. Top-Down Parsers: 
    • In this Parsing technique we expand the start symbol to the whole program.
    • Recursive Descent and LL parsers are the Top-Down parsers.
  2. Bottom-Up Parsers: 
    • In this Parsing technique we reduce the whole program to start symbol.
    • Operator Precedence Parser, LR(0) Parser, SLR Parser, LALR Parser and CLR Parser are the Bottom-Up parsers.

Recursive Descent Parser: 

It is a kind of Top-Down Parser. A top-down parser builds the parse tree from the top to down, starting with the start non-terminal. A 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.

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

**Here e is Epsilon
For Recursive Descent Parser, we are going to write one program for every variable. 
 

Example:
Grammar:

#include <stdio.h>
#include <string.h>
 
#define SUCCESS 1
#define FAILED 0
 
int E(), Edash(), T(), Tdash(), F();
 
const char *cursor;
char string[64];
 
int main()
{
    puts("Enter the string");
    // scanf("%s", string);
    sscanf("i+(i+i)*i", "%s", string);
    cursor = string;
    puts("");
    puts("Input            Action");
    puts("--------------------------------");
 
    if (E() && *cursor == '\0') {
        puts("--------------------------------");
        puts("String is successfully parsed");
        return 0;
    } else {
        puts("--------------------------------");
        puts("Error in parsing String");
        return 1;
    }
}
 
int E()
{
    printf("%-16s E  ->  T E'\n", cursor);
    if (T()) {
        if (Edash())
            return SUCCESS;
        else
            return FAILED;
    } else
        return FAILED;
}
 
int Edash()
{
    if (*cursor == '+') {
        printf("%-16s E' ->  + T E'\n", cursor);
        cursor++;
        if (T()) {
            if (Edash())
                return SUCCESS;
            else
                return FAILED;
        } else
            return FAILED;
    } else {
        printf("%-16s E' ->  $\n", cursor);
        return SUCCESS;
    }
}
 
int T()
{
    printf("%-16s T  ->  F T'\n", cursor);
    if (F()) {
        if (Tdash())
            return SUCCESS;
        else
            return FAILED;
    } else
        return FAILED;
}
 
int Tdash()
{
    if (*cursor == '*') {
        printf("%-16s T' ->  * F T'\n", cursor);
        cursor++;
        if (F()) {
            if (Tdash())
                return SUCCESS;
            else
                return FAILED;
        } else
            return FAILED;
    } else {
        printf("%-16s T' ->  $\n", cursor);
        return SUCCESS;
    }
}
 
int F()
{
    if (*cursor == '(') {
        printf("%-16s F  ->  ( E )\n", cursor);
        cursor++;
        if (E()) {
            if (*cursor == ')') {
                cursor++;
                return SUCCESS;
            } else
                return FAILED;
        } else
            return FAILED;
    } else if (*cursor == 'i') {
        cursor++;
        printf("%-16s F  ->  i\n", cursor);
        return SUCCESS;
    } else
        return FAILED;
}

Post a Comment

© Compiler Design. The Best Codder All rights reserved. Distributed by