0% found this document useful (0 votes)
48 views68 pages

Compiler Design Lab

The document describes implementing a lexical analyzer using the LEX tool. LEX is used to specify a lexical analyzer by creating a program in the LEX language. The LEX compiler then produces a C program that implements the lexical analyzer as a finite state machine using the regular expressions from the LEX source. The C program is then compiled to produce an executable that transforms an input stream into a sequence of tokens. The algorithm involves specifying declarations, translation rules, and auxiliary procedures in the LEX source to build the lexical analyzer.

Uploaded by

Ashay Sn
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
48 views68 pages

Compiler Design Lab

The document describes implementing a lexical analyzer using the LEX tool. LEX is used to specify a lexical analyzer by creating a program in the LEX language. The LEX compiler then produces a C program that implements the lexical analyzer as a finite state machine using the regular expressions from the LEX source. The C program is then compiled to produce an executable that transforms an input stream into a sequence of tokens. The algorithm involves specifying declarations, translation rules, and auxiliary procedures in the LEX source to build the lexical analyzer.

Uploaded by

Ashay Sn
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 68

Compiler Laboratory

EX.NO:1 IMPLEMENTATION OF SYMBOL TABLE


DATE:

AIM:
To write a C program to implement a symbol table.

INTRODUCTION:
A symbol table is a data structure used by a language translator such as a compiler or interpreter,
where each identifier in a program's source code is associated with information relating to its declaration
or appearance in the source.
Possible entries in a symbol table:
• Name: a string.
• Attribute:
. Reserved word
. Variable name
. Type name
. Procedure name
. Constant name
• Data type.
• Scope information: where it can be used.
• Storage allocation

ALGORITHM:

1. Start the program.


2. Get the input from the user with the terminating symbol ‘$’.
3. Allocate memory for the variable by dynamic memory allocation function.
4. If the next character of the symbol is an operator then only the memory is allocated.
5. While reading ,the input symbol is inserted in to symbol table along with its memory address.
6. The steps are repeated till ‘$’ is reached.
7. To reach a variable, enter the variable to the searched and symbol table has been checked for
corresponding variable, the variable along its address is displayed as result.
8. Stop the program.

Name: Register No:


Compiler Laboratory

PROGRAM:
#include<stdio.h>
//#include<conio.h>
#include<malloc.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
void main()
{
int i=0,j=0,x=0,n,flag=0;
void *p,*add[15];
char ch,srch,b[15],d[15],c;
//clrscr();
printf("expression terminated by $:");
while((c=getchar())!='$')
{
b[i]=c;
i++;
}
n=i-1;
printf("given expression:");
i=0;
while(i<=n)
{
printf("%c",b[i]);
i++;
}
printf("symbol table\n");
printf("symbol\taddr\ttype\n");
while(j<=n)
{
c=b[j];
if(isalpha(toascii(c)))
{
if(j==n)
{
p=malloc(c);
add[x]=p;
d[x]=c;
printf("%c\t%d\tidentifier\n",c,p);
}
else
{
ch=b[j+1];
if(ch=='+'||ch=='-'||ch=='*'||ch=='=')
{
p=malloc(c);
add[x]=p;
d[x]=c;

Name: Register No:


Compiler Laboratory

printf("%c\t%d\tidentifier\n",c,p);
x++;
}
}
}
j++;
}
printf("the symbol is to be searched\n");
srch=getch();
for(i=0;i<=x;i++)
{
if(srch==d[i])
{
printf("symbol found\n");
printf("%c%s%d\n",srch,"@address",add[i]);
flag=1;
}
}
if(flag==0)
printf("symbol not found\n");
//getch();
}

OUTPUT:

RESULT:
Thus the C program to implement the Symbol table was executed and the output is verified
successfully.

Name: Register No:


Compiler Laboratory

EX.NO:2 DEVELOP A LEXICAL ANALYZER TO RECOGNIZE A FEW PATTERNS IN C


DATE:

AIM:
To write a C program to develop a lexical analyzer to recognize a few patterns in c.
INTRODUCTION:
Lexical analysis is the process of converting a sequence of characters (such as in a computer
program or web page) into a sequence of tokens (strings with an identified "meaning"). A program that
performs lexical analysis may be called a lexer, tokenizer, or scanner.

Token
A token is a structure representing a lexeme that explicitly indicates its categorization for the
purpose of parsing. A category of tokens is what in linguistics might be called a part-of-speech. Examples
of token categories may include "identifier" and "integer literal", although the set of token categories
differ in different programming languages. The process of forming tokens from an input stream of
characters is called tokenization. Consider this expression in the C programming language:
sum = 3 + 2;
Tokenized and represented by the following table:
Lexeme Token category
sum "Identifier"
= "Assignment operator"
3 "Integer literal"
+ "Addition operator"
2 "Integer literal"
; "End of statement"

ALGORITHM:
1. Start the program.
2. Include the header files.
3. Allocate memory for the variable by dynamic memory allocation function.
4. Use the file accessing functions to read the file.
5. Get the input file from the user.
6. Separate all the file contents as tokens and match it with the functions.
7. Define all the keywords in a separate file and name it as key.c.
8. Define all the operators in a separate file and name it as oper.c.
9. Give the input program in a file and name it as input.c.

Name: Register No:


Compiler Laboratory

10. Finally print the output after recognizing all the tokens.
11. Stop the program.

PROGRAM:
#include<stdio.h>
#include<conio.h>
#include<ctype.h>
#include<string.h>
void main()
{
FILE *fi,*fo,*fop,*fk;
int flag=0,i=1;
char c,t,a[15],ch[15],file[20];
clrscr();
printf("\n Enter the File Name:");
scanf("%s",&file);
fi=fopen(file,"r");
fo=fopen("inter.c","w");
fop=fopen("oper.c","r");
fk=fopen("key.c","r");
c=getc(fi);
while(!feof(fi))
{
if(isalpha(c)||isdigit(c)||(c=='['||c==']'||c=='.'==1))
fputc(c,fo);
else
{
if(c=='\n')
fprintf(fo,"\t$\t");
else
fprintf(fo,"\t%c\t",c);
}
c=getc(fi);
}
fclose(fi);
fclose(fo);
fi=fopen("inter.c","r");
printf("\n Lexical Analysis");
fscanf(fi,"%s",a);
printf("\n Line: %d\n",i++);
while(!feof(fi))
{
if(strcmp(a,"$")==0)
{
printf("\n Line: %d \n",i++);
fscanf(fi,"%s",a);
}
fscanf(fop,"%s",ch);
while(!feof(fop))

Name: Register No:


Compiler Laboratory

{
if(strcmp(ch,a)==0)
{
fscanf(fop,"%s",ch);
printf("\t\t%s\t:\t%s\n",a,ch);
flag=1;
} fscanf(fop,"%s",ch);
}
rewind(fop);
fscanf(fk,"%s",ch);
while(!feof(fk))
{
if(strcmp(ch,a)==0)
{
fscanf(fk,"%k",ch);
printf("\t\t%s\t:\tKeyword\n",a);
flag=1;
}
fscanf(fk,"%s",ch);
}
rewind(fk);
if(flag==0)
{
if(isdigit(a[0]))
printf("\t\t%s\t:\tConstant\n",a);
else
printf("\t\t%s\t:\tIdentifier\n",a);
}
flag=0;
fscanf(fi,"%s",a); }
getch();
}

Key.C:
int
void
main
char
if
for
while
else
printf
scanf
FILE
include
stdio.h
conio.h
iostream.h

Name: Register No:


Compiler Laboratory

Oper.C:
( open para
) closepara
{ openbrace
} closebrace
< lesser
> greater
" doublequote
' singlequote
: colon
; semicolon
# preprocessor
= equal
== asign
% percentage
^ bitwise
& reference
* star
+ add
- sub
\ backslash
/ slash

Input.C:

#include "stdio.h"
#include "conio.h"
void main()
{
int a=10,b,c;
a=b*c;
getch();
}

