Сгенерировать таблицу символов из файла на С++

Итак, я пытаюсь создать таблицу символов из входного файла, который содержит вложенные блоки в стиле 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
}
}

person Nonlin    schedule 29.11.2014    source источник
comment
Хотя вы можете решить ее, используя только стандартные контейнеры, это будет очень сложно и сложно. Вместо этого нужен некоторый тип контейнера для сбора нескольких атрибутов в единую структуру.   -  person Some programmer dude    schedule 29.11.2014
comment
Разве это не то, что я делаю?   -  person Nonlin    schedule 29.11.2014
comment
Вам нужно sg как map‹blockname_t, multimap‹variablename_t, пара‹typename_t, blockname_t›››, потому что вы хотите отобразить для каждой заглавной буквы (имени блока) отображение переменной. Но реализовать его таким образом будет немного сложнее, чем мой код. Я делаю то же самое, что и вы, в моем ответе. Но вместо создания типов монстров я структурировал данные.   -  person ch0kee    schedule 30.11.2014


Ответы (2)


Я бы предложил структуру (то есть 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;

Это очень легко даст вам вложенную структуру, которую вы, кажется, хотите, а также сохраните все связанные данные вместе в более мелкие структуры.

Ваша другая большая проблема, похоже, связана с синтаксическим анализом, и я предлагаю вам больше узнать о компиляторах и синтаксическом анализе и попытаться реализовать более традиционный синтаксический анализатор типа лексер-парсер, в котором вы разделяете обработку ввода и синтаксический анализ на два компонента. Если вы хотите вручную закодировать часть синтаксического анализатора, я предлагаю парсер в стиле рекурсивного спуска, который упрощают управление масштабированием и уровнями.

person Some programmer dude    schedule 30.11.2014

Вот!

Если вы принимаете совет, начните читать основную функцию, а не типы сверху.

Мне не нужна была мультимап. Вместо того, чтобы копировать переменные родительского блока, я ссылаюсь только на родительский контейнер с его индексом. Непосредственно перед печатью происходит обход самого верхнего блока, и он собирает все переменные, видимые в текущем блоке.

#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 ваши переменные будут перетасованы. Так или иначе, вы должны следить за порядком.

Я использую векторы для этого.

(Если вы не можете скомпилировать С++ 11, просто замените цикл for на основе диапазона в самом конце main)

person ch0kee    schedule 29.11.2014
comment
Значит, то, как я это делаю (с несколькими картами), просто не поможет? - person Nonlin; 30.11.2014
comment
Если вам действительно нужны мультикарты и важен порядок символов, вам нужно, например. сохранить индекс в значении multimap. Порядок важен? Кстати, аналогичная конструкция map‹key, vector‹value›› делает почти то же самое (вдумайтесь), но может быть проще в использовании. Если у меня будет время, может быть, через несколько часов, я поищу ошибку в вашем коде, но иногда, когда получается бардак, лучше начать сначала ;) - person ch0kee; 30.11.2014