CD File
CD File
COMPILER DESIGN
CIC-351
11
12
13
EXPERIMENT-01
Aim:
Practice of LEX/YACC of Compiler design.
Theory:
1) LEX (Lexical Analyzer Generator)
Purpose:
LEX is a lexical analyzer generator that takes a specification of tokens (patterns of
characters, like keywords, operators, identifiers, etc.) and generates a lexical
analyzer (or scanner). The lexical analyzer reads the input, identifies tokens, and
passes them to the parser (generated by YACC).
Advantages of LEX:
• Automated Lexical Analysis: It simplifies the process of tokenizing input by
using regular expressions to automatically generate C code for token
recognition.
• Efficiency: LEX-generated scanners are highly efficient and fast.
2) YACC (Yet Another Compiler-Compiler)
Purpose:
YACC is a parser generator that reads a formal grammar specification and
generates C code for a parser. The parser processes tokens (provided by the lexical
analyzer) and constructs the syntactic structure of the input (typically in the form of
a parse tree or abstract syntax tree).
Advantages of YACC:
• Automated Parser Generation: YACC automates the process of writing
parsers, making it easier to design complex grammars.
• Error Detection: YACC can handle syntax errors efficiently by reporting
errors when tokens do not match grammar rules.
EXPERIMENT-02
Aim:
Write a program to check whether a string include keyword or not.
Code:
#include<iostream>
#include<string>
int main() {
std::string str, keyword;
std::cout << "Enter a string: ";
std::getline(std::cin, str);
std::cout << "Enter a keyword: ";
std::getline(std::cin, keyword);
if (str.find(keyword) != std::string::npos) {
std::cout << "The keyword is present in the string." << std::endl;
} else {
std::cout << "The keyword is not present in the string." << std::endl;
}
return 0;
}
Output:

EXPERIMENT-03
Aim:
Write a program to check whether a string contains an alphabet or not.
Code:
#include <iostream>
#include <string>
#include <cctype> // For isalpha function
int main() {
std::string str;
bool hasAlphabet = false;
if (hasAlphabet) {
std::cout << "The string contains at least one alphabet character." << std::endl;
} else {
std::cout << "The string does not contain any alphabet characters." <<
std::endl;
}
return 0;
}
Output:

EXPERIMENT-04
Aim:
Write a program to show all the operations of a stack.
Code:
#include <iostream>
#include <stack>
int main() {
std::stack<int> stack;
int choice, value;
do {
std::cout << "\nStack Operations Menu:";
std::cout << "\n1. Push";
std::cout << "\n2. Pop";
std::cout << "\n3. Top";
std::cout << "\n4. Is Empty";
std::cout << "\n5. Size";
std::cout << "\n6. Exit";
std::cout << "\nEnter your choice: ";
std::cin >> choice;
switch (choice) {
case 1:
std::cout << "Enter value to push: ";
std::cin >> value;
stack.push(value);
std::cout << value << " pushed into the stack." << std::endl;
break;
case 2:
if (!stack.empty()) {
std::cout << "Popped value: " << stack.top() << std::endl;
stack.pop();
} else {
std::cout << "Stack is empty." << std::endl;
}
break;
case 3:
if (!stack.empty()) {
std::cout << "Top value: " << stack.top() << std::endl;
} else {
std::cout << "Stack is empty." << std::endl;
}
break;
case 4:
if (stack.empty()) {
std::cout << "Stack is empty." << std::endl;
} else {
std::cout << "Stack is not empty." << std::endl;
}
break;
case 5:
std::cout << "Stack size: " << stack.size() << std::endl;
break;
case 6:
std::cout << "Exiting..." << std::endl;
break;
default:
std::cout << "Invalid choice. Please try again." << std::endl;
break;
}
} while (choice != 6);
return 0;
}
Output:
EXPERIMENT-05
Aim:
Write a program to remove left recursion from a grammar.
Code:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(){
vector<Production> productions={
{'E',{"E+T","T"}},
{'T',{"T*F","F"}},
{'F',{"(E)","id"}}
};
vector<Production> newProductions=removeLeftRecursion(productions);
cout<<"Original Productions:"<<endl;
for(const Production& prod:productions){
cout<<prod.nonTerminal<<".>";
for(const string& symbol:prod.symbols){
cout<<symbol<<"|";
}
cout<<endl;
}
Code:
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;
if (productions.size() > 1) {
string commonPrefix = productions[0];
for (int i = 1; i < productions.size(); i++) {
commonPrefix = longestCommonPrefix(commonPrefix, productions[i]);
if (commonPrefix.empty()) break;
}
vector<string> newProductions;
for (string &prod : productions) {
string suffix = prod.substr(commonPrefix.length());
if (suffix.empty()) {
suffix = "ε"; // ε represents an empty production
}
newProductions.push_back(suffix);
}
int main() {
int n;
cout << "Enter the number of grammar rules: ";
cin >> n;
// Input grammar
for (int i = 0; i < n; i++) {
string nonTerminal, arrow, production;
cout << "Enter non-terminal: ";
cin >> nonTerminal >> arrow; // Arrow input: '->'
vector<string> productions;
cout << "Enter productions (separated by space, end with newline): ";
while (cin >> production) {
productions.push_back(production);
if (cin.peek() == '\n') break; // End input when newline is encountered
}
grammar[nonTerminal] = productions;
}
return 0;
}
Output:
EXPERIMENT-07
Aim:
Write a program to find out the FIRST of the Non-terminals in a grammar.
Code:
#include <iostream>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <string>
using namespace std;
// Production rules of the grammar
unordered_map<string, vector<string>> productions;
// FIRST sets of non-terminals
unordered_map<string, unordered_set<char>> firstSets;
// Calculate the FIRST set for a non-terminal symbol
void calculateFirstSet(const string &nonTerminal)
{
if (firstSets.find(nonTerminal) != firstSets.end())
{
return; // Already calculated
}
unordered_set<char> firstSet;
for (const string &production : productions[nonTerminal])
{
char symbol = production[0];
if (isupper(symbol))
{
// Non-terminal symbol
calculateFirstSet(string(1, symbol)); // Recurse
const unordered_set<char> &subFirstSet = firstSets[string(1, symbol)];
firstSet.insert(subFirstSet.begin(), subFirstSet.end());
if (subFirstSet.find('e') != subFirstSet.end())
{
// If epsilon is in the FIRST set of the non-terminal, consider next symbol
for (size_t i = 1; i < production.size(); ++i)
{
char nextSymbol = production[i];
if (isupper(nextSymbol))
{
calculateFirstSet(string(1, nextSymbol)); // Recurse
const unordered_set<char> &nextFirstSet = firstSets[string(1,
nextSymbol)];
firstSet.insert(nextFirstSet.begin(), nextFirstSet.end());
if (nextFirstSet.find('e') == nextFirstSet.end())
{
break;
}
}
else
{
firstSet.insert(nextSymbol);
break;
}
}
}
}
else
{
// Terminal symbol
firstSet.insert(symbol);
}
}
firstSets[nonTerminal] = firstSet;
}
int main()
{
// Example grammar productions
productions["S"] = {"aBC", "b"};
productions["B"] = {"b", "C"};
productions["C"] = {"c", "e"};
// Calculate FIRST sets for each non-terminal
for (const auto &production : productions)
{
calculateFirstSet(production.first);
}
// Print the FIRST sets
for (const auto &nonTerminal : firstSets)
{
cout << "FIRST(" << nonTerminal.first << "): {";
for (char symbol : nonTerminal.second)
{
cout << symbol << " ";
}
cout << "}" << endl;
}
return 0;
}
Output:
EXPERIMENT-08
Aim:
Implementing Programs using Flex (Lexical analyzer tool).
Introduction of Flex:
Flex is a lexical analyzer generator, which is a tool for programming that
recognizes lexical patterns in the input with the help of flex specifications. Scroll
below to see the list of flex programs.
#undef yywrap
#define yywrap() 1
%}
%%
%%
main()
}
Program (b): Program to check if the given letter is a vowel or not.
%{
#undef yywrap
#define yywrap() 1
void display(int);
%}
%%
[a|e|i|o|u|] {
int flag=1;
display(flag);
return;
.+ {

int flag=0;
display(flag);
return;
%%
{ if(flag==1)
else
main()
yylex();
}

EXPERIMENT-09
Aim:
Elaborate DAG Representation with examples.
Directed Acyclic Graph for the above cases can be built as follows :
Step 1 –
• If the y operand is not defined, then create a node (y).
• If the z operand is not defined, create a node for case(1) as node(z).
Step 2 –
• Create node(OP) for case(1), with node(z) as its right child and node(OP) as
its left child (y).
• For the case (2), see if there is a node operator (OP) with one child node (y).
• Node n will be node(y) in case (3).
Step 3 –
Remove x from the list of node identifiers. Step 2: Add x to the list of attached
identifiers for node n.
Example :
T0 = a + b —Expression 1
T1 = T0 + c —-Expression 2
d = T0 + T1 —–Expression 3
Expression 1 : T0 = a + b

Expression 2: T1 = T0 + c
Expression 3 : d = T0 + T1


Example :
T1 = a + b
T2 = T1 + c
T3 = T1 x T2
Example :
T1 = a + b
T2 = a – b
T3 = T1 * T2
T4 = T1 – T3
T5 = T4 + T3