tan  0.0.1
source_file.cpp
1 #include <fstream>
2 #include "base.h"
3 #include "source_file/source_file.h"
4 
5 namespace tanlang {
6 
7 void SourceFile::open(const str &filename) {
8  std::ifstream ifs;
9  ifs.open(filename, std::ios::in);
10  if (!ifs) {
11  Error(ErrorType::FILE_NOT_FOUND, "Cannot open file: " + filename).raise();
12  }
13  _filename = filename;
14 
15  // read the whole file at once
16  str content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
17 
18  from_string(content);
19 }
20 
21 void SourceFile::from_string(const str &code) {
22  size_t line_start = 0;
23  size_t code_len = code.length();
24  for (size_t c = 0; c < code_len; ++c) {
25  if (code[c] == '\n' || c == code.length() - 1) {
26  str line;
27  if (code[c] == '\n') {
28  line = code.substr(line_start, c - line_start); // not including trailing '\n'
29  } else {
30  line = code.substr(line_start, c - line_start + 1);
31  }
32 
33  // delete whitespace at the beginning of the line
34  for (size_t i = 0; i < line.length(); ++i) {
35  if (!isspace(line[i])) { // avoid confusion with isspace in <locale>
36  line = line.substr(i);
37  break;
38  }
39  }
40 
41  _lines.push_back(line);
42  line_start = c + 1;
43  }
44  }
45 }
46 
47 bool SourceFile::is_cursor_valid(const SrcLoc &c) const {
48  if (c.l >= _lines.size()) {
49  return false;
50  }
51 
52  str line = _lines[c.l];
53  if (line.empty()) {
54  return c.c == 0;
55  } else {
56  return c.c < line.size();
57  }
58 }
59 
60 str SourceFile::substr(const SrcLoc &start, const SrcLoc &_end) const {
61  TAN_ASSERT(is_cursor_valid(start));
62 
63  /// if the end cursor is out of boundary, make it point to EOF
64  SrcLoc end(_end);
65  if (end.l >= _lines.size() || end.c > _lines[end.l].size()) {
66  end.l = (uint32_t)_lines.size() - 1;
67  end.c = (uint32_t)_lines.back().size();
68  }
69  auto s_row = start.l;
70  auto e_row = end.l;
71  str ret;
72  if (s_row == e_row) {
73  TAN_ASSERT(start.c <= end.c);
74  ret = _lines[s_row].substr(start.c, end.c - start.c);
75  } else {
76  ret += _lines[s_row].substr(start.c) + "\n";
77  for (auto r = s_row + 1; r < e_row; ++r) {
78  ret += _lines[r] + "\n";
79  }
80  if (end.c > 0) {
81  ret += _lines[e_row].substr(0, end.c);
82  }
83  }
84  return ret;
85 }
86 
87 str SourceFile::substr(const SrcLoc &start) const {
88  SrcLoc end(start.l, (uint32_t)get_line(start.l).size(), this);
89  return substr(start, end);
90 }
91 
93  if (ptr.l >= _lines.size()) {
94  return ptr;
95  }
96  size_t n_cols = _lines[ptr.l].length();
97  if (ptr.c + 1 >= n_cols) {
98  if (ptr.l < _lines.size()) {
99  ++ptr.l;
100  }
101  ptr.c = 0;
102  } else {
103  ++ptr.c;
104  }
105  return ptr;
106 }
107 
108 str SourceFile::get_filename() const { return _filename; }
109 
111  if (_lines.empty()) {
112  return SrcLoc(0, 1, this);
113  }
114  return SrcLoc((uint32_t)_lines.size() - 1, (uint32_t)_lines.back().length(), this);
115 }
116 
117 char SourceFile::at(const SrcLoc &ptr) const {
118  TAN_ASSERT(ptr.l != -1u && ptr.c != -1u);
119  if (ptr.l >= this->size()) {
120  return '\0';
121  }
122  if (ptr.c >= this->_lines[ptr.l].length()) {
123  return '\0';
124  }
125  return _lines[ptr.l][ptr.c];
126 }
127 
128 SrcLoc SourceFile::begin() const { return SrcLoc(0, 0, this); }
129 
130 str SourceFile::get_line(size_t index) const {
131  TAN_ASSERT(index < _lines.size());
132  return _lines[index];
133 }
134 
135 SrcLoc::SrcLoc(uint32_t r, uint32_t c, const SourceFile *src) : l(r), c(c), _src((SourceFile *)src) {}
136 
137 bool SrcLoc::operator==(const SrcLoc &other) const { return l == other.l && c == other.c; }
138 
139 bool SrcLoc::operator!=(const SrcLoc &other) const { return !(*this == other); }
140 
141 bool SrcLoc::operator<=(const SrcLoc &other) const {
142  if (l < other.l) {
143  return true;
144  } else if (l > other.l) {
145  return false;
146  } else {
147  return c <= other.c;
148  }
149 }
150 
151 bool SrcLoc::operator<(const SrcLoc &other) const {
152  if (l < other.l) {
153  return true;
154  } else if (l > other.l) {
155  return false;
156  } else {
157  return c < other.c;
158  }
159 }
160 
161 bool SrcLoc::operator>(const SrcLoc &other) const {
162  if (l > other.l) {
163  return true;
164  } else if (l < other.l) {
165  return false;
166  } else {
167  return c > other.c;
168  }
169 }
170 
171 SrcLoc &SrcLoc::operator++() {
172  *this = _src->forward(*this);
173  return *this;
174 }
175 
176 SrcLoc SrcLoc::operator++(int) {
177  auto ret = *this;
178  *this = _src->forward(*this);
179  return ret;
180 }
181 
182 char SrcLoc::operator*() {
183  TAN_ASSERT(_src);
184  return _src->at(*this);
185 }
186 
187 SourceSpan::SourceSpan(
188  const SourceFile *src, uint32_t start_line, uint32_t start_col, uint32_t end_line, uint32_t end_col)
189  : SourceSpan(SrcLoc(start_line, start_col, src), SrcLoc(end_line, end_col, src)) {}
190 
191 SourceSpan::SourceSpan(const SourceFile *src, uint32_t line, uint32_t col) : SourceSpan(src, line, col, line, col) {}
192 
193 SourceSpan::SourceSpan(const SrcLoc &start, const SrcLoc &end) : _start(start), _end(end) {
194  TAN_ASSERT(start._src == end._src);
195  TAN_ASSERT(start.l <= end.l);
196  if (start.l == end.l) {
197  TAN_ASSERT(start.c <= end.c);
198  }
199 }
200 
201 SourceFile *SourceSpan::src() const { return _start._src; }
202 
203 const SrcLoc &SourceSpan::start() const { return _start; }
204 
205 const SrcLoc &SourceSpan::end() const { return _end; }
206 
207 } // namespace tanlang
bool is_cursor_valid(const SrcLoc &c) const
Check if a cursor is in bound.
Definition: source_file.cpp:47
SrcLoc begin() const
The start of source file (inclusive).
str get_line(size_t index) const
Return source at a specific line.
str substr(const SrcLoc &start) const
Get a substring from start to the end of the current line.
Definition: source_file.cpp:87
SrcLoc end() const
The end of source file (exclusive).
SrcLoc forward(SrcLoc c)
Return a copy of code_ptr that points to the next character.
Definition: source_file.cpp:92