You are on page 1of 48

COMPILER DESIGN LAB

KCS-552

Session 2022-23 (ODD Semester)

Subject Name :CD Lab


Subject Code :KCS-552
Semester :B. Tech. CSE (V)

Department of Computer Science and Engineering


G. L. Bajaj Institute of Technology and Management
Greater Noida, Uttar Pradesh
Affiliated to Dr. A.P.J. AKTU, Lucknow (UP)
List of Experiments

Minimum Ten / Eight experiments to be performed (As per syllabus)

SNO List of Experiments

1 Implementation of LEXICAL ANALYZER for IF STATEMENT.

2 Implemention of LEXICAL ANALYZER for ARITHMETIC EXPRESSION.

3 Construction of NFA from REGULAR EXPRESSION.

4 Construction of DFA from NFA.

5 Implementation of SHIFT REDUCE PARSING ALGORITHM.

6 Implementation of OPERATOR PRECEDENCE PARSER.

7 Implementation of RECURSIVE DESCENT PARSER.

8 Implemenation of CODE OPTIMIZATION TECHNIQUES.

8 Implementation of CODE GENERATOR.


SESSION PLAN

COURSE: B. Tech. Computer Science & Engineering

TITLE: Compiler Design Lab

CREDIT: 1

PREREQUISITIES BY TOPIC: Concept of Data Structures and Theory of Automata &


Formal Languages.
TEXT BOOK(S) AND/OR 1. Aho, Sethi & Ullman, "Compilers: Principles,
REQUIRED MATERIALS: Techniques and Tools”, Pearson Education
2. Kenneth Louden,” Compiler Construction”,
Cengage Learning
3. K. Muneeswaran,Compiler Design,First
Edition,Oxford University Press.
4. J.P. Bennet, “Introduction to Compiler
Techniques”, Second Edition, Tata McGraw-Hill,
2003
5. Charles Fischer and Ricard LeBlanc,” Crafting a
Compiler with C”, Pearson Education
6. Henk Alblas and Albert Nymeyer,“Practice and
Principles of Compiler Building with C”, PHI,
2001.
Experiment-1

TITLE 1. Implementation of LEXICAL ANALYZER for IF STATEMENT

Brief Theory:-The lexical analyzer (either generated automatically by a tool like lex, or hand-
crafted) reads in a stream of characters, identifies the lexemes in the stream, and categorizes them
into tokens. This is called "tokenizing". If the lexer finds an invalid token, it will report an error. A
lexical analyzer generally does nothing with combinations of tokens, a task left for a parser. For
example, a typical lexical analyzer recognizes parentheses as tokens, but does nothing to ensure that
each "(" is matched with a ")". When a lexer feeds tokens to the parser, the representation used is
typically an enumerated list of number representations. For example, "Identifier" is represented with
0, "Assignment operator" with 1, "Addition operator" with 2, etc.

Procedure:-

1. Start the program.


2. Input the statement with a $ symbol at the end .
3. Identify the tokens.
4. Display the keywords, operators, constants, variables and punctuations thereby separating
the tokens.
5. Stop the program.

Program:-
#include<stdio.h>
#include<ctype.h>
#include<conio.h>
#include<string.h>
char vars[100][100];
int vcnt;
char input[1000],c;
char token[50],tlen;
int state=0,pos=0,i=0,id;
char*getAddress(char str[])
{
for(i=0;i<vcnt;i++)
if(strcmp(str,vars[i])==0)
return vars[i];
strcpy(vars[vcnt],str);
return vars[vcnt++];
}
intisrelop(char c)
{
if(c=='>'||c=='<'||c=='|'||c=='=')
return 1;
else
return 0;
}
int main(void)
{
clrscr();
printf("Enter the Input String:");
gets(input);
do
{
c=input[pos];
putchar(c);
switch(state)
{
case 0:
if(c=='i')
state=1;
break;
case 1:
if(c=='f')
{
printf("\t<1,1>\n");
state =2;
}
break;
case 2:
if(isspace(c))
printf("\b");
if(isalpha(c))
{
token[0]=c;
tlen=1;
state=3;
}
if(isdigit(c))
state=4;
if(isrelop(c))
state=5;
if(c==';')printf("\t<4,4>\n");
if(c=='(')printf("\t<5,0>\n");
if(c==')')printf("\t<5,1>\n");
if(c=='{') printf("\t<6,1>\n");
if(c=='}') printf("\t<6,2>\n");
break;
case 3:
if(!isalnum(c))
{
token[tlen]='\o';
printf("\b\t<2,%p>\n",getAddress(token));
state=2;
pos--;
}
else
token[tlen++]=c;
break;
case 4:
if(!isdigit(c))
{
printf("\b\t<3,%p>\n",&input[pos]);
state=2;
pos--;
}
break;
case 5:
id=input[pos-1];
if(c=='=')
printf("\t<%d,%d>\n",id*10,id*10);
else
{
printf("\b\t<%d,%d>\n",id,id);
pos--;
}
state=2;
break;
}
pos++;
}
while(c!=0);
getch();
return 0;
}

Result:-
Enter the input string: if(a>=b) max=a;
if <1,1>
( <5,0>
a <2,0960>
>= <620,620>
b <2,09c4>
) <5,1>
max <2,0A28>
= <61,61>
a <2,0A8c>
; <4,4>

Viva Questions:
1. Define lexical analyzer.
Lexical analyzer takes the input and scans it from left to right to generate tokens.

2. What is token?
A lexical token is a sequence of characters that can be treated as a unit in the grammar of the
programming languages.

3. What is lexeme?
A lexeme is a sequence of characters in the source program that is matched by the pattern for a
token. For example, the pattern for the RELOP token contains six lexemes ( =, <>, <, <
=, >, >=) so the lexical analyzer should return a RELOP token to parser whenever it sees any
one of the six.

4. What is pattern?
There is a set of strings in the input for which the same token is produced as output. This set of
strings is described by a rule called a pattern associated with the token. Regular expressions are
an important notation for specifying patterns. For example, the pattern for the Pascal identifier
token, id, is: id → letter (letter | digit)*.

5. What do you understand by lexical error?


Lexical errors are the errors thrown by your lexer when unable to continue. Which means that
there's no way to recognise a lexeme as a valid token for you lexer.
Experiment-2

TITLE 2. Object: -Implementation of lexical analyzer for Arithmetic Expression.


