Practical 01: Aim: Write A Program To Create, Read and Write Into A File. Code
Practical 01: Aim: Write A Program To Create, Read and Write Into A File. Code
Output:
Practical 02
Aim: Write a program to remove comment lines from C or CPP file.
Code:
#include<stdio.h>
#include<string.h>
void main()
{
FILE *fp,*fp1;
char c,s,fname[30];
printf("Enter file name: ");
scanf("%s",fname);
fp=fopen(fname,"r");
fp1=fopen("temp.c","w");
printf("Files opened and process started.\n");
c=getc(fp);
while(c!=EOF)
{
if(c=='/')
{
s=getc(fp);
if(s=='/')
{
s=getc(fp);
while(s!='\n')
s=getc(fp);
c=getc(fp);
}
else if(s=='*')
{
c=getc(fp);
while(c!=EOF)
{
if(c=='*')
{
c=getc(fp);
if(c=='/')
break;
else
c=getc(fp);
}
else
c=getc(fp);
}
c=getc(fp);
}
else
{
fputc(c,fp1);
c=getc(fp);
}
}
else
{
fputc(c,fp1);
c=getc(fp);
}
}
printf("Task completed successfully.\n");
fclose(fp);
fclose(fp1);
}
Output:
Source file:
Output file:
Practical 03
Aim: Write a program to implement the lexical analyzer.
Theory Concept:
Lexical analysis is the first phase of a language processor. It takes the modified source code
from language preprocessors that are written in the form of sentences. The lexical analyzer
breaks these syntaxes into a series of tokens, by removing any whitespace or comments in the
source code. If the lexical analyzer finds a token invalid, it generates an error. The lexical
analyzer works closely with the syntax analyzer. It reads character streams from the source
code, checks for legal tokens, and passes the data to the syntax analyzer when it demands.
Creation of symbol table is started in this phase.
Code:
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<string.h>
void main()
{
FILE *fp;
int i=0,j=0,k=0,l=0,m=0,f=0,flag=0;
char c,s,temp[10],fname[20],id[10][10];
char kw[6][7]={"int","char","float","void","main","double"};
char op[13]={'+','-','=',';','*',',','/','(',')','[',']','{','}'};
printf("Enter file name: ");
scanf("%s",fname);
fp=fopen(fname,"r");
c=getc(fp);
while(c!=EOF)
{
m=0;
while(c!=' ' && c!='\n')
{
temp[m]=c;
c=getc(fp);
m++;
}
temp[m]='\0';
printf("%s\t",temp);
for(i=0;i<14;i++)
{
if(temp[0]==op[i])
{
printf("#op%d\n",i);
f=1;
break;
}
}
if(f==0)
{
for(i=0;i<6;i++)
{
if(strcmp(temp,kw[i])==0)
{
printf("#ky%d\n",j);
j++;
f=1;
break;
}
}
}
if(f==0)
{
if(isdigit(temp[0]))
{
printf("#cn%d\n",k);
k++;
}
else
{
for(i=0;i<10;i++)
{
if(strcmp(temp,id[i])==0)
{
printf("#id%d\n",i);
flag=1;
}
}
if(flag==0)
{
strcpy(id[l],temp);
printf("#id%d\n",l);
l++;
}
flag=0;
}
}
c=getc(fp);
f=0;
}
fclose(fp);
}
Output:
Practical 04
Aim: Write a program to left factor the given grammar.
Theory Concept:
Left factoring is removing the common left factor that appears in two productions of the same
non-terminal. It is done to avoid backtracking by the parser. Suppose the parser has a look-
ahead consider this example:
A->qB | qC
where A,B,C are non-terminals and q is a string. In this case, the parser will be confused as to
which of the two productions to choose and it might have to back-trace. After left factoring,
the grammar is converted to:
A->qA’
A’->B | C
In this case, a parser with a look-ahead will always choose the right production.
Code:
#include<stdio.h>
#include<string.h>
void main()
{
int i=0,j=0,k=0,pos=0,c=0,c1=0,f=0;
char p[50],a[10][10],temp[10],b[10][10];
printf("Enter any production: ");
scanf("%s",p);
while(p[i]!='>')
i++;
i++;
while(p[i]!='\0')
{
if(p[i]=='|')
{
a[j][k]='\0';
j++;
k=0;
i++;
}
else
{
a[j][k++]=p[i];
i++;
}
}
a[j][k]='\0';
c=c1=j;
i=0,j=0;
while(c!=0)
{
if(a[i][j]==a[i+1][j] && c!=0 && f==0)
{
f=1;
pos++;
j++;
while(a[i][j]!='\0' && a[i+1][j]!='\0')
{
if(a[i][j]==a[i+1][j])
pos++;
j++;
}
}
else if(f==1)
{
while(a[i][j]!='\0' && a[i+1][j]!='\0' && j+1<=pos)
{
if(a[i][j]!=a[i+1][j])
{
pos=i+1;
break;
}
j++;
}
}
else
printf("No need of removing left factor.\n");
i++;
j=0;
c--;
}
for(i=0,j=0,k=0;j<pos;j++,k++)
temp[k]=a[i][j];
temp[k]='\0';
for(i=0;i<=c1;i++)
{
for(j=0,k=pos;a[i][k]!='\0';j++,k++)
{
b[i][j]=a[i][k];
}
b[i][j]='\0';
}
printf("After removing left factor: \n");
printf("%c->%s%c'\n",p[0],temp,p[0]);
printf("%c'->",p[0]);
for(i=0;i<=c1;i++)
{
if(i==0)
printf("%s",b[i]);
else if(b[i][0]=='\0')
printf("|*");
else
printf("|%s",b[i]);
}
}
Output:
Practical 05
Aim: Write a program to remove Left Recursion from the given grammar.
Theory Concept:
Left recursion is a grammar in which the non-terminal which is defined, appears in the
extreme left of the production. It becomes necessary to remove left recursion in a top down
parsing otherwise the parse tree would go in infinite loop.
Eg:
E->Ea/Eb
Here, E is appearing in the left most position of the production, hence it is a left recursive
grammar.
Code:
#include<stdio.h>
#include<string.h>
void main()
{
char p[20],part1[20],part2[20],part3[20],mp[20],np[20];
int i,j=0,k=0,l=0,pos;
printf("Enter Production : A::=");
scanf("%s",p);
for(i=0;p[i]!='|';i++,j++)
part1[j]=p[i];
part1[j]='\0';
for(j=++i,i=0;p[j]!='|'&&p[j]!='\0';j++,i++)
part2[i]=p[j];
part2[i]='\0';
for(k=++j,j=0;p[k]!='\0';k++,j++)
part3[j]=p[k];
part3[j]='\0';
k=0;
for(i=0;i<strlen(part1)||i<strlen(part2)||i<strlen(part3);i++)
{
if((part1[i]=='A') && (part2[i]=='A'))
{
mp[k]=part3[i];
k++;
pos=i+1;
}
}
for(i=pos,j=0;part1[i]!='\0';i++,j++)
np[j]=part1[i];
if(strlen(part1)==1)
np[j++]='*';
np[j++]='X';
np[j++]='|';
for(i=pos;part2[i]!='\0';i++,j++)
np[j]=part2[i];
if(strlen(part2)==1)
np[j++]='*';
np[j++]='X';
np[j++]='|';
for(i=pos;part3[i]!='\0';i++,j++,k++)
{
mp[k]=part3[i];
np[j]=part3[i];
}
if(strlen(part3)==1)
np[j++]='*';
mp[k]='X';
mp[++k]='\0';
np[j]='\0';
printf(" A::=%s",mp);
printf("\n X::=%s\n",np);
}
Output:
Practical 06
Aim: Write a program to implement Recursive Descent Parsing for the
given grammar.
E --> T+E | T
T --> F*T | F
F --> (E) | i
Theory Concept:
Recursive descent parsing is a top-down method of syntax analysis in which a set of recursive
procedures to process the input is executed.
This parsing technique recursively parses the input to make a parse tree, which may or may
not require back-tracking. But the grammar associated with it (if not left factored) cannot
avoid back-tracking.
A form of recursive-descent parsing that does not require any back-tracking is known as
predictive parsing.
This parsing technique is regarded recursive as it uses context-free grammar which is
recursive in nature.
Consider the following example
E --> iE’
E’ --> +iE’ | ℇ
In Recursive Descent Parsing a procedure is associated with each nonterminal of a grammar.
Code:
#include<stdio.h>
int i=0;
char l,s[10];
void F();
void T();
void match(char );
void E()
{
T();
if(l=='+')
{
match('+');
E();
}
else
T();
}
void T()
{
F();
if(l=='*')
{
match('*');
T();
}
else
F();
}
void F()
{
if(l=='(')
{
match('(');
E();
if(l==')')
match(')');
}
else if(l=='i')
match('i');
}
void match(char t)
{
if(l==t)
{
l=s[i];
i++;
}
else
printf("\nError");
}
void main()
{
printf("\nEnter string: ");
scanf("%s",s);
l=s[i];
E();
if(l=='$')
printf("\nValid String.\n");
else
printf("\nInvalid String!!!\n");
}
Output: