0% found this document useful (0 votes)
9 views

cd final file

Compiler file

Uploaded by

supriyathakur744
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views

cd final file

Compiler file

Uploaded by

supriyathakur744
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 45

PROGRAM-1

Aim : Study of LEX tool.


Theory: Lex is a lexical analyzer generator tool that is often used in the construction of
compilers and interpreters for programming languages. Lex is part of the Lex and Yacc
toolset, where Lex is used for lexical analysis (scanning), and Yacc (Yet Another Compiler
Compiler) is used for syntax analysis (parsing).
Here's a brief overview of Lex:
1.Purpose:
 Lex is designed to generate lexical analyzers (scanners or tokenizers) for a given set of
regular expressions.
 It helps break down the input source code into a sequence of tokens, which are the
smallest units of meaning in a programming language.
2. Workflow:
 Lex takes a specification file as input, which contains regular expressions and
corresponding actions to be taken when a match is found.
 The specification is written in a language called Lex language.
3. Lex Language:
 The Lex language consists of rules, where each rule associates a regular expression
with an action.
 Regular expressions define patterns to be recognized in the input stream.
4. Tokenization:
 Lex generates a finite automaton from the regular expressions in the specification.
 This automaton is used to recognize patterns in the input source code.
 When a pattern is matched, the corresponding action is executed.
5. Action Code:
 Actions in Lex are typically written in C.
 These actions are executed when a matching pattern is found, allowing for
custom processing of the input.
6. Output:
 Lex generates a C source file as output.
The generated C file includes the finite automaton and the action.
PROGRAM-2
Aim : To write a Program in C++ to push and pop elements in
stack.
#include <iostream>
using namespace std;
int stack[100], n=100, top=-1;
void push(int val) {
if(top>=n-1)
cout<<"Stack Overflow"<<endl;
else {
top++;
stack[top]=val;
}
}
void pop() {
if(top<=-1)
cout<<"Stack Underflow"<<endl;
else {
cout<<"The popped element is "<< stack[top] <<endl;
top--;
}
}
void display() {
if(top>=0) {
cout<<"Stack elements are:";
for(int i=top; i>=0; i--)
cout<<stack[i]<<" ";
cout<<endl;
} else
cout<<"Stack is empty";
}
int main() {
int ch, val;
cout<<"1) Push in stack"<<endl;
cout<<"2) Pop from stack"<<endl;
cout<<"3) Display stack"<<endl;
cout<<"4) Exit"<<endl;
do {
cout<<"Enter choice: "<<endl;
cin>>ch;
switch(ch) {
case 1: {
cout<<"Enter value to be pushed:"<<endl;
cin>>val;
push(val);
break;
}
case 2: {
pop();
break;
}
case 3: {
display();
break;
}
case 4: {
cout<<"Exit"<<endl;
break;
}
default: {
cout<<"Invalid Choice"<<endl;
}
}
}while(ch!=4);
return 0;
}

OUTPUT:
PROGRAM-3
Aim - Write a program to convert INFIX notation to POSTFIX
and PREFIX notation.
#include <iostream>
#include <stack>
#include <algorithm>
using namespace std;
// Function to check if the given character is an operator
bool isOperator(char c) {
return (c == '+' || c == '-' || c == '*' || c == '/' || c == '^');
}
// Function to check the precedence of operators
int precedence(char c) {
if (c == '^')
return 3;
else if (c == '*' || c == '/')
return 2;
else if (c == '+' || c == '-')
return 1;
else
return -1;
}
// Function to convert infix to postfix
string infixToPostfix(string infix) {
stack<char> s;
string postfix = "";
for (int i = 0; i < infix.length(); i++) {
char c = infix[i];
// If character is an operand, add it to the result
if (isalnum(c)) {
postfix += c;
}
// If character is '(', push it to the stack
else if (c == '(') {
s.push(c);
}
// If character is ')', pop and output from the stack
// until an '(' is encountered
else if (c == ')') {
while (!s.empty() && s.top() != '(') {
postfix += s.top();
s.pop();
}
s.pop(); // Remove '(' from stack
}
// If an operator is encountered
else if (isOperator(c)) {
while (!s.empty() && precedence(s.top()) >= precedence(c)) {
postfix += s.top();
s.pop();
}
s.push(c);
}
}
// Pop all the operators from the stack
while (!s.empty()) {
postfix += s.top();
s.pop();
}
return postfix;
}
// Function to convert infix to prefix
string infixToPrefix(string infix) {
// Reverse the infix expression
reverse(infix.begin(), infix.end());
// Replace '(' with ')' and vice versa
for (int i = 0; i < infix.length(); i++) {\
if (infix[i] == '(') {
infix[i] = ')';
}
else if (infix[i] == ')') {
infix[i] = '(';
}
}
// Get the postfix of the modified expression
string postfix = infixToPostfix(infix);
// Reverse the postfix to get prefix
reverse(postfix.begin(), postfix.end());
return postfix;
}
int main() {
string infix;
cout << "Enter an infix expression: ";
cin >> infix;
string postfix = infixToPostfix(infix);
string prefix = infixToPrefix(infix);
cout << "Postfix expression: " << postfix << endl;
cout << "Prefix expression: " << prefix << endl;
return;
}

OUTPUT:
PROGRAM-4
Aim - Write a program in C++ to find the first and follow of the
non-terminal string in Grammar.
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <string>

using namespace std;

class Grammar {
public:
map<char, vector<string>> productions;
map<char, set<char>> first;
map<char, set<char>> follow;

void addProduction(char nonTerminal, const string& production) {


productions[nonTerminal].push_back(production);
}

void computeFirst();
void computeFollow();
void displayFirst();
void displayFollow();
void getInput();
};
void Grammar::computeFirst() {
for (const auto& pair : productions) {
char nonTerminal = pair.first;
for (const string& production : pair.second) {
if (isupper(production[0])) { // Non-terminal
first[nonTerminal].insert(first[production[0]].begin(), first[production[0]].end());
} else { // Terminal
first[nonTerminal].insert(production[0]);
}
}
}
}

void Grammar::computeFollow() {
char startSymbol = productions.begin()->first;
follow[startSymbol].insert('$'); // End marker for start symbol

for (const auto& pair : productions) {


char nonTerminal = pair.first;
for (const string& production : pair.second) {
for (size_t i = 0; i < production.size(); ++i) {
if (isupper(production[i])) { // If it's a non-terminal
if (i + 1 < production.size()) {
if (isupper(production[i + 1])) {
follow[production[i]].insert(first[production[i + 1]].begin(), first[production[i
+ 1]].end());
} else {
follow[production[i]].insert(production[i + 1]);
}
} else {
follow[production[i]].insert(follow[nonTerminal].begin(),
follow[nonTerminal].end());
}
}
}
}
}
}

void Grammar::displayFirst() {
cout << "First Sets:\n";
for (const auto& pair : first) {
cout << "First(" << pair.first << ") = { ";
for (char c : pair.second) {
cout << c << " ";
}
cout << "}\n";
}
}

void Grammar::displayFollow() {
cout << "Follow Sets:\n";
for (const auto& pair : follow) {
cout << "Follow(" << pair.first << ") = { ";
for (char c : pair.second) {
cout << c << " ";
}
cout << "}\n";
}
}

void Grammar::getInput() {
int numProductions;
cout << "Enter number of productions: ";
cin >> numProductions;
cin.ignore(); // To ignore the newline character after number input

for (int i = 0; i < numProductions; ++i) {


char nonTerminal;
string production;
cout << "Enter production (e.g., A -> aB): ";
cin >> nonTerminal >> production;
addProduction(nonTerminal, production);
}
}

int main() {
Grammar grammar;

// Get grammar input from the user


grammar.getInput();

// Compute First and Follow sets


grammar.computeFirst();
grammar.computeFollow();

// Display First and Follow sets


grammar.displayFirst();
grammar.displayFollow();

return 0;
}

OUTPUT:
PROGRAM-5
Aim- Write a program to check whether a string is an identifier
or not.
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
bool isValidIdentifier(const string& str) {
if (str.empty() || isdigit(str[0])) {
return false;
}
for (char c : str) {
if (!isalnum(c) && c != '_') {
return false;
}
}
return true;
}
int main() {
string input;
cout << "Enter a string to check if it's a valid identifier: ";
cin >> input;
if (isValidIdentifier(input)) {
cout << "'" << input << "' is a valid identifier." << endl;
} else {
cout << "'" << input << "' is not a valid identifier." << endl;
}
return 0;
}

