You are on page 1of 71

OPERATING SYSTEMS&COMPILER DESIGN

LABORATORY MANUAL

B.TECH (R20)
(III YEAR – I SEM)
(2022‐2023)

DEPARTMENT OF CSE-Artificial Intelligence


III Year B. Tech CSE ‐ AI I Sem L T/P/D C
- -/3/- 1.5
(R2031422) OPERATING SYSTEMS&COMPILER DESIGN LAB
OBJECTIVES:
The man objective of this course is to implement
operating systems and compiler design concept.

List of Experiments:
WEEK-1 . Simulate the following CPU scheduling algorithms:
a. Round Robin (b) SJF (c) FCFS (d) Priority
WEEK-2 Simulate the following:
a) Multiprogramming with a fixed number of tasks (MFT)
b) Multiprogramming with a variable number of tasks (MVT)
WEEK-3. Simulate the following page replacement algorithms:
a) FIFO b) LRU c) LFU
WEEK-4. Write a C program that illustrates two processes communicating using shared memory
WEEK-5. Write a C program to simulate producer and consumer problem usingsemaphores
WEEK-6. Simulate Bankers Algorithm for Dead Lock Avoidance
WEEK- 7. Simulate Bankers Algorithm for Dead Lock Prevention.
WEEK- 8. Write a C program to identify different types of Tokens in a given Program.
WEEK- 9. Write a Lex Program to implement a Lexical Analyzer using Lex tool.
WEEK- 10. Write a C program to Simulate Lexical Analyzer to validating a given input String.
WEEK- 11. Write a C program to implement the Brute force technique of Top down Parsing.
WEEK- 12. Write a C program to implement a Recursive Descent Parser.
WEEK-13. Write C program to compute the First and Follow Sets for the given Grammar.
WEEK-14. Write a C program for eliminating the left recursion and left factoring of a given grammar
WEEK-15. Write a C program to check the validity of input string using Predictive Parser.
WEEK-16. Write a C program for implementation of LR parsing algorithm to accept a given input
string.
WEEK-17. 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
WEEK- 18. Simulate the calculator using LEX and YACC too

REFERENCE BOOKS:
1. An Introduction to Operating Systems, P.C.P Bhatt, 2nd edition, PHI.
2. Modern Operating Systems, Andrew S Tanenbaum, 3rd Edition, PHI

OUTCOMES:
By the end of the course, student will be able to Implement various scheduling, page
replacement algorithms and algorithms related to deadlocks
Design programs for shared memory management and semaphores
Determine predictive parsing table for a CFG
Apply Lex and Yacc tools
Examine LR parser and generating SLR Parsing table
DEPARTMENT OF CSE-Artificial Intelligence

TABLE OF CONTENTS

EXP - NAME OF THE EXPERIMENT


NO EXP PAGE-
NO
1 Simulate the following CPU scheduling algorithms:
a. Round Robin (b) SJF (c) FCFS (d) Priority 1-10
2 Simulate the following:
a) Multiprogramming with a fixed number of
tasks(MFT) 11-13
b) Multiprogramming with a variable number of
tasks(MVT)

3 Simulate the following page replacement algorithms:


a) FIFO b) LRU c) LFU
14-20
4 Write a C program that illustrates two processes
communicating using shared memory 21-23
5 Write a C program to simulate producer and consumer
problem usingsemaphores 24-28
6 Simulate Bankers Algorithm for Dead Lock Avoidance 29-33
7 Simulate Bankers Algorithm for Dead Lock Prevention. 34-36
8 Write a C program to identify different types of Tokens
in a given Program. 37-43
9 Write a Lex Program to implement a Lexical Analyzer
using Lex tool. 44-46
10 Write a C program to Simulate Lexical Analyzer to
validating a given input String. 47-48
11 Write a C program to implement the Brute force
technique of Top down Parsing. 49-54
12 Write a C program to implement a Recursive Descent
Parser. 55-63
13 Write C program to compute the First and Follow Sets
for the given Grammar. 64-77

14 Write a C program for eliminating the left recursion 78-80


and left factoring of a given grammar
15 Write a C program to check the validity of input string 81-84
using Predictive Parser.
16 Write a C program for implementation of LR parsing 85-89
algorithm to accept a given input string.
17 Write a C program for implementation of a Shift 90-99
Reduce Parser using Stack Data Structure to accept a
given input string of a given grammar
18 Simulate the calculator using LEX and YACC tool 100-103
WEEK-8
-1

AIM: 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
inputprogramintoasequenceofTokens.
A C program consists of various tokens and a token is either a keyword, an identifier, a constant, a
stringliteral,orasymbol.
For Example:
1) Keywords:
Examples- for, while, if etc.

