CD Lab Manual
CD Lab Manual
Practical File
Compiler Design
(BCS-652)
DEPARTMENT OF COMPUTER SCIENCE & ENGINEERING
Practical – 1
Aim: Design and implement a lexical analyzer for given language using C and the lexical
analyzer should ignore redundant spaces, tabs and new lines.
Program Code:
#include <stdio.h>
#include <ctype.h>
if (isalpha(current_char)) {
printf("Identifier: %c\n", current_char);
i++;
}
else if (isdigit(current_char)) {
printf("Number: %c\n", current_char);
i++;
}
else if (current_char == '+') {
printf("Operator: +\n");
i++;
}
else if (current_char == '-') {
printf("Operator: -\n");
i++;
}
else if (current_char == '*') {
printf("Operator: *\n");
i++;
}
else if (current_char == '/') {
printf("Operator: /\n");
i++;
}
else {
printf("Unknown character: %c\n", current_char);
i++;
}
}
int main() {
char input[] = "int a = 5 + 3;";
lexical_analyzer(input);
return 0;
}
Output:
Practical - 2
The Lex tool helps automate the generation of lexical analyzers. In this task, we'll use Lex to
generate a lexical analyzer.
Before proceeding with the code, you need to have Lex (or Flex, a common alternative)
installed on your system.
Code:
%{
#include <stdio.h>
#include <ctype.h>
%}
%%
[ \t\n]+ { /* Skip spaces, tabs, and newlines */ }
[0-9]+ { printf("Number: %s\n", yytext); }
[a-zA-Z]+ { printf("Identifier: %s\n", yytext); }
"+" { printf("Operator: +\n"); }
"-" { printf("Operator: -\n"); }
"*" { printf("Operator: *\n"); }
"/" { printf("Operator: /\n"); }
. { printf("Unknown character: %s\n", yytext); }
%%
int main() {
yylex(); // Start lexical analysis
return 0;
}
lex lexer.l
gcc lex.yy.c -o lexer -ll
./lexer
Input:
Output:
Identifier: int
Identifier: a
Operator: =
Number: 5
Operator: +
Number: 3
Unknown character: ;
Practical - 3
Procedure:
%{
#include "y.tab.h" // Include the YACC header
%}
%%
[0-9]+ { yylval = atoi(yytext); return NUMBER; }
[+\-*/] { return yytext[0]; } // Return operator as token
[ \t\n]+ { /* Skip spaces, tabs, and newlines */ }
%%
int yywrap() {
return 1;
}
%{
#include <stdio.h>
#include <stdlib.h>
%union {
int num;
}
%%
expr: expr '+' expr { printf("%d\n", $1 + $3); }
| expr '-' expr { printf("%d\n", $1 - $3); }
| expr '*' expr { printf("%d\n", $1 * $3); }
lex lexer.l
yacc -d parser.y
./calculator
Output:
A valid variable is defined as starting with a letter and followed by any number of letters or
digits. We can add a simple rule to Lex and YACC to handle this:
1. Lex file:
%{
#include "y.tab.h"
%}
%%
[a-zA-Z][a-zA-Z0-9]* { yylval = strdup(yytext); return VARIABLE; }
[0-9]+ { yylval = atoi(yytext); return NUMBER; }
[ \t\n]+ { /* Skip spaces */ }
%%
int yywrap() {
return 1;
}
2. YACC file:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int yylex();
void yyerror(char *s) {
fprintf(stderr, "Error: %s\n", s);
}
%}
%union {
char* str;
}
%%
var: VARIABLE { printf("Valid Variable: %s\n", $1); }
| NUMBER { printf("Number: %d\n", $1); }
;
%%
int main() {
printf("Enter a variable or number:\n");
yyparse();
return 0;
}
lex lexer.l
yacc -d parser.y
gcc lex.yy.c y.tab.c -o var_checker -ly -ll
./var_checker
Output Example:
Procedure:
%{
#include "y.tab.h" // Include the YACC header for token definitions
%}
%%
[0-9]+ { yylval = atoi(yytext); return NUMBER; }
[+\-*/] { return yytext[0]; } // Return operator tokens
[ \t\n]+ { /* Skip spaces, tabs, and newlines */ }
%%
int yywrap() {
return 1;
}
%{
#include <stdio.h>
#include <stdlib.h>
%union {
int num;
}
%%
expr: expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr {
if ($3 == 0) {
yyerror("Division by zero");
YYABORT;
} else {
$$ = $1 / $3;
}
}
| '(' expr ')' { $$ = $2; }
| '-' expr %prec UMINUS { $$ = -$2; }
| NUMBER { $$ = $1; }
;
%%
int main() {
printf("Enter an arithmetic expression:\n");
yyparse(); // Start parsing the input
return 0;
}
lex lexer.l
yacc -d parser.y
gcc lex.yy.c y.tab.c -o calculator -ly -ll
./calculator
Output:
d. Convert BNF Rules into YACC Form and Write Code to Generate Abstract Syntax
Tree.
Procedure:
%{
#include "y.tab.h"
%}
%%
[0-9]+ { yylval.num = atoi(yytext); return NUMBER; }
[+\-*/] { return yytext[0]; }
[ \t\n]+ { /* Skip spaces */ }
%%
int yywrap() {
return 1;
}
%{
#include <stdio.h>
#include <stdlib.h>
int yylex();
void yyerror(char *s) {
fprintf(stderr, "Error: %s\n", s);
}
struct ast {
int nodetype;
struct ast *left;
struct ast *right;
int value;
};
struct ast *new_ast_node(int nodetype, struct ast *left, struct ast *right, int value) {
struct ast *node = (struct ast *)malloc(sizeof(struct ast));
node->nodetype = nodetype;
node->left = left;
node->right = right;
node->value = value;
return node;
}
if (!node) return;
if (node->left) print_ast(node->left);
if (node->right) print_ast(node->right);
printf("Node Type: %d, Value: %d\n", node->nodetype, node->value);
}
%}
%union {
int num;
struct ast *ast;
}
%%
expr: expr '+' expr { $$ = new_ast_node('+', $1, $3, 0); }
| expr '-' expr { $$ = new_ast_node('-', $1, $3, 0); }
| expr '*' expr { $$ = new_ast_node('*', $1, $3, 0); }
| expr '/' expr { $$ = new_ast_node('/', $1, $3, 0); }
| '(' expr ')' { $$ = $2; }
| NUMBER { $$ = new_ast_node('N', NULL, NULL, $1); }
;
%%
int main() {
printf("Enter an arithmetic expression:\n");
yyparse();
return 0;
}
lex lexer.l
yacc -d parser.y
gcc lex.yy.c y.tab.c -o ast_calculator -ly -ll
./ast_calculator
Output:
Practical – 4
Aim: Write program to find ε – closure of all states of any given NFA with ε transition.
#include <stdio.h>
#include <stdlib.h>
#define MAX_STATES 10
#define MAX_TRANSITIONS 10
// Global variables
struct nfa_state nfa[MAX_STATES];
int nfa_size = 0;
int main() {
// Initialize NFA (for demonstration)
// Example: NFA with 4 states and some epsilon transitions
nfa_size = 4;
add_epsilon_transition(2, 3);
return 0;
}
Output:
Program – 5
Aim: Write program to convert NFA with ε transition to NFA without ε transition.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int from;
int to;
} EpsilonTransition;
typedef struct {
int from;
char symbol;
int to;
} Transition;
EpsilonTransition epsilon_transitions[MAX_EPSILON_TRANSITIONS];
Transition transitions[MAX_TRANSITIONS];
int epsilon_count = 0;
int transition_count = 0;
// Function to convert NFA with epsilon transitions to NFA without epsilon transitions
void convert_nfa_with_epsilon_to_nfa() {
int nfa_without_epsilon[MAX_STATES][MAX_STATES] = {0}; // Adjacency matrix
for new NFA
int closure[MAX_STATES];
int closure_size;
}
}
int main() {
// Example NFA with ε transitions
add_epsilon_transition(0, 1);
add_epsilon_transition(1, 2);
add_epsilon_transition(2, 3);
add_epsilon_transition(1, 4);
return 0;
}
Output:
Program – 6
#include <stdio.h>
#include <string.h>
#define STATES 10
#define SYMBOLS 2
int current_state[STATES];
memcpy(current_state, queue[front++], sizeof(current_state));
int current_index = findStateCombination(current_state, n);
printf("\t{}");
} else {
printf("\t{");
for (int k = 0; k < n; k++) {
if (dfa_states[dfa[i][j]][k])
printf("q%d,", k);
}
printf("}");
}
}
printf("\n");
}
}
int main() {
int n, m;
printf("Enter number of states in NFA: ");
scanf("%d", &n);
printf("Enter number of input symbols: ");
scanf("%d", &m);
convertNFAtoDFA(n, m);
printDFA(n, m);
return 0;
}
Output:
Practical – 7
Grammar:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
// Forward declarations
void expression();
void term();
void factor();
void match(char expected);
void factor() {
if (isdigit(lookahead)) {
printf(" %c ", lookahead); // number output
match(lookahead);
} else if (lookahead == '(') {
match('(');
expression();
match(')');
} else {
printf("Syntax Error: Unexpected character '%c'\n", lookahead);
exit(1);
}
}
int main() {
// Example input
input = "3+(4*5)-6";
nextToken(); // initialize lookahead
printf("Postfix: ");
expression();
if (lookahead != '\0') {
printf("\nSyntax Error: Unexpected characters at end of input\n");
return 1;
}
printf("\nParsing successful.\n");
return 0;
}
Output:
Practical – 8
Loop unrolling is a compiler optimization technique where the number of iterations in a loop
is decreased by executing multiple operations per iteration. This reduces loop overhead and
can improve performance, especially in compute-intensive operations.
Code:
#include <stdio.h>
#include <time.h>
return sum;
}
int main() {
int arr[SIZE];
return 0;
}
Output:
Practical – 9
Constant Propagation is a compiler optimization technique where known constant values are
substituted into expressions at compile time. In C, this is typically handled by the compiler
itself.
Code:
#include <stdio.h>
int main() {
without_constant_propagation();
with_constant_propagation();
return 0;
}
Output: