3160715-System Software Lab-Manual
3160715-System Software Lab-Manual
System Software
(3160715)
B.E. Semester 6
(Computer)
Place: __________
Date: __________
By using this lab manual students can go through the relevant theory and
procedure in advance before the actual performance which creates an interest and
students can have basic idea prior to performance. This in turn enhances pre-
determined outcomes amongst students. Each experiment in this manual begins
with competency, industry relevant skills, course outcomes as well as practical
outcomes (objectives). The students will also achieve safety and necessary
precautions to be taken while performing practical.
Utmost care has been taken while preparing this lab manual however always there
is chances of improvement. Therefore, we welcome constructive suggestions for
improvement and removal of errors if any.
System Software (3160715)
Practical List
Course Outcomes (COs):
CO-1 Explain and classify different methodologies, concepts and approaches to System Software
Programming.
CO-2 Identify elements of language processors with various data structures used in development of
one-pass and multi-pass assemblers.
CO-3 Examine macro processor, its usage and compare various loading and linking schemes.
CO-4 Build various system programs using language processor development tools such as YACC and
Lex.
CO-5 Design code optimization based solution for the given system problems by applying various
techniques
of compiler, interpreter and debugger.
Platform
Sr.
Objective(s) of Experiment to be CO1 CO2 CO3 CO4 CO5
No.
used
Write a C program to implement the
1. lexical analyzer. √
Date:
The lexical analysis phase typically involves a scanner, which reads the source code
character by character and groups them into tokens based on predefined rules and
regular expressions. The output of lexical analysis is a stream of tokens with class
the tokens belongs to.
Procedure:
1. Write a C program to work as a lexical analyzer.
Observations:
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
char str[100] = "int ans = b + 10.5 ;";
char *keywords[] = { "auto", "break", "case", "char", "const", "continue", "default", "do", "double",
"else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register", "return", "short", "signed",
"sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while" };
if (str[i] == '.')
{ hasDecimal = true;
}
}
return (hasDecimal);
}
void Identify_Tokens()
{ int left = 0, right = 0; int
len = strlen(str);
if (isKeyword(subStr) == true)
printf("'%s' IS A KEYWORD\n", subStr);
int main()
{
Identify_Tokens();
return (0);
}
Output:
AIM: Write a program in text file and generate SYMTAB and LITTAB.
Date:
Theory:
Procedure:
1. Write a C program to work as a lexical analyzer.
Observations:
Input File: “START.txt”
START 100
MOVEM BREG, ONE
MOVER BREG, A
MOVER BREG, B
MOVER BREG, C
PRINT A
PRINT B
PRINT C
STOP
A DS 1
B DS 1
C DS 1
ONE DC "1"
D DS 1
HUNDRED DC "100"
Program:
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
char str[500];
char SYMTAB[100]="";
char LITTAB[100]="";
char temp[10]; int
SYMADDRESS[10];
int name_i = 0;
int address_i = 0;
int LC; char
prev[10];
int count=0;
if (str[i] == '.')
{
hasDecimal = true;
}
}
return (hasDecimal);
}
char* subString(int left, int right)
{ int i;
char* subStr = (char*) malloc(sizeof(char) * (right - left + 2));
if (isOPCODE(subStr) == true)
{
}
else if (isAD(subStr) == true)
{
if(isSTART(subStr) == 1)
{
start_flag=1;
}
}
else if (isDL(subStr) == 1 || isDL(subStr) == 2) {
DL_flag=1;
if(isDL(subStr) == 1)
{
strcat(SYMTAB, prev);
strcat(SYMTAB, "\t\t");
sprintf(temp, "%d", LC);
strcat(SYMTAB, temp);
strcat(SYMTAB, "\n");
}
if(isDL(subStr) == 2)
{
strcat(LITTAB, prev);
strcat(LITTAB, "\t\t");
sprintf(temp, "%d", LC);
strcat(LITTAB, temp);
strcat(LITTAB, "\n");
}
}
else if (isInteger(subStr) == true)
{
if (start_flag==1)
{
LC = atoi(subStr) - 1;
start_flag=0;
}
if(DL_flag==1)
{
DL_flag=0;
}
}
else if (isRealNumber(subStr) == true)
{
}
else if (validIdentifier(subStr) == true )
{
if( (strcmp(subStr,"BREG")) !=0 )
{
strcpy(prev,subStr);
}
}
else if (validLiteral(subStr) == true)
{
// printf("\n It is a literal \n");
}
left = right;
}
}
return;
}
int main()
{
FILE *fp; char c; int
i,j; char Line[20];
fp=fopen("START.txt","r");
while((c=fgetc(fp))!=EOF)
{
printf("%c",c);
}
fclose(fp);
printf("\n------------------------\n");
fp=fopen("START.txt","r");
do
{
i=0;
while((c=fgetc(fp))!='\n')
{
if(c!=EOF)
{
Line[i++]=c;
}
else
{
goto END;
}
}
Line[i]='\0';
strcpy(str,Line);
Identify_Tokens();
LC++;
}while(1);
END:
Output:
References used by the students:
Experiment No: 3
Date:
Competency and Practical Skills: C programming, macro writing and application
1. Macro Definition: To define a macro, you use the #define directive followed by
the macro name and its replacement value. The replacement value can be an
expression, a constant, or any valid C code.
#define PI 3.14155926
Observations:
#include<stdio.h>
#define PI 3.14
#define COUNT 100
#define CAPITAL "Delhi"
void main()
{
int a=5,b=99;
scanf("%d%d",&a,&b);
Output:
References used by the students:
Experiment No: 4
AIM: Write a Lexical Analyzer (using Lex utility for UNIX).
Date:
Theory:
Lex, originally written by Mike Lesk and Eric Schmidt[3] and described in 1975,
is the standard lexical analyzer generator on many Unix systems.
Lex reads an input stream specifying the lexical analyzer and writes source
code which implements the lexical analyzer in the C programming language.
• User Code Section: This section can contain any C/C++ program code that
user want to execute.
• Yylex() function is used to flex compiler , which is embedded in this section so
user need to include this function.
Sample Program
%{
#include <iostream>
%}
%%
[0-9]+\.[0-9]+ { cout<< "Found a floating-point number:" <<yytext<<endl; }
[0-9]+ { cout<< "Found an integer:" <<yytext<<endl; }
[a-zA-Z0-9]+ { cout<< "Found a string: " <<yytext<<endl; }
%%
main()
{
// lex through the input:
yylex();
}
1. flex lexfin.l
2. gcclex.yy.c , will produce a.exe as output
3. a.exe tt.c
Observations:
%{
#include <stdio.h>
%}
%%
%%
int yywrap(void)
{
}
int main(void)
{
printf("Enter a statement : ");
yylex();
return 0;
}
Output:
Date:
Theory:
YACC stands for "Yet another Compiler Compiler." It is a tool used in the field of
computer science and software development to generate parsers and syntax
analyzers for programming languages or other formal languages. YACC is often used
in conjunction with Lex, another tool used for lexical analysis (scanning or
tokenization).
Observations:
test.l
%{
#include<stdio.h> #include
"test.tab.h"
extern int yylval;
%}
/* Rule Section */
%%
[0-9]+ { yylval=atoi(yytext);
return NUMBER; }
[\t] ;
[\n] return 0;
. return yytext[0];
%%
int yywrap()
{
return 1;
}
test.y
%{
#include<stdio.h>
int flag=0;
%}
%token NUMBER
/* Rule Section */
%%
E: E'+'E {$$=$1+$3;}
|E'-'E {$$=$1-$3;}
|E'*'E {$$=$1*$3;}
|E'/'E {$$=$1/$3;}
|E'%'E {$$=$1%$3;}
|'('E')' {$$=$2;}
| NUMBER {$$=$1;}
;
%%
//driver code
void main()
{
printf("\n Enter Any Arithmetic Expression : ");
yyparse();
if(flag==0)
{
printf(" Valid Arithmetic Expression...! \n");
}
}
yyerror()
{
printf(" Invalid Arithmetic Expression...! \n");
flag=1;
}
Output:
Date:
Theory:
Example:
S → iEtS / iEtSeS / a
E→b
S → iEtSS’ / a
S’ → eS / ∈
E→b
Observations:
#include<stdio.h> char
NT[5] = "A";
char new_NT[5];
char new_P[5][10];
int i,j,x,count=2,common=1;
void Leftfactor()
{
for(i=0;i<count;i++)
{
for(j=i+1;j<count;j++)
{
while(common-1 < strlen(P[i]))
{
if( ! (strncmp(P[i],P[j],common)))
{
common++;
}
else
{
printf("\nGrammar after removing left factoring : \n");
printf("%s -> ",NT);
for(x=0;x<common-1;x++)
{
printf("%c",P[i][x]);
}
printf("%s'",NT);
break;
}
}
}
}
x=0;
x=0;
for(i=common-1 ; i<strlen(P[1]) ; i++)
{
while(P[1][i] != '\0')
{
new_P[1][x++]=P[1][i++];
}
new_P[1][x]='\0';
}
printf("%s | %s ",new_P[0],new_P[1]);
} void
main()
{
printf("Grammar rule is as follows: \n");
printf("%s -> ", NT);
for(i=0;i<count;i++)
{
if(i!=count-1)
{
printf(" %s |",P[i]);
}
else
{
printf(" %s \n",P[i]);
}
}
Leftfactor();
}
Output:
AIM: Write a C program to remove the Left Recursion from a given grammar.
Date:
Theory:
Left recursion is a concept in formal grammar theory and parsing algorithms used in
computer science and natural language processing. It refers to a situation in which
a grammar rule directly or indirectly references itself from the left-hand side of the
production rule.
A → α1 | α2 | ... | αn
Where "A" is a non-terminal symbol, and α1, α2, ..., αn are sequences of non-
terminal and terminal symbols. Left recursion occurs when one of the αi sequences
starts with the same non-terminal symbol "A" that is on the left-hand side:
Left recursion can lead to problems in parsing and can make it difficult to construct
efficient parsers using certain parsing techniques, such as recursive descent
parsing. To eliminate left recursion from a grammar, you can use a technique called
left recursion elimination, which involves rewriting the grammar rules to remove
the left recursion. The idea is to factor out the left recursion so that the grammar
becomes:
Observations:
#include<stdio.h>
#include<string.h>
char new_P[5][10];
char R1[10],R2[10],New_R1[10],New_R2[10];
int i,j,x,count=2;
int flag1=0,flag2=0;
void PRINT()
{
printf("%s -> ",NT);
if(flag1==1)
printf("%s",R2);
else if(flag2==1)
printf("%s",R1);
strcpy(new_NT,NT);
strcat(new_NT,"\'");
printf(" | %c",238);
}
void LeftRecursion()
{
for(i=0;i<2;i++)
{
if(NT[0]==P[i][0])
{
if(i==0)
flag1=1;
if(i==1)
flag2=1;
}
}
if(flag1==1)
{
for(i=0,j=1;i<strlen(P[0]);i++,j++)
{
New_R1[i] = P[0][j];
}
New_R1[i]='\0';
strcat(New_R1,NT);
strcat(New_R1,"\'");
}
else
{
strcpy(R1,P[0]);
}
if(flag2==1)
{
for(i=0,j=1;i<strlen(P[1]);i++,j++)
{
New_R2[i] = P[1][j];
}
New_R2[i]='\0';
strcat(New_R2,NT);
strcat(New_R2,"\'");
}
else
{
strcpy(R2,P[1]);
strcat(R2,"A\'");
}
PRINT();
}
void main()
{
printf("Grammar rule is as follows: \n");
printf("%s -> ", NT);
for(i=0;i<count;i++)
{
if(i!=count-1)
{
printf(" %s |",P[i]);
}
else
{
printf(" %s \n",P[i]);
}
}
printf("\n--------------------------\n");
LeftRecursion();
}
Output:
AIM: Write C program to Implement Recursive Descendent Parsing for the given
Grammar.
E -> T + E / T
T -> F * T / F
F ->( E ) / i
Date:
Theory:
It is a kind of Top-Down Parser. A top-down parser builds the parse tree from the
top to down, starting with the start non-terminal. A Predictive Parser is a special
case of Recursive Descent Parser, where no Back Tracking is required. By carefully
writing a grammar means eliminating left recursion and left factoring from it, the
resulting grammar will be a grammar that can be parsed by a recursive descent
parser.
It can be defined as a Parser that uses the various recursive procedures to process
the input string with no backtracking. It can be simply performed using a Recursive
language. The first symbol of the string of R.H.S of production will uniquely
determine the correct alternative to choose.
The recursive procedures can be simply to write and adequately effective if written
in a language that executes the procedure call effectively. There is a procedure for
each non-terminal in the grammar. It can consider a global variable lookahead,
holding the current input token and a procedure match (Expected Token) is the
action of recognizing the next token in the parsing process and advancing the input
stream pointer, such that lookahead points to the next token to be parsed. Match ()
is effectively a call to the lexical analyzer to get the next token.
lookahead == a
match()
lookahead == +
match ()
lookahead == b
……………………….
……………………….
Observations:
#include<stdio.h>
#include<string.h>
char new_P[5][10];
char R1[10],R2[10],New_R1[10],New_R2[10];
int i,j,x,count=2;
int flag1=0,flag2=0;
void PRINT()
{
printf("%s -> ",NT);
if(flag1==1)
printf("%s",R2);
else if(flag2==1)
printf("%s",R1);
strcpy(new_NT,NT);
strcat(new_NT,"\'");
printf(" | %c",238);
}
void LeftRecursion()
{
for(i=0;i<2;i++)
{
if(NT[0]==P[i][0])
{
if(i==0)
flag1=1;
if(i==1)
flag2=1;
}
}
if(flag1==1)
{
for(i=0,j=1;i<strlen(P[0]);i++,j++)
{
New_R1[i] = P[0][j];
}
New_R1[i]='\0';
strcat(New_R1,NT);
strcat(New_R1,"\'");
}
else
{
strcpy(R1,P[0]);
}
if(flag2==1)
{
for(i=0,j=1;i<strlen(P[1]);i++,j++)
{
New_R2[i] = P[1][j];
}
New_R2[i]='\0';
strcat(New_R2,NT);
strcat(New_R2,"\'");
}
else
{
strcpy(R2,P[1]);
strcat(R2,"A\'");
}
PRINT();
}
void main()
{
printf("Grammar rule is as follows: \n");
printf("%s -> ", NT);
for(i=0;i<count;i++)
{
if(i!=count-1)
{
printf(" %s |",P[i]);
}
else
{
printf(" %s \n",P[i]);
}
}
printf("\n--------------------------\n");
LeftRecursion();
}
Output:
Date:
Theory:
Predictive parsing uses a stack and a parsing table to parse the input and
generate a parse tree. Both the stack and the input contains an end symbol
$ to denote that the stack is empty and the input is consumed. The parser
refers to the parsing table to take any decision on the input and stack
element combination.
Observations:
input.l
%option noyywrap
%{
#include"y.tab.h"
extern yylval;
%}
%%
[0-9]+ {yylval=atoi(yytext); return NUMBER;}
[a-zA-Z]+ {return ID;}
[\t]+ ;
\n {return 0;}
. {return yytext[0];}
%%
input.y
%{
#include<stdio.h>
int flag=0;
%}
%token NUMBER ID
%left '+' '-'
%left '*' '/'
%%
expr: expr '+' expr
|expr '-' expr
|expr '*' expr
|expr '/' expr
|'-'NUMBER
|'-'ID
|'('expr')'
|NUMBER
|ID
;
%%
void main()
{
printf("\n Enter the expression\n\n ");
yyparse();
if(flag==0)
{
printf("\n Expression is valid \n\n");
}
}
int yyerror(char *s)
{
printf("\n Expression is invalid \n\n");
flag=1;
}
Output: -
AIM: Write a C program which generates Quadruple Table for the given postfix String.
Date:
Theory:
Observations:
#include<stdio.h>
#include<conio.h>
#include<string.h>
struct quad
{
char
op; char
op1; char
op2;
char r;
};
void
main(
)
{
struct quad a[20]; char
s[20],temp2[20],temp1[10];
int k=0,i=0,count,j,len;
clrscr();
strcpy(s,temp1);
strcat(s,temp2);
len=strlen(s);
i++;
k=k-1;
}
else
k++;
}
printf("---------------------------------------\n");
getch();
}
Output: