Поэтому я пытаюсь сгенерировать таблицу символов из входного файла, который содержит вложенные блоки в стиле C, как это в C ++;
A: { int a; float b;
B: { float c; int d;
C: { int b; int c;
}
}
D: { float a;
}
}
Вывод должен выглядеть следующим образом.
A: a -> <int, A>
b -> <float, A>
B: a -> <int, A>
b -> <float, A>
c -> <float, B>
d -> <int, B>
C: a -> <int, A>
b -> <int, C> -> <float, A>
c -> <int C> -> <float, B>
d -> <int, local to B>
D: a -> <float D> -> <int, A>
b -> <float, A>
Я пробовал так много вещей. Используя векторы, карты и теперь, наконец, я решил использовать мультикарты. Независимо от того, что я делаю, я сталкиваюсь с одной и той же проблемой, поэтому она, вероятно, не имеет ничего общего с выбранной структурой данных.
Проблема в том, что, поскольку я читаю построчно, я в конечном итоге занимаюсь больше, чем нужно. Но если бы у меня не было этого cout / итерация мультикарты в цикле for для каждой строки, я бы сделал итерацию после того, как они были стерты / вытолкнуты. Я не уверен, что делать с логикой, чтобы вывод отображался так, как должен, или если я даже на правильном пути.
Вот мой файл .cpp до сих пор. Не обращайте внимания на комментарии, так как они были прошлыми попытками, которые я пока не использовал. Также в этой версии я не использую векторы, чтобы вы могли игнорировать вектор-связанный код. Я сейчас просто использую мультикарты.
#include<iostream>
#include<fstream>
#include<string>
#include <sstream>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
void ReadFromFile();
void main(){
ReadFromFile();
cin.get();
}
void ReadFromFile(){
stringstream ss;
string type = "";
string var = "";
string lable = "";
string Obraket = "";
string Cbraket = "";
int braketCount = -1;
ifstream myfile("input1.txt");
multimap<string, string> symbol;
multimap<string, multimap<string, string>> symbolL;
if (myfile.is_open())
{
for (string line; getline(myfile, line);)
{
istringstream in(line);
if (in.str().find("}") == string::npos && in.str().find("{") != string::npos){
in >> lable;
in >> Obraket;
braketCount++;
cout << Obraket << endl;
in >> type;
in >> var;
symbol.insert(pair<string, string>(var.substr(0, 1), type));
if (in.str().find("float") != string::npos || in.str().find("int") != string::npos){
var = "";
type = "";
in >> type;
in >> var;
if (type.length() > 1){
symbol.insert(pair<string, string>(var.substr(0, 1), type));
}
}
symbolL.insert( pair<string, multimap<string, string>>(lable,symbol));
for (multimap<string, multimap<string, string>>::iterator it = symbolL.begin(); it != symbolL.end(); ++it){
cout << it->first;
for (multimap<string, string>::iterator it2 = symbol.begin(); it2 != symbol.end(); ++it2){
cout << it2->first << "-> " << "<" << it2->second << ">, " << it->first.substr(0, 1) << endl;
}
}
}
else if (in.str().find("}") != string::npos){
in >> Cbraket;
//braketCount--;
cout << Cbraket << endl;
symbolL.erase(prev(symbolL.end()));
//symbol.erase(prev(symbol.end()));
}
}
myfile.close();
}
else cout << "Unable to open file";}
Это вывод, который я получаю.
{
A:a-> <int>, A
b-> <float>, A
{
A:a-> <int>, A
b-> <float>, A
c-> <float>, A
d-> <int>, A
B:a-> <int>, B
b-> <float>, B
c-> <float>, B
d-> <int>, B
{
A:a-> <int>, A
b-> <float>, A
b-> <int>, A
c-> <float>, A
c-> <int>, A
d-> <int>, A
B:a-> <int>, B
b-> <float>, B
b-> <int>, B
c-> <float>, B
c-> <int>, B
d-> <int>, B
C:a-> <int>, C
b-> <float>, C
b-> <int>, C
c-> <float>, C
c-> <int>, C
d-> <int>, C
}
}
{
A:a-> <int>, A
a-> <float>, A
b-> <float>, A
b-> <int>, A
c-> <float>, A
c-> <int>, A
d-> <int>, A
D:a-> <int>, D
a-> <float>, D
b-> <float>, D
b-> <int>, D
c-> <float>, D
c-> <int>, D
d-> <int>, D
}
}
Я бы предложил состав (т.е. struct
или class
) для верхнего уровня, есть std::map
из этих структур верхнего уровня. Тогда каждая структура в свою очередь содержит std::map
для содержащихся символов, опять же со структурой, которая содержит, помимо прочего, тип символа.
Что-то простое, как это:
struct LocalSymbol
{
std::string name;
enum
{
FLOAT,
INT
} type;
// Possibly other information needed for local symbols
};
struct GlobalSymbol
{
std::string name;
// Possibly other information needed for global symbols
std::map<std::string, LocalSymbol> locals;
}
std::map<std::string, GlobalSymbol> globals;
Это очень легко даст вам вложенную структуру, которую вы, кажется, хотите, а также сведете все связанные данные в единое целое.
Другая серьезная проблема, по-видимому, связана с синтаксическим анализом, и я предлагаю вам прочитать больше о компиляторах и синтаксическом анализе и попытаться реализовать более традиционный тип синтаксического анализатора с использованием лексера-парсера, где вы разделяете обработку ввода и анализ на два компонента. Если вы хотите вручную написать код парсера, я предлагаю парсер стиля рекурсивного спуска что позволит очень легко справиться с областью видимости и уровнями.
Вот!
Если вы принимаете совет, начните читать основную функцию вместо типов сверху.
Мне не нужна мультикарта. Вместо того, чтобы копировать переменные родительского блока, я ссылаюсь только на родительский контейнер с его индексом. Непосредственно перед печатью происходит обход самого верхнего блока, который собирает все переменные, видимые в текущем блоке.
#include <algorithm>
#include <cassert>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef string type_t; //type of variable
typedef string variable_t; //name of variable
typedef string class_t; //name of 'class' (container)
const int NO_PARENT = -1; //top-most
typedef vector<class ClassSymbolTable> symbols_t; //we use vector to preserve order
// main class, it stores symbols of a single class, and references its parent
class ClassSymbolTable {
class_t _name; //class name
map<variable_t, type_t> _types; //map of variable types
vector<variable_t> _variables; //we use this vector to preserve order
symbols_t& _symbols; //reference to the symbol table
int _parent_index = NO_PARENT; //reference to parent index in symbol vector//!! parent class, nullptr if top-level
ClassSymbolTable* parent() const { return _parent_index != NO_PARENT ? &_symbols[_parent_index] : nullptr; }
// does this class directly declares var ?
bool declares_variable(const variable_t& var) const {
return _types.find(var) != _types.end();
}
// print variable info in desired format
void print_variable(const variable_t& var) {
if (declares_variable(var)) {
cout << " -> <" << _types[var] << ", " << _name << ">";
}
if (parent()) {
parent()->print_variable(var);
}
}
// traverse classes up to top-level and collect variables in order
void collect_variables_to_print(vector<variable_t>& vars) {
if (ClassSymbolTable* p = parent()) {
p->collect_variables_to_print(vars);
// add variables defined on this level
vector<variable_t> add_vars;
for (size_t i = 0; i < _variables.size(); ++i) {
if (find(vars.begin(), vars.end(), _variables[i]) == vars.end()) {
// defined on this level
add_vars.push_back(_variables[i]);
}
}
vars.insert(vars.end(), add_vars.begin(), add_vars.end());
}
else {
//top-level
vars = _variables;
}
}
// get depth for indentation
int get_depth() const {
int depth = 0;
for (ClassSymbolTable* p = parent(); p; p = p->parent()) {
++depth;
}
return depth;
}static size_t s_max_class_name_length; //for printing
public:
// ctor
ClassSymbolTable(const string& name, int parent_index, symbols_t& symbols)
: _name(name), _parent_index(parent_index), _symbols(symbols)
{
s_max_class_name_length = max(s_max_class_name_length, name.length());
}
// add variable
void add(const variable_t& var, const type_t& type) {
_variables.push_back(var);
_types[var] = type;
}
// print this class' vars in desired format
void print() {
cout.fill(' ');
const int indent = get_depth() + s_max_class_name_length + 3 /*for ':' */;
vector<variable_t> vars;
collect_variables_to_print(vars);
// print class name
string classname = _name + ": ";
cout.fill(' ');
cout.width(indent);
cout << classname;
// print vars
cout.width(0);
cout << vars[0];
print_variable(vars[0]);
cout << endl;
for (size_t i = 1; i < vars.size(); ++i) {
cout.width(indent);
cout << ' '; //pad before
cout.width(0);
cout << vars[i];
print_variable(vars[i]);
cout << endl;
}
cout.width(0);
}};
size_t ClassSymbolTable::s_max_class_name_length = 0;int main(int argc, char* argv[])
{
ifstream in("input1.txt");
assert(in.is_open());
symbols_t symbols;
//collect symbols
const char* delimiters = ":;{}";
vector<string> current_tokens;
string buffer;
stack<int> class_stack; //to manage class hierarchy, we stack the classes' index in the symbol vector
class_stack.push(NO_PARENT); //so we dont have to branch at first level
while (in >> buffer) {
size_t found = buffer.find_first_of(delimiters);
current_tokens.push_back(buffer.substr(0, found)); //either whole or until delimiter
if (found != string::npos) { //delimiter found
char delimiter = buffer[found];
switch (delimiter) {
case ':': //class name
assert(current_tokens.size() == 1);
{
// add new class symbol table and refer to parent class
symbols.emplace_back(current_tokens[0], class_stack.top(), symbols);
// we rather store index in '{' for symmetric code
}
break;
case '{': //block open
assert(!symbols.empty());
{
class_stack.push(symbols.size()-1); //stack the index for nested classes
}
break;
case '}': //block close
assert(!class_stack.empty());
{
class_stack.pop(); //done with this class
}
break;
case ';': //variable
assert(!symbols.empty());
assert(current_tokens.size() == 2);
{
// add variable to the current class symbol table
ClassSymbolTable& current_class = symbols.back();
current_class.add(current_tokens[1], current_tokens[0]);
}
break;
}
//put back the remaining characters
current_tokens.clear();
if (found < buffer.size() - 1) {
current_tokens.push_back(buffer.substr(found + 1));
}
}
}
assert(class_stack.size() == 1 && class_stack.top() == NO_PARENT); //just to be sure
for (ClassSymbolTable& c : symbols) {
c.print();
}
cout << "." << endl;
return 0;
}
Это может быть оптимизировано, чтобы избежать поиска во время печати, и вы можете
также избегайте хранения символов, если вы хотите только распечатать их.
Вы можете хранить локальные переменные здесь и там, но основная идея будет одинаковой.
И да, я использую еще один контейнер для управления вложением, стек 🙂
Только при использовании multimap ваши переменные будут перемешаны.
Так или иначе, вы должны следить за порядком.
Я использую векторы, чтобы сделать это.
(Если вы не можете скомпилировать C ++ 11, просто замените основанный на диапазоне цикл for в самом конце main)