Brief Theory:-The lexical analyzer (either generated automatically by a tool like lex, or hand-
crafted) reads in a stream of characters, identifies the lexemes in the stream, and categorizes them
into tokens. This is called "tokenizing". If the lexer finds an invalid token, it will report an error. A
lexical analyzer generally does nothing with combinations of tokens, a task left for a parser. For
example, a typical lexical analyzer recognizes parentheses as tokens, but does nothing to ensure that
each "(" is matched with a ")". When a lexer feeds tokens to the parser, the representation used is
typically an enumerated list of number representations. For example, "Identifier" is represented with
0, "Assignment operator" with 1, "Addition operator" with 2, etc.

Procedure:-

1. Start the program.


2. Input the statement with a $ symbol at the end .
3. Identify the tokens.
4. Display the keywords, operators, constants, variables and punctuations thereby separating
the tokens.
5. Stop the program.

Program:-

#include<stdio.h>
#include<ctype.h>
#include<conio.h>
#include<string.h>
char vars[100][100];
int vcnt;
char input[1000],c;
char token[50],tlen;
int state=0,pos=0,i=0,id;
char *getAddress(char str[])
{
for(i=0;i<vcnt;i++)
if(strcmp(str,vars[i])==0)
return vars[i];
strcpy(vars[vcnt],str);
return vars[vcnt++];
}
intisrelop(char c)
{
if(c=='+'||c=='-'||c=='*'||c=='/'||c=='%'||c=='^')
return 1;
else
return 0;
}
int main(void)
{
clrscr();
printf("Enter the Input String:");
gets(input);
do
{
c=input[pos];
putchar(c);
switch(state)
{
case 0:
if(isspace(c))
printf("\b");
if(isalpha(c))
{
token[0]=c;
tlen=1;
state=1;
}
if(isdigit(c))
state=2;
if(isrelop(c))
state=3;
if(c==';')
printf("\t<3,3>\n");
if(c=='=')
printf("\t<4,4>\n");
break;
case 1:
if(!isalnum(c))
{
token[tlen]='\o';
printf("\b\t<1,%p>\n",getAddress(token));
state=0;
pos--;
}
else
token[tlen++]=c;
break;
case 2:
if(!isdigit(c))
{
printf("\b\t<2,%p>\n",&input[pos]);
state=0;
pos--;
}
break;
case 3:
id=input[pos-1];
if(c=='=')
printf("\t<%d,%d>\n",id*10,id*10);
else
{
printf("\b\t<%d,%d>\n",id,id);
pos--;
}
state=0;
break;
}
pos++;
}
while(c!=0);
getch();
return 0;
}
Results:

Enter the Input String: a=a*2+b/c;


a <1,08CE>
= <4,4>
a <1,08CE>
* 42,42>
2 <2,04E9>
+ <43,43>
b <1,0932>
/ <47,47>
c <1,0996>
; <3,3>
Viva-voce Questions
1. Define lexical analyzer.
Lexical analyzer takes the input and scans it from left to right to generate tokens.

2. What id token?
A lexical token is a sequence of characters that can be treated as a unit in the grammar of the
programming languages.

3. What is lexeme?
A lexeme is a sequence of characters in the source program that is matched by the pattern for a
token. For example, the pattern for the RELOP token contains six lexemes ( =, <>, <, <
=, >, >=) so the lexical analyzer should return a RELOP token to parser whenever it sees any
one of the six.

4. What is pattern?
There is a set of strings in the input for which the same token is produced as output. This set of
strings is described by a rule called a pattern associated with the token. Regular expressions are
an important notation for specifying patterns. For example, the pattern for the Pascal identifier
token, id, is: id → letter (letter | digit)*.

5. What do you understand by lexical error?


Lexical errors are the errors thrown by your lexer when unable to continue. Which means that
there's no way to recognise a lexeme as a valid token for you lexer.
Experiment-3

Object: -Construction of NFA from Regular Expression


Brief Theory:-Regular expression and nondeterministic finite automaton are two abstract
representation of formal languages. While regular expressions are used e.g. to describe advanced
search patterns in "find and replace"-like operations of text processing utilities, the NFA format is
better suited for execution on a computer. Hence, this algorithm is of practical interest, since it can
be considered as a compiler from regular expression to NFA. On a more theoretical point of view,
this algorithm is a part of the proof that they both accept exactly the same languages, that is, the
regular languages.

Procedure:-

1. Start the Program.


