0% found this document useful (0 votes)
9 views12 pages

CD Practical File

The document is a practical file for a Compiler Design Lab at Guru Tegh Bahadur Institute of Technology, detailing various experiments related to LEX and YACC, including their purposes, structures, and advantages. It includes code examples for checking keywords in strings, verifying alphabet presence, stack operations, and removing left recursion from grammars. Each experiment outlines the aim, code, and expected output.

Uploaded by

kaurashmeet2004
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views12 pages

CD Practical File

The document is a practical file for a Compiler Design Lab at Guru Tegh Bahadur Institute of Technology, detailing various experiments related to LEX and YACC, including their purposes, structures, and advantages. It includes code examples for checking keywords in strings, verifying alphabet presence, stack operations, and removing left recursion from grammars. Each experiment outlines the aim, code, and expected output.

Uploaded by

kaurashmeet2004
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Guru Tegh Bahadur Institute of Technology, Delhi

(Affiliated to Guru Gobind Singh Indraprastha University, Dwarka, New Delhi)

Department of Computer Science & Engineering

PRACTICAL FILE

Compiler Design Lab

(Paper code: CIC 351)

Submitted to: Submitted by:


MS. ESHA SAXENA Name: ASHMEET KAUR
Assistant Professor Class: CSE-03(5th sem)
Enroll. No.: 09576802722
Index

S.No. Name of Experiment Date T.Sign


1

10

11
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).

How LEX Works:


LEX works by specifying patterns using regular expressions. Each regular
expression describes a pattern of characters that make up a token. LEX translates
these patterns into C code for scanning text.
1. Input: A specification file containing regular expressions and actions to
perform when these patterns are matched.
2. Output: C code that implements a lexical analyzer.
3. Execution: The generated lexical analyzer reads the input stream, identifies
tokens by matching them against the patterns, and returns the tokens to the
parser.

Structure of a LEX Program:


A typical LEX program is divided into three sections:
1. Definition Section: Used for defining variables, constants, or libraries.
2. Rules Section: Contains regular expressions and associated actions.
3. User Code Section: Contains user-defined helper functions, typically in C.

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).

How YACC Works:


YACC takes a context-free grammar as input and generates a bottom-up parser
(typically a LALR(1) parser). It processes tokens provided by the lexical analyzer
(LEX) and applies grammar rules to determine if the input sequence of tokens is
syntactically valid.
1. Input: A specification file that defines the grammar rules.
2. Output: C code that implements the syntax analyzer (parser).
3. Execution: The parser reads tokens, applies grammar rules, and reports
syntax errors or builds an abstract syntax tree (AST).

Structure of a YACC Program:


A typical YACC program is divided into three sections:
• Definition Section: Declarations of tokens and types.
• Rules Section: Grammar rules, each with an associated action.
• User Code Section: Auxiliary C functions and code.

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;

std::cout << "Enter a string: ";


std::getline(std::cin, str);

for (char c : str) {


if (isalpha(c)) {
hasAlphabet = true;
break;
}
}

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;

// A structure to represent a production rule


struct Production {
char nonTerminal;
vector<string> symbols;
};

// Function to remove left recursion


vector<Production> removeLeftRecursion(const vector<Production> &
productions) {
vector<Production> newProductions;

for (const Production& prod : productions) {


vector<string> newSymbols;
vector<string> newSymbolsRec;

// Group symbols into recursive and non-recursive


for (const string&symbol: prod.symbols){
if (symbol[0]==prod.nonTerminal){
newSymbolsRec.push_back(symbol.substr(1));
} else{
newSymbols.push_back(symbol);
}
}

// If there's left recursion, modify the production


if(!newSymbolsRec.empty()){
char newNonTerminal = prod.nonTerminal+1;
newProductions.push_back({prod.nonTerminal, newSymbols});
vector<string> newRecSymbols;
for(const string& recSymbol:newSymbolsRec){
newRecSymbols.push_back(recSymbol+newNonTerminal);
}
newRecSymbols.push_back(""); //Add an epsilon production
newProductions.push_back({newNonTerminal, newRecSymbols});
}else{
newProductions.push_back(prod);
}
}
return newProductions;
}

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;
}

cout<<"\nProductions after removing left recursion:"<<endl;


for(const Production& prod:newProductions){
cout<<prod.nonTerminal<<".>";
for(const string&symbol:prod.symbols){
cout<<symbol<<"|";
}
cout<<endl;
}
return 0;
}
Output:

You might also like