OUTPUT:
PROGRAM-6
Aim- Write a program to check whether a string belongs to
grammar or not.
#include <iostream>
#include <map>
#include <vector>
using namespace std;
map<char, vector<string>> grammar;
bool isValid(string input, char start)
{
if (input.empty()) return false;
vector<string> productions = grammar[start];
for (string prod : productions)
{
if (input == prod)
{
return true;
}
}
return false;
}
int main()
{
int n;
cout << "Enter number of productions: ";
cin >> n;
cout << "Enter productions (e.g., A->a):\n";
for (int i = 0; i < n; i++)
{
char nonTerminal;
string arrow, production;
cin >> nonTerminal >> arrow >> production;
grammar[nonTerminal].push_back(production);
}
char start;
cout << "Enter start symbol: ";
cin >> start;
string input;
cout << "Enter string to check: ";
cin >> input;
if (isValid(input, start))
{
cout << "The string belongs to the grammar.\n";
}
else
{
cout << "The string does NOT belong to the grammar.\n";
}
return 0;
}

OUTPUT:
PROGRAM-7
Aim- Write a program to remove left recursion from a
grammar.
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
map<char, vector<string>> grammar;
void removeLeftRecursion()
{
for (auto& entry : grammar)
{
char A = entry.first;
vector<string> alpha, beta;
for (const string& production : entry.second)
{
if (production[0] == A)
{
alpha.push_back(production.substr(1));
}
else
{
beta.push_back(production);
}
}
if (!alpha.empty())
{
char newNonTerminal = A + 1;
while (grammar.find(newNonTerminal) != grammar.end())
{
newNonTerminal++;
}
grammar[A].clear();
for (const string& b : beta)
{
grammar[A].push_back(b + newNonTerminal);
}
grammar[newNonTerminal] = vector<string>();
for (const string& a : alpha)
{
grammar[newNonTerminal].push_back(a + newNonTerminal);
}
grammar[newNonTerminal].push_back("ε");
}
}
}
int main()
{
int numProductions;
cout << "Enter the number of productions: ";
cin >> numProductions;
cin.ignore();
cout << "Enter the productions in the format 'S->aA|b':" << endl;
for (int i = 0; i < numProductions; i++)
{
string production;
getline(cin, production);
char nonTerminal = production[0];
string rhs = production.substr(3);
size_t pos = 0;
while ((pos = rhs.find('|')) != string::npos)
{
grammar[nonTerminal].push_back(rhs.substr(0, pos));
rhs.erase(0, pos + 1);
}
grammar[nonTerminal].push_back(rhs);
}
cout << "\nOriginal Grammar:" << endl;
for (const auto& entry : grammar)
{
cout << entry.first << " -> ";
for (size_t i = 0; i < entry.second.size(); i++)
{
cout << entry.second[i];
if (i < entry.second.size() - 1) cout << " | ";
}
cout << endl;
}
removeLeftRecursion();
cout << "\nGrammar after removing left recursion:" << endl;
for (const auto& entry : grammar)
{
cout << entry.first << " -> ";
for (size_t i = 0; i < entry.second.size(); i++)
{
cout << entry.second[i];
if (i < entry.second.size() - 1) cout << " | ";
}
cout << endl;
}
return 0;
}

OUTPUT:
PROGRAM-8
Aim- Write a program to remove left factoring from a grammar
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void removeLeftFactoring(string nonTerminal, vector<string>
productions)
{
string prefix = productions[0];
for (string prod : productions)
{
int j = 0;
while (j < prefix.length() && j < prod.length() && prefix[j]
== prod[j])
{
j++;
}
prefix = prefix.substr(0, j);
}
if (prefix.empty())
{
cout << "No left factoring found.\n";
return;
}
cout << nonTerminal << " -> " << prefix << nonTerminal << "'\n";
cout << nonTerminal << "' -> ";
for (string prod : productions)
{
if (prod != prefix)
{
cout << prod.substr(prefix.length()) << " | ";
}
}
cout << "ε\n";
}
int main()
{
int n;
cout << "Enter the number of productions: ";
cin >> n;
string nonTerminal;
cout << "Enter the non-terminal: ";
cin >> nonTerminal;
vector<string> productions(n);
cout << "Enter the productions:\n";
for (int i = 0; i < n; i++)
{
cin >> productions[i];
}
removeLeftFactoring(nonTerminal, productions);
return 0;
}
OUTPUT:
PROGRAM-9
Aim- Write a program to generate SDT for a given graph.
#include <iostream>
#include <vector>
using namespace std;

// Structure for an adjacency list node


struct AdjListNode {
int data;
AdjListNode* next;
};

// Structure for an adjacency list


struct AdjList {
AdjListNode* head; // Pointer to head node of list
};

// Structure for a graph. A graph is an array of adjacency lists


struct Graph {
int V; // Number of vertices
AdjList* arr; // Array of adjacency lists
};

// Function to create a new adjacency list node


AdjListNode* newAdjListNode(int data) {
AdjListNode* newNode = new AdjListNode;
newNode->data = data;
newNode->next = nullptr;
return newNode;
}

// Function to create a graph of V vertices


Graph* createGraph(int V) {
Graph* graph = new Graph;
graph->V = V;
graph->arr = new AdjList[V];

// Initialize each adjacency list as empty by setting head pointer to NULL


for (int i = 0; i < V; i++) {
graph->arr[i].head = nullptr;
}

return graph;
}

// Function to add an edge to an undirected graph


void addEdge(Graph* graph, int src, int dest) {
// Add an edge from src to dest. A new node is added to the adjacency list of src
AdjListNode* newNode = newAdjListNode(dest);
newNode->next = graph->arr[src].head;
graph->arr[src].head = newNode;

// Since the graph is undirected, add an edge from dest to src as well
newNode = newAdjListNode(src);
newNode->next = graph->arr[dest].head;
graph->arr[dest].head = newNode;
}
// Function to print the adjacency list representation of the graph
void printGraph(Graph* graph) {
for (int i = 0; i < graph->V; i++) {
AdjListNode* root = graph->arr[i].head;
cout << "Adjacency list of vertex " << i << ": ";
while (root != nullptr) {
cout << root->data << " -> ";
root = root->next;
}
cout << "nullptr" << endl; // Indicate end of the list
}
}

int main() {
// Create a new graph with a specified number of vertices
int totalVertices = 10; // Change this value as needed
Graph* graph = createGraph(totalVertices);

// Add edges to the graph (you can modify these values)


addEdge(graph, 15, 16);
addEdge(graph, 0, 4);
addEdge(graph, 1, 2);
addEdge(graph, 7, 8);
addEdge(graph, 1, 4);
addEdge(graph, 150, 200); // Ensure these values are within range if using fixed size
addEdge(graph, 3, 4);

// Print the adjacency list representation of the graph


printGraph(graph);

return 0;
}

OUTPUT:
PROGRAM-10
Aim- Write a program to generate a parse tree.
#include <iostream>
#include <sstream>
#include <vector>
#include <stack>
#include <string>

using namespace std;

// Node structure for the parse tree


struct TreeNode {
string value;
TreeNode* left;
TreeNode* right;

TreeNode(string val) : value(val), left(nullptr), right(nullptr) {}


};

// Function to check if a character is an operator


bool isOperator(const string& token) {
return (token == "+" || token == "-" || token == "*" || token == "/");
}

// Function to build the parse tree from an expression


TreeNode* buildParseTree(const vector<string>& tokens) {
stack<TreeNode*> nodeStack;
for (const string& token : tokens) {
if (token == "(") {
// Push a null node as a marker for a new expression
nodeStack.push(nullptr);
} else if (token == ")") {
// Pop nodes until we find the null marker
TreeNode* rightChild = nodeStack.top(); nodeStack.pop();
TreeNode* operatorNode = nodeStack.top(); nodeStack.pop();
TreeNode* leftChild = nodeStack.top(); nodeStack.pop();
nodeStack.pop(); // Remove the null marker

// Create a new subtree with the operator as the root


operatorNode->left = leftChild;
operatorNode->right = rightChild;
nodeStack.push(operatorNode);
} else {
// Create a new tree node for operands or operators
TreeNode* newNode = new TreeNode(token);
if (!isOperator(token)) {
// If it's an operand, it should be a leaf node
nodeStack.push(newNode);
} else {
// If it's an operator, we need to push it as well
nodeStack.push(newNode);
}
}
}

// The final tree will be the only element in the stack


return nodeStack.top();
}

// Function to print the parse tree in postorder traversal


void printPostOrder(TreeNode* root) {
if (root == nullptr) return;
printPostOrder(root->left);
printPostOrder(root->right);
cout << root->value << " ";
}

int main() {
string expression = "( ( 10 + 5 ) * 3 )";

// Tokenizing the input expression


stringstream ss(expression);
vector<string> tokens;
string token;

while (ss >> token) {


tokens.push_back(token);
}

// Build the parse tree from tokens


TreeNode* parseTree = buildParseTree(tokens);

// Print the parse tree in postorder


cout << "Postorder traversal of the parse tree: ";
printPostOrder(parseTree);
cout << endl;

return 0;
}

OUTPUT:
PROGRAM-11
Aim- Write a program to show all the operations of a stack.
#include <iostream>
using namespace std;

#define MAX 100 // Maximum size of the stack

class Stack {
private:
int arr[MAX]; // Array to store stack elements
int top; // Index of the top element

public:
Stack() { top = -1; } // Constructor to initialize the stack

// Function to add an item to the stack


void push(int x) {
if (top >= (MAX - 1)) {
cout << "Stack Overflow! Cannot push " << x << endl;
return;
}
arr[++top] = x;
cout << x << " pushed onto stack." << endl;
}

// Function to remove an item from the stack


int pop() {
if (isEmpty()) {
cout << "Stack Underflow! Cannot pop." << endl;
return -1; // Return -1 to indicate failure
}
return arr[top--];
}

// Function to return the top item of the stack


int peek() {
if (!isEmpty()) {
return arr[top];
}
cout << "Stack is empty!" << endl;
return -1; // Return -1 if the stack is empty
}

// Function to check if the stack is empty


bool isEmpty() {
return top == -1;
}

// Function to display all elements in the stack


void display() {
if (isEmpty()) {
cout << "Stack is empty!" << endl;
return;
}
cout << "Stack elements: ";
for (int i = top; i >= 0; i--) {
cout << arr[i] << " ";
}
cout << endl;
}
};

int main() {
Stack s; // Create a Stack object

s.push(10);
s.push(20);
s.push(30);

s.display(); // Display current stack

cout << "Top element is: " << s.peek() << endl;

cout << s.pop() << " popped from stack." << endl;

s.display(); // Display current stack after pop

cout << "Top element is: " << s.peek() << endl;

s.push(40);

s.display(); // Display current stack after pushing 40

while (!s.isEmpty()) {
cout << s.pop() << " popped from stack." << endl;
}
s.display(); // Display current stack after popping all elements

return 0;
}

OUTPUT:

PROGRAM-12
Aim- Write a program to draw the dependency graph.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>

using namespace std;

class DependencyGraph {
private:
vector<string> nodes; // List of nodes
vector<pair<string, string>> edges; // List of edges (dependencies)

public:
// Function to add a node to the graph
void addNode(const string& node) {
nodes.push_back(node);
}

// Function to add a directed edge (dependency) from one node to another


void addEdge(const string& from, const string& to) {
edges.emplace_back(from, to);
}

// Function to generate the DOT representation of the graph


void generateDotFile(const string& filename) {
ofstream dotFile(filename);
if (!dotFile.is_open()) {
cerr << "Error creating DOT file!" << endl;
return;
}

dotFile << "digraph DependencyGraph {\n";


for (const auto& node : nodes) {
dotFile << " " << node << ";\n"; // Add nodes
}
for (const auto& edge : edges) {
dotFile << " " << edge.first << " -> " << edge.second << ";\n"; // Add edges
}
dotFile << "}\n";
dotFile.close();
cout << "DOT file generated: " << filename << endl;
}
};

