0% found this document useful (0 votes)
3 views17 pages

Ss Lab 3

The document outlines the design of a predictive parser for a user-defined context-free grammar (CFG) with operations including left recursion removal, left factoring, and computation of FIRST and FOLLOW sets. It also details the construction of a parsing table and validation of input strings using a stack. The provided C++ code implements these functionalities, demonstrating the parsing process through a series of steps.

Uploaded by

kapilyadav20005
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)
3 views17 pages

Ss Lab 3

The document outlines the design of a predictive parser for a user-defined context-free grammar (CFG) with operations including left recursion removal, left factoring, and computation of FIRST and FOLLOW sets. It also details the construction of a parsing table and validation of input strings using a stack. The provided C++ code implements these functionalities, demonstrating the parsing process through a series of steps.

Uploaded by

kapilyadav20005
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/ 17

AIM : - Design Predictive parser for user input CFG with the following operations:

1. Left Recursion
2. Left Factoring
3. First() set of NTs
4. Follow() set of NTs
5. Build parsing table
6. Validate input string using stack

CODE : -

#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <algorithm>

using namespace std;

char getUniqueNonTerminal(map<char, vector<string>>& grammar, map<char,


vector<string>>& newGrammar) {
char newNonTerminal = 'A';
while (grammar.count(newNonTerminal) || newGrammar.count(newNonTerminal)) {
newNonTerminal++;
}
newGrammar[newNonTerminal] = {}; return
newNonTerminal;
}

// Function to remove left recursion


map<char, vector<string>> removeLeftRecursion(map<char, vector<string>> grammar)
{
map<char, vector<string>> newGrammar = grammar; set<char>
nonTerminalsSet;

for (const auto& [nonTerminal, _] : grammar) { nonTerminalsSet.insert(nonTerminal);


}
vector<char> nonTerminals(nonTerminalsSet.begin(), nonTerminalsSet.end());

for (char A : nonTerminals) {


if (newGrammar.count(A)) {
vector<string> productions = newGrammar[A];
vector<string> recursive;
vector<string> nonRecursive;

for (const string& production : productions) { if


(production[0] == A) {
recursive.push_back(production.substr(1));
} else {
nonRecursive.push_back(production);
}
}

if (!recursive.empty()) {
char A_prime = getUniqueNonTerminal(grammar, newGrammar); newGrammar[A] =
nonRecursive;
for (string& production : newGrammar[A]) {
production += string(1, A_prime);
}
newGrammar[A_prime] = recursive;
for (string& production : newGrammar[A_prime]) {
production += string(1, A_prime);
}
newGrammar[A_prime].push_back("epsilon"); // Store epsilon as a string
}
}
}
return newGrammar;
}

// Function to perform left factoring


map<char, vector<string>> leftFactoring(map<char, vector<string>> grammar) { map<char,
vector<string>> factoredGrammar;

for (auto& [nonTerminal, productions] : grammar) {


map<string, vector<string>> prefixMap;
string commonPrefix;
// Find common prefix among productions for
(size_t i = 0; i < productions.size(); i++) {
for (size_t j = i + 1; j < productions.size(); j++) {
string a = productions[i], b = productions[j];
size_t k = 0;
while (k < a.size() && k < b.size() && a[k] == b[k]) {
k++;
}
if (k > 0) {
commonPrefix = a.substr(0, k);
break;
}
}
}

if (!commonPrefix.empty()) {
char newNT = getUniqueNonTerminal(grammar, factoredGrammar); vector<string>
newProductions;
vector<string> factoredPart;

for (const string& production : productions) { if


(production.find(commonPrefix) == 0) {
factoredPart.push_back(production.substr(commonPrefix.size()));
} else {
newProductions.push_back(production);
}
}

if (factoredPart.empty()) {
factoredPart.push_back("epsilon");
}

factoredGrammar[nonTerminal] = newProductions;
factoredGrammar[nonTerminal].push_back(commonPrefix + string(1, newNT));
factoredGrammar[newNT] = factoredPart;
} else {
factoredGrammar[nonTerminal] = productions;
}
}
return factoredGrammar;
}

// FIRST and FOLLOW sets


map<char, set<string>> firstSets, followSets; // Change to sets of strings for epsilon handling

// Function to compute FIRST set


void computeFirst(map<char, vector<string>>& grammar, char nonTerminal) { if
(!firstSets[nonTerminal].empty()) return;

for (const string& production : grammar[nonTerminal]) { if


(production == "epsilon") {
firstSets[nonTerminal].insert("epsilon"); // Use "epsilon" instead of 'ε'
} else if (!isupper(production[0])) {
firstSets[nonTerminal].insert(string(1, production[0]));
} else {
computeFirst(grammar, production[0]);
firstSets[nonTerminal].insert(firstSets[production[0]].begin(),
firstSets[production[0]].end());
}
}
}

// Function to compute FOLLOW set


void computeFollow(map<char, vector<string>>& grammar, char nonTerminal) { if
(!followSets[nonTerminal].empty()) return;

if (nonTerminal == 'R') { // Assuming 'R' is the start symbol


followSets[nonTerminal].insert("$");
}

for (const auto& [nt, productions] : grammar) { for


(const string& production : productions) {
size_t pos = production.find(nonTerminal); if
(pos != string::npos) {
if (pos + 1 < production.size()) {
char nextSymbol = production[pos + 1]; if
(isupper(nextSymbol)) {
followSets[nonTerminal].insert(firstSets[nextSymbol].begin(),
firstSets[nextSymbol].end());
} else {
followSets[nonTerminal].insert(string(1, nextSymbol));
}
} else {
if (nt != nonTerminal) {
computeFollow(grammar, nt);
followSets[nonTerminal].insert(followSets[nt].begin(),
followSets[nt].end());
}
}
}
}
}
}

// Function to build the parsing table


map<pair<char, char>, string> buildParsingTable(map<char, vector<string>>&
grammar) {
map<pair<char, char>, string> parsingTable;

for (const auto& [nonTerminal, productions] : grammar) { for


(const string& production : productions) {
if (!production.empty()) {
char firstSymbol = production[0]; if
(!isupper(firstSymbol)) {
parsingTable[{nonTerminal, firstSymbol}] = production;
} else {
for (const string& terminal : firstSets[firstSymbol]) { if
(terminal != "epsilon") { // Skip epsilon
parsingTable[{nonTerminal, terminal[0]}] = production;
}
}
if (firstSets[firstSymbol].count("epsilon")) {
for (const string& followSymbol : followSets[nonTerminal]) {
parsingTable[{nonTerminal, followSymbol[0]}] = production;
}
}
}
} else {
// Handle epsilon
for (const string& followSymbol : followSets[nonTerminal]) {
parsingTable[{nonTerminal, followSymbol[0]}] = "epsilon";
}
}
}
}
return parsingTable;
}

int main() {
map<char, vector<string>> grammar;

grammar['R'] = {"TCA", "TCDA"};


grammar['T'] = {"T*F", "F"};
grammar['F'] = {"(E)", "x"};

cout << "Original Grammar:\n";


for (const auto& [nonTerminal, productions] : grammar) { cout
<< nonTerminal << " -> ";
for (const string& production : productions) { cout
<< production << " | ";
}
cout << "\n";
}
cout << endl;

map<char, vector<string>> noLeftRecursionGrammar = removeLeftRecursion(grammar);

cout << "Grammar after removing left recursion:\n";


for (const auto& [nonTerminal, productions] : noLeftRecursionGrammar) { cout <<
nonTerminal << " -> ";
for (const string& production : productions) { cout
<< production << " | ";
}
cout << "\n";
}
cout << endl;
map<char, vector<string>> factoredGrammar = leftFactoring(noLeftRecursionGrammar);

cout << "Grammar after left factoring:\n";


for (const auto& [nonTerminal, productions] : factoredGrammar) { cout <<
nonTerminal << " -> ";
for (const string& production : productions) { cout
<< production << " | ";
}
cout << "\n";
}
cout << endl;

// Compute FIRST sets


for (const auto& [nonTerminal, _] : factoredGrammar) { computeFirst(factoredGrammar,
nonTerminal);
}

cout << "FIRST Sets:\n";


for (const auto& [nonTerminal, firstSet] : firstSets) { cout
<< "FIRST(" << nonTerminal << ") = { ";
for (const string& ch : firstSet) cout << ch << " "; cout
<< "}\n";
}
cout << endl;

// Compute FOLLOW sets


for (const auto& [nonTerminal, _] : factoredGrammar) {
computeFollow(factoredGrammar, nonTerminal);
}

cout << "FOLLOW Sets:\n";


for (const auto& [nonTerminal, followSet] : followSets) {
cout << "FOLLOW(" << nonTerminal << ") = { ";
for (const string& ch : followSet) cout << ch << " "; cout
<< "}\n";
}

// Build Parsing Table


map<pair<char, char>, string> parsingTable = buildParsingTable(factoredGrammar);

cout << "\nParsing Table:\n";


for (const auto& entry : parsingTable) {
cout << "M[" << entry.first.first << ", " << entry.first.second << "] = " << entry.second
<< "\n";
}

return 0;
}

OUTPUT : -
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <algorithm>
#include <iomanip>

using namespace std;

char getUniqueNonTerminal(map<char, vector<string>>& grammar, map<char,


vector<string>>& newGrammar) {
char newNonTerminal = 'A';
while (grammar.count(newNonTerminal) || newGrammar.count(newNonTerminal)) {
newNonTerminal++;
}
newGrammar[newNonTerminal] = {};
return newNonTerminal;
}

// Remove Left Recursion


map<char, vector<string>> removeLeftRecursion(map<char, vector<string>> grammar)
{
map<char, vector<string>> newGrammar = grammar; set<char>
nonTerminalsSet;

for (const auto& [nonTerminal, _] : grammar) { nonTerminalsSet.insert(nonTerminal);


}
vector<char> nonTerminals(nonTerminalsSet.begin(), nonTerminalsSet.end());

for (char A : nonTerminals) {


if (newGrammar.count(A)) {
vector<string> productions = newGrammar[A];
vector<string> recursive, nonRecursive;

for (const string& production : productions) { if


(production[0] == A) {
recursive.push_back(production.substr(1));
} else {
nonRecursive.push_back(production);
}
}

if (!recursive.empty()) {
char A_prime = getUniqueNonTerminal(grammar, newGrammar); newGrammar[A] =
nonRecursive;
for (string& production : newGrammar[A]) {
production += string(1, A_prime);
}
newGrammar[A_prime] = recursive;
for (string& production : newGrammar[A_prime]) {
production += string(1, A_prime);
}
newGrammar[A_prime].push_back("epsilon");
}
}
}
return newGrammar;
}

// Compute FIRST set


map<char, set<string>> firstSets, followSets;

void computeFirst(map<char, vector<string>>& grammar, char nonTerminal) { if


(!firstSets[nonTerminal].empty()) return;

for (const string& production : grammar[nonTerminal]) { if


(production == "epsilon") {
firstSets[nonTerminal].insert("epsilon");
} else if (!isupper(production[0])) {
firstSets[nonTerminal].insert(string(1, production[0]));
} else {
computeFirst(grammar, production[0]);
firstSets[nonTerminal].insert(firstSets[production[0]].begin(),
firstSets[production[0]].end());
}
}
}

// Compute FOLLOW set


void computeFollow(map<char, vector<string>>& grammar, char nonTerminal) { if
(!followSets[nonTerminal].empty()) return;

if (nonTerminal == 'R') {
followSets[nonTerminal].insert("$");
}

for (const auto& [nt, productions] : grammar) { for


(const string& production : productions) {
size_t pos = production.find(nonTerminal); if
(pos != string::npos) {
if (pos + 1 < production.size()) {
char nextSymbol = production[pos + 1]; if
(isupper(nextSymbol)) {
followSets[nonTerminal].insert(firstSets[nextSymbol].begin(),
firstSets[nextSymbol].end());
} else {
followSets[nonTerminal].insert(string(1, nextSymbol));
}
} else {
if (nt != nonTerminal) {
computeFollow(grammar, nt);
followSets[nonTerminal].insert(followSets[nt].begin(),
followSets[nt].end());
}
}
}
}
}
}

// Build Parsing Table


map<pair<char, char>, string> buildParsingTable(map<char, vector<string>>&
grammar) {
map<pair<char, char>, string> parsingTable;

for (const auto& [nonTerminal, productions] : grammar) { for


(const string& production : productions) {
if (!production.empty()) {
char firstSymbol = production[0]; if
(!isupper(firstSymbol)) {
parsingTable[{nonTerminal, firstSymbol}] = production;
} else {
for (const string& terminal : firstSets[firstSymbol]) { if
(terminal != "epsilon") {
parsingTable[{nonTerminal, terminal[0]}] = production;
}
}
if (firstSets[firstSymbol].count("epsilon")) {
for (const string& followSymbol : followSets[nonTerminal]) {
parsingTable[{nonTerminal, followSymbol[0]}] = production;
}
}
}
} else {
for (const string& followSymbol : followSets[nonTerminal]) {
parsingTable[{nonTerminal, followSymbol[0]}] = "epsilon";
}
}
}
}
return parsingTable;
}

// Print stack content


void printStack(stack<char> s) {
vector<char> stackContent; while
(!s.empty()) {
stackContent.push_back(s.top()); s.pop();
}
reverse(stackContent.begin(), stackContent.end()); for
(char c : stackContent) {
cout << c;
}
}

// Parse input string


bool parseString(map<pair<char, char>, string>& parsingTable, string input) { stack<char>
parseStack;
parseStack.push('$');
parseStack.push('R');

input += '$';
int index = 0;

cout << "\nParsing Steps:\n";


cout << "---------------------------------- \n";
cout << setw(20) << "Stack" << setw(20) << "Input" << setw(20) << "Action" << endl; cout
<< " --------------------------------------- \n";

while (!parseStack.empty()) { char


top = parseStack.top();
parseStack.pop();

cout << setw(20);


printStack(parseStack);
cout << setw(20) << input.substr(index) << setw(20);

if (top == input[index]) {
cout << "Match " << top << endl;
index++;
} else if (isupper(top)) {
if (parsingTable.count({top, input[index]})) {
string production = parsingTable[{top, input[index]}]; cout
<< top << " -> " << production << endl;
if (production != "epsilon") {
for (int i = production.size() - 1; i >= 0; i--) {
parseStack.push(production[i]);
}
}
} else {
cout << "Error: No rule found!" << endl;
return false;
}
} else {
cout << "Error: Mismatch!" << endl;
return false;
}
}

cout << "---------------------------------- \n";


return index == input.size() - 1;
}

int main() {
map<char, vector<string>> grammar;
grammar['R'] = {"TCA", "TCDA"};
grammar['T'] = {"T*F", "F"};
grammar['F'] = {"(E)", "x"};

map<char, vector<string>> noLeftRecursionGrammar = removeLeftRecursion(grammar);

for (const auto& [nonTerminal, _] : noLeftRecursionGrammar) {


computeFirst(noLeftRecursionGrammar, nonTerminal);
}
for (const auto& [nonTerminal, _] : noLeftRecursionGrammar) {
computeFollow(noLeftRecursionGrammar, nonTerminal);
}

map<pair<char, char>, string> parsingTable = buildParsingTable(noLeftRecursionGrammar);

string input;
cout << "\nEnter a string to verify: "; cin
>> input;

if (parseString(parsingTable, input)) {
cout << "String is ACCEPTED by the grammar.\n";
} else {
cout << "String is REJECTED by the grammar.\n";
}

return 0;
}

OUTPUT : -

You might also like