0% found this document useful (0 votes)
5 views

CD Lab Manual

The document outlines a Compiler Design Lab with a list of experiments focused on various aspects of lexical analysis and parsing using C and Lex/Yacc tools. It includes detailed descriptions and example programs for identifying tokens, implementing lexical analyzers, simulating lexical analysis, and various parsing techniques such as top-down and recursive descent parsing. Each exercise provides a program structure, descriptions, and expected outputs to facilitate understanding of compiler design concepts.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

CD Lab Manual

The document outlines a Compiler Design Lab with a list of experiments focused on various aspects of lexical analysis and parsing using C and Lex/Yacc tools. It includes detailed descriptions and example programs for identifying tokens, implementing lexical analyzers, simulating lexical analysis, and various parsing techniques such as top-down and recursive descent parsing. Each exercise provides a program structure, descriptions, and expected outputs to facilitate understanding of compiler design concepts.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 48

COMPILER DESIGN LAB

List of Experiments:

1. Write a C program to identify different types of Tokens in a given Program.


2. Write a Lex Program to implement a Lexical Analyzer using Lex tool.
3. Write a C program to Simulate Lexical Analyzer to validating a given input String.
4. Write a C program to implement the Brute force technique of Top down Parsing.
5. Write a C program to implement a Recursive Descent Parser.
6. Write C program to compute the First and Follow Sets for the given Grammar.
7. Write a C program for eliminating the left recursion and left factoring of a given grammar
8. Write a C program to check the validity of input string using Predictive Parser.
9. Write a C program for implementation of LR parsing algorithm to accept a given input string.
10. Write a C program for implementation of a Shift Reduce Parser using Stack Data Structure to
accept a given input string of a given grammar.
11. Simulate the calculator using LEX and YACC tool.
12. Generate YACC specification for a few syntactic categories.
13. Write a C program for generating the three address code of a given expression/statement.
14. Write a C program for implementation of a Code Generation Algorithm of a given
Expression / statement.

EXERCISE – 1:
Write a c program to identify different types of tokens in a given program.

Description:
As it is known that Lexical Analysis is the first phase of compiler also known as scanner. It
converts the input program into a sequence of Tokens.
A C program consists of various tokens and a token is either a keyword, an identifier, a
constant, a string literal, or a symbol.

Example:
Keywords: for, if, include, etc
Identifier: variables, functions, etc
Separators: ‘,’, ‘;’, etc
Operators: ‘-’, ‘=’, ‘++’, etc

PROGRAM:
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

bool isValidDelimiter(char ch)


{
if (ch == ' ' || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == ',' || ch == ';' || ch == '>' || ch ==
'<' || ch == '=' || ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == '{' || ch == '}')
return (true);
return (false);
}

bool isValidOperator(char ch)


{
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '>' || ch == '<' || ch == '=')
return (true);
return (false);
}

bool isvalidIdentifier(char* str)


{
if (str[0] == '0' || str[0] == '1' || str[0] == '2' || str[0] == '3' || str[0] == '4' || str[0] == '5' ||
str[0] == '6' || str[0] == '7' || str[0] == '8' || str[0] == '9' || isValidDelimiter(str[0]) == true)
return (false);
return (true);
}

bool isValidKeyword(char* str)


{
if (!strcmp(str, "if") || !strcmp(str, "else") || !strcmp(str, "while") || !strcmp(str, "do")
|| !strcmp(str, "break") || !strcmp(str, "continue") || !strcmp(str, "int") || !strcmp(str, "double")
|| !strcmp(str, "float") || !strcmp(str, "return") || !strcmp(str, "char") || !strcmp(str, "case") ||
!strcmp(str, "char") || !strcmp(str, "sizeof") || !strcmp(str, "long") || !strcmp(str, "short") ||
!strcmp(str, "typedef") || !strcmp(str, "switch") || !strcmp(str, "unsigned") || !strcmp(str, "void")
|| !strcmp(str, "static") || !strcmp(str, "struct") || !strcmp(str, "goto"))
return (true);
return (false);
}

bool isValidInteger(char* str)


{
int i, len = strlen(str);
if (len == 0)
return (false);
for (i = 0; i < len; i++) {
if (str[i] != '0' && str[i] != '1' && str[i] != '2'&& str[i] != '3' && str[i] != '4' && str[i] != '5'
&& str[i] != '6' && str[i] != '7' && str[i] != '8' && str[i] != '9' || (str[i] == '-' && i > 0))
return (false);
}
return (true);
}

bool isRealNumber(char* str)


{
int i, len = strlen(str);
bool hasDecimal = false;
if (len == 0)
return (false);
for (i = 0; i < len; i++)
{
if (str[i] != '0' && str[i] != '1' && str[i] != '2' && str[i] != '3' && str[i] != '4' && str[i] != '5'
&& str[i] != '6' && str[i] != '7' && str[i] != '8' && str[i] != '9' && str[i] != '.' || (str[i] == '-'
&& i > 0))
return (false);
if (str[i] == '.')
hasDecimal = true;
}
return (hasDecimal);
}

char* subString(char* str, int left, int right)


{
int i;
char* subStr = (char*)malloc( sizeof(char) * (right - left + 2));
for (i = left; i <= right; i++)
subStr[i - left] = str[i];
subStr[right - left + 1] = '\0';
return (subStr);
}

void detectTokens(char* str)


{
int left = 0, right = 0;
int length = strlen(str);
while (right <= length && left <= right)
{
if (isValidDelimiter(str[right]) == false)
right++;
if (isValidDelimiter(str[right]) == true && left == right)
{
if (isValidOperator(str[right]) == true)
printf("Valid operator : '%c'", str[right]);
right++;
left = right;
}
else if (isValidDelimiter(str[right]) == true && left != right || (right == length && left !=
right))
{
char* subStr = subString(str, left, right - 1);
if (isValidKeyword(subStr) == true)
printf("Valid keyword : '%s'", subStr);
else if (isValidInteger(subStr) == true)
printf("Valid Integer : '%s'", subStr);
else if (isRealNumber(subStr) == true)
printf("Real Number : '%s'", subStr);
else if (isvalidIdentifier(subStr) == true && isValidDelimiter(str[right - 1]) == false)
printf("Valid Identifier : '%s'", subStr);
else if (isvalidIdentifier(subStr) == false && isValidDelimiter(str[right - 1]) == false)
printf("Invalid Identifier : '%s'", subStr);
left = right;
}
}
return;
}
int main(){
char str[100] = "float x = a + 1b; ";
printf("The Program is : '%s' ", str);
printf("All Tokens are : ");
detectTokens(str);
return (0);
}

OUTPUT:

EXERCISE-2:
Write a Lex Program to implement a Lexical Analyzer using Lex tool.

DESCRIPTION:
 LEX is a lexical analyzer generator
 LEX is commonly used with the YACC parser generator.
 LEX is a Unix utility which generates the lexical analyzer
 LEX reads an input stream specifying the lexical analyzer and outputs source code
implementing the lexer in the C programming language.
 LEX specification file can be created with an extension .l EX x.l.This is given to LEX
compiler to produce lex.yy.c
 The lex.yy.c is a C program which is actually a lexical analyzer program. It consists of
the tabular representations of the transition diagrams constructed for the regular
expressions of specification file say x.l
 Finally the C compiler compiles this generated lex.yy.c and produces an object program
a.out.
 When some input stream is given to a.out then sequence of tokens get generated.
 LEX program consists of three parts that are represented as follows
%{
Declaration Section
%}
%%
Rule Section
%%
Auxiliary Procedures Section

 The following Commands are used to run the lex program x.l
$ lex x.l
$ cc lex.yy.c -ll
$ .\a.out