2. Enter the regular expression R over alphabet E.
3. Decompose the regular expression R into its primitive components
4. For each component construct finite automata.
5. To construct components for the basic regular expression way that corresponding to that
way compound regular expression.
6. Stop the Program.
Program:-
#include<stdio.h>
#include<conio.h>
#include<ctype.h>
#include<string.h>
#include<graphics.h>
#include<math.h>
#include<process.h>
int minx=1000,miny=0;
void star(int *x1,int *y1,int *x2,int *y2)
{
char pr[10];
ellipse(*x1+(*x2-*x1)/2,*y2-10,0,180,(*x2-*x1)/2,70);
outtextxy(*x1-2,*y2-17,"v");
line(*x2+10,*y2,*x2+30,*y2);
outtextxy(*x1-15,*y1-3,">");
circle(*x1-40,*y1,10);
circle(*x1-80,*y1,10);
line(*x1-30,*y2,*x1-10,*y2);
outtextxy(*x2+25,*y2-3,">");
sprintf(pr,"%c",238);
outtextxy(*x2+15,*y2-9,pr);
outtextxy(*x1-25,*y1-9,pr);
outtextxy((*x2-*x1)/2+*x1,*y1-30,pr);
outtextxy((*x2-*x1)/2+*x1,*y1+30,pr);
ellipse(*x1+(*x2-*x1)/2,*y2+10,180,360,(*x2-*x1)/2+40,70);
outtextxy(*x2+37,*y2+14,"^");
if(*x1-40<minx)minx=*x1-40;
miny=*y1;
}
void star1(int *x1,int *y1,int *x2,int *y2)
{
char pr[10];
ellipse(*x1+(*x2-*x1)/2+15,*y2-10,0,180,(*x2-*x1)/2+15,70);
outtextxy(*x1-2,*y2-17,"v");
line(*x2+40,*y2,*x2+60,*y2);
outtextxy(*x1-15,*y1-3,">");
circle(*x1-40,*y1,10);
line(*x1-30,*y2,*x1-10,*y2);
outtextxy(*x2+25,*y2-3,">");
sprintf(pr,"%c",238);
outtextxy(*x2+15,*y2-9,pr);
outtextxy(*x1-25,*y1-9,pr);
outtextxy((*x2-*x1)/2+*x1,*y1-30,pr);
outtextxy((*x2-*x1)/2+*x1,*y1+30,pr);
ellipse(*x1+(*x2-*x1)/2+15,*y2+10,180,360,(*x2-*x1)/2+50,70);
outtextxy(*x2+62,*y2+13,"^");
if(*x1-40<minx)minx=*x1-40;
miny=*y1;
}
void basis(int *x1,int *y1,char x)
{
char pr[5];
circle(*x1,*y1,10);
line(*x1+30,*y1,*x1+10,*y1);
sprintf(pr,"%c",x);
outtextxy(*x1+20,*y1-10,pr);
outtextxy(*x1+23,*y1-3,">");
circle(*x1+40,*y1,10);
if(*x1<minx)minx=*x1;
miny=*y1;
}
void slash(int *x1,int *y1,int *x2,int *y2,int *x3,int *y3,int *x4,int *y4)
{
char pr[10];
int c1,c2;
c1=*x1;
if(*x3>c1)c1=*x3;
c2=*x2;
if(*x4>c2)c2=*x4;
line(*x1-10,*y1,c1-40,(*y3-*y1)/2+*y1-10);
outtextxy(*x1-15,*y1-3,">");
outtextxy(*x3-15,*y4-3,">");
circle(c1-40,(*y4-*y2)/2+*y2,10);
sprintf(pr,"%c",238);
outtextxy(c1-40,(*y4-*y2)/2+*y2+25,pr);
outtextxy(c1-40,(*y4-*y2)/2+*y2-25,pr);
line(*x2+10,*y2,c2+40,(*y4-*y2)/2+*y2-10);
line(*x3-10,*y3,c1-40,(*y3-*y1)/2+*y2+10);
circle(c2+40,(*y4-*y2)/2+*y2,10);
outtextxy(c2+40,(*y4-*y2)/2+*y2-25,pr);
outtextxy(c2-40,(*y4-*y2)/2+*y2+25,pr);
outtextxy(c2+35,(*y4-*y2)/2+*y2-15,"^");
outtextxy(c1+35,(*y4-*y2)/2+*y2+10,"^");
line(*x4+10,*y2,c2+40,(*y4-*y2)/2+*y2+10);
minx=c1-40;
miny=(*y4-*y2)/2+*y2;
}
void main()
{
int d=0,l,x1=200,y1=200,len,par=0,op[10];
int cx1=200,cy1=200,cx2,cy2,cx3,cy3,cx4,cy4;
char str[20];
int gd=DETECT,gm;
int stx[20],endx[20],sty[20],endy[20];
int pos=0,i=0;
clrscr();
initgraph(&gd,&gm,"c:\\dosapp\\tcplus\\bgi");
printf("\n enter the regular expression:");
scanf("%s",str);
len=(strlen(str));
while(i<len)
{
if(isalpha(str[i]))
{
if(str[i+1]=='*')x1=x1+40;
basis(&x1,&y1,str[i]);
stx[pos]=x1;
endx[pos]=x1+40;
sty[pos]=y1;
endy[pos]=y1;
x1=x1+40;
pos++;
}
if(str[i]=='*')
{
star(&stx[pos-1],&sty[pos-1],&endx[pos-1],&endy[pos-1]);
stx[pos-1]=stx[pos-1]-40;
endx[pos-1]=endx[pos-1]+40;
x1=x1+40;
}
if(str[i]=='(')
{
int s;
s=i;
while(str[s]!=')')s++;
if((str[s+1]=='*')&&(pos!=0))x1=x1+40;
op[par]=pos;
par++;
}
if(str[i]==')')
{
cx2=endx[pos-1];
cy2=endy[pos-1];
l=op[par-1];
cx1=stx[1];
cx2=sty[1];
par--;
if(str[i+1]=='*')
{
i++;
star1(&cx1,&cy1,&cx2,&cy2);
cx1=cx1-40;
cx2=cx2+40;
stx[1]=stx[1]-40;
endx[pos-1]=endx[pos-1]+40;
x1=x1+40;
}
if(d==1)
{
slash(&cx3,&cy3,&cx4,&cy4,&cx1,&cy1,&cx2,&cy2);
if(cx4>cx2)x1=cx4+40;
else x1=cx2+40;
y1=(y1-cy4)/2.0+cy4;
d=0;
}
}
if(str[i]=='/')
{
cx2=endx[pos-1];
cy2=endy[pos-1];
x1=200;
y1=y1+100;
if(str[i+1]=='(')
{
d=1;
cx3=cx1;
cy3=cy1;
cx4=cx2;
cy4=cy2;
}
if(isalpha(str[i+1]))
{
i++;
basis(&x1,&y1,str[i]);
stx[pos]=x1;
endx[pos]=x1+40;
sty[pos]=y1;
endy[pos]=y1;
if(str[i+1]=='*')
{
i++;
star(&stx[pos],&sty[pos],&endx[pos],&endy[pos]);
stx[pos]=stx[pos]-40;
endx[pos]=endx[pos]+40;
}
slash(&cx1,&cy1,&cx2,&cy2,&stx[pos],&sty[pos],&endx[pos],&endy[pos]);
if(cx2>endx[pos])x1=cx2+40;
else x1=endx[pos]+40;
y1=(y1-cy2)/2.0+cy2;
cx1=cx1-40;
cy1=(sty[pos]-cy1)/2.0+cy1;
cx2=cx2+40;
cy2=(endy[pos]-cy2)/2.0+cy2;
l=op[par-1];
stx[1]=cx1;
sty[1]=cy1;
endx[pos]=cx2;
endy[pos]=cy2;
pos++;
}
}
i++;
}
circle(x1,y1,13);
line(minx-30,miny,minx-10,miny);
outtextxy(minx-100,miny-10,"start");
outtextxy(minx-15,miny-3,">");
getch();
closegraph();
}
Results:

Viva-voce Questions
1. Define Non Deterministic finite automata.
A NFA is a mathematical model that consists of
A set of states S.
A set of input symbols Σ.
A transition for move from one state to an other.
A state so that is distinguished as the start (or initial) state.
A set of states F distinguished as accepting (or final) state.
A number of transition to a single symbol.

2. Define Deterministic finite automata.


DFA formally defined by 5 tuple notation M = (Q, Σ, δ, qo, F), where
Q is a finite ‘set of states’, which is non empty.
Σ is ‘input alphabets’, indicates input set.
qo is an ‘initial state’ and qo is in Q ie, qo, Σ, Q
F is a set of ‘Final states’,
δ is a ‘transmission function’ or mapping function, using this function the next state can be
determined.

3. What is regular expression?


Regular expression is a formula that describes a possible set of string.
Component of regular expression..
X the character x
. any character, usually accept a new line
[x y z] any of the characters x, y, z, …..
R? a R or nothing (=optionally as R)
R* zero or more occurrences…..
R+ one or more occurrences ……
R1R2 an R1 followed by an R2
R2R1 either an R1 or an R2.
A token is either a single string or one of a collection of strings of a certain type. If we view
the set of strings in each token class as an language, we can use the regular-expression notation
to describe tokens.

4. Why NFA to regular conversion performed?


Regular expressions are used e.g. to describe advanced search patterns in "find and replace"-
like operations of text processing utilities, the NFA format is better suited for execution on a
computer. Hence, this algorithm is of practical interest, since it can be considered as a
compiler from regular expression to NFA.
Experiment No.:4

Object: -Construction of DFA from NFA


Brief Theory:-An NFA is a Nondeterministic Finite Automaton. Nondeterministic means it can
transition to, and be in, multiple states at once (i.e. for some given input). A DFA is a
Deterministic Finite Automaton. Deterministic means that it can only be in, and transition to, one
state at a time (i.e. for some given input). Let X = (Qx, ∑, δx, q0, Fx) be an NDFA which accepts the
language L(X). We have to design an equivalent DFA Y = (Qy, ∑, δy, q0, Fy) such that L(Y) =
L(X).

Procedure:-

1. Start the program.


2. Create state table from the given NDFA.
3. Create a blank state table under possible input alphabets for the equivalent DFA.
4. Mark the start state of the DFA by q0 (Same as the NDFA).
5. Find out the combination of States {Q0, Q1,... , Qn} for each possible input alphabet.
6. Each time we generate a new DFA state under the input alphabet columns, we have to apply
step 4 again, otherwise go to step 6.
7. The states which contain any of the final states of the NDFA are the final states of the
equivalent DFA
Program:-

#include<stdio.h>
#include<conio.h>
#include<ctype.h>
#include<process.h>
typedef struct
{
int num[10],top;
}
stack;
stack s;
int mark[16][31],e_close[16][31],n,st=0;
char data[15][15];
void push(int a)
{
s.num[s.top]=a;
s.top=s.top+1;
}
int pop()
{
int a;
if(s.top==0)
return(-1);
s.top=s.top-1;
a=s.num[s.top];
return(a);
}
void epi_close(int s1,int s2,int c)
{
int i,k,f;
for(i=1;i<=n;i++)
{
if(data[s2][i]=='e')
{
f=0;
for(k=1;k<=c;k++)
if(e_close[s1][k]==i)
f=1;
if(f==0)
{
c++;
e_close[s1][c]=i;
push(i);
}
}
}
while(s.top!=0) epi_close(s1,pop(),c);
}
int move(int sta,char c)
{
int i;
for(i=1;i<=n;i++)
{
if(data[sta][i]==c)
return(i);
}
return(0);
}
void e_union(int m,int n)
{
int i=0,j,t;
for(j=1;mark[m][i]!=-1;j++)
{
while((mark[m][i]!=e_close[n][j])&&(mark[m][i]!=-1))
i++;
if(mark[m][i]==-1)mark[m][i]=e_close[n][j];
}
}
void main()
{
int i,j,k,Lo,m,p,q,t,f;
clrscr();
printf("\n enter the NFA state table entries:");
scanf("%d",&n);
printf("\n");
for(i=0;i<=n;i++)
printf("%d",i);
printf("\n");
for(i=0;i<=n;i++)
printf("------");
printf("\n");
for(i=1;i<=n;i++)
{
printf("%d|",i);
fflush(stdin);
for(j=1;j<=n;j++)
scanf("%c",&data[i][j]);
}
for(i=1;i<=15;i++)
for(j=1;j<=30;j++)
{
e_close[i][j]=-1;
mark[i][j]=-1;
}
for(i=1;i<=n;i++)
{
e_close[i][1]=i;
s.top=0;
epi_close(i,i,1);
}
for(i=1;i<=n;i++)
{
for(j=1;e_close[i][j]!=-1;j++)
for(k=2;e_close[i][k]!=-1;k++)
if(e_close[i][k-1]>e_close[i][k])
{
t=e_close[i][k-1];
e_close[i][k-1]=e_close[i][k];
e_close[i][k]=t;
}
}
printf("\n the epsilon closures are:");
for(i=1;i<=n;i++)
{
printf("\n E(%d)={",i);
for(j=1;e_close[i][j]!=-1;j++)
printf("%d",e_close[i][j]);
printf("}");
}
j=1;
while(e_close[1][j]!=-1)
{
mark[1][j]=e_close[1][j];
j++;
}
st=1;
printf("\n DFA Table is:");
printf("\n a b ");
printf("\n--------------------------------------");
for(i=1;i<=st;i++)
{
printf("\n{");
for(j=1;mark[i][j]!=-1;j++)
printf("%d",mark[i][j]);
printf("}");
while(j<7)
{
printf(" ");
j++;
}
for(Lo=1;Lo<=2;Lo++)
{
for(j=1;mark[i][j]!=-1;j++)
{
if(Lo==1)
t=move(mark[i][j],'a');
if(Lo==2)
t=move(mark[i][j],'b');
if(t!=0)
e_union(st+1,t);
}
for(p=1;mark[st+1][p]!=-1;p++)
for(q=2;mark[st+1][q]!=-1;q++)
{
if(mark[st+1][q-1]>mark[st+1][q])
{
t=mark[st+1][q];
mark[st+1][q]=mark[st+1][q-1];
mark[st+1][q-1]=t;
}
}
f=1;
for(p=1;p<=st;p++)
{
j=1;
while((mark[st+1][j]==mark[p][j])&&(mark[st+1][j]!=-1))
j++;
if(mark[st+1][j]==-1 && mark[p][j]==-1)
f=0;
}
if(mark[st+1][1]==-1)
f=0;
printf("\t{");
for(j=1;mark[st+1][j]!=-1;j++)
{
printf("%d",mark[st+1][j]);
}
printf("}\t");
if(Lo==1)
printf(" ");
if(f==1)
st++;
if(f==0)
{
for(p=1;p<=30;p++)
mark[st+1][p]=-1;
}
}
}
getch();
}
Results:

Enter the NFA state table entries: 11

(Note: Instead of '-' symbol use blank spaces in the output window)
The Epsilon Closures Are:
E(1)={12358}
E(2)={235}
E(3)={3}
E(4)={234578}
E(5)={5}
E(6)={235678}
E(7)={23578}
E(8)={8}
E(9)={9}
E(10)={10}
E(11)={11}

DFA Table is:

a b
----------------------------------------------------
{12358} {2345789} {235678}
{2345789} {2345789} {23567810}
{235678} {2345789} {235678}
{23567810} {2345789} {23567811}
{23567811} {2345789} {235678}

Viva-voce Questions
1. Define Non Deterministic finite automata.
A NFA is a mathematical model that consists of
A set of states S.
A set of input symbols Σ.
A transition for move from one state to an other.
A state so that is distinguished as the start (or initial) state.
A set of states F distinguished as accepting (or final) state.
A number of transition to a single symbol.

2. Define Deterministic finite automata.


DFA formally defined by 5 tuple notation M = (Q, Σ, δ, qo, F), where
Q is a finite ‘set of states’, which is non empty.
Σ is ‘input alphabets’, indicates input set.
qo is an ‘initial state’ and qo is in Q ie, qo, Σ, Q
F is a set of ‘Final states’,
δ is a ‘transmission function’ or mapping function, using this function the next state can be
determined.

3. Differentiate between NFA and DFA in terms of power?


NFA is more generalized form of finite automata while DFA is consolidated form of finite
automata that’s why NFA is more powerful than DFA.
Experiment No.:5

Object: -Implementation of Shift Reduce Parsing Algorithm

Brief Theory:-A shift-reduce parser scans and parses the input text in one forward pass over the text,
without backing up. (That forward direction is generally left-to-right within a line, and top-to-bottom
for multi-line inputs.) The parser builds up the parse tree incrementally, bottom up, and left to right,
without guessing or backtracking. At every point in this pass, the parser has accumulated a list of
subtrees or phrases of the input text that have been already parsed. Those subtrees are not yet joined
together because the parser has not yet reached the right end of the syntax pattern that will combine
them. A shift-reduce parser works by doing some combination of Shift steps and Reduce steps, hence
the name.

• A Shift step advances in the input stream by one symbol. That shifted symbol becomes a new
single-node parse tree.
• A Reduce step applies a completed grammar rule to some of the recent parse trees, joining them
together as one tree with a new root symbol.

Procedure:-
Grammar:
E->E+E
E->E*E
E->E/E
E->a/b
Method:
Stack Input Symbol Action
$ id1*id2$ shift
$id1 *id2 $ shift *
$* id2$ shift id2
$id2 $ shift
$ $ accept

Shift: Shifts the next input symbol onto the stack.


Reduce: Right end of the string to be reduced must be at the top of the stack.
Accept: Announce successful completion of parsing.
Error: Discovers a syntax error and call an error recovery routine

Program:-

#include<conio.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char ip_sym[15],stack[15];
int ip_ptr=0,st_ptr=0,len,i;
char temp[2],temp2[2];
char act[15];
void check();
void main()
{
clrscr();
printf("\n\n\t Shift Reduce Parser\n");
printf("\n\t***** ****** ******");
printf("\n Grammar\n\n");
printf("E->E+E\nE->E/E\n");
printf("E->E*E\nE->a/b");
printf("\n Enter the Input Symbol:\t");
gets(ip_sym);
printf("\n\n\t Stack Implementation Table");
printf("\n Stack\t\t Input Symbol\t\t Action");
printf("\n $\t\t %s$\t\t\t --",ip_sym);
strcpy(act,"shift");
temp[0]=ip_sym[ip_ptr];
temp[1]='\0';
strcat(act,temp);
len=strlen(ip_sym);
for(i=0;i<=len-1;i++)
{
stack[st_ptr]=ip_sym[ip_ptr];
stack[st_ptr+1]='\0';
ip_sym[ip_ptr]=' ';
ip_ptr++;
printf("\n$%s\t\t%s$\t\t\t%s",stack,ip_sym,act);
strcpy(act,"shift");
temp[0]=ip_sym[ip_ptr];
temp[1]='\0';
strcat(act,temp);
check();
st_ptr++;
}
st_ptr++;
check();
getch();
}
void check()
{
int flag=0;
temp2[0]=stack[st_ptr];
temp[1]='\0';
if((!strcmpi(temp2,"a"))||(!strcmpi(temp2,"b")))
{
stack[st_ptr]='E';
if(!strcmpi(temp2,"a"))
printf("\n$%s\t\t%s$\t\t\tE->a",stack,ip_sym);
else
printf("\n$%s\t\t%s$\t\t\tE->a",stack,ip_sym);
flag=1;
}
if((!strcmpi(temp2,"+"))||(strcmpi(temp2,"*"))||(!strcmpi(temp2,"/")))
{
flag=1;
}
if((!strcmpi(stack,"E+E"))||(!strcmpi(stack,"E/E"))||(!strcmpi(stack,"E*E")))
{
strcpy(stack,"E");
st_ptr=0;
if(!strcmpi(stack,"E+E"))
printf("\n$%s\t\t%s$\t\t\tE->E+E",stack,ip_sym);
else
if(!strcmpi(stack,"E/E"))
printf("\n$%s\t\t\t%s$\t\tE->E/E",stack,ip_sym);
else
printf("\n$%s\t\t%s$\t\t\tE->E*E",stack,ip_sym);
flag=1;
}
if(!strcmpi(stack,"E")&&ip_ptr==len)
{
printf("\n$%s\t\t%s$\t\t\tAccept",ip_sym);
getch();
exit(0);
}
if(flag==0)
{
printf("\n %s \t\t\t %s \t\t Reject",stack,ip_sym);
}
return;
}
Results:

Shift Reduce Parser


***** ****** *****
Grammar
E->E+E
E->E/E
E->E*E
E->a/b
Enter the input symbol: if(a*b)
Stack Implementation Table
Stack Input Symbol Action
$ if(a*b)$ --
$i f(a*b)$ shift i
$if (a*b)$ shift f
$if( a*b)$ shift (
$if(a *b)$ shift a
$if(E *b)$ E->a
$if(E* b)$ shift *
$if(E* b )$ shift b
$if(E*E )$ E->b
$if(E*E) $ shift )
$if(E) $ E->E*E
$if(E) $ reject

Viva-voce Questions
1. Define parse tree.
A derivation can be conveniently represented by a derivation tree( parse tree).
o The root is labeled by the start symbol.
o Each leaf is labeled by a token or ε.
o Each interior none is labeled by a nonterminal symbol.
o When a production A→x1… xn is derived, nodes labeled by x1… xn are made as children
nodes of node labeled by A.
• root : the start symbol
• internal nodes : nonterminal
• leaf nodes : terminal

2. What is Shift reduce parsing?


A shift-reduce parser triesto reduce thegiveninput stringinto the startingsymbol. At each
reductionstep, a substring of the inputmatching tothe rightside of a production ruleis replaced
by the non-terminalat the leftside of thatproduction rule. Ifthesubstringis chosencorrectly, the
rightmost derivationof that string is createdin thereverse order.

3. Define bottom up parsing?

• bottom-upparsercreates the parse treeof the giveninput starting from leavestowards


theroot.
• A bottom-upparser triestofindtheright-most derivation of the given inputin the
reverseorder.
(a) S ... (the right-most derivation of )
(b) (the bottom-up parser findsthe right-most derivation in thereverseorder)
• Bottom-up parsing is also known asshift-reduce parsingbecause its twomain actions
areshiftand reduce.
– At each shiftaction, the current symbol inthe input string is pushed toa stack.

– At each reductionstep, the symbols at the top of thestack (thissymbol sequence


istheright side of a production) will replacedbythenon-terminalat theleftside
ofthatproduction.
– Thereare also two moreactions: acceptanderror.

4. What is handle?
Informally,a handleof a string is a substringthat matches the right side of a production rule.
-But notevery substringthat matches theright sideof a productionrule ishandle.
A handle of arightsententialform ( )is a production ruleA and a position
of where the string may be foundand replacedby A to produce the previous right-
sententialform ina rightmostderivationof .

S A
Experiment No.:6
Object: -Implementation of Operator Precedence Parser
Brief Theory:-An operator-precedence parser is a simple shift-reduce parser that is capable of
parsing a subset of LR(1) grammars. More precisely, the operator-precedence parser can parse all
LR(1) grammars where two consecutive nonterminals never appear in the right-hand side of any rule.

Operator-precedence parsers are not used often in practice; however they do have some properties
that make them useful within a larger design. First, they are simple enough to write by hand, which
is not generally the case with more sophisticated right shift-reduce parsers. Second, they can be
written to consult an operator table at run time, which makes them suitable for languages that can add
to or change their operators while parsing.

Procedure:-

1- Let the input string to be initially the stack contains, when the reduce action takes place we
have to reach create parent child relationship.
2- See IP to pointer to the first symbol of input string and repeat forever if only $ is on the input
accept and break else begin.
3- Let 'd’ be the top most terminal on the stack and 'b' be current input IF(a<b) or a=b then Begin
push 'b' onto the stack.
4- Advance Input to the stack to the next Input symbol end;
else if(a>b)
5- Repeat pop the stack until the top most terminal is related by < to the terminal most recently
popped else error value routine end;

Program:-
#include<stdio.h>
#include<conio.h>
#include<string.h>
#include<ctype.h>
char q[9][9]={
{'>','>','<','<','<','<','>','<','>' },
{'>','>','<','<','<','<','>','<','>' },
{'>','>','>','>','<','<','>','<','>' },
{'>','>','>','>','<','<','>','<','>' },
{'>','>','<','<','<','<','>','<','>' },
{'<','<','<','<','<','<','=','<','E' },
{'>','>','>','>','>','E','>','E','>' },
{'>','>','>','>','>','E','>','E','>' },
{'<','<','<','<','<','<','E','<','A' }
};
char s[30],st[30],qs[30];
int top=-1,r=-1,p=0;
void push(char a)
{
top++;
st[top]=a;
}
char pop()
{
char a;
a=st[top];
top--;
return a;
}
int find(char a)
{
switch(a)
{
case '+':return 0;
case '-':return 1;
case '*':return 2;
case '/':return 3;
case '^':return 4;
case '(':return 5;
case ')':return 6;
case 'a':return 7;
case '$':return 8;
default :return -1;
}
}
void display(char a)
{
printf("\n Shift %c",a);
}
void display1(char a)
{
if(isalpha(a))
printf("\n Reduce E->%c",a);
else if((a=='+')||(a=='-')||(a=='*')||(a=='/')||(a=='^'))
printf("\n Reduce E->E%cE",a);
else if(a==')')
printf("\n Reduce E->(E)");
}
intrel(char a,char b,char d)
{
if(isalpha(a)!=0)
a='a';
if(isalpha(b)!=0)
b='a';
if(q[find(a)][find(b)]==d)
return 1;
else
return 0;
}
void main()
{
char s[100];
int i=-1;
clrscr();
printf("\n\t Operator Preceding Parser\n");
printf("\n Enter the Arithmetic Expression End with $..");
gets(s);
push('$');
while(i)
{
if((s[p]=='$')&&(st[top]=='$'))
{
printf("\n\nAccepted");
break;
}
else if(rel(st[top],s[p],'<')||rel(st[top],s[p],'='))
{
display(s[p]);
push(s[p]);
p++;
}
else if(rel(st[top],s[p],'>'))
{
do
{
r++;
qs[r]=pop();
display1(qs[r]);
}
while(!rel(st[top],qs[r],'<'));
}
}
getch();
}
Results:

Enter the Arithmetic Expression End with $: a-(b*c)^d$


Shift a
Reduce E->a
Shift -
Shift (
Shift b
Reduce E->b
Shift *
Shift c
Reduce E->c
Reduce E->E*E
Shift )
Reduce E->(E)
Shift ^
Shift d
Reduce E->d
Reduce E->E^E
Reduce E->E-E
Accepted

Viva-voce Questions

1. What is Operator precedence parsing?


An operator-precedence parser is a simple shift-reduce parser that is capable of parsing a
subset of LR(1) grammars. More precisely, the operator-precedence parser can parse all
LR(1) grammars where two consecutive nonterminals never appear in the right-hand
side of any rule.
2. Define operator precedence grammar?
In operator precedence grammar no two consecutive non-terminals appears and there is
always a operator between two non-terminals.
3. Define operator precedence relation.
In operator-precedence parsing,we define three disjoint precedence relations
betweencertainpairs of terminals.

a<.b bhas higher precedence than aa=·b


b has same precedence as a
a.>b b has lower precedence than a
The determination ofcorrect precedence relations between terminals are based on the
traditional notions of associativity and precedence of operators.
4. Explain advantage and disadvantage of operator precedence grammar.