2) Identifier
Examples- Variable name, function name etc.

3) Operators:
Examples- '+', '++', '-' etc.

4) Separators:
Examples- ', ' ';' etc
Below is a C program to print all the keywords, literals, valid identifiers, invalid identifiers, integer
number, real number in a given C program:

#include <stdbool.h>

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

// Returns 'true' if the character is a DELIMITER.

bool isDelimiter(char ch)

if (ch == ' ' || ch == '+' || ch == '-' || ch == '*' ||


ch == '/' || ch == ',' || ch == ';' || ch == '>' ||

ch == '<' || ch == '=' || ch == '(' || ch == ')' ||

ch == '[' || ch == ']' || ch == '{' || ch == '}')

return (true);

return (false);

// Returns 'true' if the character is an OPERATOR.

bool isOperator(char ch)

if (ch == '+' || ch == '-' || ch == '*' ||

ch == '/' || ch == '>' || ch == '<' ||

ch == '=')

return (true);

return (false);

// Returns 'true' if the string is a VALID IDENTIFIER.

bool validIdentifier(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' || isDelimiter(str[0]) == true)

return (false);

return (true);

// Returns 'true' if the string is a KEYWORD.

bool isKeyword(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);

// Returns 'true' if the string is an INTEGER.

bool isInteger(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);
}

// Returns 'true' if the string is a REAL NUMBER.

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);

// Extracts the SUBSTRING.

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);

// Parsing the input STRING.

void parse(char* str)

int left = 0, right = 0;

int len = strlen(str);

