#ifndef __SYMBOL_HPP__
#define __SYMBOL_HPP__

#include <map>
#include <string>
#include <vector>

enum Type { TYPE_int, TYPE_bool };

inline void sem_error(const std::string& m1, char v, const std::string& m2) {
  std::cerr << "\033[91mError: " << m1 << v << m2 << "\033[0m" << std::endl;
  std::exit(2);
}

struct SymTabEntry {
  Type type;
  int offset;
  SymTabEntry() {}
  SymTabEntry(Type t, int o) : type(t), offset(o) {}
};

class Scope {
 public:
  Scope(int o) : offset(o) {}
  void insert(char v, Type t) {
    if (locals.find(v) != locals.end())
      sem_error("Duplicate variable ", v, " declaration");
    locals[v] = SymTabEntry(t, offset++);
  }
  SymTabEntry *lookup(char v) {
    if (locals.find(v) == locals.end()) return nullptr;
    return &(locals[v]);
  }
  int get_offset() {
    return offset;
  }
 private:
  std::map<char, SymTabEntry> locals;
  int offset;
};

class SymbolTable {
 public:
  void insert(char v, Type t) {
    scopes.back().insert(v, t);
  }
  SymTabEntry *lookup(char v) {
    for (auto s = scopes.rbegin(); s != scopes.rend(); ++s) {
      SymTabEntry *e = s->lookup(v);
      if (e != nullptr) return e;
    }
    sem_error("Variable ", v, " not found");
    return nullptr;
  }
  void enter_scope() {
    int o = scopes.empty() ? 0 : scopes.back().get_offset();
    scopes.push_back(Scope(o));
  }
  void exit_scope() {
    scopes.pop_back();
  }
 private:
  std::vector<Scope> scopes;
};

extern SymbolTable st;

#endif
