Joos1W Compiler Framework
All Classes Functions Typedefs Pages
SourceManager.h
1 #pragma once
2 
3 #include <iostream>
4 #include <list>
5 #include <ranges>
6 #include <string>
7 #include <string_view>
8 #include <fstream>
9 #include <utils/Error.h>
10 
11 class SourceManager;
12 class SourceLocation;
13 class SourceRange;
14 
15 /* ===--------------------------------------------------------------------=== */
16 // SourceFile
17 /* ===--------------------------------------------------------------------=== */
18 
19 /// @brief An opaque identifier representing a source file.
20 class SourceFile {
21 public:
22  /// @brief Construct a new SourceFile with no associated file.
23  SourceFile() : id_{nullptr} {}
24 
25  bool operator==(SourceFile const& other) const { return id_ == other.id_; }
26 
27 private:
28  explicit SourceFile(void const* ptr) : id_{ptr} {}
29 
30  friend class SourceManager;
31  friend class SourceLocation;
32  friend class SourceRange;
33 
34 private:
35  /// @brief The unique identifier for this source file.
36  /// This is used to index into the SourceManager's file list.
37  void const* id_;
38 };
39 
40 /* ===--------------------------------------------------------------------=== */
41 // SourceManager
42 /* ===--------------------------------------------------------------------=== */
43 
45 public:
46  SourceManager() = default;
47  SourceManager(SourceManager const&) = delete;
48  SourceManager& operator=(SourceManager const&) = delete;
49 
50  /// @brief Add a file and its contents to the SourceManager.
51  /// @param path The path to the file
52  void addFile(std::string_view path) {
53  // Check the path ends in ".java"
54  if(path.size() < 5 || path.substr(path.size() - 5) != ".java") {
55  throw utils::FatalError{"File " + std::string{path} + " is not a .java file"};
56  }
57  std::ifstream file{std::string{path}};
58  if(!file) {
59  throw utils::FatalError{"File " + std::string{path} + " does not exist"};
60  }
61  files_.emplace_back(path, std::istreambuf_iterator<char>{file}, this);
62  }
63 
64  /// @brief Push a new buffer onto the buffer stack.
65  void emplaceBuffer() {
66  auto name = "Buffer " + std::to_string(files_.size() + 1);
67  files_.emplace_back(name, this);
68  }
69 
70  /// @brief Grab a reference to the current buffer.
71  std::string& currentBuffer() { return files_.back().buffer; }
72 
73  /// @brief Get iterator for files
74  auto files() const {
75  return files_ | std::views::all |
76  std::views::transform([](File const& file) -> SourceFile {
77  return SourceFile{static_cast<File const*>(&file)};
78  });
79  }
80 
81  /// @brief Get the name of a file
82  static std::string getFileName(SourceFile file) {
83  auto* f = static_cast<File const*>(file.id_);
84  if(f == nullptr || !f->isFile)
85  return "";
86  return f->name;
87  }
88 
89  /// @brief Print the name of the file to the output stream.
90  /// @param os The output stream to print to
91  /// @param file The SourceFile to print
92  static void print(std::ostream& os, SourceFile const& file) {
93  if(file.id_ == nullptr) {
94  os << "??";
95  } else {
96  os << static_cast<File const*>(file.id_)->name;
97  }
98  }
99 
100  /// @brief Get the buffer for a file
101  /// @param file The file to get the buffer for
102  /// @return A string_view of the buffer
103  static std::string const& getBuffer(SourceFile file) {
104  return static_cast<File const*>(file.id_)->buffer;
105  }
106 
107 private:
108  struct File {
109  std::string name;
110  std::string buffer;
111  bool isFile = false;
112  SourceManager* parent;
113  File(std::string_view name, std::istreambuf_iterator<char> begin,
114  SourceManager* parent)
115  : name{name}, buffer{begin, {}}, isFile{true}, parent{parent} {}
116  File(std::string_view name, SourceManager* parent)
117  : name{name}, buffer{}, parent{parent} {}
118  };
119 
120  std::list<File> files_;
121 };