Name: Register No:


Compiler Laboratory

OUTPUT:

RESULT:
Thus the above program for developing the lexical analyzer and recognizing the few patterns in c
is executed successfully and the output is verified.

Name: Register No:


Compiler Laboratory

EX.NO:3 IMPLEMENTATION OF LEXICAL ANALYZER USING LEX TOOL


DATE:

AIM:
To write a program to implement the Lexical Analyzer using Lex Tool.
INTRODUCTION:
o A language for specifying lexical analyzer.
o There is a wide range of tools for construction of lexical analyzer. The majority of these tools are
based on regular expressions.
o The one of the traditional tools of that kind is lex.We refer to the tool as the lex compiler and to its
input specification as the lex language.
LEX:
o The lex is used in the manner depicted.A specification of the lexical analyzer is preferred by
creating a program lex.l in the lex language.
o Then lex,l is run through the lex compiler to produce a ‘c’program lex.yy.c
o The program lex.yy.c consists of a tabular representation of a transition diagram constructed from
the regular expression of lex.l together with a standard routine that uses the table of recognize
leximes.
o Lex.yy.c is run through the ‘c’ compiler to produce as object program a.out,which is the lexical
analyzer that transform as input stream into sequence of tokens.
o Creating a lexical analyzer with lex is shown in below.
LEX SOURCE

Name: Register No:


Compiler Laboratory

ALGORITHM:
1.    Start the program.
2.    Lex program consists of three parts.
a.    Declaration         %%
b.    Translation rules %%
c.    Auxiliary procedure.
3.    The declaration section includes declaration of variables, maintest, constants and regular
definitions.
4.    Translation rule of lex program are statements of the form
a.    P1 {action}
b.    P2 {action}
c.    …
d.    …
e.    Pn {action}
5.    Write a program in the vi editor and save it with .l extension.
6.    Compile the lex program with lex compiler to produce output file as lex.yy.c.
eg  $ lex filename.l
     $ gcc lex.yy.c -ll
7.    Compile that file with C compiler and verify the output.

PROGRAM:
Program name: lexana.l
%{
int COMMENT=0;
%}
identifier[a-z][A-Z0-9]*
digit[0-9]
%%
#.* {printf("\n%s is a peprocessor diective",yytext);}
"int" |
"float" |
"char" |
"double" |
"for" |
"do" |
"if" |
"else" |
"break" |
"continue" |

Name: Register No:


Compiler Laboratory

"void" |
"case" |
"long" |
"struct" |
"typedef" |
"goto" |
"const" |
"while" |
"printf" |
"scanf" |
"switch" |
"main()" |
"return" {printf("\n\t%s is a keyword",yytext);}
"\*" {COMMENT=1;}
"*/" {COMMENT=0;}
\{ {if(!COMMENT) printf("\nblocks starts");}
\} {if(!COMMENT) printf("\n block end");}
{identifier} {if(!COMMENT) printf("\n\t%s is an identifier",yytext);}
\".*\" {if(!COMMENT) printf("\n\t%s is a string",yytext);}
{digit} {if(!COMMENT) printf("\n\t%s is a number",yytext);}
\)(\;)? {if(!COMMENT) printf("\n\t"); ECHO; printf("\n");}
\ECHO;
= {if(!COMMENT) printf("\n\t= is an assignment operator");}
\<= |
\>= |
\< |
== |
\> {if(!COMMENT) printf("\n\t%s is an relational operator");}
%%
int main(int argc,char **argv)
{
if(argc>1)
{
FILE *f1;
f1=fopen("file.c","r");
if(!f1)
{
printf("could not open%s\n",argv[1]);
exit(0);
}
yyin=f1;
}
yylex();
printf("\n");
return 1;
}
int yywrap()
{
return 0;

Name: Register No:


Compiler Laboratory

}
INPUT PROGRAM:
#include<stdio.h>
void main()
{
int a,b,c;
printf("enter the value for a,b");
scanf("%d%d",&a,&b)';
c=a+b;
printf("the value of c:%d",&c);
}

OUTPUT:

RESULT:
Thus the above program is compiled and executed successfully using the LEX tool and the sample
output is verified.

Name: Register No:


Compiler Laboratory

EX.NO:4 a PROGRAM TO RECOGNIZE A VALID ARITHMETIC


DATE: EXPRESSION USING YACC

AIM:
To write a Program to recognize a valid arithmetic expression that uses operator +, – , * and /.
INTRODUCTION
1. Introduction

The unix utility yacc (Yet Another Compiler Compiler) parses a stream of token, typically generated by
lex, according to a user-specified grammar.
2. Structure of a yacc file
A yacc file looks much like a lex file:
...definitions...
%%
...rules...
%%
...code...
In the example you just saw, all three sections are present:

Definitions
All code between %{ and %} is copied to the beginning of the resulting C file.
Rules
A number of combinations of pattern and action: if the action is more than a single command it needs to
be in braces.
Code
This can be very elaborate, but the main ingredient is the call to yylex, the lexical Analyzer. If the code
segment is left out, a default main is used which only calls yylex.

3. Definitions section

There are three things that can go in the definitions section:


C code Any code between %{ and %} is copied to the C file. This is typically used for defining file
variables, and for prototypes of routines that are defined in the code segment.
Definitions The definitions section of a lex file was concerned with characters; in yacc this is tokens.
These token definitions are written to a .h file when yacc compiles this file.
Associativity Rules These handle associativity and priority of operators.

4. Lex Yacc interaction


Conceptually, lex parses a file of characters and outputs a stream of tokens; yacc accepts stream of
tokens and parses it, performing actions as appropriate. In practice, they are more tightly coupled. If your
lex program is supplying a tokenizer, the yacc program will repeatedly call theyylex routine. The lex rules
will probably function by calling return every time they have parsed a token. We will now see the way lex
returns information in such a way that yacc can use it for parsing.

4.1 The shared header file of return codes

Name: Register No:


Compiler Laboratory

If lex is to return tokens that yacc will process, they have to agree on what tokens there are. This is done
as follows.
• The yacc file will have token definitions
%token NUMBER
in the definitions section.
• When the yacc file is translated with yacc -d, a header file y.tab.h is created that has
definitions like
#define NUMBER 258
This file can then be included in both the lex and yacc program.
• The lex file can then call return NUMBER, and the yacc program can match on this token.
The return codes that are defined from %TOKEN definitions typically start at around 258, so that single
characters can simply be returned as their integer value:
/* in the lex program */
[0-9]+ {return NUMBER}
[-+*/] {return *yytext}
/* in the yacc program */
sum : TERMS ’+’ TERM

4.2 Return values


In the above, very sketchy example, lex only returned the information that there was a number, not the
actual number. For this we need a further mechanism. In addition to specifying the return code, the lex
parse can return a symbol that is put on top of the stack, so that yacc can access it. This symbol is returned
in the variable yylval. By default, this is defined as an int, so the lex program would have
extern int llval;
%%
[0-9]+ {llval=atoi(yytext); return NUMBER;}
If more than just integers need to be returned, the specifications in the yacc code become more
complicated. Suppose we want to return double values, and integer indices in a table.
The following three actions are needed.
1. The possible return values need to be stated:
%union {int ival; double dval;}
2. These types need to be connected to the possible return tokens:

%token <ival> INDEX


%token <dval> NUMBER
3. The types of non-terminals need to be given:
%type <dval> expr
%type <dval> mulex

%type <dval> term


The generated .h file will now have
#define INDEX 258
#define NUMBER 259
typedef union {int ival; double dval;} YYSTYPE;
extern YYSTYPE yylval;

5. Rules section
The rules section contains the grammar of the language you want to parse. This looks like name1 : THING
something OTHERTHING {action}

Name: Register No:


Compiler Laboratory

| othersomething THING {other action}


