EX - NO:1 Implementation of Symbol Table Date
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:
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;
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();
}
SAMPLE OUTPUT:
RESULT:
Thus the C program to implement the Symbol table was executed and the output is verified
successfully.
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.
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))
{
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
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();
}
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.
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:
THEORY:
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
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" |
"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;
}
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.
EX.NO:4 a SYNTAX ANALYSIS USING YACC
DATE:
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
5. Rules section
The rules section contains the grammar of the language you want to parse. This looks like name1 : THING
something OTHERTHING {action}
| 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;
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;
}
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:
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.
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())==' ');
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.
EX.NO:4 c IMPLEMENTATION OF CALCULATOR USING LEX AND YACC
DATE:
AIM:
To write a Program to implement the calculator using lex and yacc.
ALGORITHM:
1. Start the program.
9. By using switch check the operator +,- ,* , / do the corresponding calc operation.
PROGRAM:
Program Name: input.l
%{
#include "y.tab.h"
#include<math.h>
%}
NUMBER [0-9]+|([0-9]*"."[0-9]+)
%%
{NUMBER} {yylval.dval=atof(yytext);
return NUMBER;}
sin|SIN {return SIN;}
cos|COS {return COS;}
tan|TAN {return TAN;}
sqrt|SQRT {return SQRT;}
square|SQUARE {return SQUARE;}
exp|EXP {return EXP;}
reci|RECI {return RECI;}
cube|CUBE {return CUBE;}
fact|FACT {return FACT;}
LOG { return LOG;}
ln|LN {return LN;}
MOD|mod {return MOD;}
[\t]+ ;
\n|. return yytext[0];
%%
int yywrap(void)
{ return 1;
}
Program Name: intput.y
%{
#include "y.tab.h"
#include <stdio.h>
#include <math.h>
#define PI 3.141592
int i,j;
%}
%union
{
double dval;
}
%token <dval> NUMBER
%token SIN COS TAN SQRT SQUARE RECI EXP MOD CUBE FACT
%left LN LOG
%left '+''-''*''/'
%right '^'
%nonassoc NEG
%type <dval> E
%%
SL : S '\n'
| SL S '\n'
;
S : E {printf("=%g\n",$1);}
;
E : E '+' E {$$=$1+$3;
printf("Addition is");}
|E '-' E {$$=$1-$3;
printf("substraction is");
}
|E '*' E {$$=$1*$3;
printf("Multiplication is");
}
|E '/' E {
if($3==0)
printf("Error! Divide by zero!!");
else
$$=$1/$3;
printf("Division is");
}
|E '^' E {$$=pow($1,$3);
printf("Power is=");
}
|SIN '(' E ')' {$$=sin($3/180*PI);
printf("SIN is=");
}
|COS '(' E ')' {$$=cos($3/180*PI);
printf("COS is");
}
|TAN '(' E ')' { if($3==90)
printf("Undefined (Infinity)");
else
$$=tan($3/180*PI);
printf("TAN is");
}
|SQRT '(' E ')' {$$=sqrt($3);
printf("Square Root is");
}
|SQUARE '(' E ')' {$$=$3*$3;
printf("square is=");
}
|EXP '(' E ')' {$$=exp($3);
printf("EXPONENTIAL is");
}
|RECI '(' E ')' {$$=1/($3);
printf("Reciprocal is");}
$$=1;
for(j=1;j<=$3;j++)
$$=$$*j;
printf("Factorial is");
}
|'(' E ')' {$$=$2;
printf("The simple number is");}
|'-' E %prec NEG {$$=-$2;
printf("The number with Negative sign is ");
}
|LOG E {$$=log($2)/log(10);
printf("LOG of base 10 is");
}
|LN E { $$=log($2);
printf("LOG with base 2 is");
}
|MOD '(' E','E')' {$$=fmod($3,$5);
}
|NUMBER
;
%%
extern FILE*yyin;
int main()
{
do{
yyparse();
}while(!feof(yyin));
}
yyerror(char*a)
{
fprintf(stderr,"parse error!!!");
}
OUTPUT:
RESULT:
Thus the program to implement calculator using Lex and Yacc was executed and verified.
EX.NO:5 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:
Every name in Backus-Naur form is surrounded by angle brackets, < >, whether it appears on the left- or
right-hand side of the rule.
A terminal symbol is a literal like ("+" or "function") or a class of literals (like integer).
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;
{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];
%%
SAMPLE 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
ACTUAL OUTPUT:
RESULT:
Thus the program for convert the bnf rules into yacc form and write code to generate abstract
syntax tree was executed and verified.
EX.NO:6 IMPLEMENTATION OF TYPE CHECKING
DATE:
AIM:
To write a program to implement type checking.
ALGORITHM:
1. Start a program.
2. Include all the header files.
3. Initialize all the functions and variables.
4. Get the expression from the user and separate into the tokens.
5. After separation, specify the identifiers, operators and number.
6. Print the output.
7. Stop the program.
PROGRAM:
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
#define SIZE 128
#define NONE -1
#define EOS '\0'
#define NUM 257
#define KEYWORD 258
#define ID 259
#define DONE 260
#define MAX 999
char lexemes[MAX];
char buffer[SIZE];
int lastchar=-1;
int lastentry=0;
int tokenval=DONE;
int lineno=1;
int lookahead;
struct entry
{
char *lexptr;
int token;
}
symtable[100];
struct entry
keywords[]={"if",KEYWORD,"else",KEYWORD,"for",KEYWORD,"int",KEYWORD,
"float",KEYWORD,"double",KEYWORD,"char",KEYWORD,"struct",KEYWORD,"ret
urn",KEYWORD,0,0};
void Error_Message(char *m)
{
fprintf(stderr,"line %d, %s \n",lineno,m);
exit(1);
}
int look_up(char s[ ])
{
int k;
for(k=lastentry;k>0;k--)
if(strcmp(symtable[k].lexptr,s)==0)
return k;
return 0;
}
int insert(char s[ ],int tok)
{
int len;
len=strlen(s);
if(lastentry+1>=MAX)
Error_Message("Symbpl table is full");
if(lastchar+len+1>=MAX)
Error_Message("Lexemes array is full");
lastentry=lastentry+1;
symtable[lastentry].token=tok;
symtable[lastentry].lexptr=&lexemes[lastchar+1];
lastchar=lastchar+len+1;
strcpy(symtable[lastentry].lexptr,s);
return lastentry;
}
/*void Initialize()
{
struct entry *ptr;
for(ptr=keywords;ptr->token;ptr+1)
insert(ptr->lexptr,ptr->token);
}*/
int lexer()
{
int t;
int val,i=0;
while(1)
{
t=getchar();
if(t==' '||t=='\t');
else
if(t=='\n')
lineno=lineno+1;
else
if(isdigit(t))
{
ungetc(t,stdin);
scanf("%d",&tokenval);
return NUM;
}
else
if(isalpha(t))
{
while(isalnum(t))
{
buffer[i]=t;
t=getchar();
i=i+1;
if(i>=SIZE)
Error_Message("Compiler error");
}
buffer[i]=EOS;
if(t!=EOF)
ungetc(t,stdin);
val=look_up(buffer);
if(val==0)
val=insert(buffer,ID);
tokenval=val;
return symtable[val].token;
}
else
if(t==EOF)
return DONE;
else
{
tokenval=NONE;
return t;
}
}
}
void Match(int t)
{
if(lookahead==t)
lookahead=lexer();
else
Error_Message("Syntax error");
}
void display(int t,int tval)
{
if(t=='+'||t=='-'||t=='*'||t=='/')
printf("\nArithmetic Operator: %c",t);
else if(t==NUM)
printf("\n Number: %d",tval);
else if(t==ID)
printf("\n Identifier: %s",symtable[tval].lexptr);
else
printf("\n Token %d tokenval %d",t,tokenval);
}
void F()
{
void E();
switch(lookahead)
{
case '(' : Match('(');
E();
Match(')');
break;
case NUM : display(NUM,tokenval);
Match(NUM);
break;
case ID : display(ID,tokenval);
Match(ID);
break;
default : Error_Message("Syntax error");
}
}
void T()
{
int t;
F();
while(1)
{
switch(lookahead)
{
case '*' : t=lookahead;
Match(lookahead);
F();
display(t,NONE);
continue;
case '/' : t=lookahead;
Match(lookahead);
display(t,NONE);
continue;
default : return;
}
}
}
void E()
{
int t;
T();
while(1)
{
switch(lookahead)
{
case '+' : t=lookahead;
Match(lookahead);
T();
display(t,NONE);
continue;
case '-' : t=lookahead;
Match(lookahead);
T();
display(t,NONE);
continue;
default : return;
}
}
}
void parser()
{
lookahead=lexer();
while(lookahead!=DONE)
{
E();
Match(';');
}
}
main()
{
char ans[10];
printf("\n Enter the expression ");
printf("And place ; at the end\n");
printf("Press Ctrl-Z to terminate\n");
parser();
}
RESULT:
Thus the program was executed and verified.
EX.NO:8 IMPLEMENTATION OF STORAGE ALLOCATION STRATEGY
DATE:
AIM:
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.
ALGORITHM:
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();
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();
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
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;
}
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;
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");
getch();
clrscr();
}
}
SAMPLE OUTPUT:
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
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
1. Create
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:
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;
}
*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);
}
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)
{
printf("\nno=%d",temp->no);
temp=temp->next;
}
printf("\nno=%d",temp->no);
}
OUTPUT:
EX.NO:9 CONSTRUCTION OF DAG
DATE:
AIM:
ALGORITHM:
PROGRAM:
#include<stdio.h>
#include<conio.h>
#include<ctype.h>
#define size 20
typedef struct node
{
char data;
struct node *left;
struct node *right;
}btree;
btree *stack[size];
int top;
void main()
{
btree *root;
char exp[80];
btree *create(char exp[80]);
void dag(btree *root);
clrscr();
printf("\nEnter the postfix expression:\n");
scanf("%s",exp);
top=-1;
root=create(exp);
printf("\nThe tree is created.....\n");
printf("\nInorder DAG is : \n\n");
dag(root);
getch();
}
btree* pop()
{
btree *Node;
if(top==-1)
printf("\nerror: stack is empty..\n");
Node=stack[top];
top--;
return(Node);
}
SAMPLE OUTPUT:
,
Enter the postfix expression : ab+cd-*
ACTUAL OUTPUT:
RESULT:
EX.NO:10 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
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);
//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.
EX.NO:11 IMPLEMENTATION OF SIMPLE CODE OPTIMIZATION TECHNIQUES
DATE:
AIM:
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;
do
{
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:
PROGRAM:
#include<stdio.h>
#include<conio.h>
#include<string.h>
char s[20],o[20];
void main()
{
int i=0,j=0,k,f1=1,f=1,k1=0;
void part();
clrscr();
printf("\n Enter the input string\n");
scanf("%s",o);
strlen(o);
while(o[k1]!='\0')
{
if((o[k1]=='=')==1)
{
break;
}
k1++;
}
for(j=k1+1;j<strlen(o);j++)
{
s[i]=o[j];
i++;
}
s[i]='\0';
i=strlen(s);
j=0;
printf("\n Three address code is\n");
if(i>3)
{
while(s[j]!='\0')
{
if((s[j]=='*')==1||(s[j]=='/')==1)
{
k=j;
if(f1!=0)
{
printf("t1=%c\n",s[k+1]);
printf("t2=%c%ct1",s[k-1],s[k]);
}
else
{
if(k>3)
{
printf("t2=t1%c%c\n",s[k],s[k+1]);
}
else
{
printf("\t2=t1%c%c\n",s[k],s[k-1]);
}
}
f=0;
break;
}
j++;
}
j=0;
while(s[j]!='\0')
{
if((s[j]=='+')==1||(s[j]=='-')==1)
{
k=j;
if(f==0)
{
if(k<3)
{
printf("\nt3=t2%c%c\n",s[k],s[k-1]);
}
else
{
printf("\nt3=t2%c%c\n",s[k],s[k+1])
}
}
else
{
printf("t1=%c%c%c\n",s[k-1],s[k],s[k+1]);
}
f1=0;
}
j++;
}
printf("%c=t3",o[0]);
}
else
{
printf("t1=%s\n",s);
printf("%c=t1",o[0]);
}
part();
getch();
}
void part()
{
int i=0,j=0,k,f1=1,f=1,k1=0;
while(o[k1]!='\0')
{
if((o[k1]=='=')==1)
{
break;
}
k1++;
}
for(j=k1+1;j<strlen(o);j++)
{
s[i]=o[j];
i++;
}
s[i]='\0';
i=strlen(s);
j=0;
printf("\n OPTIMIZED CODE\n");
if(i>3)
{
while(s[j]!='\0')
{
if((s[j]=='*')==1||(s[j]=='/')==1)
{
k=j;
if(f1!=0)
{
printf("t1=%c%c%c\n",s[k-1],s[k],s[k+1]);
}
else
{
if(k>3)
{
printf("t2=t1%c%c\n",s[k],s[k+1]);
}
else
{
printf("\t2=t1%c%c\n",s[k],s[k-1]);
}
}
f=0;
break;
}
j++;
}
j=0;
while(s[j]!='\0')
{
if((s[j]=='+')==1||(s[j]=='-')==1)
{
k=j;
if(f==0)
{
if(k<3)
{
printf("t2=t1%c%c\n",s[k],s[k-1]);
}
else
{
printf("t2=t1%c%c\n",s[k],s[k+1]);
}
}
else
{
printf("t1=%c%c%c\n",s[k-1],s[k],s[k+1]);
}
f1=0;
}
j++;
}
printf("%c=t2",o[0]);
}
else
{
printf("t1=%s\n",s);
printf("%c=t1",o[0]);
}
}
SAMPLE OUTPUT:
Enter the input string
a=b+c*d
OPTIMIZED CODE
t1=c*d
t2=t1+b
a=t2
RESULT:
Thus the program for implementation of simple code optimization was executed and verified.