while (right <= len && left <= right) {

if (isDelimiter(str[right]) == false)

right++;

if (isDelimiter(str[right]) == true && left == right) {

if (isOperator(str[right]) == true)

printf("'%c' IS AN OPERATOR\n", str[right]);

right++;

left = right;

} else if (isDelimiter(str[right]) == true && left != right

|| (right == len && left != right)) {

char* subStr = subString(str, left, right - 1);

if (isKeyword(subStr) == true)

printf("'%s' IS A KEYWORD\n", subStr);

else if (isInteger(subStr) == true)

printf("'%s' IS AN INTEGER\n", subStr);


else if (isRealNumber(subStr) == true)

printf("'%s' IS A REAL NUMBER\n", subStr);

else if (validIdentifier(subStr) == true

&& isDelimiter(str[right - 1]) == false)

printf("'%s' IS A VALID IDENTIFIER\n", subStr);

else if (validIdentifier(subStr) == false

&& isDelimiter(str[right - 1]) == false)

printf("'%s' IS NOT A VALID IDENTIFIER\n", subStr);

left = right;

return;

// DRIVER FUNCTION

int main()

// maximum length of string is 100 here

char str[100] = "int a = b + 1c; ";

parse(str); // calling the parse function

return (0);

}
OUTPUT

'int' IS A KEYWORD
'a' IS A VALID IDENTIFIER
'=' IS AN OPERATOR
'b' IS A VALID IDENTIFIER
'+' IS AN OPERATOR
'1c' IS NOT A VALID IDENTIFIER
-2
WEEK-9

AIM:’ Write a Lex Program to implement a Lexical Analyzer using Lex tool

Description: Lexical analyzer: scans the input stream and converts sequences of characters
into tokens. Lex is a tool for writing lexical analyzers. Syntactic Analysis: reads tokens and
assembles them into language constructs using the grammar rules of the language. Yacc is a tool
for constructing parsers.

/* program name is lexp.l */


%{
/* program to recognize a c program */
int COMMENT=0;
int cnt=0;
%}
identifier [a-zA-Z][a-zA-Z0-9]*
%%
#.* { printf("\n%s is a PREPROCESSOR DIRECTIVE",yytext);}
int |
float |
char |
double |
while |
for |
do |
if |
break |
continue |
void |
switch |
case |
long |
struct |
const |
typedef |
return |
else |
goto {printf("\n\t%s is a KEYWORD",yytext);}
"/*" {COMMENT = 1;}
"*/" {COMMENT = 0; cnt++;}
{identifier}\( {if(!COMMENT)printf("\n\nFUNCTION\n\t%s",yytext);}
\{ {if(!COMMENT) printf("\n BLOCK BEGINS");}
\} {if(!COMMENT) printf("\n BLOCK ENDS");}
{identifier}(\[[0-9]*\])? {if(!COMMENT) printf("\n %s IDENTIFIER",yytext);}
\".*\" {if(!COMMENT) printf("\n\t%s is a STRING",yytext);}
[0-9]+ {if(!COMMENT) printf("\n\t%s is a NUMBER",yytext);}
\)(\;)? {if(!COMMENT) printf("\n\t");ECHO;printf("\n");}
\( ECHO;
= {if(!COMMENT)printf("\n\t%s is an ASSIGNMENT OPERATOR",yytext);}
\<= |
\>= |
\< |
== |
\> {if(!COMMENT) printf("\n\t%s is a RELATIONAL OPERATOR",yytext);}
%%
int main(int argc,char **argv)
{
if (argc > 1)
{
FILE *file;
file = fopen(argv[1],"r");
if(!file)
{
printf("could not open %s \n",argv[1]);
exit(0);
}
yyin = file;
}
yylex();
printf("\n\n Total No.Of comments are %d",cnt);
return 0;
}
int yywrap()
{
return 1;
Input:
#include<stdio.h>
main()
{
int a,b;
}
Output:
#include<stdio.h> is a PREPROCESSOR DIRECTIVE
FUNCTION
main (
)
BLOCK BEGINS
int is a KEYWORD
a IDENTIFIER
b IDENTIFIER
BLOCK ENDS
WEEK-10
-3

AIM: Write a C program to Simulate Lexical Analyzer to validating a given input String.

DESCRIPTION:

RESOURCE: Turbo C++

PROGRAM LOGIC: Read the given input.

If the given input matches with any operator symbol.

Then display in terms of words of the particular symbol.

Else print not a operator.

Go to debug -> run or press CTRL + F9 to run the program.


-4 AIM: Write a C program to implement the Brute force technique of Top down Parsing
WEEK-11

Description:

Brute force technique of Top down Parsing:

Top-Down Parser:

Recursive descent parser is also known as the Brute force parser or the backtracking parser. It

basically generates the parse tree by using brute force and backtracking. Non-recursive

descent parser is also known as LL(1) parser or predictive parser or without backtracking parser

or dynamic parser.

Brute force approach

A brute force approach is an approach that finds all the possible solutions to find a satisfactory

solution to a given problem. The brute force algorithm tries out all the possibilities till a

satisfactory solution is not found.

Such an algorithm can be of two types:

o Optimizing: In this case, the best solution is found. To find the best solution, it may

either find all the possible solutions to find the best solution or if the value of the best

solution is known, it stops finding when the best solution is found. For example: Finding

the best path for the travelling salesman problem. Here best path means that travelling all

the cities and the cost of travelling should be minimum.

o Satisficing: It stops finding the solution as soon as the satisfactory solution is found. Or

example, finding the travelling salesman path which is within 10% of optimal.
o Often Brute force algorithms require exponential time. Various heuristics and

optimization can be used:

o Heuristic: A rule of thumb that helps you to decide which possibilities we should look at

first.

o Optimization: A certain possibilities are eliminated without exploring all of them.

#include<iostream.h>
#include<conio.h>
#include<string.h>
class parse
{
int nt,t,m[20][20],i,s,n,p1,q,k,j;
char p[30][30],n1[20],t1[20],ch,b,c,f[30][30],fl[30][30];
public:
int scant(char);
int scannt(char);
void process();
void input();
};

int parse::scannt(char a)
{
int c=-1,i;
for(i=0;i<nt;i++)
{
if(n1[i]==a)
{
return i;
}
}
return c;
}

int parse::scant(char b)
{
int c1=-1,j;
for(j=0;j<t;j++)
{
if(t1[j]==b)
{
return j;
}
}
return c1;
}

void parse::input()

{
cout<<"Enter the number of productions:";
cin>>n;
cout<<"Enter the productions one by one"<<endl;
for(i=0;i<n;i++)
cin>>p[i];
nt=0;
t=0;
}

void parse::process()
{
for(i=0;i<n;i++)
{
if(scannt(p[i][0])==-1)
n1[nt++]=p[i][0];
}
for(i=0;i<n;i++)
{
for(j=3;j<strlen(p[i]);j++)
{
if(p[i][j]!='e')
{
if(scannt(p[i][j])==-1)
{
if((scant(p[i][j]))==-1)
t1[t++]=p[i][j];
}
}
}
}
t1[t++]='$';
for(i=0;i<nt;i++)
{
for(j=0;j<t;j++)
m[i][j]=-1;
}
for(i=0;i<nt;i++)
{
cout<<"Enter first["<<n1[i]<<"]:";
cin>>f[i];
}

for(i=0;i<nt;i++)
{
cout<<"Enter follow["<<n1[i]<<"]:";
cin>>fl[i];
}
for(i=0;i<n;i++)
{
p1=scannt(p[i][0]);
if((q=scant(p[i][3]))!=-1)
m[p1][q]=i;
if((q=scannt(p[i][3]))!=-1)
{
for(j=0;j<strlen(f[q]);j++)
m[p1][scant(f[q][j])]=i;
}
if(p[i][3]=='e')
{
for(j=0;j<strlen(fl[p1]);j++)
m[p1][scant(fl[p1][j])]=i;
}
}
for(i=0;i<t;i++)
cout<<"\t"<<t1[i];
cout<<endl;
for(j=0;j<nt;j++)
{
cout<<n1[j];
for(i=0;i<t;i++)
{
cout<<"\t"<<" ";
if(m[j][i]!=-1)
cout<<p[m[j][i]];
}
cout<<endl;
}
}

void main()
{
clrscr();
parse p;
p.input();
p.process();
getch();
}
Top Down Parsing in Complier Design output
Enter the number of productions:8
Enter the productions one by one
E->TA
A->+TA
A->e
T->FB
B->e
B->*FB
F->(E)
F->i

Enter first[E]: (i
Enter first[A]: +e
Enter first[T]: (i
Enter first[B]: *e
Enter first[F]: (i
Enter follow[E]: $)
Enter follow[A]: $)
Enter follow[T]: +)$
Enter follow[B]: +)$
Enter follow[F]: +*)$
+()i*$
E E->TA E->TA
A A->+TA A->e A->e
T T->FB T->FB
B B->e B->e B->*FB B->e
F F->(E) F->i
Top down Parsing in Compiler Design Output
Enter the no of productions:
8
Enter the production one by one:
E->TX
X->+TX
X->e
T->FY
Y->*FY
Y->e
F->(E)
F->i
Enter FIRST[E](i
Enter FIRST[X]+e
Enter FIRST[T](i
Enter FIRST[Y]*e
Enter FIRST[F](i
Enter FOLLOW[E])$
Enter FOLLOW[X])$
Enter FOLLOW[T]+)$
Enter FOLLOW[Y]+)$
Enter FOLLOW[F]*+)$
+*()i$
E E->TX E->TX
X X->+TX X->e X->e
T T->FY T->FY
Y Y->e Y->*FY Y->e Y->e
F F->(E) F->i
5 AIM: Write a C program to implement a Recursive Descent Parser
WEEK-12