• Advantages:
– simple
– powerful enough for expressions in programming languages
• Disadvantages:
– It cannot handle the unary minus(the lexical analyzer should handle the unary
minus).
– Small class of grammars.
Difficult to decide which language is recognized by the grammar

Experiment No.:7
Object: -Implementation of Recursive Descent Parser
Brief Theory:-A predictive parser is a recursive descent parser that does not require backtracking.
Predictive parsing is possible only for the class of LL(k) grammars, which are the context-free
grammars for which there exists some positive integer k that allows a recursive descent parser to
decide which production to use by examining only the next k tokens of input. The LL(k) grammars
therefore exclude all ambiguous grammars, as well as all grammars that contain left recursion. Any
context-free grammar can be transformed into an equivalent grammar that has no left recursion, but
removal of left recursion does not always yield an LL(k) grammar. A predictive parser runs in linear
time.

Recursive descent with backtracking is a technique that determines which production to use by trying
each production in turn. Recursive descent with backtracking is not limited to LL(k) grammars, but
is not guaranteed to terminate unless the grammar is LL(k). Even when they terminate, parsers that
use recursive descent with backtracking may require exponential time.

Procedure:-

Begin
T()
E_prime();
print E-> TE'
end
procedureeprime():
ifip_sym+='+' then
begin
advance();
T();
eprime();
prime E'->TE'
end
else
print E'->e
procedure T();
begin
e();
Tprime();
print T->FT';
end;
procedureTprime();
ifip_sym='*' then
begin
advance();
F();
Tprime()
print T'->T*FT'
end
else print T'->e
procedure F()
ifip_sym =id then
begin
advance();
print->id
end
else
Error();
end;
else
Error();

Program:-
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<string.h>
char ip_sym[15],ip_ptr=0;
void e_prime();
void t();
void e();
void t_prime();
void f();
void advance();
void e()
{
printf("\n\t\tE'------->TE'");
t();
e_prime();
}
void e_prime()
{
if(ip_sym[ip_ptr]=='+')
{
printf("\n\t\tE'------->+TE'");
advance();
t();
e_prime();
}
else
printf("\n\t\tE'----->e'");
}
void t()
{
printf("\n\t\tT'------->FT'"); f();
t_prime();
}
void t_prime()
{
if(ip_sym[ip_ptr]=='*')
{
printf("\n\t\tT------>*FT'"); advance();
f();
t_prime();
}
else
{
printf("\n\t\tT'----->e");
}
}
void f()
{
if((ip_sym[ip_ptr]=='i')||(ip_sym[ip_ptr]=='j'))
{
printf("\n\t\tF------>i"); advance();
}
else
{
if(ip_sym[ip_ptr]=='(')
{
advance();
e();
if(ip_sym[ip_ptr]==')')
{
advance();
printf("\n\t\tF----->(E)");
}
else
{
printf("\n\t\tSyntax Error");
getch();
exit(1);
}
}
}
}
void advance()
{
ip_ptr++;
}
void main()
{
int i;
clrscr();
printf("\n\t\tGRAMMER WITHOUT RECURSION");
printf("\n\t\tE------>TE'\n\t\tE'/e\r\t\tT----->FT");
printf("\n\t\tT------>*FT/e\n\t\tF------>(E)/id");
printf("\n\t\tEnter the Input Symbol: ");
gets(ip_sym);
printf("\n\t\tSequence of Production Rules");
e();
getch();
}

Results:

GRAMMER WITHOUT RECURSION


E------>TE'
T----->FT
T------>*FT/e
F------>(E)/id
Enter the Input Symbol: T
Sequence of Production Rules
E'------->TE'
T'------->FT'
T'----->e
E'----->e'
Viva-voce Questions

1. What is LL parser?
An LL Parser accepts LL grammar. LL grammar is a subset of context-free grammar but with
some restrictions to get the simplified version, in order to achieve easy implementation. LL
grammar can be implemented by means of both algorithms namely, recursive-descent or table-
driven.

2. Define top down parser.


Top-downparsers check to see if a string can be generated by a grammar by creating a parse
tree starting from the initial symbol and working down. Bottom-up parsers, however, check
to see a string can be generated from a grammar by creating a parse tree from the leaves, and
working up.

3. What is recursive descent parsing?


Recursive descent parsing, also known as LL(k) parsing where the first L stands for left-to-
right, the second L stands for leftmost-derivation, and k indicates k-symbol lookahead.
Therefore, a parser using the single symbol look-ahead method and top-down parsing without
backtracking is called LL(1) parser. In the following sections, we will also use an extended
BNF notation in which some regulation expression operators are to be incorporated.

4. Differentiate between recursive descent parsing and predictive parsing?


a. Recursive-DescentParsing
i. Backtrackingis needed (Ifa choice ofa production ruledoes notwork,we
backtrack totry other alternatives.)
ii. It is a generalparsingtechnique, but notwidely used.
iii. Not efficient
b. Predictive Parsing
i. No backtracking
ii. Efficient
iii. Needs aspecial form ofgrammars i.e. LL (1) grammars.
iv. Recursive Predictive Parsingis a special formof RecursiveDescentparsing
withoutbacktracking.
v. Non-Recursive (Table Driven)PredictiveParseris also knownas LL (1) parser.

Experiment No.:8
Object: -Implementation of Code Optimization Techniques
Brief Theory:-In optimization, high-level general programming constructs are replaced by very
efficient low-level programming codes. A code optimizing process must follow the three rules given
below:

• The output code must not, in any way, change the meaning of the program.

• Optimization should increase the speed of the program and if possible, the program should
demand less number of resources.

• Optimization should itself be fast and should not delay the overall compiling process.

Efforts for an optimized code can be made at various levels of compiling the process.

• At the beginning, users can change/rearrange the code or use better algorithms to write the code.

• After generating intermediate code, the compiler can modify the intermediate code by address
calculations and improving loops.

• While producing the target machine code, the compiler can make use of memory hierarchy and
CPU registers.

Optimization can be categorized broadly into two types : machine independent and machine
dependent.

Procedure:-

Take the input code then perform the following:


