Professional Documents
Culture Documents
Batch:C1 2010-11
Practical - 1
Source Code:
#include<stdio.h>
#include<conio.h>
#define MAX 30
void main()
{
char str[MAX];
int state=0;
int i=0, j, startid=0, endid, startcon, endcon;
FILE *fp;
clrscr();
fp=fopen("lex.txt","r");
printf("Given Grammar:\n");
while(!feof(fp))
{
str[i]=getc(fp);
printf("%c",str[i]);
i++ ;
switch(state)
{
case 0: if(str[i]=='i') state=1; //if
else if(str[i]=='w') state=3; //while
else if(str[i]=='d') state=8; //do
else if(str[i]=='e') state=10; //else
else if(str[i]=='f') state=14; //for
else if(isalpha(str[i]) || str[i]=='_')
{
state=17;
startid=i;
} //identifiers
break;
//States for 'if'
case 1: if(str[i]=='f') state=2;
else { state=17; startid=i-1; i--; }
break;
state=0;
i--;
}
break;
elseif(str[i]==NULL||str[i]=='<'||str[i]=='>'||str[i]=='('||str[i]==')'
||str[i]==';'||str[i]=='='||str[i]=='+'||str[i]=='-') state=18;
i--;
break;
case 18:if(str[i]==NULL || str[i]=='<' || str[i]=='>' || str[i]=='(' ||
str[i]==')' || str[i]==';' || str[i]=='=' || str[i]=='+' ||str[i]=='-')
{
endid=i-1;
printf(" ");
for(j=startid; j<=endid; j++)
printf("%c", str[j]);
printf(" : Identifier\n");
state=0;
i--;
}
break;
}
break;
case 26: printf("\n( : Special character\n");
x1 : Identifier
<= : Relational operator
10 : Constant
; : Special character
x1 : Identifier
+ : Operator
+ : Operator
) : Special character
; : Special character
Disadvantages:A large amount of time is spent in reading the source program and
partitioning it into tokens.
So specialized buffering techniques for reading input characters and processing
tokens need to be applied to speed up compiler.
One more disadvantage is that the lexical analyzer needs to read the whole
symbol table every time an identifier is encountered in the source code.
Conclusion:A Lexical Analyzer reads and converts the input into a stream of tokens to be
analyzed by the parser.
Practical - 2
The basic idea is that when it is not clear which of the two alternative
productions to use to expand a non terminal A ,we may be able to rewrite the
A production to defer the decision until we have seen enough of the input to
make the right choice.
Algorithm:
Input: Grammar G with no cycles or Є productions.
for each non terminal A find the longest prefix a common to two or
more of its alternatives.
Here A is the new non terminal repeatedly apply this alternative until
no two alternative have common prefix
Source Code:
#include<stdio.h>
#include<conio.h>
#include<string.h>
FILE *fsrc;
void main()
{
int i=0,j=0,k=0,l=0,m=0,temp=0,a=0,len1,len2;
char grammar[100], nt[5][10][10], dummy[2]="\0", final[10]="\0",
final1[10]="\0";
clrscr();
fsrc=fopen("grammer.txt","r");
printf("Given Grammar:\n");
while(!feof(fsrc))
{
grammar[i]=getc(fsrc);
printf("%c",grammar[i]);
i++;
}
for(j=0;j<i;j++)
{
if(grammar[j]!='\n')
{
if(grammar[j]!='|')
{
if(l==0)
{
if(temp<=2)
{
dummy[temp]=grammar[j];
temp++;
}
else
{
nt[k][l][m]=grammar[j];
m++;
}
}
else
{
nt[k][l][m]=grammar[j];
m++;
}
}
else
{
nt[k][l][m]='\0'; l++;
temp=0;
m=0;
}
}
else
{
nt[k][l][m]='\0';
k++;
l=0;
m=0;
}
}
len1=strlen(nt[0][0]);
len2=strlen(nt[0][1]);
for(i=0;i<len1;i++)
{
if(len1<len2)
{
if(nt[0][0][i]!=nt[0][1][i])
goto aa;
else
a++;
}
else
{
if(nt[0][1][i]!=nt[0][0][i])
goto aa;
else
a++;
}
}
aa: if(len1<len2)
{
for(i=0;i<len2;i++)
final[i]=nt[0][1][i+a];
for(i=0;i<a;i++)
final1[i]=nt[0][0][i];
}
else
{ for(i=0;i<len2;i++)
final[i]=nt[0][0][i+a];
for(i=0;i<a;i++)
final1[i]=nt[0][1][i];
}
printf("\n\nAfter Left Factoring the Grammer is:");
printf("\n%c->%sS'|%s\nS'->%s|@",grammar[0],final1,nt[0][2],final);
printf("\n%c->%c",dummy[0],nt[1][0][0]);
getch();
}
Output:
Given grammer S=iEtSeS|iEtS|b
S1 : iEtSeS
S2 : iEtS
S3 : b
S=iEtSA|b
A=eS|#
Advantage:
Disadvantage:
There might be in the grammar that no left factoring is there so in that case this
method is not useful in parsing.
The program requires more data structure in order to make left factoring possible
because the new non terminals have to be added, and if the grammar contains more
alternatives than there would be memory over flow problem.
Application: Syntax Analyzer in compilers.
Conclusion:
Left factoring requires to find out the longest prefix in each right side production
alternatives in order to make top down parsing without backtracking successful.
Question: Give another example of the left factoring to properly understand the method?
The grammar is:
S->iEts | itSes | a
E->b
Practical - 3
Aim: Write a program to remove the Left Recursion from a given grammar.
Software Requirements:Turbo C Compiler
Knowledge Requirement: Left Recursion Technique for grammars
Theory/Logic: Top-down parsing methods can not handle left-recursive grammars.
So a transformation that eliminates left-recursion is needed.
This program will eliminate left-recursion problem of given grammar.
Algorithm:
Input: Grammar G with no cycles or null-productions.
Output:An equivalent grammar with no left recursion.
Method:Apply the algorithm given below to G.
Note that the resulting non-left-recursive grammar may have null-productions.
1. Arrange the non terminals in some order A1, A2 ,…..An.
2. for i:=1 to n do begin
for j:=1 to i-1 do begin
Replace each production of the form Ai->Ajy
by the production Aj->s1y|s2y|….|sky.
Where Aj->s1|s2|…|sk are all the current Aj-productions;
End
Eliminate the immediate left recursion among the Ai-productions.
End
Source Code:
#include<stdio.h>
#include<conio.h>
#include<string.h>
void ELR(char nonterminal);
FILE *fsrc,*fpw;
int i,j,k,l,m,i1;
char nt[10][10][10],grammar[100];
void main()
{
clrscr();
fsrc=fopen("left.txt","r");
printf("Given Grammar\n");
i=k=l=m=0;
while(!feof(fsrc))
{
grammar[i]=getc(fsrc);
printf("%c",grammar[i]);
i++;
}
printf("\n\nAfter Elimination of Left Recursion (For Above Grammar):");
for(j=0;j<=i;j++)
{
if(grammar[j]!='\n')
{
if(grammar[j]!='|')
{ nt[k][l][m]=grammar[j];
m++;
}
else
{
nt[k][l][m]='\0';
l++;
m=0;
}
}
else
{
nt[k][l][m]='\0';
k++;
l=0;
m=0;
}
}
for(i1=0;i1<=k;i1++)
ELR(nt[i1][0][3]);
getch();
}
void ELR(char prod1)
{
char prod0,prod2,prod3,prod4;
prod0=nt[i1][0][0];
prod1=nt[i1][0][3];
prod2=nt[i1][1][0];
prod3=nt[i1][0][4];
prod4=nt[i1][0][5];
if(prod0==prod1)
{ printf("\n%c->%c%c'",prod0,prod2,prod0);
printf("\n%c'->%c%c%c'|@",prod0,prod3,prod4,prod0);
}
else
printf("\n%s|%s",nt[i1][0],nt[i1][1]);
}
T→T*F|F
F → (E) | id
E → TE’
E’ → +TE’ | €
T → FT’
T’ → *FT’ | €
F → (E) | id
Disadvantage:It is not easier process for each and every grammar to eliminate left recursion.
There might be in the grammar that no left recursion is there so in that case this method is not
useful in parsing.
Conclusion:Removing Left recursion requires to eliminate the same non terminal in left and
right hand side production alternatives in order to remove the infinite looping of the
productions as parsing process goes on.
Questions:
1. Give another example of the left recursion to properly understand the method?
The grammar is:
S->Aa | b
A-> Ac | Sd | @
Answer:
By eliminating left-recursion,
S-> Aa | b
A-> bdA’ | A’
A’->cA’| adA’ | #
Practical - 4
Aim: Write a C program for finding a FIRST set of all non-terminals of a given grammar.
E → TE’
E’ → +TE’ | €
T → FT’
T’ → *FT’ | €
F → (E) | id
Software Required:Turbo C Compiler
Knowledge Required: Concepts of First set of the grammar.
What is first and what is included in the first set of the non terminal.
Theory/Logic:
If α is any string of grammar symbols, let FIRST(α) be the set of terminals that begin
the strings derived from α. If α =*> €, then € is also in FIRST(α).
The construction of a predictive parser is aided by two functions associated with a
grammar G. These functions, FIRST and FOLLOW, allow us to fill in the entries of a
predictive parsing table for G, whenever possible. Sets of tokens yielded by the
FOLLOW function can also be used as synchronizing tokens during panic-mode error
recovery.
To compute FIRST(X) for all grammar symbols X, the following rules are used until
no more terminals or € can be added to any FIRST set.
1. If X is terminal, then FIRST(X) is {X}.
2. If X → € is a production, then add € to FIRST(X).
#include<stdio.h>
#include<conio.h>
#include<string.h>
void Find_First(char nt);
FILE *fsrc,*fpw;
int i=0,j,k=0,l=0,m=0,i1,i2;
char nt[10][10][10],grammar[100];
void main()
{
clrscr();
fsrc=fopen("grammar.txt","r");
if(fsrc==NULL)
{
printf("\nThe File Cant be opened");
}
else
{
printf("Given Grammar\n");
while(!feof(fsrc))
{
grammar[i]=getc(fsrc);
printf("%c",grammar[i]);
i++;
}
printf("\n\nFIRST SET (For Above Grammar):");
for(j=0;j<=i;j++)
{
if(grammar[j]!='\n')
{
if(grammar[j]!='|')
{
nt[k][l][m]=grammar[j];
m++;
}
else
{
nt[k][l][m]='\0';
l++;
m=0;
}
}
else
{
nt[k][l][m]='\0';
k++;
l=0;
m=0;
}
}
for(i2=0;i1,i2<=k;i1++,i2++)
{
printf("\nFIRST(%c)={",nt[i1][0][0]);
Find_First (nt[i1][0][3]);
printf("}");
}
}
getch();
}
void Find_First(char prod1)
{
char prod2;
prod1=nt[i1][0][3];
prod2=nt[i1][1][0];
if(prod1<65 || prod1>90)
{
printf("%c,",prod1);
if(prod2=='@' || prod2=='i')
{
printf("%c",prod2);
}
}
else
{
for(i=0;i<5;i++)
{
if(prod1==nt[i][0][0])
{
i1=i;
Find_First(prod1);
i1=i2;
}
}
if(prod2=='@')
{
printf("%c",prod2);
}
}
}
Output:
Given Grammar:
E → TE’
E’ → +TE’ | €
T → FT’
T’ → *FT’ | €
F → (E) | id
First Set:
FIRST(E)={ ( , id }
FIRST(E’)={ + , € }
FIRST(T)={ ( , id }
FIRST(T’)={ * , € }
FIRST(F)={ ( , id }
Conclusion:
FIRST is the function, used in the construction of predictive parser.
If α is any string of grammar symbols, let FIRST(α) be the set of
terminals that begin the strings derived from α.
Questions:
S=ABa
A=aaB
B=bB
B=^
Answer:
FIRST(S)=FIRST(A)={a}
FIRST(B)={b}{^}
FIRST(B)={b}{^}
Practical - 5
Aim: Write a C program for finding a FOLLOW set of all non-terminals of given grammar.
E → TE’
E’ → +TE’ | €
T → FT’
T’ → *FT’ | €
F → (E) | id
To compute Follow(A) for all non-terminals A, the following rules are used
u8ntill nothing can be added to any Follow set:
int i,j,k,l,lines,pipe_sym;
char ch;
char temp_str[10];
clrscr();
fp1=fopen("source.txt","r");
if(!fp1)
{
printf("Grammar not Found");
getch();
exit(0);
}
printf("\t\t\t This is My Grammar\n");
lines=0;
while(fgets(str,79,fp1))
{
printf("%s",str);
strcpy(statement[lines],str);
lines++;
}
fp2=fopen("temp.txt","w");
for(i=0;i<lines;i++)
{
ch=statement[i][0];
pipe_sym=0;
for(j=0;j<strlen(statement[i]);j++)
{
if(statement[i][j]=='|')
{
pipe_sym++;
}
}
strtok(statement[i],":");
for(j=0;j<pipe_sym;j++)
{
strcpy(temp_str,strtok(NULL,"|"));
fprintf(fp2,"%c:%s\n",ch,temp_str);
}
strcpy(temp_str,strtok(NULL,"\n"));
fprintf(fp2,"%c:%s\n",ch,temp_str);
}
fclose(fp2);
fclose(fp1);
}
void main()
{
FILE *fp1,*fp2;
char *temp;
char str[79];
int i,j,k,l;
char temp_str[10];
char* find_first(char);
char* find_follow(char);
//int first_for_follow(char);
fp1=fopen("temp.txt","r");
lines=0;
sep_pipes();
getch();
while(fgets(str,79,fp1))
{
strcpy(statement[lines],str);
lines++;
}
getch();
printf("\n\t\t\t Finding FIRST\n\n\n");
for(i=0;i<lines;i++)
{
non_terminals[i]=statement[i][0];
}
for(i=0;i<lines;i++)
{
for(j=i+1;j<lines;j++)
{
if(non_terminals[j]==non_terminals[i])
{
non_terminals[i]='*';
}
}
}
no_of_non_terminal=0;
for(i=0;i<lines;i++)
{
if(non_terminals[i]!='*')
{
non_terminals[no_of_non_terminal]=non_terminals[i];
no_of_non_terminal++;
}
}
for(i=0;i<no_of_non_terminal;i++)
{
printf("FIRST( %c )",non_terminals[i]);
strcpy(str_temp,"");
pos=0;
find_first(non_terminals[i]);
printf(" ==> { %s }",str_temp);
printf("\n");
}
printf("\n\n\t\t\t Finding FOLLOW\n\n\n");
for(i=0;i<no_of_non_terminal;i++)
{
printf("FOLLOW( %c )",non_terminals[i]);
strcpy(str_temp,"");
pos=0;
find_follow(non_terminals[i]);
printf(" ==> { ");
for(j=0;j<strlen(str_temp);j++)
{
for(k=j+1;k<strlen(str_temp);k++)
{
if(str_temp[j]==str_temp[k])
{
str_temp[k]='œ';
}
}
}
for(j=0;j<strlen(str_temp);j++)
{
if(str_temp[j]!='œ')
{
printf("%c",str_temp[j]);
if(str_temp[j+1]!='œ'&&str_temp[j+1]!='\0')
{
printf(", ");
}
}
}
printf(" } ");
printf("\n");
}
fclose(fp1);
getch();
}
char* find_first(char ch)
{
int i,j;
for(i=0;i<lines;i++)
{
if(ch==statement[i][0])
{
for(j=0;j<lines;j++)
{
if(statement[i][2]==statement[j][0])
{
find_first(statement[j][0]);
return 0;
}
}
str_temp[pos]=statement[i][2];
pos++;
str_temp[pos]=',';
pos++;
str_temp[pos]='\0';
}
}
pos--;
str_temp[pos]='\0';
return str_temp;
}
char* find_follow(char ch)
{
int i,j,k;
int found,line_no,b_pos;
char *temp;
if(ch==statement[i][0] && i==0)
{
str_temp[pos]='$';
pos++;
str_temp[pos]='\0';
}
for(i=0;i<lines;i++)
{
for(j=2;j<strlen(statement[i]);j++)
{
if(ch==statement[i][j])
{
found=1;
b_pos=j;
line_no=i;
if(b_pos<strlen(statement[i])-2)
{
first_for_follow(statement[line_no][b_pos+1]);
}
else
{
temp = find_follow(statement[line_no][0]);
return str_temp;
}
}
}
}
return str_temp;
}
int first_for_follow(char ch)
{
int i,j;
int flag=0;
for(i=0;i<no_of_non_terminal;i++)
{
if(ch==non_terminals[i])
{
flag = 1;
break;
}
}
if (flag==1)
{
for(i=0;i<lines;i++)
{
if(ch==statement[i][0])
{
for(j=0;j<lines;j++)
{
if(statement[i][2]==statement[j][0])
{
first_for_follow(statement[j][0]);
return 0;
}
}
if(statement[i][2]=='^')
{
t=find_follow(statement[i][0]);
strcat(str_temp,t);
return 0;
}
if(statement[i][2]!='^')
{
str_temp[pos]=statement[i][2];
pos++;
str_temp[pos]='\0';
}
}
}
}
else
{
if(ch!='^')
{
str_temp[pos]=ch;
pos++;
str_temp[pos]='\0';
}
}
}
Output: Source.txt
E → TE’ E’ → +TE’ | €
T → FT’ T’ → *FT’ | €
F → (E) | id
FOLLOW(E) = { ) , $ }
FOLLOW(E’) = { ) , $ }
FOLLOW(T) = { + , ) , $ }
FOLLOW(T’) = { + , ) , $ }
FOLLOW(F) = { + , * , ) , $ }
Advantages: Allows us to fill the entries of a predictive parsing table for G, whenever
possible.
Application: In the construction of a predictive parser
Sets of tokens yielded by the FOLLOW function can also be used as
synchronizing tokens during panic-mode error recovery.
Practical - 6
Aim: Write a C program to parse a given string using Predictive parsing for given grammar.
type → simple | ↑id | array [ simple ] of type
simple → integer | char | num dotdot num
Software Required:Turbo C Compiler
Knowledge Required: Concepts of Predictive parsing
Theory/Logic:
Source code:
#include<stdio.h>
#include<conio.h>
#include<string.h>
#include<process.h>
void simple(void);
void type(void);
void match(char *a);
FILE *fsrc,*fpw;
int i,j,k,l;
char token[20][20],source[100];
char *lookahead,*token1;
void main()
{
clrscr();
src=fopen("one.txt","r");
rintf("Given String:\n");
i=k=l=0;
while(!feof(fsrc))
{
source[i]=getc(fsrc);
printf("%c",source[i]);
i++;
}
for(j=0;j<=i;j++)
{
if(source[j]!=' ')
{
token[k][l]=source[j];
l++;
}
else
{
token[k][l]='\0';
k++;
l=0;
}
}
printf("\nString Parse Up To:\n");
lookahead=token[0];
type();
printf("\nSuccesfully parse");
getch();
}
void match(char *token1)
{
if(strcmp(lookahead,token1)==0)
{
i1++;
lookahead=token[i1];
}
else
{
printf("\nERROR: String is not in given Grammar\n");
getch();
exit(0);
}
}
void type()
{
if(strcmp(lookahead,"integer")==0 || strcmp(lookahead,"char")==0 ||
strcmp(lookahead,"num")==0)
{
simple();
}
else if(strcmp(lookahead,"^")==0)
{
printf("^ ");
match("^");
printf("id ");
match("id");
}
else if(strcmp(lookahead,"array")==0)
{
printf("array ");
match("array");
printf("[ ");
match("[");
simple();
printf("] ");
match("]");
printf("of ");
match("of");
type();
}
else
{
printf("\nERROR: String is not in given Grammar\n");
getch();
exit(0);
}
}
void simple()
{
if(strcmp(lookahead,"integer")==0)
{
printf("integer ");
match("integer");
}
else if(strcmp(lookahead,"char")==0)
{
printf("char ");
match("char");
}
else if(strcmp(lookahead,"num")==0)
{
printf("num ");
match("num");
printf("dotdot ");
match("dotdot");
printf("num ");
match("num");
}
else
{
printf("\nERROR: String is not in given Grammar\n");
getch();
exit(0);
}
}
Input: one.txt
array [ num dotdot num ] of integer
Output:
Given String:
array [ num dotdot num ] of integer
String Parse Up To:
array [ num dotdot num ] of integer
Successfully Parse
Practical - 7
Source code:-
#include<stdio.h>
#include<conio.h>
#include<string.h>
void main(int argc,char *argv[])
{
char str[80],prev,ch;
int flag=0,fflag=0,tflag=0,iflag=0,bflag=0,eflag=0,line=0;
FILE *p;
clrscr();
p=fopen(argv[1],"r");
if(p==NULL)
{
printf("can not open the file");
exit();
}
while(fscanf(p,"%s",str)!=EOF)
{
if(strcmp(str,"tushar()")==0)
tflag=1;
else if(strcmp(str,"{")==0)
{
if(tflag==1)
bflag=1;
else
{
flag=1;
printf("\nerror on line 1");
break;
}
}
else if(strcmp(str,"Tint")==0)
{
if(bflag==1)
iflag=1;
else
{
flag=1;
printf("\nerror on line 2");
break;
}
}
else if(strcmp(str,"Tfloat")==0)
{
if(iflag==1)
fflag=1;
else
{
flag=1;
printf("\nerror on line 3");
break;
}
}
else if(strcmp(str,"}")==0)
{
if(fflag==1)
eflag=1;
else
{
flag=1;
printf("\nerror on line 4");
break;
}
}
}
fclose(p);
p=fopen("text.txt","r");
if(p==NULL)
{
printf("can not open the file");
exit();
}
while((ch=getc(p))!=EOF)
{
if(ch=='\n')
{
line++;
if(prev==')'| prev=='{'| prev=='}')
continue;
if(prev!=';')
{
flag=1;
printf("\nerror on line %d ,semicolon missing",line);
}
}
prev=ch;
}
fclose(p);
if(flag==1)
printf("\n incorrect");
else
printf("\n correct");
getch();
}
Output:-
Input string: text.txt
tushar()
{
Tint a
Tfloat b=1.3;
a=b;
}
Output string:-
Error on line 3, semicolon missing
Incorrect
Practical - 8
flag=1;
printf("\nerror on line 2");
break;
}
}
else if(strcmp(str,"Tfloat")==0)
{
if(iflag==1)
fflag=1;
else
{
flag=1;
printf("\nerror on line 3");
break;
}
}
else if(strcmp(str,"}")==0)
{
if(fflag==1)
eflag=1;
else
{
flag=1;
printf("\nerror on line 4");
break;
}
}
}
fclose(p);
p=fopen("text.txt","r");
if(p==NULL)
{
printf("can not open the file");
exit();
}
while((ch=getc(p))!=EOF)
{
if(ch=='\n')
{
line++;
if(prev==')'| prev=='{'| prev=='}')
continue;
if(prev!=';')
{
flag=1;
Output:-
Input string: text.txt
func()
{
Tint a
Tfloat b=1.3;
a=b;
}
Output string:-
Error on line 3, semicolon missing
Incorrect
Practical: – 9
Aim: Write a program for simple calculator having support of +, - , * , / and unary minus
operators with LEX and YACC.
Source code ( Simple Calculator ):
FILE : hoc.y
%
{
#define YYSTYPE double /* data type of yacc stack */
%
}
%token NUMBER
%left ‘+’ ‘-‘ /* left associative, same precedence */
%left ‘*’ ‘/‘ /* left associative, higher precedence */
%%
list: /* nothing */
| list ‘\n’
| list expr ‘\n’ { printf(“\t%.8g\n”,$2); }
expr: NUMBER { $$ = $1; }
| expr ‘+’ expr { $$ = $1 + $3; }
| expr ‘-’ expr { $$ = $1 - $3; }
| expr ‘*’ expr { $$ = $1 * $3; }
| expr ‘/’ expr { $$ = $1 / $3; }
| ‘(‘ expr ‘)’ { $$ = $2; }
;
%%
/* end of grammar */
#include <stdio.h>
#include <ctype.h>
char *progname; /* for error message */
int lineno = 1;
Practical: – 10
Aim: Write a program for calculator with arbitrary variable names and built-in functions.
This program adds several major new capabilities to program in practical-8 and
practical-9. The main new feature is access to built-in functions:
sin cos atan exp log log10 sqrt int abs
We have also added an exponentiation operator ‘^’, it has highest precedence, and is
right associative.
The program that results from all these changes is big enough that it is best split into
separate files for easier editing and faster compilation. There are now five files
instead of one:
hoc.y Grammar, main, yylex
hoc.h Global data structure for inclusion
symbol.c Symbol table routines, lookup, install
init.c Built-in and constants, init
math.c Interfaces to math routines, sqrt, log, etc.
Since the program is now lives on five files, not one, the makefile is more
complicated:
$ cat makefile
YFLAGS = -d # force creation of y.tab.h
OBJS = hoc.o init.o math.o symbol.o # abbreviation
hoc3: $ ( OBJS )
cc $ ( OBJS ) –lm -o hoc3
hoc.o: hoc.h
init.o symbol.o: hoc.h y.tab.h
pr:
@pr hoc.y hoc.h init.c math.c symbol.c makefile
clean:
rm –f $ ( OBJS ) y.tab. [ ch ]
$
$ make -n
$ make pr | lpr
$ make clean
Source code( Calculator with built-in functions ):
FILE : hoc.h
typedef struct Symbol
{ /* symbol table entry */
char *name;
short type; /* VAR, BLTIN, UNDEF */
union
{
double val; /* if VAR */
double ( *ptr ) ( ); /* if BLTIN*/
} u;
struct Symbol *next; /* to link to another */
} Symbol;
Symbol *install( ), *lookup( ) ;
FILE : symbol.c
#include “hoc.h”
#include “y.tab.h”
static Symbol *symlist = 0; /* symbol table: linked list */
Symbol *lookup( s ) /* find s in symbol table */
Char *s;
{
Symbol *sp;
for ( sp = symlist; sp ! = (Symbol * ) 0; sp = sp -> next )
if ( strcmp( sp -> name, s) = = 0 )
return sp;
return 0;
}
Symbol *install( s, t, d) /* install s in symbol list */
char *s;
int t;
double d;
{
Symbol *sp;
char *emalloc( ) ;
sp = ( Symbol * ) emalloc ( sizeof ( Symbol ) );
sp -> name = emalloc ( strlen ( s ) + 1 ) ; /* + 1 for ‘\0’ */
strcpy(sp -> name, s);
sp -> type = t; sp -> u.val = d;
sp -> next = symlist; /* put at front of list */
symlist = sp;
return sp;
}
char * emalloc ( n) /* check return from malloc */
unsigned n;
{
char *p, *malloc ( );
p = malloc ( n );
if ( p = = 0 )
execerror ( “out of memory“, ( char * ) 0 );
}
FILE : init.c
#include “hoc.h”
#include “y.tab.c”
#include <math.h>
extern double Log( ), Log10( ), Exp( ), Sqrt( ), integer( );
static struct
{
char *name;
double cval;
}
consts[] = { “PI”, 3.14159265358979323846,
“E”, 2.71828182845904523536,
“GAMMA”, 0.57721566490153286060, /* Euler */
“DEG”, 57.29577951308232087680, /* deg/radian */
“PHI”, 1.61803398874989484820, /* golden ratio */
0, 0};
static struct
{
char *name;
double (*func) ( );
} /* Built-ins */
built-ins[] = { “sin”, sin,
“cos”, cos,
“atan”, atan,
“log”, Log, /* checks argument */
“log 10, Log 10, /* checks argument */
“exp”, Exp, /* checks argument */
“sqrt”, Sqrt, /* checks argument */
“int”, integer,
“abs”, fabs,
0, 0};
init( ) /* install constants and built-ins in table */
{
int i;
Symbol *s;
for (i = 0; consts[i].name; i++)
#include <signal.h>
#include <setjmp.h>
jmp_buf begin;
main(argc, argv) /* hoc3 */
char *agrv[];
{
int fpecatch();
progname = argv[0];
init( );
setjmp(begin);
signal(SIGFPE, fpecatch);
yyparse();
}
execerror(s, t) /* recover from run-time error */
char *s, *t;
{
warning(s, t);
longjmp(begin, 0);
}
fpecatch() /* catch floating point exceptions*/
{
execerror(“floating point exception”, (char *) 0 ); }
yylex() /* Continuing in hoc.y */
{
int c;
while ( ( c=getchar() ) = = ‘ ‘ || c = = ‘\t’ );
if ( c = = EOF )
return 0;
if ( c = = ‘.’ || isdigit ( c ) ) /* number */
{
ungetc(c, stdin);
scanf(“%lf”, &yylval.val);
return NUMBER;
}
if(islower(c) )
{
yylavl.index = c – ‘a’; /* ASCII only */
}
if(isalpha(c) )
{
Symbol *s;
char sbuf(100), *p = sbuf;
do
{
*p++ = c;
}
while ( ( c = getchar( ) ) != EOF && isalnum(c) );
ungetc(c, stdin);
*p = ‘\0’;
if( ( s = lookup(sbuf) ) = = 0 )
s = install(sbuf, UNDEF, 0.0 );
yylval.sym = s;
return s-> type = = UNDEF ? VAR : s -> type;
}
if ( c = = ‘\n’ )
lineno++;
return c;
}
yyerror(s) /* called for yacc syntax error */
char *s;
{
warning( s, ( char * ) 0);
}
warning(s, t) /* print warning message */
cahr *s, *t;
{
fprintf(stderr, “%s: %s” , progname, s);
if ( t )
FILE: math.c
#include <math.h>
#include <errorno.h>
extern int errorno;
double errcheck( );
double Log( x )
double x;
{
return errcheck( log( x ), “log” );
}
double Log10( x )
double x;
{
return errcheck( log10( x ), “log10” );
}
double Exp( x )
double x;
{
return errcheck( exp( x ), “exp” );
}
double Sqrt( x )
double x;
{
return errcheck( sqrt( x ), “sqrt” );
}
double Pow( x , y )
double x , y;
{
return errcheck( pow( x , y ), “exponentiation” );
}
double integer( x )
double x;
{
return ( double ) ( long ) x;
}
double errcheck( d, s ) /* check result of library call */
double d;
char *s;
{
if ( erno = =EDOM )
{
errno = 0;
execerror( s, “argument out of domain”);
}
else if ( errno = = ERANGE )
{
errno = 0;
execerror( s, “result out of range” );
}
return d;
}
Output:
$ hoc3
1.5 ^ 2.3
2.5410306
Practical - 11
Aim: Write a program to perform Recursive Descendent Parsing for Grammar given below.
E -> T + E / T
T -> F * T / F
F -> ( E ) / i
This function returns next character from the input string and assign it to the global variable
‘next’. It also increment the ‘cursor’ pointer.
2. Expr()
3. Term()
4. Factor()
Source Code:-
#include<stdio.h>
#include<conio.h>
# define TRUE 1
# define FALSE 0
char next;
char *input;
int cursor;
char Get_Char();
int Expr();
int Term();
int Factor();
void main()
{
cursor=0;
clrscr();
printf("The Grammar is::\n\n");
printf("E -> T+E / T\nT -> F*T / F\nF -> (E) / i\n\n");
printf("Enter String For RDP(Please append '#' at end od input):: ");
gets(input);
next=Get_Char();
if(Expr())
{
if(next=='#')
printf("\nValid Statement");
else
printf("\nInvalid Statement");
}
else
printf("\nInvalid Statement");
getch();
}
int Expr()
{
if(!Term())
return FALSE;
if(next=='+')
{
next=Get_Char();
if(next=='#')
return FALSE;
if(!Expr())
return FALSE;
else
return TRUE;
}
else
return TRUE;
}
int Term()
{
if(!Factor())
return FALSE;
if(next=='*')
{
next=Get_Char();
if(next=='#')
return FALSE;
if(!Term())
return FALSE;
else
return TRUE;
}
else
return TRUE;
}
int Factor()
{
if(next=='#')
return FALSE;
if(next=='(')
{
next=Get_Char();
if(next=='#')
return FALSE;
if(!Expr())
return FALSE;
if(next!=')')
return FALSE;
else
{
next=Get_Char();
return TRUE;
}
}
if(next!='i')
return FALSE;
else
{
next=Get_Char();
return TRUE;
}
}
char Get_Char()
{
char ch;
ch=input[cursor];
cursor++;
return ch;
OUTPUT:: 1:
E -> T+E / T
T -> F*T / F
F -> (E) / i
Valid Statement
OUTPUT 2:
E -> T+E / T
T -> F*T / F
F -> (E) / i
Invalid Statement
Advantage: Early detection of syntax errors saves the compilation time. These are will not
propagate in the next phases.
Disadvantage: It recursively calls functions and thus requires implicit use of stack data
structure. There may be a chance of stack overflow.
Conclusion: Recursive–Descent parser requires one function for each Non-Terminal given in
the grammar.
Questions: