//Solution by Soumyadeep Ghosh
//LinkedIn : https://www.linkedin.com/in/soumyadeep-ghosh-90a1951b6/
//IG : soumyadepp
// Github : https://github.com/soumyadepp
#include <bits/stdc++.h>
#define NULLTERMINAL "#"
using namespace std;
class Grammar{
unordered_map<char,vector<string>>rules;
set<char>terminals;
set<char>nonTerminals;
public:
Grammar(vector<pair<string,string>>userRules){
//extract terminals and non terminals from the grammar
for(auto i : userRules){
rules[i.first[0]].push_back(i.second);
nonTerminals.insert(i.first[0]);
for(auto j : i.second){
if(isupper(j)){
nonTerminals.insert(j);
}
else{
terminals.insert(j);
}
}
}
//validate the grammar
for(auto i : nonTerminals){
char key = i;
if(rules[key].size() == 0){
cout<<"Incomplete grammar
";
exit(0);
}
}
}
void showGrammar(){
for(auto i : rules){
cout<<i.first<<" : ";
for(auto j : i.second){
cout<<j<<" | ";
}
cout<<"
";
}
}
void dfs(vector<string>mapped,vector<char>&ans,unordered_map<char,bool>&visited){
for(auto i : mapped){
char toBeChecked = i[0];
if(terminals.find(toBeChecked) != terminals.end()){
ans.push_back(toBeChecked);
}
else{
if(!visited[toBeChecked]){
visited[toBeChecked] = true;
dfs(rules[toBeChecked],ans,visited);
}
}
}
}
unordered_map<char,vector<char>> getFirst(){
unordered_map<char,vector<char>>res;
for(auto i : nonTerminals){
unordered_map<char,bool>visited;
vector<char>first;
visited[i] = true;
//find the first of corresponding non terminal
dfs(rules[i],first,visited);
res[i] = first;
}
return res;
}
void showTerminals(){
for(auto i : terminals) if(i != NULLTERMINAL[0]) cout<< i << " ";
cout<<"
";
}
void showNonTerminals(){
for(auto j : nonTerminals) cout << j << " ";
}
};
int main(){
vector<pair<string,string>>vpp = {{"S","AB"},{"S","ac"},{"A","bB"},{"B","c"},{"A",NULLTERMINAL}};
Grammar grammar(vpp);
grammar.showGrammar();
cout<<"Terminals : " << endl;
grammar.showTerminals();
cout<<"Non terminals : "<<endl;
grammar.showNonTerminals();
cout<<endl;
unordered_map<char,vector<char>>ans = grammar.getFirst();
cout << "First of the grammar is :
";
for(auto i : ans){
cout<<i.first<<" : ";
for(auto j : i.second){
cout<<j<<" ";
}
cout<<"
";
}
return 0;
}