• Dead Code Elimination
• Common Sub-expression Elimination
• Copy Propagation
• Code Motion
• Induction Variable Elimination
• Reduction In Strength
Program:-
#include<stdio.h>
#include<conio.h>
#include<string.h>
struct op
{
char l;
char r[20];
}
op[10],pr[10];
void main()
{
int a,i,k,j,n,z=0,m,q;
char *p,*l;
char temp,t;
char *tem;
clrscr();
printf("Enter the Number of Values:");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("left: ");
op[i].l=getche();
printf("\tright: ");
scanf("%s",op[i].r);
}
printf("Intermediate Code\n") ;
for(i=0;i<n;i++)
{
printf("%c=",op[i].l);
printf("%s\n",op[i].r);
}
for(i=0;i<n-1;i++)
{
temp=op[i].l;
for(j=0;j<n;j++)
{
p=strchr(op[j].r,temp);
if(p)
{
pr[z].l=op[i].l;
strcpy(pr[z].r,op[i].r);
z++;
}
}
}
pr[z].l=op[n-1].l;
strcpy(pr[z].r,op[n-1].r);
z++;
printf("\nAfter Dead Code Elimination\n");
for(k=0;k<z;k++)
{
printf("%c\t=",pr[k].l);
printf("%s\n",pr[k].r);
}
for(m=0;m<z;m++)
{
tem=pr[m].r;
for(j=m+1;j<z;j++)
{
p=strstr(tem,pr[j].r);
if(p)
{
t=pr[j].l;
pr[j].l=pr[m].l;
for(i=0;i<z;i++)
{
l=strchr(pr[i].r,t) ;
if(l)
{
a=l-pr[i].r;
printf("pos: %d",a);
pr[i].r[a]=pr[m].l;
}
}
}
}
}
printf("Eliminate Common Expression\n");
for(i=0;i<z;i++)
{
printf("%c\t=",pr[i].l);
printf("%s\n",pr[i].r);
}
for(i=0;i<z;i++)
{
for(j=i+1;j<z;j++)
{
q=strcmp(pr[i].r,pr[j].r);
if((pr[i].l==pr[j].l)&&!q)
{
pr[i].l='\0';
strcpy(pr[i].r,'\0');
}
}
}
printf("Optimized Code\n");
for(i=0;i<z;i++)
{
if(pr[i].l!='\0')
{
printf("%c=",pr[i].l);
printf("%s\n",pr[i].r);
}
}
getch();
}

Results:

Enter the Number of Values: 5


Left: a right: 9
Left: b right: c+d
Left: e right: c+d
Left: f right: b+e
Left: r right: f
Intermediate Code
a=9
b=c+d
e=c+d
f=b+e
r=:f
After Dead Code Elimination
b =c+d
e =c+d
f =b+e
r =:f
Eliminate Common Expression
b =c+d
b =c+d
f =b+b
r =:f
Optimized Code
b=c+d
f=b+b
r=:f
Viva-voce Questions
1. What is machine independent optimization?
In this optimization, the compiler takes in the intermediate code and transforms a part of the code that
does not involve any CPU registers and/or absolute memory locations. For example:

2. What is machine dependent optimization?


Machine-dependent optimization is done after the target code has been generated and when the code
is transformed according to the target machine architecture. It involves CPU registers and may have
absolute memory references rather than relative references. Machine-dependent optimizers put efforts
to take maximum advantage of memory hierarchy.

3. Define Basic Blocks.


Source codes generally have a number of instructions, which are always executed in sequence and
are considered as the basic blocks of the code. These basic blocks do not have any jump statements
among them, i.e., when the first instruction is executed, all the instructions in the same basic block
will be executed in their sequence of appearance without losing the flow control of the program.

4. What do you understand by loop optimization?


Most programs run as a loop in the system. It becomes necessary to optimize the loops in order to
save CPU cycles and memory. Loops can be optimized by the following techniques:

• Invariant code : A fragment of code that resides in the loop and computes the same value at each
iteration is called a loop-invariant code. This code can be moved out of the loop by saving it to
be computed only once, rather than with each iteration.

• Induction analysis : A variable is called an induction variable if its value is altered within the
loop by a loop-invariant value.

• Strength reduction : There are expressions that consume more CPU cycles, time, and memory.
These expressions should be replaced with cheaper expressions without compromising the output
of expression. For example, multiplication (x * 2) is expensive in terms of CPU cycles than (x <<
1) and yields the same result.

5. Explain Dead code elimination.

Dead code is one or more than one code statements, which are:Either never executed or unreachable,
Or if executed, their output is never used. Thus, dead code plays no role in any program operation
and therefore it can simply be eliminated.
Experiment :9

Object: -Implementation of Code Generator


Brief Theory:-Code generation can be considered as the final phase of compilation. Through post
code generation, optimization process can be applied on the code, but that can be seen as a part of
code generation phase itself. The code generated by the compiler is an object code of some lower-
level programming language, for example, assembly language. We have seen that the source code
written in a higher-level language is transformed into a lower-level language that results in a lower-
level object code, which should have the following minimum properties:

• It should carry the exact meaning of the source code.


• It should be efficient in terms of CPU usage and memory management.

Procedure:-

1-Start
2- Get address code sequence.
3- Determine current location of 3 using address (for 1st operand).
4- If current location not already exist generate move (B,O).
5- Update address of A(for 2nd operand).
6- If current value of B and () is null, exit.
7- If they generate operator () A,3 ADPR.
8- Store the move instruction in memory
9- Stop.

Program:-

#include<stdio.h>
#include<conio.h>
#include<string.h>
#include<ctype.h>
#include<graphics.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();
}
}

Results:

Enter the Three Address Code:


a=b+c
c=a*c
exit

The Equivalent Assembly Code is:


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

Viva-voce Questions
1. Define DAG.

Directed Acyclic Graph (DAG) is a tool that depicts the structure of basic blocks, helps to see the
flow of values flowing among the basic blocks, and offers optimization too. DAG provides easy
transformation on basic blocks. DAG can be understood here:

• Leaf nodes represent identifiers, names or constants.

• Interior nodes represent operators.

• Interior nodes also represent the results of expressions or the identifiers/name where the values
are to be stored or assigned.

2. What do you understand by Unreachable code?


Unreachable code is a part of the program code that is never accessed because of programming
constructs. Programmers may have accidently written a piece of code that can never be reached.

3. What is code generator?


A code generator is expected to have an understanding of the target machine’s runtime environment
and its instruction set.
4. Explain Strength Reduction method.
There are operations that consume more time and space. Their ‘strength’ can be reduced by replacing
them with other operations that consume less time and space, but produce the same result.
For example, x * 2 can be replaced by x << 1, which involves only one left shift. Though the output
of a * a and a2 is same, a2 is much more efficient to implement.

You might also like