#ifndef __SYMBOL_HPP__
#define __SYMBOL_HPP__

#include <map>
#include <vector>

void yyerror(const char *msg);

enum Type { TYPE_int, TYPE_bool };

struct SymbolEntry {
  Type type;
  int nesting, offset;
  SymbolEntry() {}
  SymbolEntry(Type t, int nst, int ofs) : type(t), nesting(nst), offset(ofs) {}
};

class Scope {
public:
  Scope(int nst) : locals(), nesting(nst), offset(1), size(0) {}
  SymbolEntry *lookup(char c) {
    if (locals.find(c) == locals.end())
      return nullptr;
    return &locals[c];
  }
  void insert(char c, Type t) {
    if (locals.find(c) != locals.end())
      yyerror("Duplicate variable");
    locals[c] = SymbolEntry(t, nesting, offset++);
    ++size;
  }
  int getSize() const { return size; }

private:
  std::map<char, SymbolEntry> locals;
  int nesting, offset, size;
};

class SymbolTable {
public:
  void openScope() {
    int nesting = scopes.size();
    scopes.push_back(Scope(nesting));
  }
  void closeScope() { scopes.pop_back(); }
  SymbolEntry *lookup(char c) {
    for (auto i = scopes.rbegin(); i != scopes.rend(); ++i) {
      SymbolEntry *e = i->lookup(c);
      if (e != nullptr)
        return e;
    }
    yyerror("Variable not found");
    return nullptr;
  }
  void insert(char c, Type t) { scopes.back().insert(c, t); }
  int getSizeOfCurrentScope() const { return scopes.back().getSize(); }
  int getCurrentNesting() const { return scopes.size() - 1; }

private:
  std::vector<Scope> scopes;
};

extern SymbolTable st;

#endif