Description:

Source code:

#include"stdio.h"

#include"conio.h"

#include"string.h"

#include"stdlib.h"

#include"ctype.h"

char ip_sym[15],ip_ptr=0,op[50],tmp[50];

void e_prime();

void e();

void t_prime();

void t();

void f();

void advance();

int n=0;

void e()

strcpy(op,"TE'");

printf("E=%-25s",op);

printf("E->TE'\n");

t();

e_prime();
}

void e_prime()

int i,n=0,l;

for(i=0;i<=strlen(op);i++)

if(op[i]!='e')

tmp[n++]=op[i];

strcpy(op,tmp);

l=strlen(op);

for(n=0;n < l && op[n]!='E';n++);

if(ip_sym[ip_ptr]=='+')

i=n+2;

do

op[i+2]=op[i];

i++;

}while(i<=l);

op[n++]='+';

op[n++]='T';

op[n++]='E';

op[n++]=39;

printf("E=%-25s",op);
printf("E'->+TE'\n");

advance();

t();

e_prime();

else

op[n]='e';

for(i=n+1;i<=strlen(op);i++)

op[i]=op[i+1];

printf("E=%-25s",op);

printf("E'->e");

void t()

int i,n=0,l;

for(i=0;i<=strlen(op);i++)

if(op[i]!='e')

tmp[n++]=op[i];

strcpy(op,tmp);

l=strlen(op);

for(n=0;n < l && op[n]!='T';n++);


i=n+1;

do

op[i+2]=op[i];

i++;

}while(i < l);

op[n++]='F';

op[n++]='T';

op[n++]=39;

printf("E=%-25s",op);

printf("T->FT'\n");

f();

t_prime();

void t_prime()

int i,n=0,l;

for(i=0;i<=strlen(op);i++)

if(op[i]!='e')

tmp[n++]=op[i];

strcpy(op,tmp);

l=strlen(op);
for(n=0;n < l && op[n]!='T';n++);

if(ip_sym[ip_ptr]=='*')

i=n+2;

do

op[i+2]=op[i];

i++;

}while(i < l);

op[n++]='*';

op[n++]='F';

op[n++]='T';

op[n++]=39;

printf("E=%-25s",op);

printf("T'->*FT'\n");

advance();

f();

t_prime();