name2 : .....
This is the general form of context-free grammars, with a set of actions associated with each matching
right-hand side. It is a good convention to keep non-terminals (names that can be expanded further) in
lower case and terminals (the symbols that are finally matched) in upper case.
The terminal symbols get matched with return codes from the lex tokenizer. They are typically defines
coming from %token definitions in the yacc program or character values;

6. User code section


The minimal main program is
int main()
{
yyparse();
return 0;
}
Extensions to more ambitious programs should be self-evident. In addition to the main program, the code
section will usually also contain subroutines, to be used either in the yacc or the lex program

ALGORITHM:
1. Start the program.
2. In lex program start lex specification with declarative section.
3. In translating rule section define data type identifier along with their action.
4. In main function open the input file in read mode.
5. The key words, identifiers and data type are defined.
6. Stop the program.

PROGRAM:
Program name: variable_test.l
%{
/* This LEX program returns the tokens for the Expression */
#include "y.tab.h"
%}
%%
"int " {return INT;}
"float" {return FLOAT;}
"double" {return DOUBLE;}
[a-zA-Z]*[0-9]*{
printf("\nIdentifier is %s",yytext);
return ID;
}
return yytext[0];
\n return 0;
int yywrap()
{
return 1;
}

Name: Register No:


Compiler Laboratory

Program name: variable_test.y


%{
#include
/* This YACC program is for recognising the Expression*/
%}
%token ID INT FLOAT DOUBLE
%%
D:T L;
L:L,ID
|ID
;
T:INT
|FLOAT
|DOUBLE
;
%%
extern FILE *yyin;
main()
{
do
{
yyparse();
}while(!feof(yyin));
}
yyerror(char*s)
{
}

OUTPUT:

[root@localhost]$ lex variable_test.I


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

Identifier is a
Identifier is b [root@localhost]$

RESULT:
Thus the program to recognize valid arithmetic expression that uses operator +, – , * and / was
executed and verified.

Name: Register No:


Compiler Laboratory

EX.NO:4 b TO CHECK THE GIVEN EXPRESSION A VALID EXPRESSION


DATE: OR NOT USING YACC

AIM:
To write a Program to recognize a valid variable which starts with a letter followed by any number
of letters or digits.
ALGORITHM:
1. Start the program.
2. Declare the required variables and methods.
3. Get the input
4. Then check whether the given string is valid or not
5. Print the result
6. Stop the program

PROGRAM :
Program Name: 4b.y
%{
#include<stdio.h>
#include<ctype.h>
%}
%token LETTER DIGIT
%left '+' '-'
%left '*' '/'
%%
st:st expr '\n' {printf("\n valid expression \n");}
| st '\n'
|
;
expr:expr '+' expr
|expr '-' expr
|expr '*' expr
|expr '/' expr
|'(' expr ')'
| NUM
| LETTER
;
NUM: DIGIT
|NUM DIGIT
;
%%
int yylex()
{
char c;
while((c=getchar())==' ');

Name: Register No:


Compiler Laboratory

if(isalpha(c)) return LETTER;


if(isdigit(c)) return DIGIT;
return(c);
}
int main()
{
printf("\n enter an expression\n");
yyparse();
}
int yyerror()
{
printf("invalid\n");
return 0;
}

OUTPUT:

RESULT:
Thus the program to recognize a valid variable which starts with a letter followed by any number
of letters or digits was executed and verified.

Name: Register No:


Compiler Laboratory

EX.NO:4 c IMPLEMENTATION OF CALCULATOR USING LEX AND YACC


DATE:

AIM
To write a program for implementing a calculator for computing the given expression using
semantic rules of the YACC tool.

ALGORITHM
Step 1: A Yacc source program has three parts as follows:
Declarations
%%
translation rules
%%
supporting C routines
Step 2: Declarations Section:
This section contains entries that:
i. Include standard I/O header file.
ii. Define global variables.
iii. Define the list rule as the place to start processing.
iv. Define the tokens used by the parser.
v. Define the operators and their precedence.
Step 3: Rules Section:
The rules section defines the rules that parse the input stream.
Each rule of a grammar production and the associated semantic action.
Step 4: Programs Section:
The programs section contains the following subroutines. Because these subroutines are included
in this file, it is not necessary to use the yacc library when processing this file.
Step 5: Main- The required main program that calls the yyparse subroutine to start the program.
Step 6: yyerror(s) -This error-handling subroutine only prints a syntax error message.
Step 7: yywrap -The wrap-up subroutine that returns a value of 1 when the end of input occurs. The
calc.lex file contains include statements for standard input and output, as programmar file
information if we use the -d flag with the yacc command. The y.tab.h file contains definitions for
the tokens that the parser program uses.
Step 8: calc.lex contains the rules to generate these tokens from the input stream.

PROGRAM
Cal.l
%{
#include<stdio.h>
#include<math.h>
#include"y.tab.h"
%}
%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?)
{yylval.dval=atof(yytext);
return NUMBER;}
MEM {return MEM;}
[\t];

Name: Register No:


Compiler Laboratory

\$ {return 0;}
\n {return yytext[0];}
. {return yytext[0];}
%%
Cal.y
%{
#include<stdio.h>
#include<math.h>
double memvar;
%}
%union
{
double dval;
}
%token<dval> NUMBER
%token<dval> MEM
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS
%type<dval> expression
%%
start:statement '\n'
|start statement '\n'
statement:MEM '=' expression {memvar=$3;}
|expression {printf("answer=%g\n",$1);}
;
expression:expression'+'expression {$$=$1+$3;}
|expression'-'expression {$$=$1-$3;}
|expression'*'expression {$$=$1*$3;}
|expression'/'expression {if($3==0)
yyerror("divide by zero");
else
$$=$1/$3;
};
expression:'-'expression %prec UMINUS {$$= -$2;}
|'('expression')' {$$=$2;}
|NUMBER {$$=$1;}
|MEM {$$=memvar;};
%%
int main(void)
{
printf("Enter the expression");
yyparse();
printf("\n\n");
return 0;
}
int yywrap()
{
.coreturn 0;

Name: Register No:


Compiler Laboratory

}
int yyerror(char *error)
{
printf("%s\n",error);
return 0;
}

OUTPUT
[CSE@localhost ~]$ lex cal.l
[CSE@localhost ~]$ yacc -d cal.y
[CSE@localhost ~]$ cc lex.yy.c y.tab.c -ll
[CSE@localhost ~]$ ./a.out
Enter the expression5+3
answer=8
[cse@NFSSERVER ~]$ ./a.out
Enter the expression5+-5
answer=0
[cse@NFSSERVER ~]$ ./a.out
Enter the expression+5/
syntax error

RESULT:
Thus the program to implement calculator using Lex and Yacc was executed and verified.

Name: Register No:


Compiler Laboratory

EX.NO:5 IMPLEMENTATION OF TYPE CHECKING


DATE:

AIM
To implement analyzer to check the type of variable for the given operator.

ALGORITHM
Step 1: Start the program.
Step 2: Input the arithmetic expression with operators and operands by using yytext function.
Step 3: Parse the input and check the digits for type.
Step 4: If the type of operands match to the operator or same type of operands, print the result of
expression.
Step 5: It the types of variables or identifiers mismatch, print the syntax error and type mismatch using the
function yyerror ().
Step 6: Stop the program.

PROGRAM

Parser.l
%{
#include<stdio.h>
#include "y.tab.h"
%}
%%
[0-9]+ {yylval.dval=atof(yytext);
return DIGIT;
}
\n|. return yytext[0];
%%

parser.y
%{
#include<stdio.h>
%}
%union
{
double dval;
}
%token <dval> DIGIT
%type <dval> expr
%type <dval> term
%type <dval> factor
%%
line: expr '\n' {
printf("%g\n",$1);
}
;
expr: expr '+' term {$$=$1 + $3 ;}
| term

Name: Register No:


Compiler Laboratory

;
term: term '*' factor {$$=$1 * $3 ;}
| factor
;
factor: '(' expr ')' {$$=$2 ;}
| DIGIT
;
%%
int main()
{
yyparse();
}
yyerror(char *s)
{
printf("%s",s);
printf("\n Type mismatch");
}

