CD Lab Manual
CD Lab Manual
KCS-552
CREDIT: 1
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:-
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)*.
Procedure:-
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:
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)*.
Procedure:-
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.
Procedure:-
#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:
(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}
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.
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
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:
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
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:
Viva-voce Questions
• 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:
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.
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:-
Results:
• 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.
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
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:
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:
• Interior nodes also represent the results of expressions or the identifiers/name where the values
are to be stored or assigned.