int main() {
DependencyGraph graph;

// Adding nodes
graph.addNode("A");
graph.addNode("B");
graph.addNode("C");
graph.addNode("D");

// Adding dependencies (edges)


graph.addEdge("A", "B");
graph.addEdge("A", "C");
graph.addEdge("B", "D");
graph.addEdge("C", "D");

// Generate the DOT file


string filename = "dependency_graph.dot";
graph.generateDotFile(filename);

cout << "You can visualize the graph using Graphviz with the following command:" <<
endl;
cout << "dot -Tpng dependency_graph.dot -o dependency_graph.png" << endl;

return 0;
}

OUTPUT:

PROGRAM-13
Aim- Write a program to draw the DAG.
#include <iostream>
#include <fstream>
#include <vector>
#include <string>

using namespace std;

class DAG {
private:
vector<string> nodes; // List of nodes
vector<pair<string, string>> edges; // List of edges (dependencies)

public:
// Function to add a node to the graph
void addNode(const string& node) {
nodes.push_back(node);
}

// Function to add a directed edge (dependency) from one node to another


void addEdge(const string& from, const string& to) {
edges.emplace_back(from, to);
}

// Function to generate the DOT representation of the graph


void generateDotFile(const string& filename) {
ofstream dotFile(filename);
if (!dotFile.is_open()) {
cerr << "Error creating DOT file!" << endl;
return;
}

dotFile << "digraph DAG {\n";


for (const auto& node : nodes) {
dotFile << " " << node << ";\n"; // Add nodes
}
for (const auto& edge : edges) {
dotFile << " " << edge.first << " -> " << edge.second << ";\n"; // Add edges
}
dotFile << "}\n";
dotFile.close();
cout << "DOT file generated: " << filename << endl;
}
};

int main() {
DAG dag;

// Adding nodes
dag.addNode("A");
dag.addNode("B");
dag.addNode("C");
dag.addNode("D");

// Adding dependencies (edges)


dag.addEdge("A", "B"); // A -> B
dag.addEdge("A", "C"); // A -> C
dag.addEdge("B", "D"); // B -> D
dag.addEdge("C", "D"); // C -> D
// Generate the DOT file
string filename = "dag.dot";
dag.generateDotFile(filename);

cout << "You can visualize the graph using Graphviz with the following command:" <<
endl;
cout << "dot -Tpng dag.dot -o dag.png" << endl;

return 0;
}

OUTPUT:

PROGRAM-14
Aim- Write a program to show the symbol table enteries.
#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct Symbol {
string name; // Identifier name
string type; // Type of the identifier (e.g., int, float)
string scope; // Scope of the identifier (e.g., local, global)
int lineNo; // Line number where the identifier is declared

Symbol(string n, string t, string s, int ln)


: name(n), type(t), scope(s), lineNo(ln) {}
};

class SymbolTable {
private:
vector<Symbol> symbols; // Vector to store symbols

public:
// Function to insert a new symbol into the symbol table
void insert(const string& name, const string& type, const string& scope, int lineNo) {
symbols.push_back(Symbol(name, type, scope, lineNo));
cout << "Inserted: " << name << " (Type: " << type << ", Scope: " << scope << ", Line: "
<< lineNo << ")" << endl;
}

// Function to find a symbol in the symbol table


bool find(const string& name) {
for (const auto& symbol : symbols) {
if (symbol.name == name) {
cout << "Found: " << symbol.name
<< " (Type: " << symbol.type
<< ", Scope: " << symbol.scope
<< ", Line: " << symbol.lineNo << ")" << endl;
return true;
}
}
cout << "Identifier " << name << " not found." << endl;
return false;
}

// Function to print all entries in the symbol table


void printTable() {
cout << "\nSymbol Table Entries:\n";
for (const auto& symbol : symbols) {
cout << "Identifier: " << symbol.name
<< ", Type: " << symbol.type
<< ", Scope: " << symbol.scope
<< ", Line Number: " << symbol.lineNo << endl;
}
}
};

int main() {
SymbolTable st;

// Inserting some symbols into the symbol table


st.insert("x", "int", "global", 1);
st.insert("y", "float", "local", 2);
st.insert("func", "function", "global", 3);

// Finding symbols in the table


st.find("x"); // Should find 'x'
st.find("y"); // Should find 'y'
st.find("unknown"); // Should not find 'unknown'

// Print all entries in the symbol table


st.printTable();

return 0;
}

OUTPUT:

You might also like