else

op[n]='e';

for(i=n+1;i<=strlen(op);i++)
op[i]=op[i+1];

printf("E=%-25s",op);

printf("T'->e\n");

void f()

int i,n=0,l;

for(i=0;i<=strlen(op);i++)

if(op[i]!='e')

tmp[n++]=op[i];

strcpy(op,tmp);

l=strlen(op);

for(n=0;n < l && op[n]!='F';n++);

if((ip_sym[ip_ptr]=='i')||(ip_sym[ip_ptr]=='I'))

op[n]='i';

printf("E=%-25s",op);

printf("F->i\n");

advance();

else

{
if(ip_sym[ip_ptr]=='(')

advance();

e();

if(ip_sym[ip_ptr]==')')

advance();

i=n+2;

do

op[i+2]=op[i];

i++;

}while(i<=l);

op[n++]='(';

op[n++]='E';

op[n++]=')';

printf("E=%-25s",op);

printf("F->(E)\n");

else

printf("\n\t syntax error");


getch();

exit(1);

void advance()

ip_ptr++;

void main()

int i;

clrscr();

printf("\nGrammar without left recursion");

printf("\n\t\t E->TE' \n\t\t E'->+TE'|e \n\t\t T->FT' ");

printf("\n\t\t T'->*FT'|e \n\t\t F->(E)|i");

printf("\n Enter the input expression:");

gets(ip_sym);

printf("Expressions");

printf("\t Sequence of production rules\n");

e();

for(i=0;i < strlen(ip_sym);i++)


{

if(ip_sym[i]!='+'&&ip_sym[i]!='*'&&ip_sym[i]!='('&&

ip_sym[i]!=')'&&ip_sym[i]!='i'&&ip_sym[i]!='I')

printf("\nSyntax error");

break;

for(i=0;i<=strlen(op);i++)

if(op[i]!='e')

tmp[n++]=op[i];

strcpy(op,tmp);

printf("\nE=%-25s",op);

getch();

Output:
WEEK-13
6
AIM: Write C program to compute the First and Follow Sets for the given Grammar
Description:
Assuming the reader is familiar with the basics discussed above, let’s start discussing how to
implement the C program to calculate the first and follow of given grammar.
Example :
Input :
E -> TR
R -> +T R| #
T -> F Y
Y -> *F Y | #
F -> (E) | i

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

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

Follow(E) = { $, ), }
Follow(R) = { $, ), }
Follow(T) = { +, $, ), }
Follow(Y) = { +, $, ), }
Follow(F) = { *, +, $, ), }
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.
Assumptions :
1. Epsilon is represented by ‘#’.
2. Productions are of the form A=B, where ‘A’ is a single Non-Terminal and ‘B’ can be
any combination of Terminals and Non- Terminals.
3. L.H.S. of the first production rule is the start symbol.
4. Grammar is not left recursive.
5. Each production of a non-terminal is entered on a different line.
6. Only Upper Case letters are Non-Terminals and everything else is a terminal.
7. Do not use ‘!’ or ‘$’ as they are reserved for special purposes.
Explanation :
Store the grammar on a 2D character array production. findfirst function is for calculating
the first of any non terminal. Calculation of first falls under two broad cases :
 If the first symbol in the R.H.S of the production is a Terminal then it can directly be
included in the first set.
 If the first symbol in the R.H.S of the production is a Non-Terminal then call the
findfirst function again on that Non-Terminal. To handle these cases like Recursion is the
best possible solution. Here again, if the First of the new Non-Terminal contains an
epsilon then we have to move to the next symbol of the original production which can
again be a Terminal or a Non-Terminal.
Note : For the second case it is very easy to fall prey to an INFINITE LOOP even if the code
looks perfect. So it is important to keep track of all the function calls at all times and never
call the same function again.
Recommended: Please try your approach on {IDE} first, before moving on to the solution.

Below is the implementation :


// C program to calculate the First and

// Follow sets of a given grammar

#include<stdio.h>

#include<ctype.h>

#include<string.h>

// Functions to calculate Follow

void followfirst(char, int, int);

void follow(char c);

// Function to calculate First

void findfirst(char, int, int);


int count, n = 0;

// Stores the final result

// of the First Sets

char calc_first[10][100];

// Stores the final result

// of the Follow Sets

char calc_follow[10][100];

int m = 0;

// Stores the production rules

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;
// The Input grammar

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;

// Initializing the calc_first array

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;

// Checking if First of c has

// already been calculated

for(kay = 0; kay <= ptr; kay++)

if(c == done[kay])

xxx = 1;

if (xxx == 1)

continue;

// Function call

findfirst(c, 0, 0);

ptr += 1;

// Adding c to the calculated list

done[ptr] = c;

printf("\n First(%c) = { ", c);

calc_first[point1][point2++] = c;

// Printing the First Sets of the grammar

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;

// Initializing the calc_follow array


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;

// Checking if Follow of ck

// has already been calculated

for(kay = 0; kay <= ptr; kay++)

if(ck == donee[kay])

xxx = 1;

if (xxx == 1)

continue;

land += 1;

// Function call

follow(ck);
ptr += 1;

// Adding ck to the calculated list

donee[ptr] = ck;

printf(" Follow(%c) = { ", ck);

calc_follow[point1][point2++] = ck;

// Printing the Follow Sets of the grammar

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;

// Adding "$" to the follow

// set of the start symbol

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')

// Calculate the first of the next

// Non-Terminal in the production

followfirst(production[i][j+1], i, (j+2));
}

if(production[i][j+1]=='\0' && c!=production[i][0])

// Calculate the follow of the Non-Terminal

// in the L.H.S. of the production

follow(production[i][0]);

void findfirst(char c, int q1, int q2)

int j;

// The case where we

// encounter a Terminal

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))

// Recursion to calculate First of New

// Non-Terminal we encounter after epsilon

findfirst(production[q1][q2], q1, (q2+1));

else

first[n++] = '#';

else if(!isupper(production[j][2]))

first[n++] = production[j][2];

else

// Recursion to calculate First of

// New Non-Terminal we encounter

// at the beginning

findfirst(production[j][2], j, 3);
}

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

int k;

// The case where we encounter

// a Terminal

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;

//Including the First set of the

// Non-Terminal in the Follow of

// the original query


while(calc_first[i][j] != '!')

if(calc_first[i][j] != '#')

f[m++] = calc_first[i][j];

else

if(production[c1][c2] == '\0')

// Case where we reach the

// end of a production

follow(production[c1][0]);

else

// Recursion to the next symbol

// in case we encounter a "#"

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) = { *, +, $, ), }
7 Write a C program for eliminating the left recursion and left factoring of a given grammar.
WEEK-14

Description:
7:
Left factoring is removing the common left factor that appears in two productions of the same
non-terminal. It is done to avoid back-tracing by the parser. Suppose the parser has a look-ahead,
consider this example:

A -> qB | qC
where A, B and C are non-terminals and q is a sentence.

In this case, the parser will be confused as to which of the two productions to choose and it might
have to back-trace. After left factoring, the grammar is converted to:

A -> qD
D -> B | C
In this case, a parser with a look-ahead will always choose the right production.

Left recursion is a case when the left-most non-terminal in a production of a non-terminal is the
non-terminal itself (direct left recursion) or through some other non-terminal definitions, rewrites
to the non-terminal again (indirect left recursion).

Consider these examples:

(1) A -> Aq (direct)

(2) A -> Bq
B -> Ar (indirect)
Left recursion has to be removed if the parser performs top-down parsing.

THE LEFT RECURSION

#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->ε
LEFT FACTORING

#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("\nGrammar Without Left Factoring : : \n");
printf(" A->%s",modifiedGram);
printf("\n X->%s\n",newGram);
}

+
WEEK-15
-8 AIM: Write a C program to check the validity of input string using Predictive Parser

Description:

A predictive parser is an effective technique of executing recursive-descent parsing by


managing the stack of activation records, particularly. Predictive Parsers has the following
components − Input Buffer − The input buffer includes the string to be parsed followed by an
end marker $ to denote the end of the string.

#include<stdio.h>

#include<conio.h>

#include<string.h>

char prol[7][10]={"S","A","A","B","B","C","C"};

char pror[7][10]={"A","Bb","Cd","aB","@","Cc","@"};

char prod[7][10]={"S->A","A->Bb","A->Cd","B->aB","B->@","C->Cc","C->@"}; char


first[7][10]={"abcd","ab","cd","a@","@","c@","@"}; char
follow[7][10]={"$","$","$","a$","b$","c$","d$"};

char table[5][6][10];

int numr(char c)

switch(c){

case 'S': return 0;

case 'A': return 1;

case 'B': return 2;

case 'C': return 3;

case 'a': return 0;

case 'b': return 1;

case 'c': return 2;

case 'd': return 3;

case '$': return 4;


}

return(2);

void main()

int i,j,k;

clrscr();

for(i=0;i<5;i++)

for(j=0;j<6;j++)

strcpy(table[i][j]," ");