OUTPUT
[root@localhost ~]# lex parser.l
[root@localhost ~]# yacc -d parser.y
[root@localhost ~]# gcc lex.yy.c y.tab.c -ll
[root@localhost ~]# ./a.out
4+5.5
syntax error
Type mismatch
[root@localhost ~]#./a.out
5+5
10

RESULT
Thus the program to implement YACC specification to type checking using LEX tool was
executed successfully

Name: Register No:


Compiler Laboratory

EX.NO:6 a IMPLEMENTATION OF HEAP STORAGE ALLOCATION STRATEGY


DATE:

AIM:

To write a C program to implement heap storage allocation strategy.

INTRODUCTION:
Storage Allocation
Runtime environment manages runtime memory requirements for the following entities:

Code: It is known as the text part of a program that does not change at runtime. Its memory requirements
are known at the compile time.

Procedures: Their text part is static but they are called in a random manner. That is why, stack storage is
used to manage procedure calls and activations.

Variables: Variables are known at the runtime only, unless they are global or constant. Heap memory
allocation scheme is used for managing allocation and de-allocation of memory for variables in runtime.

Static Allocation
In this allocation scheme, the compilation data is bound to a fixed location in the memory and it
does not change when the program executes. As the memory requirement and storage locations are known
in advance, runtime support package for memory allocation and de-allocation is not required.

Stack Allocation
Procedure calls and their activations are managed by means of stack memory allocation. It works
in last-in-first-out (LIFO) method and this allocation strategy is very useful for recursive procedure calls.

Heap Allocation
Variables local to a procedure are allocated and de-allocated only at runtime. Heap allocation is
used to dynamically allocate memory to the variables and claim it back when the variables are no more
required.
Except statically allocated memory area, both stack and heap memory can grow and shrink
dynamically and unexpectedly. Therefore, they cannot be provided with a fixed amount of memory in the
system.

As shown in the image above, the text part of the code is allocated a fixed amount of memory.
Stack and heap memory are arranged at the extremes of total memory allocated to the program. Both
shrink and grow against each other.

Name: Register No:


Compiler Laboratory

ALGORITHM:

1. Start the program.


2. Enter the expression for which intermediate code is to be generated.
3. If the length of the string is greater than 3, then call the procedure to return the precedence
among the operands.
4. Assign the operand to exp array and operators to the array.
5. Create the three address code using quadruple structure.
6. Reduce the no of temporary variables.
7. Continue this process until we get an output.
8. Stop the program.

