context_stack.h 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. #pragma once
  2. #include <deque>
  3. #include <map>
  4. #include <optional>
  5. #include <jvalidate/detail/on_block_exit.h>
  6. namespace jvalidate::detail {
  7. template <typename Source, typename Key, typename Value> class ContextStack {
  8. private:
  9. std::deque<Source> sources_;
  10. std::map<Key, std::deque<std::optional<Value>>> data_;
  11. public:
  12. OnBlockExit scope(Source const & source, std::map<Key, Value> const & frame) {
  13. if (frame.empty() && data_.empty()) {
  14. return nullptr;
  15. }
  16. sources_.push_back(source);
  17. for (auto const & [k, v] : frame) {
  18. data_[k].push_back(data_[k].empty() ? v : data_[k].front());
  19. }
  20. for (auto & [k, stack] : data_) {
  21. if (not frame.contains(k)) {
  22. stack.push_back(std::nullopt);
  23. }
  24. while (stack.size() < sources_.size()) {
  25. stack.push_front(std::nullopt);
  26. }
  27. }
  28. return [this]() {
  29. sources_.pop_back();
  30. for (auto it = data_.begin(); it != data_.end();) {
  31. if (it->second.size() == 1) {
  32. it = data_.erase(it);
  33. } else {
  34. it->second.pop_back();
  35. ++it;
  36. }
  37. }
  38. };
  39. }
  40. bool contains(Key const & key) const { return data_.contains(key); }
  41. std::optional<Value> lookup(Source const & source, Key const & key) const {
  42. if (auto it = data_.find(key); it != data_.end()) {
  43. return it->second.at(index_of(source));
  44. }
  45. return std::nullopt;
  46. }
  47. size_t index_of(Source const & source) const {
  48. for (size_t i = sources_.size(); i-- > 0;) {
  49. if (sources_[i] == source) {
  50. return i;
  51. }
  52. }
  53. return sources_.size() - 1;
  54. }
  55. bool empty() const { return data_.empty(); }
  56. };
  57. }