printf("\nThe following is the predictive parsing table for the following


grammar:\n"); for(i=0;i<7;i++)

printf("%s\n",prod[i]);

printf("\nPredictive parsing table is\n");

fflush(stdin);

for(i=0;i<7;i++){

k=strlen(first[i]);

for(j=0;j<10;j++)

if(first[i][j]!='@')

strcpy(table[numr(prol[i][0])+1][numr(first[i][j])+1],prod[i]);

for(i=0;i<7;i++){

if(strlen(pror[i])==1)

if(pror[i][0]=='@')
{

k=strlen(follow[i]);

for(j=0;j<k;j++)

strcpy(table[numr(prol[i][0])+1][numr(follow[i][j])+1],prod[i]);

strcpy(table[0][0]," ");

strcpy(table[0][1],"a");

strcpy(table[0][2],"b");

strcpy(table[0][3],"c");

strcpy(table[0][4],"d");

strcpy(table[0][5],"$");

strcpy(table[1][0],"S");

strcpy(table[2][0],"A");

strcpy(table[3][0],"B");

strcpy(table[4][0],"C");

printf("\n--------------------------------------------------------\n");

for(i=0;i<5;i++)

for(j=0;j<6;j++){

printf("%-10s",table[i][j]);

if(j==5)

printf("\n--------------------------------------------------------\n");

}
getch();

Output:

The following is the predictive parsing table for the following grammar:

S->A
A->Bb
A->Cd
B->aB
B->@
C->Cc
C->@
Predictive parsing table is
--------------------------------------------------------
abcd$
--------------------------------------------------------
S S->A S->A S->A S->A
--------------------------------------------------------
A A->Bb A->Bb A->Cd A->Cd
--------------------------------------------------------
B B->aB B->@ B->@ B->@
--------------------------------------------------------
C C->@ C->@ C->@
--------------------------------------------------------
9
WEEK-16

AMI: Write a C program for implementation of LR parsing algorithm to accept a given input string.

Description:

ALGORITHM:
1. Get the input expression and store it in the input buffer.
2. Read the data from the input buffer one at the time and convert in to corresponding Non
Terminal using production rules available.
3. Perform push & pop operation for LR parsing table construction.
4. 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

#include

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++)< span="" style="box-sizing: border-box;">


{

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();

} </l;i++)<>
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

$
10
WEEK-17

AIM: 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

Shift Reduce parser attempts for the construction of parse in a similar manner as done in
bottom-up parsing i.e. the parse tree is constructed from leaves(bottom) to the root(up). A more
general form of the shift-reduce parser is the LR parser.
This parser requires some data structures i.e.
 An input buffer for storing the input string.
 A stack for storing and accessing the production rules.
Basic Operations –
 Shift: This involves moving symbols from the input buffer onto the stack.
 Reduce: If the handle appears on top of the stack then, its reduction by using appropriate
production rule is done i.e. RHS of a production rule is popped out of a stack and LHS
of a production rule is pushed onto the stack.
 Accept: If only the start symbol is present in the stack and the input buffer is empty then,
the parsing action is called accept. When accepted action is obtained, it is means
successful parsing is done.
 Error: This is the situation in which the parser can neither perform shift action nor
reduce action and not even accept action.
Example 1 – Consider the grammar
S– >S+S
S– >S*S
S– > id
Perform Shift Reduce parsing for input string “id + id + id”.

Example 2 – Consider the grammar


E– > 2E2
E– > 3E3
E– >4
Perform Shift Reduce parsing for input string “32423”.

Example 3 – Consider the grammar


S –> ( L ) | a
L –> L , S | S
Perform Shift Reduce parsing for input string “( a, ( a, a ) ) “.

Stack Input Buffer Parsing Action

$ (a,(a,a))$ Shift

$( a,(a,a))$ Shift

$(a ,(a,a))$ Reduce S → a

$(S ,(a,a))$ Reduce L → S

$(L ,(a,a))$ Shift

$(L, (a,a))$ Shift

$(L,( a,a))$ Shift

$(L,(a ,a))$ Reduce S → a

$(L,(S ,a))$ Reduce L → S


Stack Input Buffer Parsing Action

$(L,(L ,a))$ Shift

$(L,(L, a))$ Shift

$(L,(L,a ))$ Reduce S → a

$ ( L, ( L, S ) ))$ Reduce L →L, S

$ ( L, ( L ))$ Shift

$ ( L, ( L ) )$ Reduce S → (L)

$ ( L, S )$ Reduce L → L, S

$(L )$ Shift

$(L) $ Reduce S → (L)

$S $ Accept

// Including Libraries

#include <bits/stdc++.h>

using namespace std;

// Global Variables

int z = 0, i = 0, j = 0, c = 0;
// Modify array size to increase

// length of string to be parsed

char a[16], ac[20], stk[15], act[10];

// This Function will check whether

// the stack contain a production rule

// which is to be Reduce.

// Rules can be E->2E2 , E->3E3 , E->4

void check()

// Copying string to be printed as action

strcpy(ac,"REDUCE TO E -> ");

// c=length of input string

for(z = 0; z < c; z++)

// checking for producing rule E->4

if(stk[z] == '4')
{

printf("%s4", ac);

stk[z] = 'E';

stk[z + 1] = '\0';

//printing action

printf("\n$%s\t%s$\t", stk, a);

for(z = 0; z < c - 2; z++)

// checking for another production

if(stk[z] == '2' && stk[z + 1] == 'E' &&

stk[z + 2] == '2')

printf("%s2E2", ac);

stk[z] = 'E';

stk[z + 1] = '\0';
stk[z + 2] = '\0';

printf("\n$%s\t%s$\t", stk, a);

i = i - 2;

for(z = 0; z < c - 2; z++)

//checking for E->3E3

if(stk[z] == '3' && stk[z + 1] == 'E' &&

stk[z + 2] == '3')

printf("%s3E3", ac);

stk[z]='E';

stk[z + 1]='\0';

stk[z + 1]='\0';

printf("\n$%s\t%s$\t", stk, a);

i = i - 2;
}

return ; // return to main

// Driver Function

int main()

printf("GRAMMAR is -\nE->2E2 \nE->3E3 \nE->4\n");

// a is input string

strcpy(a,"32423");

// strlen(a) will return the length of a to c

c=strlen(a);

// "SHIFT" is copied to act to be printed

strcpy(act,"SHIFT");
// This will print Labels (column name)

printf("\nstack \t input \t action");

// This will print the initial

// values of stack and input

printf("\n$\t%s$\t", a);

// This will Run upto length of input string

for(i = 0; j < c; i++, j++)

// Printing action

printf("%s", act);

// Pushing into stack

stk[i] = a[j];

stk[i + 1] = '\0';

// Moving the pointer

a[j]=' ';
// Printing action

printf("\n$%s\t%s$\t", stk, a);

// Call check function ..which will

// check the stack whether its contain

// any production or not

check();

// Rechecking last time if contain

// any valid production then it will

// replace otherwise invalid

check();

// if top of the stack is E(starting symbol)

// then it will accept the input

if(stk[0] == 'E' && stk[1] == '\0')

printf("Accept\n");
else //else reject

printf("Reject\n");

// This code is contributed by Shubhamsingh10

Output –
GRAMMAR is -
E->2E2
E->3E3
E->4

stack input action


$ 32423$ SHIFT
$3 2423$ SHIFT
$32 423$ SHIFT
$324 23$ REDUCE TO E -> 4
$32E 23$ SHIFT
$32E2 3$ REDUCE TO E -> 2E2
$3E 3$ SHIFT
$3E3 $ REDUCE TO E -> 3E3
$E $ Accept
11
WEEK-18

AIM: Simulate the calculator using LEX and YACC tool

Description:

Lex is a computer program that generates lexical analyzers. Lex reads an


input stream specifying the lexical analyzer and outputs source code
implementing the lexer in the C programming language.
The commands for executing the LEX program are:
lex abc.l (abc is the file name)
cc lex.yy.c -efl
./a.out
Let’s see LEX program to implement a simple calculator.
Examples:
Input :
3+3
Output :
6.0

Input :
5*4
Output :
20.0
Below is the implementation:
SOURCE CODE:

/*lex program to implement

- a simple calculator.*/

%{

int op = 0,i;

float a, b;

%}
dig [0-9]+|([0-9]*)"."([0-9]+)

add "+"

sub "-"

mul "*"

div "/"

pow "^"

ln \n

%%

/* digi() is a user defined function */

{dig} {digi();}

{add} {op=1;}

{sub} {op=2;}

{mul} {op=3;}

{div} {op=4;}

{pow} {op=5;}

{ln} {printf("\n The Answer :%f\n\n",a);}

%%

digi()

if(op==0)

/* atof() is used to convert

- the ASCII input to float */


a=atof(yytext);

else

b=atof(yytext);

switch(op)

case 1:a=a+b;

break;

case 2:a=a-b;

break;

case 3:a=a*b;

break;

case 4:a=a/b;

break;

case 5:for(i=a;b>1;b--)

a=a*i;

break;

op=0;
}

main(int argv,char *argc[])

yylex();

yywrap()

return 1;

OUTPUT:

You might also like