PROGRAM:
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
typedef struct Heap
{
int data;
struct Heap *next;
}node;
node *create();
void main()
{
int choice, val;
char ans;
node *head;
void display(node *);
node *search(node *,int);
node *insert(node *);
void dele(node **);
head=NULL;
do
{
clrscr();
printf("\n Program to perform various operations on heap using dynamic memory
management");
printf ("\n1.Create");
printf ("\n2.Display");
printf ("\n3.Insert an element in a list");
printf ("\n4.Delete an element from list");
printf ("\n5.Quit");
printf ("\n Enter Your Choice(1-5)");
scanf("%d",&choice);
switch(choice)
{
case 1:head=create();

Name: Register No:


Compiler Laboratory

break;
case 2:display(head);
break;
case 3:head=insert(head);
break;
case 4:dele(&head);
break;
case 5:exit(0);
default:clrscr();
printf("Invalid Choice,Try again");
getch();
}
}
while(choice!=5);
}
node *create()
{
node *temp,*new1,*head;
int val,flag;
char ans='y';
node *get_node();
temp=NULL;
flag=TRUE;
do
{
printf("\n Enter the Element");
scanf("%d",&val);
/*allocate new node*/
new1=get_node();
if(new1==NULL)
printf("\n Memory is not allocated");
new1-> data=val;
if (flag==TRUE)/* Executed only for the first time*/
{
head=new1;
temp=head; /*head is the first node in the heap*/
flag=FALSE;
}
else
{
temp->next=new1;
temp=new1;
}
printf("\nDo you want to enter more elements?(y/n)");
ans=getch();
}
while(ans=='y');
printf("\nThe list is created");
getch();

Name: Register No:


Compiler Laboratory

clrscr();
return head;
}
node *get_node()
{
node *temp;
temp=(node*)malloc(sizeof(node));
//using the mem. Allocation function
temp->next=NULL;
return temp;
}
void display(node*head)
{
node *temp;
temp=head;
if(temp==NULL)
{
printf("\n The list is empty\n");
getch();
clrscr();
return;
}
while(temp!= NULL)
{
printf("%d->",temp-> data);
temp=temp->next;
}
printf("NULL");
getch();
clrscr();
}
node *search(node *head,int key)
{
node *temp;
int found;
temp=head;
if (temp==NULL)
{
printf("The linked list is empty\n");
getch();
clrscr();
return NULL;
}
found=FALSE;
while(temp!=NULL&&found==FALSE)
{
if(temp->data != key)
temp = temp->next;
else

Name: Register No:


Compiler Laboratory

found=TRUE;
}
if(found == TRUE)
{
printf("\n The Elements is present in the list\n");
getch();
return temp;
}
else
printf("\n The Element is not present in the list\n");
getch();
return NULL;
}
node *insert(node *head)
{
int choice;
node *insert_head(node*);
void insert_after(node*);
void insert_last(node*);
printf("\n 1.Insert a node as a head node");
printf("\n 1.Insert a node as a last node");
printf("\n 1.Insert a node as at the intermediate position in the list ");
printf("\n 1.Enter your choice for insertion of node ");
scanf("%d",&choice);
switch(choice)
{
case 1:head = insert_head(head);
break;
case 2:insert_last(head);
break;
case 3:insert_after (head);
break;
}
return head;
}
node *insert_head(node*head)
{
node *New,*temp;
New=get_node();
printf ("\n Enter the element which you want to insert ");
scanf("%d",&New->data);
if(head == NULL)
head = New;
else
{
temp=head;
New->next = temp;
head= New;
}

Name: Register No:


Compiler Laboratory

return head;
}
void insert_last(node *head)
{
node *New,*temp;
New = get_node();
printf ("\n Enter the element which you want to insert ");
scanf("%d",&New->data);
if(head == NULL)
{
head = New;
}
else
{
temp=head;
while(temp->next!=NULL)
temp=temp->next;
temp->next=New;
New->next=NULL;
}
}
void insert_after(node *head)
{
int key;
node *New,*temp;
New = get_node();
printf("Enter the element after which you want to insert ");
scanf("%d",&key);
temp=head;
do
{
if(temp->data==key)
{
printf ("Enter element which you want to insert ");
scanf("%d",&New->data);
New->next=temp->next;
temp->next=New;
return;
}
else
temp=temp->next;
}
while(temp!=NULL);
}
node *get_prev(node *head,int val)
{
node *temp,*prev;
int flag;
temp = head;

Name: Register No:


Compiler Laboratory

if(temp == NULL)
return NULL;
flag=FALSE;
prev=NULL;
while(temp!=NULL && !flag)
{
if(temp->data!=val)
{
prev = temp;
temp = temp->next;
}
else
flag = TRUE;
}
if(flag) /*if Flag is true*/
return prev;
else
return NULL;
}
void dele(node **head)
{
int key;
node *New,*temp,*prev;
temp=*head;
if (temp== NULL)
{
printf ("\n The list is empty\n ");
getch();
clrscr();
return;
}
clrscr();
printf("\nENTER the Element you want to delete:");
scanf("%d",&key);
temp=search(*head,key);
if(temp!=NULL)
{
prev=get_prev(*head,key);
if(prev!=NULL)
{
prev ->next = temp-> next;
free(temp);
}
else
{
*head = temp->next;
free(temp); // using the mem. Dellocation function
}
printf("\n The Element is deleted\n");

Name: Register No:


Compiler Laboratory

getch();
clrscr();
}
}

OUTPUT:

Program to perform various operations on heap using Dynamic memory management.

1. Create
2. Display
3. Insert an element in a list
4. Delete an element from list
5. Quit
Enter your choice(1-5) 1

Enter the element: 10

Do you want to enter more elements? (y/n) y

Enter the element:20

Do you want to enter more elements?(y/n)y

Enter the element:30

Do you want to enter more elements?(y/n)n

The List is created

Program to perform various operations on Heap using Dynamic memory management.

1. Create
2. Display
3. Insert an element in a list
4. Delete an element from list
5. Quit
Enter your choice(1-5) 4

Enter the element you want to delete: 20

The element is present in the list

The element is deleted

Program to perform various operations on Heap using Dynamic memory management.

1. Create

Name: Register No:


Compiler Laboratory

2. Display
3. Insert an element in a list
4. Delete an element from list
5. Quit
Enter your choice(1-5) 2
10-> 30-> NULL

RESULT:

To write a C program to implement heap storage allocation strategy.

Name: Register No:


Compiler Laboratory

EX.NO:6 b IMPLEMENTATION OF STACK ALLOCATION STRATEGY


DATE:

AIM:
To write a program to implement storage allocation method using stack strategy.

ALGORITHM:
1. Start the program
2. Declare the necessary variables
3. Get the choice to insert, delete and display the values in stack
4. Perform PUSH( ) operation
t = newnode( )
Enter info to be inserted
Read n
t->info = n
t->next = top
top = t
Return
5. Perform POP( ) operation
If (top = NULL)
Print “underflow”
Return
x = top
top = top->next
delnode(x)
Return
6. Display the values
7. Stop the program
PROGRAM:
#include<stdio.h>
#include<conio.h>
struct stack
{
int no;
struct stack *next;

Name: Register No:


Compiler Laboratory

}
*start=NULL;
typedef struct stack st;
void push();
int pop();
void display();
void main()
{
char ch;
int choice,item;
do
{
clrscr();
printf("\n 1: push");
printf("\n 2: pop");
printf("\n 3: display");
printf("\n Enter your choice");
scanf("%d",&choice);
switch (choice)
{
case 1: push();
break;
case 2: item=pop();
printf("The delete element in %d",item);
break;
case 3: display();
break;
default : printf("\n Wrong choice");
};
printf("\n do you want to continue(Y/N)");
fflush(stdin);
scanf("%c",&ch);
}

Name: Register No:


Compiler Laboratory

while (ch=='Y'||ch=='y');
}
void push()
{
st *node;
node=(st *)malloc(sizeof(st));
printf("\n Enter the number to be insert");
scanf("%d",&node->no);
node->next=start;
start=node;
}
int pop()
{
st *temp;
temp=start;
if(start==NULL)
{
printf("stack is already empty");
getch();
exit();
}
else
{
start=start->next;
free(temp);
}
return(temp->no);
}
void display()
{
st *temp;
temp=start;
while(temp->next!=NULL)

Name: Register No:


Compiler Laboratory

{
printf("\nno=%d",temp->no);
temp=temp->next;
}
printf("\nno=%d",temp->no);
}

OUTPUT:

Name: Register No:


Compiler Laboratory

Name: Register No:


Compiler Laboratory

RESULT:
Thus the program to perform stack allocation strategy is executed successfully.

Name: Register No:


Compiler Laboratory

EX.NO: 7 CONSTRUCTION OF DAG


DATE:

AIM
To implement DAG construction using file organization.

ALGORITHM

Step 1: Start the program


Step 2: Declare structure and structure variables
Step 3:Start main and declare variables Node *root Root = NULL Create root null read the shared file
information
Step 4: Draw link lines for shared files Search root find the first and second
Step 5: If check root !-NULL If check string comparing root ->name, s==0
Then search root->link
Step 6: Creates a directory tree structure If check *root==NULL Display directory name or file
name
Step 7: Display the directory tree in graphical mode Display node *root
Step 8: If check root!=NULL draw lines
Step 9: Line of root->x,root->y,root->link[i]->x, root->link[i]->y
If check root->ftype==1 if it is directory
Step10: Then root->x,root->y,20,20 if it a file Root->x,root->y,root->name
Step11: For 0 to i<root->nc display children Display root->link[i]
Step 12: Stop the process.

PROGRAM

#include<stdio.h>
#include<graphics.h>
struct tree_element
{
char name[20];
int x,y,ftype,lx,rx,nc,level;
struct tree_element *link[5];
};
typedef struct tree_element node;
void main()
{
int gd=DETECT,gm;
node *root;
root=NULL;
clrscr();
create(&root,0,"null",0,639,320);
clrscr();
initgraph(&gd,&gm,"c:\tc\bgi");
display(root);
getch();
closegraph();
}

Name: Register No:


Compiler Laboratory

create(node **root,int lev,char *dname,int lx,int rx,int x)


{
int i,gap;
if(*root==NULL)
{
(*root)=(node*)malloc(sizeof(node));
printf("enter name of dir/file(under%s):",dname);
fflush(stdin);
gets((*root)->name);
if(lev==0||lev==1)
(*root)->ftype=1;
else
(*root)->ftype=2;
(*root)->level=lev;
(*root)->y=50+lev*50;
(*root)->x=x; (*root)->lx=lx; (*root)->rx=rx;
for(i=0;i<5;i++)
(*root)->link[i]=NULL;
if((*root)->ftype==1)
{
if(lev==0||lev==1)
{
if((*root)->level==0)
printf("How many users");
else
printf("hoe many files");
printf("(for%s):",(*root)->name);
scanf("%d",&(*root)->nc);
}
else (*root)->nc=0;
if((*root)->nc==0)
gap=rx-lx;
else
gap=(rx-lx)/(*root)->nc;
for(i=0;i<(*root)->nc;i++)
create(&((*root)->link[i]),lev+1,(*root)->name,lx+gap*i, lx+gap*i+gap, lx+gap*i+gap/2); }
else (*root)->nc=0;
}
}
display(node *root)
{
int i;
settextstyle(2,0,4);
settextjustify(1,1);
setfillstyle(1,BLUE);
setcolor(14);
if(root!=NULL) {
for(i=0;i<root->nc;i++)
{

Name: Register No:


Compiler Laboratory

line(root->x,root->y,root->link[i]->x,root->link[i]->y);
}
if(root->ftype==1) bar3d(root->x-20,root->y-10, root->x+20,root->y+10,0,0);
else
fillellipse(root->x,root->y,20,20);
outtextxy(root->x,root->y,root->name); for(i=0;i<root->nc;i++)
{ display(root->link[i]);
} } }

OUTPUT
enter name of dir/file (under null):sld How many users(forsld):2
enter name of dir/file(under sld):tld hoe many files(fortld):2
enter name of dir/file(under tld):hir enter name of dir/file(under tld):dag
enter name of dir/file(under sld):bin how many files(forbin):2
enter name of dir/file(under bin):exe enter name of dir/file(under bin):obj

RESULT
Thus the construction of DAG using file organization has been successfully executed and completed

Name: Register No:


Compiler Laboratory

EX.NO:8 BACK END COMPILER


DATE:

AIM:
To Implement the back end of the compiler which takes the three address code and produces the 8086
assembly language instructions that can be assembled and run using a 8086 assembler. The target
assembly instructions can be simple move, add, sub, jump. Also simple addressing modes are used.

INTRODUCTION:
A compiler is a computer program that implements a programming language specification to
"translate" programs, usually as a set of files which constitute the source code written in source language,
into their equivalent machine readable instructions (the target language, often having a binary form known
as object code). This translation process is called compilation.
Back end
 some local optimization
 register allocation
 peep-hole optimization
 code generation
 instruction scheduling
The main phases of the back end include the following:
 Analysis: This is the gathering of program information from the intermediate representation
derived from the input; data-flow analysis is used to build use-define chains, together
with dependence analysis, alias analysis, pointer analysis, escape analysis, etc. Accurate analysis is
the basis for any compiler optimization. The call graph and control are usually also built during the
analysis phase.

 Optimization: the intermediate language representation is transformed into functionally


equivalent but faster (or smaller) forms. Popular optimizations are expansion, dead, constant
propagation, loop transformation, register allocation and even automatic parallelization.

 Code generation: the transformed intermediate language is translated into the output language,
usually the native machine language of the system. This involves resource and storage decisions,
such as deciding which variables to fit into registers and memory and the selection and scheduling
of appropriate machine instructions along with their associated addressing modes. Debug data may
also need to be generated to facilitate debugging.

Name: Register No:


Compiler Laboratory

ALGORITHM:
1. Start the program.
2. Open the source file and store the contents as quadruples.
3. Check for operators, in quadruples, if it is an arithmetic operator generate it or if an assignment
operator generate it, else perform unary minus on register c.
4. Write the generated code into output definition of the file in outp.c.
5. Print the output.
6. Stop the program.

PROGRAM :
#include<stdio.h>
#include<stdio.h>
//#include<conio.h>
#include<string.h>
void main()
{
char icode[10][30],str[20],opr[10];
int i=0;
//clrscr();
printf("\n Enter the set of intermediate code (terminated by exit):\n");
do
{
scanf("%s",icode[i]);
} while(strcmp(icode[i++],"exit")!=0);
printf("\n target code generation");
printf("\n************************");
i=0;
do
{
strcpy(str,icode[i]);
switch(str[3])
{
case '+':
strcpy(opr,"ADD");
break;
case '-':
strcpy(opr,"SUB");
break;
case '*':
strcpy(opr,"MUL");
break;
case '/':
strcpy(opr,"DIV");
break;
}
printf("\n\tMov %c,R%d",str[2],i);
printf("\n\t%s%c,R%d",opr,str[4],i);
printf("\n\tMov R%d,%c",i,str[0]);
}while(strcmp(icode[++i],"exit")!=0);

Name: Register No:


Compiler Laboratory

//getch();
}

OUTPUT:

RESULT:
Thus the program to Implement the back end of the compiler which takes the three address code
and produces the 8086 assembly language instructions that can be assembled and run using a 8086
assembler was executed and verified.

Name: Register No:


Compiler Laboratory

EX.NO:9 IMPLEMENTATION OF SIMPLE CODE OPTIMIZATION TECHNIQUES


DATE:

AIM:

To write a C program to implement simple code optimization technique.

INTRODUCTION:
Optimization is a program transformation technique, which tries to improve the code by making it
consume less resource (i.e. CPU, Memory) and deliver high speed.
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.
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:

do
{
item = 10;
value = value + item;
} while(value<100);

This code involves repeated assignment of the identifier item, which if we put this way:

Item = 10;

Name: Register No:


Compiler Laboratory

do
{
value = value + item;
} while(value<100);

should not only save the CPU cycles, but can be used on any processor.

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.

ALGORITHM

Step 1:The code generation algorithm takes as input a sequence of three – address statements constituting
a basic block.
Step 2:For each three – address statement of the form x := y op z we perform the following actions:
Step 3:Invoke a function getreg to determine the location L where the result of the computation y op z
should be stored. L will usually be a register, but it could also be a memory location. We shall
describe getreg shortly.
Step 4: Consult the address descriptor for y to determine y, (one of) the current location(s) of y. prefer the
register for y if the value of y is currently both in memory and a register. If the value of y is not
already in L, generate the instruction MOV y, L to place a copy of y in L.
Step 5:Generate the instruction OP z, L where z is a current location of z. Again, prefer a register to a
memory location if z is in both. Update the address descriptor of x to indicate that x is in location
L. If L is a register, update its descriptor to indicate that it contains the value of x, and remove x
from all other register descriptors.
Step 6:If the current values of y and/or z have no next users, are not live on exit from the block, and are in
register descriptor to indicate that, after execution of x := y op z, those registers no longer will
contain y and/or z, respectively.

PROGRAM

#include<stdio.h>
#include<conio.h>
#include<string.h>
struct op
{
char l;
char r[20];
}
op[10],pr[10];
void main()

Name: Register No:


Compiler Laboratory

{
int a,i,k,j,n,z=0,m,q;
char *p,*l;
char temp,t;
char *tem;
clrscr();
printf("enter no of values");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("left\t");
op[i].l=getche();
printf("right:\t");
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);
}
//sub expression elimination
for(m=0;m<z;m++)
{
tem=pr[m].r;
for(j=m+1;j<z;j++)

Name: Register No:


Compiler Laboratory

{
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);
}
// duplicate production elimination

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");
for(i=0;i<z;i++)
{
if(pr[i].l!='\0')
{
printf("%c=",pr[i].l);
printf("%s\n",pr[i].r);
}
}
getch();

