Search code examples
c++compiler-constructionsemantic-analysis

Holding a map reference to a vector of maps


My task is to construct a compiler which lexes, parses and analyses the resultant Abstract Syntax Tree to ensure type matching, avoid duplicate declaration etc.

It was instructed for us to construct a Symbol Table, which holds a map of variables and their types for each scope. I opted for a vector of maps. I preferred this over a stack since I can iterate over it when checking for variables at any scope.

I constructed Push, Pop, Lookup and Insert operations for this structure as shown below and my idea was to hold a reference to the latest map in the vector and add variables to it. As a new scope is entered a push operation is carried out creating a new map in the vector within which to store the arrays.

When a scope is exited a Pop operation is done to remove the map at the end of the vector and acquire the previous map which is now at the back of the vector.

Via debugging I've noticed the vector is simply holding no map details and working by reference seems to be doing nothing to update the vector that's supposed to be holding this map. How do I correctly reference a map inside a vector and maintain this structure?

Symbol Table:

struct SymbolTable {
        // Stack defining scopes holding identifier / type details
        std::vector<std::map<std::string,std::string>> _scopeVector;

        // Tracks current working stack
        std::map<std::string,std::string> _currentMap;

        SymbolTable() = default;

        void Push() {
            std::map<std::string,std::string> *_tempMap;
            _tempMap = new std::map<std::string,std::string>();
            _scopeVector.push_back(*_tempMap);
            _currentMap = _scopeVector.back();
        }

        void Insert(std::string p_name, std::string p_type) {
            _currentMap.insert(std::make_pair(p_name,p_type));
        }

        // Returns type if found, empty if not
        std::string Lookup (std::string p_name) {
            for (int i = 0; i < _scopeVector.size(); i++) {
                if (_scopeVector[i].find(p_name) == _scopeVector[i].end()) {
                    // No match yet
                } else {
                    return _scopeVector[i].find(p_name)->first; // return var name
                }
            }
            std::cerr << "Type name " << p_name << " not found in all of stack" << std::endl;
            return "";
        }

        void Pop () {
            _scopeVector.pop_back();
            _currentMap = _scopeVector.back();
        }
    };

    SymbolTable *ST;

Class constructor which sets up the Symbol Table:

SemanticAnalysisVisitor() {
    ST = new SymbolTable();
    ST->Push();
}

Debugger image of an empty vector but populated map


Solution

  • As for your problem (I think) the statement

    _currentMap = _scopeVector.back();
    

    copies the map from the vector.

    _currentMap will always be a separate and distinct map, totally unrelated from whatever is in the vector.

    It seems you want to use references, but that's not possible in this case (unless you do a redesign).

    You could solve this problem (and the memory leak I mentioned in a comment) by having a vector of (smart) pointers to the maps, and make _currentMap a (smart) pointer as well.