PROGRAM:
/* Program to implement lexical Analyzer using Lex tool*/
%{
int key=0;op=0;
%}
LETTER [a-z A-Z]
DIGIT [0-9]
%%
(begin|if|else|for|when|then) {key++;}
[-+/*<>=] {op++;}
(<=|>=|!=) {op++;}
%%
main()
{
yylex();
printf("\n no of keywords:%d",key);
printf("\n no of operators:%d",op);
}
OUTPUT:
[stu@bvcits ~]$ vi prog1.l
[stu@bvcits ~]$ lex prog1.l
[stu@bvcits ~]$ cc lex.yy.c -ll
[stu@bvcits ~]$ cat>file1
else
if
for
*
+
^z
[1]+ Stopped cat > file1
[stu@bvcits ~]$ ./a.out<file1
no of keywords:3
no of operators:2

EXERCISE – 3:
Write a C program to Simulate Lexical Analyzer to validating a given input String

DESCRIPTION:
 LEX is a lexical analyzer generator
 LEX is commonly used with the YACC parser generator.
 LEX is a Unix utility which generates the lexical analyzer
 LEX reads an input stream specifying the lexical analyzer and outputs source code
implementing the lexer in the C programming language.
 LEX specification file can be created with an extension .l EX x.l.This is given to LEX
compiler to produce lex.yy.c
 The lex.yy.c is a C program which is actually a lexical analyzer program. It consists of
the tabular representations of the transition diagrams constructed for the regular
expressions of specification file say x.l
 Finally the C compiler compiles this generated lex.yy.c and produces an object program
a.out.
 When some input stream is given to a.out then sequence of tokens get generated.
 LEX program consists of three parts that are represented as follows
%{
Declaration Section
%}
%%
Rule Section
%%
Auxiliary Procedures Section

 The following Commands are used to run the lex program x.l
$ lex x.l
$ cc lex.yy.c -ll
$ .\a.out

PROGRAM:
/* Program to implement Lexical Analyzer using Lextool*/
%{
int key=0;op=0;
%}
LETTER [a-z A-Z]
DIGIT [0-9]
%%
begin {printf("\n keyword:%s",yytext);}
if {printf("\n keyword:%s",yytext);}
else {printf("\n keyword:%s",yytext);}
for {printf("\n keyword:%s",yytext);}
while {printf("\n keyword:%s",yytext);}
do {printf("\n keyword:%s",yytext);}
"+" {printf("\n operator:%s",yytext);}
"*" {printf("\n operator:%s",yytext);}
"<" {printf("\n operator:%s",yytext);}
">" {printf("\n operator:%s",yytext);}
"<=" {printf("\n operator:%s",yytext);}
">=" {printf("\n operator:%s",yytext);}
{LETTER}({LETTER}|{DIGIT})* {printf("\n identifier:%s",yytext);}
{DIGIT} {printf("\n number:%s",yytext);}
%%
main()
{
yylex();
}
OUTPUT:
[stu@bvcits ~]$ vi prog.l
[stu@bvcits ~]$ lex prog.l
[stu@bvcits ~]$ cc lex.yy.c -ll
[stu@bvcits ~]$ cat>file1
while
do
for
else
*
+
9
2
7
^Z
[2]+ Stopped cat > file1
[stu@bvcits ~]$ ./a.out<file1
keyword:while
keyword:do
keyword:for
keyword:else
operator:*
operator:+
number:9
number:2
number:7
EXERCISE – 4:
Write a C program to implement Brute force technique of Top down parsing.

DESCRIPTION:
PARSING:
 Parsing is the process of determining how a string of terminals can be generated by a
grammar
TOP-DOWN PARSING:
 Top-down parsing can be viewed as an attempt to find a left most derivation for the input
starting from the root and creating nodes of a parse tree in preorder
 Let us consider an example ScAd, Aab/a
 For input W=cad to construct the parse tree for this sentence top down parser ,initially
create a tree consisting of a single node labeled “S”
 An input pointer points to c, the first symbol of “W” Scad
 Otherwise by replacing ab we represent it by Scabd
 Otherwise we replace “A” by any other symbol like “d” that is Scdd is an invalid
string.

PROGRAM:
/*Program to implement Top down parsing*/

#include<stdio.h>
#include<string.h>
int A();
void disp();
void error();
char s[20];
int i;
void main()
{
printf("s->cAd \n");
printf("A->ab/a \n");
printf("Enter the String \n");
scanf("%s",&s);
i=0;
if(s[i++]=='c' && A())
{
` if(s[++i]=='d' && s[++i]==NULL)
disp();
else
error();
}
}
int A()
{
if(s[i++]=='a' && s[i]=='b')
return(1);
else if(s[--i]=='a')
return(1);
else
return(0);
}
void disp()
{
printf("\n String is Valid \n");
}
void error()
{
printf("\n String is Invalid \n");
}

OUTPUT:

ubuntu@ubuntu:~/$ cd cd
ubuntu@ubuntu:~/cd$ cc tdp.c
ubuntu@ubuntu:~/cd$ ./a.out
s->cAd
A->ab/a
Enter the String
cad
String is Valid

ubuntu@ubuntu:~/cd$ ./a.out
s->cAd
A->ab/a
Enter the String
cbd
String is Invalid

EXERCISE – 5:
Write a C program to implement Recursive-Descent parsing
DESCRIPTION:
PARSING:
 Parsing is the process of determining how a string of terminals can be generated
by a grammar
RECURSIVE-DESCENT PARSING:
 A parser that uses collection of recursive procedures for parsing the given input
string is called “Recursive Descent Parser.
 This is a top-down process in which the parser attempts to verify that the syntax
of the input stream is correct as it is read from left to right.
 A basic operation necessary for this involves reading characters from the input
stream and matching then with terminals from the grammar that describes the
syntax of the input
 Basic steps for construction. The RHS of the rule is directly converted into
program code symbol by symbol
 The RHS of the rule is directly converted into program code symbol by symbol
 If the input symbol is terminal then it is matched with look ahead from input .The
look ahead pointer has to be advanced on matching of the input symbol.
 If the production rule has many alternatives then all these alternates has to be
combined into a single body of procedure
 Let us take an example of grammar
ETE1
E1TE1/#
TFT1
T1*FT1/#
F(E)/d
 When we enter the string as d+d*d it is a valid string.
 When we enter the string as ddd it is a invalid string.

PROGRAM:
/*Program to implement Recursive descent parsing*/

#include<stdio.h>
int n,i=0;
char s[20];
void E(void);
void E1(void);
void T(void);
void T1(void);
void F(void);
void error(void);
void main()
{
printf("\n Given Grammar is \n");
printf("E->TE1\n");
printf("E1->TE1/# \n");
printf("T->FT1 \n");
printf("T1->*FT1/# \n");
printf("F->(E)/d \n");
printf("Enter the string you want to parse");
scanf("%s",&s);
E();
if(s[i]==NULL)
printf("String is Valid \n");
else
printf("String is Invalid \n");
}

void E()
{
T();
E1();
}

void T()
{
F();
T1();
}

void E1()
{
if(s[i]=='+')
{
i++;
T();
E1();
}
}
void T1()
{
if(s[i]=='*')
{
i++;
F();
T1();
}
}

void F()
{
if(s[i]=='(')
{
i++;
E();
if(s[i]==')')
i++;
else
error();
}
else if(s[i]=='d')
i++;
else
error();
}

void error()
{
printf("string is invalid \n");
}

OUTPUT:
Given Grammar is
E->TE1
E1->TE1/#
T->FT1
T1->*FT1/#
F->(E)/d
Enter the string you want to parse(d)
String is Valid

Given Grammar is
E->TE1
E1->TE1/#
T->FT1
T1->*FT1/#
F->(E)/d
Enter the string you want to parse(id)*(id)
String is Invalid
EXERCISE – 6:
Write a C program to compute the First and Follow sets for the given grammar

DESCRIPTION:
 The functions follow and followfirst() are both involved in the calculation of the Follow
Set of a given Non-Terminal. The follow set of the start symbol will always contain
“$”. Now the calculation of Follow falls under three broad cases
 If a Non-Terminal on the R.H.S. of any production is followed immediately by a
Terminal then it can immediately be included in the Follow set of that Non-Terminal.
 If a Non-Terminal on the R.H.S. of any production is followed immediately by a Non-
Terminal, then the First Set of that new Non-Terminal gets included on the follow set of
our original Non-Terminal. In case encountered an epsilon i.e. ” # ” then, move on to
the next symbol in the production.
Note: “#” is never included in the Follow set of any Non-Terminal.
 If reached the end of a production while calculating follow, then the Follow set of that
non-terminal will include the Follow set of the Non-Terminal on the L.H.S. of that
production. This can easily be implemented by recursion.

PROGRAM:
#include<stdio.h>
#include<ctype.h>
#include<string.h>
void followfirst(char, int, int);
void follow(char c);
void findfirst(char, int, int);
int count, n = 0;
char calc_first[10][100];
char calc_follow[10][100];
int m = 0;
char production[10][10];
char f[10], first[10];
int k;
char ck;
int e;

int main(int argc, char **argv)


{
int jm = 0;
int km = 0;
int i, choice;
char c, ch;
count = 8;
strcpy(production[0], "E=TR");
strcpy(production[1], "R=+TR");
strcpy(production[2], "R=#");
strcpy(production[3], "T=FY");
strcpy(production[4], "Y=*FY");
strcpy(production[5], "Y=#");
strcpy(production[6], "F=(E)");
strcpy(production[7], "F=i");
int kay;
char done[count];
int ptr = -1;
for(k = 0; k < count; k++)
{
for(kay = 0; kay < 100; kay++)
{
calc_first[k][kay] = '!';
}
}
int point1 = 0, point2, xxx;
for(k = 0; k < count; k++)
{
c = production[k][0];
point2 = 0;
xxx = 0;
for(kay = 0; kay <= ptr; kay++)
if(c == done[kay])
xxx = 1;
if (xxx == 1)
continue;
findfirst(c, 0, 0);
ptr += 1;
done[ptr] = c;
printf("\n First(%c) = { ", c);
calc_first[point1][point2++] = c;
for(i = 0 + jm; i < n; i++)
{
int lark = 0, chk = 0;
for(lark = 0; lark < point2; lark++)
{
if (first[i] == calc_first[point1][lark])
{
chk = 1;
break;
}
}
if(chk == 0)
{
printf("%c, ", first[i]);
calc_first[point1][point2++] = first[i];
}
}
printf("}\n");
jm = n;
point1++;
}
printf("\n");
printf("-----------------------------------------------\n\n");
char donee[count];
ptr = -1;
for(k = 0; k < count; k++)
{
for(kay = 0; kay < 100; kay++)
{
calc_follow[k][kay] = '!';
}
}
point1 = 0;
int land = 0;
for(e = 0; e < count; e++)
{
ck = production[e][0];
point2 = 0;
xxx = 0;
for(kay = 0; kay <= ptr; kay++)
if(ck == donee[kay])
xxx = 1;

if (xxx == 1)
continue;
land += 1;
follow(ck);
ptr += 1;
donee[ptr] = ck;
printf(" Follow(%c) = { ", ck);
calc_follow[point1][point2++] = ck;
for(i = 0 + km; i < m; i++)
{
int lark = 0, chk = 0;
for(lark = 0; lark < point2; lark++)
{
if (f[i] == calc_follow[point1][lark])
{
chk = 1;
break;
}
}
if(chk == 0)
{
printf("%c, ", f[i]);
calc_follow[point1][point2++] = f[i];
}
}
printf(" }\n\n");
km = m;
point1++;
}
}

void follow(char c)
{
int i, j;
if(production[0][0] == c)
{
f[m++] = '$';
}
for(i = 0; i < 10; i++)
{
for(j = 2;j < 10; j++)
{
if(production[i][j] == c)
{
if(production[i][j+1] != '\0')
{
followfirst(production[i][j+1], i, (j+2));
}
if(production[i][j+1]=='\0' && c!=production[i][0])
{
follow(production[i][0]);
}
}
}
}
}
void findfirst(char c, int q1, int q2)
{
int j;
if(!(isupper(c)))
{
first[n++] = c;
}
for(j = 0; j < count; j++)
{
if(production[j][0] == c)
{
if(production[j][2] == '#')
{
if(production[q1][q2] == '\0')
first[n++] = '#';
else if(production[q1][q2] != '\0' && (q1 != 0 || q2 != 0))
{
findfirst(production[q1][q2], q1, (q2+1));
}
else
first[n++] = '#';
}
else if(!isupper(production[j][2]))
{
first[n++] = production[j][2];
}
else
{
findfirst(production[j][2], j, 3);
}
}
}
}

void followfirst(char c, int c1, int c2)


{
int k;
if(!(isupper(c)))
f[m++] = c;
else
{
int i = 0, j = 1;
for(i = 0; i < count; i++)
{
if(calc_first[i][0] == c)
break;
}
while(calc_first[i][j] != '!')
{
if(calc_first[i][j] != '#')
{
f[m++] = calc_first[i][j];
}
else
{
if(production[c1][c2] == '\0')
{
follow(production[c1][0]);
}
else
{
followfirst(production[c1][c2], c1, c2+1);
}
}
j++;
}
}
}

OUTPUT :

First(E)= { (, i, }
First(R)= { +, #, }
First(T)= { (, i, }
First(Y)= { *, #, }
First(F)= { (, i, }

-----------------------------------------------

Follow(E) = { $, ), }
Follow(R) = { $, ), }
Follow(T) = { +, $, ), }
Follow(Y) = { +, $, ), }
Follow(F) = { *, +, $, ), }

EXERCISE -7:
Write a C program for eliminating the Left Recursion and Left Factoring of a given
grammar.

DESCRIPTION:

LEFT FACTORING:
 Left factoring is used to convert a left-factored grammar into an equivalent grammar to
remove the uncertainty for the top-down parser. In left factoring, we separate the
common prefixes from the production rule.
 The following algorithm is used to perform left factoring in the grammar-

A ⇒ αβ1 | αβ2 | αβ3 | …… | αβn | γ


 Suppose the grammar is in the form:

 Where A is a non-terminal and α is the common prefix.


 We will separate those productions with a common prefix and then add a new production
rule in which the new non-terminal we introduced will derive those productions with a

A ⇒ αA`
common prefix.

A` ⇒ β1 | β2 | β3 | …… | βn

 The top-down parser can easily parse this grammar to derive a given string. So this is
how left factoring in compiler design is performed on a given grammar.
PROGRAM:
#include<stdio.h>
#include<string.h>
int main()
{
char gram[20],part1[20],part2[20],modifiedGram[20],newGram[20],tempGram[20];
int i,j=0,k=0,l=0,pos;
printf("Enter Production : A->");
gets(gram);
for(i=0;gram[i]!='|';i++,j++)
part1[j]=gram[i];
part1[j]='\0';
for(j=++i,i=0;gram[j]!='\0';j++,i++)
part2[i]=gram[j];
part2[i]='\0';
for(i=0;i<strlen(part1)||i<strlen(part2);i++)
{
if(part1[i]==part2[i])
{
modifiedGram[k]=part1[i];
k++;
pos=i+1;
}
}
for(i=pos,j=0;part1[i]!='\0';i++,j++){
newGram[j]=part1[i];
}
newGram[j++]='|';
for(i=pos;part2[i]!='\0';i++,j++){
newGram[j]=part2[i];
}
modifiedGram[k]='X';
modifiedGram[++k]='\0';
newGram[j]='\0';
printf("\n A->%s",modifiedGram);
printf("\n X->%s\n",newGram);
}

OUTPUT:
Enter Production : A->aE+bcD|aE+eIT
A->aE+X
X-> bcD| eIT

LEFT RECURSION:
 A Grammar G (V, T, P, S) is left recursive if it has a production in the form.
A → A α |β.
 The above Grammar is left recursive because the left of production is occurring at a first
position on the right side of production. It can eliminate left recursion by replacing a pair
of production with
A → βA′
A → αA′|ϵ
Elimination of Left Recursion
 Left Recursion can be eliminated by introducing new non-terminal A such that.

PROGRAM:
#include<stdio.h>
#include<string.h>
void main()
{
char input[100],l[50],r[50],temp[10],tempprod[20],productions[25][50];
int i=0,j=0,flag=0,consumed=0;
printf("Enter the productions: ");
scanf("%1s->%s",l,r);
printf("%s",r);
while(sscanf(r+consumed,"%[^|]s",temp) == 1 && consumed <= strlen(r))
{
if(temp[0] == l[0])
{
flag = 1;
sprintf(productions[i++],"%s->%s%s'\0",l,temp+1,l);
}
else
sprintf(productions[i++],"%s'->%s%s'\0",l,temp,l);
consumed += strlen(temp)+1;
}
if(flag == 1)
{
sprintf(productions[i++],"%s->ε\0",l);
printf("The productions after eliminating Left Recursion are:\n");
for(j=0;j<i;j++)
printf("%s\n",productions[j]);
}
else
printf("The Given Grammar has no Left Recursion");
}

OUTPUT:
Enter the productions: E->E+E|T
The productions after eliminating Left Recursion are:
E->+EE'
E'->TE'
E->ε

EXERCISE – 8:
Write a C Program to check the validity of input String using Predictive Parser

DESCRIPTION:
PARSING:
 Parsing is the process of determining how a string of terminals can be generated by a
grammar
LL(1) PARSING:
 An LL parser is a top-down parser for a subset of the context-free grammars. It parses
the input from left to right, and constructs a left most derivation of the sentence
 For LL(1) – The first Lmeans the input is scaned from left to right, The second L means
it uses left most derivation for input string. And the number 1 in the input symbol means
it uses only one input symbol to predict the parsing process.
 The class of grammars which are parse table in this way is known as the LL grammars
 Am LL parser is called an LL(k) parser if it uses k tokens of look ahead when parsing a
sentence.
 If such a parser exists for a certain grammar and it can parse sentences of this grammar
with out back tracking then it is called as LL(k) grammar.
 A language that has an LL(k) grammar is known as an LL(k) language.
 Procedure for constructing LL(1)Parser
1. Computation of FIRST,FOLLOW Functions
2. Construct the Predictive Parsing Table Using FIRST ,FOLLOW functions
3. Parse the input string with the help of Predictive Parsing Table.

PROGRAM:

#include<stdio.h>
#include<string.h>
int i,j,k,r,l,c;
char ch;
char prod[8][7]={"E->TA","A->+TA","A->@","T->FB","B->*FB","B->@","F->(E)","F->i"};
char first[8][8]={"i","+","@","i","*","@","(","i"};
char follow[8][5]={"$)","+)$","@$","*)$","@)$",")$","$)"};
char table[6][7][6];
main()
{
char trow[6]={'i','+','*','(','$',')'};
char tcol[5]={'E','A','T','B','F'};
printf("--------------------\n");
for(j=0;j<6;j+)
{
printf("\t %c",trow[j]);
}
printf("\n");
printf("--------------------\n");
for(i=0;i<8;i++)
{
if(strcmp(first[i],"@")==0)
k=strlen(follow[i]);
else
k=strlen(first[i]);
for(j=0;j<k;j++)
{
if(strcmp(first[i],"@")==0)
ch=follow[i][j];
else
ch=first[i][j];
r=row();
c=col();
strcpy(table[r][c],prod[i]);
}
}
for(i=0;i<5;i++)
{
printf("%c \t",tcol[i]);
for(j=0;j<6;j++)
{
printf("%s \t",table[i][j]);
}
printf("\n");
}
printf("----------\n");
}
row()
{
switch(prod[i][0])
{
case 'E':return 0;
case 'A':return 1;
case 'T':return 2;
case 'B':return 3;
case 'F':return 4;
}
}

col()
{
switch(ch)
{
case 'i':return 0;
case '+':return 1;
case '*':return 2;
case '(':return 3;
case ')':return 4;
case '$':return 5;
}
}

OUTPUT:
ubuntu@ubuntu:~/$ cd cd
ubuntu@ubuntu:~/cd$ cc ll.c
ubuntu@ubuntu:~/cd$ ./a.out

------------------------------------------------------------------------------------------------------------
i + * ( $ )
------------------------------------------------------------------------------------------------------------
E E->TA
A A->+TA A->@
T T->FB
B B->*FB B->@ B->@
F F->i F->(E)
-----------------------------------------------------------------------------------------------------------

EXERCISE – 9:
Write a C program to implement LR parsing algorithm to accept a given input string

DECRIPTION:

 Get the input expression and store it in the input buffer.


 Read the data from the input buffer one at the time and convert in to corresponding Non
Terminal using production rules available.
 Perform push & pop operation for LR parsing table construction.
 Display the result with conversion of corresponding input symbols to production and
production reduction to start symbol. No operation performed on the operator.

PROGRAM:
#include<stdio.h>
#include<string.h>
char stack[30];
int top=-1;
void push(char c)
{
top++;
stack[top]=c;
}
char pop()
{
char c;
if(top!=-1)
{
c=stack[top];
top--;
return c;
}
return 'x';
}

void printstat()
{
int i;

printf("\n\t\t\t $");
for(i=0;i<=top;i++)
printf("%c",stack[i]);
}

void main()
{
int i,j,k,l;
char s1[20],s2[20],ch1,ch2,ch3;
clrscr();
printf("\n\n\t\t LR PARSING");
printf("\n\t\t ENTER THE EXPRESSION");
scanf("%s",s1);
l=strlen(s1);
j=0;
printf("\n\t\t $");
for(i=0;i<l;i++)
{
if(s1[i]=='i' && s1[i+1]=='d')
{
s1[i]=' ';
s1[i+1]='E';
printstat();
printf("id");
push('E');
printstat();
}
else if(s1[i]=='+'||s1[i]=='-'||s1[i]=='*' ||s1[i]=='/' ||s1[i]=='d')
{
push(s1[i]);
printstat();
}
}
printstat();
l=strlen(s2);
while(l)
{
ch1=pop();
if(ch1=='x')
{
printf("\n\t\t\t $");
break;
}
if(ch1=='+'||ch1=='/'||ch1=='*'||ch1=='-')
{
ch3=pop();
if(ch3!='E')
{
printf("errror");
exit();
}
else
{
push('E');
printstat();
}
}
ch2=ch1;
}
getch();
}

OUTPUT:
LR PARSING
ENTER THE EXPRESSION
id+id*id-id
$
$id
$E
$E+
$E+id
$E+E
$E+E*
$E+E*id
$E+E*E
$E+E*E-
$E+E*E-id
$E+E*E-E
$E+E*E-E
$E+E*E
$E
$
EXERCISE – 10:
Write a C program for implementation of a Shift Reduce Parser using Stack Data
Structure to accept a given input string of a given grammar.

DESCRIPTION:
PARSING:
 Parsing is the process of determining how a string of terminals can be generated
by a grammar
SHIFT REDUCE PARSING:
 A Shift-Reduce parser uses a parse stack which contains grammar symbols.
 During the operation of the parser , symbols from the input are shifted on to the
stack.
 If a prefix of the symbols on top of the stack matches the RHS of a grammar rule
which is the correct rule to use with in the current context, then the parser reduces
the RHS of the rule to its LHS, replacing the RHS symbols on top of the stack
with non terminal occurring on the LHS of the rule.
 This shift reduce process continues until the parser terminates reporting either
success or failure
 It terminates with success when the input is legal and is accepted by the parser. it
terminates with failure if an error is detected in the input
 The operation of the parser is controlled by a couple of tables:
ACTION table:-
 The action table is a table with rows indexed by states and columns indexed by
terminal symbols. when the parser is in some state s and the current look ahead
terminal is t, the action taken by the parser depends on the contents of action[s]
[t],which contain 4 different kinds of entries:
 Shift‘s’: Shift state‘s’ on to the parse stack
 Reduce ‘r’: Reduce by rule ‘r’. This is explained in more detail below
 Accept: Terminate the parse with success, accepting the input.
 Error: Signal a parse error
GOTO Table:
 The goto table is a table with rows indexed by states and columns indexed by non
terminal symbols. when the parser is in state s immediately after reducing by rule
N, then next state to enter is given by goto[s][N].

PROGRAM:
/*Program to implement Shift reduce parsing*/
#include<stdio.h>
#include<string.h>
void error();
void shift(char);
void reduce(char);
int top=0,k;
char stk[10];
char str[10];
int i,p;
void main()
{
char a[6][10]={"E+E","E*E","E-E","E/E","i"};
printf("Enter the string");
printf("\n E->E+E/E*E/E-E/E/E/i \n");
scanf("%s",str);
strcat(str,"$");
stk[top++]='$';
printf("\n stack input maintaining \n");
printf("-----------------------------");
do
{
printf("\n %s \t",stk);
for(i=p;str[i]!='\0';i++)
printf("%c",str[i]);
switch(str[p])
{
case '+': shift(str[p]);
break;
case '-': shift(str[p]);
break;
case '*': shift(str[p]);
break;
case '/': shift(str[p]);
break;
case 'i': shift(str[p]);
reduce('E');
break;
case '$': if(str[p+1]==NULL)
break;
else
error();
default: error();
}
}
while(str[p++]!='$');
if(stk[top-1]=='E' &&stk[top-2]=='E' && stk[--p]=='$')
error();
if(stk[--top]=='E' && str[--p]=='$')
{
printf("\n $%c\t",stk[top]);
for(i=p;str[i]!='\0';i++)
printf("%c",str[i]);
printf("\n string is valid \n");
}
else
error();
}

void shift(char ch)


{
stk[top++]=ch;
if(ch=='c')
{
printf("\n %s\t",stk);
for(i=str[p+1];str[i]!='\0';i++)
printf("%c",str[i]);
}
}

void reduce(char ch)


{
stk[--top]=ch;
top++;
}

void error()
{
printf("\n string is invalid \n");
}

OUTPUT:
ubuntu@ubuntu:~/$ cd cd
ubuntu@ubuntu:~/cd$ cc srp.c
ubuntu@ubuntu:~/cd$ ./a.out
Enter the string
E->E+E/E*E/E-E/E/E/i
i+i
stack input maintaining
-----------------------------------------
$ i+i$
$E +i$
$E+ i$
$E+E $
$E $
string is valid

ubuntu@ubuntu:~/cd$ ./a.out
Enter the string
E->E+E/E*E/E-E/E/E/i
e

stack input maintaining


------------------------------------------
$ e$
string is invalid
Exercise – 11:
Simulate the calculator using LEX and YACC tool.

DESCRIPTION:
A YACC source program has three parts as follows:
Declarations
%%
translation rules
%%
supporting C routines
Declarations Section: This section contains entries that:
i. Include standard I/O header file.
ii. Define global variables.
iii. Define the list rule as the place to start processing.
iv. Define the tokens used by the parser. v. Define the operators and their precedence.
Rules Section: The rules section defines the rules that parse the input stream. Each rule of a
grammar production and the associated semantic action.

Programs Section: The programs section contains the following subroutines. Because these
subroutines are included in this file, it is not necessary to use the yacc library when processing
this file.

Main- The required main program that calls the yyparse subroutine to start the program.

yyerror(s) -This error-handling subroutine only prints a syntax error message.

yywrap -The wrap-up subroutine that returns a value of 1 when the end of input occurs. The
calc.lex file contains include statements for standard input and output, as programmar file
information if we use the -d flag with the yacc command. The y.tab.h file contains definitions for
the tokens that the parser program uses.
calc.lex contains the rules to generate these tokens from the input stream.

PROGRAM:
//Implementation of calculator using LEX and YACC
LEX PART:
%{
#include<stdio.h>
#include "y.tab.h"
extern int yylval;
%}
%%
[0-9]+ { yylval=atoi(yytext);
return NUMBER;
}

[\t] ;
[\n] return 0;
. return yytext[0];

%%

int yywrap()
{
return 1;
}

YACC PART:
%{
#include<stdio.h>
int flag=0;
%}
%token NUMBER
%left '+' '-'
%left '*' '/' '%'
%left '(' ')'
%%
ArithmeticExpression: E{ printf("\nResult=%d\n",$$);
return 0;
};
E: E'+'E {$$=$1+$3;}
|E'-'E {$$=$1-$3;}
|E'*'E {$$=$1*$3;}
|E'/'E {$$=$1/$3;}
|E'%'E {$$=$1%$3;}
|'('E')' {$$=$2;}
| NUMBER {$$=$1;}
;
%%

void main()
{

printf("\nEnter Any Arithmetic Expression which can have operations Addition,


Subtraction, Multiplication, Divison, Modulus and Round brackets:\n");
yyparse();
if(flag==0)
printf("\nEntered arithmetic expression is Valid\n\n");
}

void yyerror()
{
printf("\nEntered arithmetic expression is Invalid\n\n");
flag=1;
}

OUTPUT:

1. Enter Any Arithmetic Expression which can have operations Addition, Subtraction,
Multiplication, Divison, Modulus and Round brackets:
5*6(1+2)
Result = 90
Entered arithmetic expression is Valid

2. Enter Any Arithmetic Expression which can have operations Addition, Subtraction,
Multiplication, Divison, Modulus and Round brackets:
5*6(1+2
Entered arithmetic expression is Invalid
EXERCISE – 12:
Generate YACC specification for a few syntactic categories.

DESCRIPTION:
A YACC source program has three parts as follows:
Declarations
%%
translation rules
%%
supporting C routines
Declarations Section: This section contains entries that:
i. Include standard I/O header file.
ii. Define global variables.
iii. Define the list rule as the place to start processing.
iv. Define the tokens used by the parser. v. Define the operators and their precedence.
Rules Section: The rules section defines the rules that parse the input stream. Each rule of a
grammar production and the associated semantic action.

Programs Section: The programs section contains the following subroutines. Because these
subroutines are included in this file, it is not necessary to use the yacc library when processing
this file.

PROGRAM:

a) Program to recognize a valid arithmetic expression that uses operator +, - , * and /.

%{
#include “y.tab.h”
%}

%%
“=” {printf(“\n Operator is EQUAL”);}
“+” {printf(“\n Operator is PLUS”);}
“-“ {printf(“\n Operator is MINUS”);}
“/” {printf(“\n Operator is DIVISION”);}
“*” {printf(“\n Operator is MULTIPLICATION”);}
[a-z A-Z]*[0-9]* {
printf(“\n Identifier is %s”,yytext);
return ID;
}
return yytext[0];
\n return 0;
%%

int yywrap()
{
return 1;
}

Program Name : arith_id.y

%{
#include
%}
%%
statement: A’=’E
E{
printf(“\n Valid arithmetic expression”);
$$ = $1;
};

E: E’+’ID
| E’-’ID
| E’*’ID
| E’/’ID
| ID
;
%%
extern FILE *yyin;
main()
{
do
{
yyparse();
}while(!feof(yyin));
}
yyerror(char*s)
{
}

OUTPUT:
[root@localhost]# lex arith_id.1
[root@localhost]# yacc –d arith_id.y
[root@localhost]# gcc lex.yy.c y.tab.c
[root@localhost]# ./a.out

x=a+b;
Identifier is x
Operator is EQUAL
Identifier is a
Operator is PLUS
Identifier is b

(b) Program to recognize a valid variable which starts with a letter followed by any
number of letters or digits.

Program name: variable_test.l

%{
#include "y.tab.h"
%}
%%
"int " {return INT;}
"float" {return FLOAT;}
"double" {return DOUBLE;}
[a-zA-Z]*[0-9]*{
printf("\nIdentifier is %s",yytext);
return ID;
}
return yytext[0];
\n return 0;
int yywrap()
{
return 1;
}

Program name: variable_test.y


%{
#include
%}
%token ID INT FLOAT DOUBLE
%%
D;T L
;
L:L,ID
|ID
;
T:INT
|FLOAT
|DOUBLE
;
%%
extern FILE *yyin;
main()
{
do
{
yyparse();
}while(!feof(yyin));
}
yyerror(char*s)
{
}

OUTPUT:
[root@localhost]# lex variable_test.I
[root@localhost]# yacc –d variable_test.y
[root@localhost]# gcc lex.yy.c y.tab.c
[root@localhost]# ./a.out
int a,b;

Identifier is a
Identifier is b

[root@localhost]#
EXERCISE – 13:
Write a C program for generating the three address code of a given expression/statement.

DESCRIPTION:
Three address code is a type of intermediate code which is easy to generate and can be easily
converted to machine code. It makes use of at most three addresses and one operator to
represent an expression and the value computed at each instruction is stored in temporary
variable generated by compiler. The compiler decides the order of operation given by three
address code.
General representation –
a = b op c
Where a, b or c represents operands like names, constants or compiler generated temporaries
and op represents the operator
Example: a * – (b + c)
Three address code statements are
t1=b+c
t2=unary minus t1
t3=a*t2

Implementation of Three Address Code –


There are 3 representations of three address code namely
1. Quadruple
2. Triples
3. Indirect Triples

PROGRAM:
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<string.h>

struct three
{
char data[10],temp[7];
}s[30];
void main()
{
char d1[7],d2[7]="t";
int i=0,j=1,len=0;
FILE *f1,*f2;
clrscr();
f1=fopen("sum.txt","r");
f2=fopen("out.txt","w");
while(fscanf(f1,"%s",s[len].data)!=EOF)
len++;
itoa(j,d1,7);
strcat(d2,d1);
strcpy(s[j].temp,d2);
strcpy(d1,"");
strcpy(d2,"t");
if(!strcmp(s[3].data,"+"))
{
fprintf(f2,"%s=%s+%s",s[j].temp,s[i+2].data,s[i+4].data);
j++;
}
else if(!strcmp(s[3].data,"-"))
{
fprintf(f2,"%s=%s-%s",s[j].temp,s[i+2].data,s[i+4].data);
j++;
}
for(i=4;i<len-2;i+=2)
{
itoa(j,d1,7);
strcat(d2,d1);
strcpy(s[j].temp,d2);
if(!strcmp(s[i+1].data,"+"))
fprintf(f2,"\n%s=%s+%s",s[j].temp,s[j-1].temp,s[i+2].data);
else if(!strcmp(s[i+1].data,"-"))
fprintf(f2,"\n%s=%s-%s",s[j].temp,s[j-1].temp,s[i+2].data);
strcpy(d1,"");
strcpy(d2,"t");
j++;
}
fprintf(f2,"\n%s=%s",s[0].data,s[j-1].temp);
fclose(f1);
fclose(f2);
getch();
}

INPUT: sum.txt

out = in1 + in2 + in3 - in4

OUTPUT : out.txt
t1=in1+in2
t2=t1+in3
t3=t2-in4
out=t3

EXERCISE -14:

Write a C program for implementation of a Code Generation Algorithm of a given


Expression / statement.

DESCRIPTION:
The algorithm takes a sequence of three-address statements as input. For each three address
statement of the form a:= b op c perform the various actions. These are as follows:

1. Invoke a function getreg() to find out the location L where the result of computation b op
c should be stored.
2. Consult the address description for y to determine y'. If the value of y currently in
memory and register both then prefer the register y' . If the value of y is not already in L
then generate the instruction MOV y' , L to place a copy of y in L.
3. Generate the instruction OP z' , L where z' is used to show the current location of z. if z
is in both then prefer a register to a memory location. Update the address descriptor of x
to indicate that x is in location L. If x is in L then update its descriptor and remove x from
all other descriptor.
4. If the current value of y or z have no next uses or not live on exit from the block or in
register then alter the register descriptor to indicate that after execution of x : = y op z
those register will no longer contain y or z.

PROGRAM:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#include<process.h>
typedef struct
{
char var[10];
int alive;
}
regist;
regist preg[10];
void substring(char exp[],int st,int end)
{
int i,j=0;
char dup[10]="";
for(i=st;i<end;i++)
dup[j++]=exp[i];
dup[j]='0';
strcpy(exp,dup);
}
int getregister(char var[])
{
int i;
for(i=0;i<10;i++)
{
if(preg[i].alive==0)
{
strcpy(preg[i].var,var);
break;
}
}
return(i);
}
void getvar(char exp[],char v[])
{
int i,j=0;
char var[10]="";
for(i=0;exp[i]!='\0';i++)
if(isalpha(exp[i]))
var[j++]=exp[i];
else
break;
strcpy(v,var);
}
void main()
{
char basic[10][10],var[10][10],fstr[10],op;
int i,j,k,reg,vc,flag=0;
clrscr();
printf("\nEnter the Three Address Code:\n");
for(i=0;;i++)
{
gets(basic[i]);
if(strcmp(basic[i],"exit")==0)
break;
}
printf("\nThe Equivalent Assembly Code is:\n");
for(j=0;j<i;j++)
{
getvar(basic[j],var[vc++]);
strcpy(fstr,var[vc-1]);
substring(basic[j],strlen(var[vc-1])+1,strlen(basic[j]));
getvar(basic[j],var[vc++]);
reg=getregister(var[vc-1]);
if(preg[reg].alive==0)
{
printf("\nMov R%d,%s",reg,var[vc-1]);
preg[reg].alive=1;
}
op=basic[j][strlen(var[vc-1])];
substring(basic[j],strlen(var[vc-1])+1,strlen(basic[j]));
getvar(basic[j],var[vc++]);
switch(op)
{
case '+': printf("\nAdd"); break;
case '-': printf("\nSub"); break;
case '*': printf("\nMul"); break;
case '/': printf("\nDiv"); break;
}
flag=1;
for(k=0;k<=reg;k++)
{
if(strcmp(preg[k].var,var[vc-1])==0)
{
printf("R%d, R%d",k,reg);
preg[k].alive=0;
flag=0;
break;
}
}
if(flag)
{
printf(" %s,R%d",var[vc-1],reg);
printf("\nMov %s,R%d",fstr,reg);
}
strcpy(preg[reg].var,var[vc-3]);
getch();
}
}

OUTPUT:
Enter the Three Address Code:

a=b+c
c=a*c
exit

The Equivalent Assembly Code is:


Mov R0,b
Add c,R0
Mov R1,a
Mul R1,c
Mov c,R1

You might also like