Name: Register No:


Compiler Laboratory

OUTPUT
enter no of values 5
left aright: 9
left bright: c+d
left eright: c+d
left fright: b+e
left rright: 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

RESULT

Name: Register No:


Compiler Laboratory

Thus the program to optimize the three address code using c program was successfully completed
and verified.

EX.NO:10 IMPLEMENTATION OF INTERMEDIATE CODE


DATE: GENERATION

AIM:

To write a C program to implement the intermediate code for the given set of input expressions.

ALGORITHM:

1. Start the program.


2. Get the input expression from the user.
3. Check the expressions for its validation.
4. If it is invalid return the error message.
5. Otherwise, for each computation store the result in the three – address statement (store it in
temporary variable say t1, t2, etc.,) .
6. Assign the final temporary value to the variable in which the result has to be stored.
7. Stop the program.

PROGRAM:

#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
#include<conio.h>
#include<string.h>
void small();
void dove(int );
int p[5]={0,1,2,3,4},c=1,i,k,l,m,pi;
char sw[5]={'=','-','+','/','*'},j[20],a[5],b[5],ch[2];
void main()
{
clrscr();
printf("Enter the expression:");
scanf("%s",j);
printf("\n\n\tThe Intermediate code is:\n");
small();
}
void dove(int i)
{
a[0]='\0';b[0]='\0';

if(!isdigit(j[i+2]) && !isdigit(j[i-2]))


{
a[0]=j[i-1];
b[0]=j[i+1];
}

Name: Register No:


Compiler Laboratory

if(isdigit(j[i+2]))
{
a[0]=j[i-1];
b[0]='t';
b[1]=j[i+2];
}
if(isdigit(j[i-2]))
{ b[0]=j[i+1];
a[0]='t';
a[1]=j[i-2];
b[1]='\0'; }
if(isdigit(j[i+2]) && isdigit(j[i-2]))
{ a[0]='t';
b[0]='t';
a[1]=j[i-2];
b[1]=j[i+2];
itoa(c,ch,10);
j[i+2]=j[i-2]=ch[0]; }
if(j[i]=='*')
printf("\tt%d=%s*%s\n",c,a,b);
if(j[i]=='/')
printf("\tt%d=%s/%s\n",c,a,b);
if(j[i]=='+')
printf("\tt%d=%s+%s\n",c,a,b);
if(j[i]=='-')
printf("\tt%d=%s-%s\n",c,a,b);
if(j[i]=='=')
printf("\t%c=t%d",j[i-1],--c);
itoa(c,ch,10);
j[i]=ch[0];
c++;
small();
}

void small()
{ pi=0;l=0;
for(i=0;i<strlen(j);i++)
{ for(m=0;m<5;m++)
if(j[i]==sw[m])
if(pi<=p[m])
{
pi=p[m];
l=1;
k=i;
} }
if(l==1)
dove(k);
else
{

Name: Register No:


Compiler Laboratory

getch();
exit (0);
}
}

OUTPUT:

Enter the expression:


a=b+c*d

The Intermediate code is:


t1=c*d
t2=b+t1
a=t2

RESULT :

Thus the program to convert the given expression into the intermediate code has been implemented.

Name: Register No:


Compiler Laboratory

