Compiler Design Lab
Compiler Design Lab
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:
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;
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.
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.
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
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.
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
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
5. Rules section
The rules section contains the grammar of the language you want to parse. This looks like name1 : THING
something OTHERTHING {action}
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;
}
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())==' ');
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.
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];
\$ {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;
}
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.
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
;
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
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();
}
}
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:
RESULT:
Thus the program to perform stack allocation strategy is executed successfully.
AIM
To implement DAG construction using file organization.
ALGORITHM
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();
}
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
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.
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.
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);
//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.
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
{
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()
{
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++)
{
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();
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
Thus the program to optimize the three address code using c program was successfully completed
and verified.
AIM:
To write a C program to implement the intermediate code for the given set of input expressions.
ALGORITHM:
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]))
{
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
{
getch();
exit (0);
}
}
OUTPUT:
RESULT :
Thus the program to convert the given expression into the intermediate code has been implemented.
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)
{
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;
}
}
//create last node
codeline[i]='\0';
createnode(codeline);
fclose(fpr);
// printlist();
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();
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;
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
OUTPUT
RESULT:
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:
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];
%%
| 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,"");
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[])
{
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);
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);
}
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
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.