EX.NO:11 IMPLEMENTATION OF CONTROL FLOW ANLYSIS AND


DATE DATA FLOW ANALYSIS

AIM
To implement c program to check the flow of control and flow of data in the program execution.

ALGORITHM
Step 1: Input the block of code with labels in the file named “input.txt”.
Step 2: Create node to store label, data and target using structures.
Step 3: Find out leaders, conditional statements and targets.
Step 4: Identify the number of blocks and name the blocks in order from the input file.
Step 5: Display the basic blocks of the given code.
Step 6: Output the control flow and data flow of blocks from the order of blocks.
Step 7: The final block should moves to the null point, it represents the end of the process.
Step 8: Display the flow of blocks.

PROGRAM
# include<stdio.h>
# include<conio.h>
#include<alloc.h>
#include<string.h>
struct Listnode
{
char data[50];
int leader,block,u_goto,c_goto;
struct Listnode *next;
char label[10],target[10];
}*temp,*cur,*first=NULL,*last=NULL,*cur1;

FILE *fpr;
void createnode(char code[50])
{
temp=(struct Listnode *)malloc(sizeof(struct Listnode));
strcpy(temp->data,code);
strcpy(temp->label,'\0');
strcpy(temp->target,'\0');

temp->leader=0;
temp->block=0;
temp->u_goto=0;
temp->c_goto=0;
temp->next=NULL;

if(first==NULL)
{

Name: Register No:


Compiler Laboratory

first=temp;
last=temp;
}
else
{
last->next=temp;
last=temp;
}
}
void printlist()
{
cur=first;
printf("\nMIR code is \n\n");
while(cur!=NULL)
{
printf("\ncode:%s",cur->data);
printf("\nleader:%d\t",cur->leader);
printf("block:%d\t",cur->block);
printf("u_goto:%d\t",cur->u_goto);
printf("c_goto:%d\t",cur->c_goto);
printf("label:%s\t",cur->label);
printf("target:%s\n",cur->target);

cur=cur->next;
}

void main()
{
char codeline[50];
char c,dup[50],target[10];
char *substring,*token;
int i=0,block,block1;
int j=0;
fpr= fopen("input.txt","r");
clrscr();
while((c=getc(fpr))!=EOF)
{
if(c!='\n')
{
codeline[i]=c;
i++;
}
else
{
codeline[i]='\0';
createnode(codeline);
i=0;

Name: Register No:


Compiler Laboratory

}
}
//create last node
codeline[i]='\0';
createnode(codeline);
fclose(fpr);
// printlist();

// find out leaders,conditional stmts


cur=first;
cur->leader=1;
while(cur!=NULL)
{
substring=strstr((cur->data),"if");
if(substring==NULL)
{
if((strstr((cur->data),"goto"))!=NULL)
{
cur->u_goto=1;
(cur->next)->leader=1;
}
}
else
{
cur->c_goto=1;
(cur->next)->leader=1;
}
substring=strstr((cur->data),":");
if(substring!=NULL)
{
cur->leader=1;
}
substring=strstr((cur->data),"call");
if(substring!=NULL)
{
cur->leader=1;
}
if(strstr(cur->data,"return")!=NULL)
{
cur->leader=1;
(cur->next)->leader=1;
}
cur=cur->next;
}
//to find labels and targets
cur=first;
while(cur!=NULL)
{

Name: Register No:


Compiler Laboratory

if((cur->u_goto==1)||(cur->c_goto==1))
{
substring=strstr(cur->data,":");
if(substring!=NULL)
{
token=strstr(substring,"L" );
if(token!=NULL)
strcpy(cur->target,token);
}
else
{
substring=strstr(cur->data,"L");
if(substring!=NULL)
strcpy(cur->target,substring);
}
}

if(strstr(cur->data,":")!=NULL)
{
strcpy(dup,cur->data);
token=strtok(dup,":");
// printf("\ntoken:%s",token);
if(token!=NULL)
strcpy(cur->label,token);
}
cur=cur->next;
}

// printlist();
//to identify blocks
cur=first;
while(cur!= NULL)
{
cur=cur->next;
if((cur->leader)==1)
{
j++;
cur->block=j;
}
else
cur->block=j;
}
// printlist();

// print basic blocks


printf("\n\n......Basic Blocks......\n");
cur=first;
j=0;
printf("\nBlock %d:",j);

Name: Register No:


Compiler Laboratory

while(cur!=NULL)
{

if ((cur->block)==j)
{

printf("%s",cur->data);
printf("\n\t");
cur=cur->next;
}
else
{
j++;
printf("\nBlock %d:",j);
}

}
//to output the control flow from each block
printf ("\t\t.......Control Flow and Data flow.......\n\n");
cur=first;
i=0;
while(cur!=NULL)
{
if((cur->block)!=(cur->next)->block)
{
block=cur->block;
if(cur->u_goto==1)
{
strcpy(target,cur->target);
cur1=first;
while(cur1!=NULL)
{
if(strcmp(cur1->label,target)==0)
{
block1=cur1->block;
printf("\t\tBlock%d---------->Block%d\n",block,block1);
}
cur1=cur1->next;
}
}
else if(cur->c_goto==1)
{
strcpy(target,cur->target);
cur1=first;
while(cur1!=NULL)
{
if(strcmp(cur1->label,target)==0)
{
block1=cur1->block;

Name: Register No:


Compiler Laboratory

printf("\t\tBlock%d---TRUE--->Block%d---FALSE--->Block%d\n",block,block1,(block+1));
}
cur1=cur1->next;
}
}
else if(strstr(cur->data,"return")==NULL)
{
printf("\t\tBlock%d---------->Block%d\n",block,(block+1));
}
else
printf("\t\tBlock%d---------->NULL\n",block);
}

cur=cur->next;
}
cur=last;
block= cur->block;
printf("\t\tBlock%d--------->NULL",block);
getch();
}

Input.txt
m <- 0
v <- 0
L1 : if v < n goto L5
r <- v
s <- 0
return
L2 : if r >= n goto L3
v <- v + 1
goto L1
L3 : x = a
s <- s + x
if s <= m goto L2
call P
L4 : r <- r + 1
goto L2
L5 : return m
m <- m+10
s <- s -10

Name: Register No:


Compiler Laboratory

OUTPUT

RESULT:

Name: Register No:


Compiler Laboratory

Thus the program to check the flow of control and flow of data in the program execution has been
executed successfully.

EX.NO:12 CONVERT THE BNF RULES INTO YACC FORM AND WRITE CODE TO
DATE: GENERATE ABSTRACT SYNTAX TREE.

AIM:
To write a program to convert BNF rules into YACC form and write code to generate Abstract
Syntax Tree using LEX and YACC.

INTRODUCTION:
Backus-Naur Form (BNF) notation
Backus-Naur form (BNF) is a formal notation for encoding grammars intended for human
consumption. Many programming languages, protocols or formats have a BNF description in their
specification.
Every rule in Backus-Naur form has the following structure:

\mathit{name} ::= \mathit{expansion}


The symbol ::= means "may expand into" and "may be replaced with."
In some texts, a name is also called a non-terminal symbol.

Every name in Backus-Naur form is surrounded by angle brackets, < >, whether it appears on the left- or
right-hand side of the rule.

An \mathit{expansion} is an expression containing terminal symbols and non-terminal symbols, joined


together by sequencing and choice.

A terminal symbol is a literal like ("+" or "function") or a class of literals (like integer).

Simply juxtaposing expressions indicates sequencing.

A vertical bar | indicates choice.

For example, in BNF, the classic expression grammar is:

<expr> ::= <term> "+" <expr>


| <term>

<term> ::= <factor> "*" <term>


| <factor>

<factor> ::= "(" <expr> ")"


| <const>

Name: Register No:


Compiler Laboratory

<const> ::= integer

Naturally, we can define a grammar for rules in BNF:

\mathit{rule} \rightarrow \mathit{name} ::= \mathit{expansion}


\mathit{name} \rightarrow < \mathit{identifier} >
\mathit{expansion} \rightarrow \mathit{expansion} \mathit{expansion}
\mathit{expansion} \rightarrow \mathit{expansion} | \mathit{expansion}
\mathit{expansion} \rightarrow \mathit{name}
\mathit{expansion} \rightarrow \mathit{terminal}
We might define identifiers as using the regular expression [-A-Za-z_0-9]+.
A terminal could be a quoted literal (like "+", "switch" or "<<=") or the name of a class of literals (like
integer).
The name of a class of literals is usually defined by other means, such as a regular expression or even
prose.

ALGORITHM:
1. Start a program
2. declare the declarations as a header file
{include<ctype.h>}
3. token digit
4. define the translations rules like line, expr, term, factor
Line:exp „\n‟ {print(“\n %d \n”,$1)}
Expr:expr‟+‟ term ($$=$1=$3}
Term:term „+‟ factor($$ =$1*$3}
Factor
Factor:‟(„enter‟) „{$$ =$2)
%%
5. define the supporting C routines
6. Stop the program

PROGRAM:
Program Name : int.l
%{
#include"y.tab.h"
#include<stdio.h>
#include<string.h>
int LineNo=1;
%}
identifier [a-zA-Z][_a-zA-Z0-9]*
number [0-9]+|([0-9]*\.[0-9]+)
%%
main\(\) return MAIN;
if return IF;
else return ELSE;
while return WHILE;
int |
char |
float return TYPE;

Name: Register No:


Compiler Laboratory

{identifier} {strcpy(yylval.var,yytext);
return VAR;}
{number} {strcpy(yylval.var,yytext);
return NUM;}
\< |
\> |
\>= |
\<= |
== {strcpy(yylval.var,yytext);
return RELOP;}
[ \t] ;
\n LineNo++;
. return yytext[0];
%%

Program Name : int.y


%{
#include<string.h>
#include<stdio.h>
struct quad
{
char op[5];
char arg1[10];
char arg2[10];
char result[10];
}QUAD[30];
struct stack
{
int items[100];
int top;
}stk;
int Index=0,tIndex=0,StNo,Ind,tInd;
extern int LineNo;
%}
%union
{
char var[10];
}
%token <var> NUM VAR RELOP
%token MAIN IF ELSE WHILE TYPE
%type <var> EXPR ASSIGNMENT CONDITION IFST ELSEST WHILELOOP
%left '-' '+'
%left '*' '/'
%%
PROGRAM : MAIN BLOCK
;
BLOCK: '{' CODE '}'
;
CODE: BLOCK

Name: Register No:


Compiler Laboratory

| STATEMENT CODE
| STATEMENT
;
STATEMENT: DESCT ';'
| ASSIGNMENT ';'
| CONDST
| WHILEST
;
DESCT: TYPE VARLIST
;
VARLIST: VAR ',' VARLIST
| VAR
;
ASSIGNMENT: VAR '=' EXPR{
strcpy(QUAD[Index].op,"=");
strcpy(QUAD[Index].arg1,$3);
strcpy(QUAD[Index].arg2,"");
strcpy(QUAD[Index].result,$1);
strcpy($$,QUAD[Index++].result);
}
;
EXPR: EXPR '+' EXPR {AddQuadruple("+",$1,$3,$$);}
| EXPR '-' EXPR {AddQuadruple("-",$1,$3,$$);}
| EXPR '*' EXPR {AddQuadruple("*",$1,$3,$$);}
| EXPR '/' EXPR {AddQuadruple("/",$1,$3,$$);}
| '-' EXPR {AddQuadruple("UMIN",$2,"",$$);}
| '(' EXPR ')' {strcpy($$,$2);}
| VAR
| NUM
;
CONDST: IFST{
Ind=pop();
sprintf(QUAD[Ind].result,"%d",Index);
Ind=pop();
sprintf(QUAD[Ind].result,"%d",Index);
}
| IFST ELSEST
;
IFST: IF '(' CONDITION ')' {
strcpy(QUAD[Index].op,"==");
strcpy(QUAD[Index].arg1,$3);
strcpy(QUAD[Index].arg2,"FALSE");
strcpy(QUAD[Index].result,"-1");
push(Index);
Index++;
}
BLOCK {
strcpy(QUAD[Index].op,"GOTO");
strcpy(QUAD[Index].arg1,"");

Name: Register No:


Compiler Laboratory

strcpy(QUAD[Index].arg2,"");
strcpy(QUAD[Index].result,"-1");
push(Index);
Index++;
};
ELSEST: ELSE{
tInd=pop();
Ind=pop();
push(tInd);
sprintf(QUAD[Ind].result,"%d",Index);
}
BLOCK{
Ind=pop();
sprintf(QUAD[Ind].result,"%d",Index);
};
CONDITION: VAR RELOP VAR {AddQuadruple($2,$1,$3,$$);
StNo=Index-1;
}
| VAR
| NUM
;
WHILEST: WHILELOOP{
Ind=pop();
sprintf(QUAD[Ind].result,"%d",StNo);
Ind=pop();
sprintf(QUAD[Ind].result,"%d",Index);
}
;
WHILELOOP: WHILE '(' CONDITION ')' {
strcpy(QUAD[Index].op,"==");
strcpy(QUAD[Index].arg1,$3);
strcpy(QUAD[Index].arg2,"FALSE");
strcpy(QUAD[Index].result,"-1");
push(Index);
Index++;
}
BLOCK {
strcpy(QUAD[Index].op,"GOTO");
strcpy(QUAD[Index].arg1,"");
strcpy(QUAD[Index].arg2,"");
strcpy(QUAD[Index].result,"-1");
push(Index);
Index++;
}
;
%%
extern FILE *yyin;
int main(int argc,char *argv[])
{

Name: Register No:


Compiler Laboratory

FILE *fp;
int i;
if(argc>1)
{
fp=fopen(argv[1],"r");
if(!fp)
{
printf("\n File not found");
exit(0);
}
yyin=fp;
}
yyparse();
printf("\n\n\t\t ----------------------------""\n\t\t Pos Operator Arg1 Arg2 Result" "\n\t\t
--------------------");
for(i=0;i<Index;i++)
{
printf("\n\t\t %d\t %s\t %s\t %s\t
%s",i,QUAD[i].op,QUAD[i].arg1,QUAD[i].arg2,QUAD[i].result);
}
printf("\n\t\t -----------------------");
printf("\n\n");
return 0;
}
void push(int data)
{
stk.top++;
if(stk.top==100)
{
printf("\n Stack overflow\n");
exit(0);
}
stk.items[stk.top]=data;
}
int pop()
{
int data;
if(stk.top==-1)
{
printf("\n Stack underflow\n");
exit(0);
}
data=stk.items[stk.top--];
return data;
}
void AddQuadruple(char op[5],char arg1[10],char arg2[10],char result[10])
{
strcpy(QUAD[Index].op,op);
strcpy(QUAD[Index].arg1,arg1);

Name: Register No:


Compiler Laboratory

strcpy(QUAD[Index].arg2,arg2);
sprintf(QUAD[Index].result,"t%d",tIndex++);
strcpy(result,QUAD[Index++].result);
}
yyerror()
{
printf("\n Error on line no:%d",LineNo);
}

Input Program: test.c


main()
{
int a,b,c;
if(a<b)
{
a=a+b;
}
while(a<b)
{
a=a+b;
}
if(a<=b)
{
c=a-b;
}
else
{
c=a+b;
}
}

OUTPUT:
[root@localhost]$ lex int.l
[root@localhost]$ yacc –d int.y
[root@localhost]$ gcc lex.yy.c y.tab.c –ll –lm
[root@localhost]$ ./a.out test.c

Name: Register No:


Compiler Laboratory

Name: Register No:


Compiler Laboratory

RESULT:
Thus the program to convert BNF rules into YACC form and write code to generate Abstract Syntax
Tree using LEX and YACC has been executed.

Name: Register No:

You might also like