1use std::{cmp::Ordering, fmt::Display, rc::Rc};
3
4#[derive(Clone, Debug, Eq)]
6pub enum Source {
7 File {
14 name: String,
15 contents: String
16 },
17 Unknown
18}
19
20impl Source {
21 pub fn file(name: String, contents: String) -> Rc<Source> {
22 Rc::new(Source::File { name, contents })
23 }
24
25 pub fn contents(&self) -> &str {
27 match self {
28 Self::File { name: _, contents } => contents,
29 Self::Unknown => ""
30 }
31 }
32
33 fn lines(
35 &self, pos: usize, before: usize, after: usize
36 ) -> (Vec<String>, usize) {
37 match self {
38 Self::File { name: _, contents } => {
39 assert!(pos < contents.len());
40 let bytes = contents.as_bytes();
41
42 let mut start_pos = pos;
44 while start_pos > 0 && bytes[start_pos - 1] != b'\n' {
45 start_pos -= 1;
46 }
47 let mut end_pos = start_pos;
48 while end_pos < contents.len() && bytes[end_pos] != b'\n' {
49 end_pos += 1;
50 }
51 end_pos += 1;
52
53 let line = contents
55 .get(start_pos..end_pos - 1)
56 .unwrap_or_default()
57 .to_string();
58
59 let before_lines: Vec<_> = {
61 let (before_contents, _) = contents.split_at(start_pos);
62 let mut result: Vec<_> = before_contents
63 .lines()
64 .rev()
65 .take(before)
66 .map(String::from)
67 .collect();
68 result.reverse();
69 result
70 };
71 let after_lines: Vec<_> = if end_pos < contents.len() {
72 let (_, after_contents) = contents.split_at(end_pos);
73 after_contents
74 .lines()
75 .take(after)
76 .map(String::from)
77 .collect()
78 } else {
79 std::iter::empty().collect()
80 };
81
82 let mut result = vec![];
84 result.extend(before_lines);
85 let pos_current_line = result.len();
86 result.push(line);
87 result.extend(after_lines);
88
89 (result, pos_current_line)
90 }
91 Self::Unknown => (vec![], 0)
92 }
93 }
94}
95
96impl Display for Source {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 match self {
99 Source::File { name, contents: _ } => write!(f, "{}", name),
100 Source::Unknown => write!(f, "<unknown>")
101 }
102 }
103}
104
105impl Default for Source {
106 fn default() -> Self {
107 Source::File {
108 name: String::new(),
109 contents: String::new()
110 }
111 }
112}
113
114impl PartialEq for Source {
115 fn eq(&self, other: &Self) -> bool {
116 match (self, other) {
117 (Self::Unknown, Self::Unknown) => true,
118 (
119 Self::File { name, contents: _ },
120 Self::File {
121 name: other_name,
122 contents: _
123 }
124 ) => name == other_name,
125 _ => false
126 }
127 }
128}
129
130#[derive(Debug, Clone, Eq)]
137pub struct Loc {
138 pub line: isize,
139 pub col: isize,
140 pub pos: isize,
141 pub source: Rc<Source>
142}
143
144impl Loc {
145 pub fn lines(&self, before: usize, after: usize) -> (Vec<String>, usize) {
152 self.source.lines(self.pos as usize, before, after)
153 }
154
155 pub fn make_invalid() -> Self {
156 Loc {
157 line: 0,
158 col: 0,
159 pos: 0,
160 source: Rc::new(Source::Unknown)
161 }
162 }
163
164 pub fn is_invalid(&self) -> bool {
165 let invalid = Loc::make_invalid();
166 self.line == invalid.line
167 && self.col == invalid.col
168 && self.pos == invalid.pos
169 }
170}
171
172impl Display for Loc {
173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174 write!(f, "{}:{}:{}", self.source, self.line, self.col)
175 }
176}
177
178impl Default for Loc {
179 fn default() -> Self {
180 Loc {
181 line: 1,
182 col: 1,
183 pos: 0,
184 source: Rc::new(Source::Unknown)
185 }
186 }
187}
188
189impl PartialEq for Loc {
190 fn eq(&self, other: &Self) -> bool {
191 self.line == other.line
192 && self.col == other.col
193 && self.pos == other.pos
194 && self.source.as_ref() == other.source.as_ref()
195 }
196}
197
198impl PartialOrd for Loc {
199 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
200 if self.source != other.source {
201 None
202 } else {
203 self.pos.partial_cmp(&other.pos)
204 }
205 }
206}
207
208#[derive(Debug, Default, PartialEq, Eq, Clone)]
214pub struct Region {
215 pub start: Loc,
218
219 pub end: Loc
222}
223
224pub struct LineSection {
228 pub start: isize,
230
231 pub end: isize
235}
236
237impl LineSection {
238 pub fn new(start: isize, end: isize) -> Self {
239 assert!(start <= end);
240 Self { start, end }
241 }
242
243 pub fn length(&self) -> usize {
244 (self.end - self.start) as usize
245 }
246}
247
248impl Region {
249 pub fn new(start: Loc, end: Loc) -> Region {
251 assert!(start <= end, "`Region::from`: `start` and `end` must from the same source and `end` must be at least after `start`.");
252 Region { start, end }
253 }
254
255 pub fn unit(start: Loc) -> Region {
260 let mut end = start.clone();
261 end.pos += 1;
262 end.col += 1;
263 Region::new(start, end)
264 }
265
266 pub fn source(&self) -> Rc<Source> {
268 self.start.source.clone()
269 }
270
271 pub fn start_line(&self) -> isize {
272 self.start.line
273 }
274
275 pub fn end_line(&self) -> isize {
276 self.end.line
277 }
278
279 pub fn find_intersection(
286 &self, lines: &[String], start_line: isize
287 ) -> Vec<LineSection> {
288 let mut result = vec![];
289 for (i, line) in lines.iter().enumerate() {
290 let actual_line = start_line + (i as isize);
291 if actual_line >= self.start_line()
292 && actual_line <= self.end_line()
293 {
294 let mut start_pos = 0;
295 let mut end_pos = line.len() as isize;
296
297 if actual_line == self.start_line() {
298 start_pos = self.start.col - 1;
299 }
300
301 if actual_line == self.end_line() {
302 end_pos = self.end.col - 1;
303 }
304
305 result.push(LineSection::new(start_pos, end_pos));
306 }
307 }
308 result
309 }
310}
311
312impl Display for Region {
313 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314 write!(f, "[{}, {})", self.start, self.end)
315 }
316}
317
318pub trait RegionProvider {
319 fn start(&self) -> Loc;
321
322 fn end(&self) -> Loc;
325
326 fn region(&self) -> Region {
328 Region::new(self.start(), self.end())
329 }
330}
331
332impl RegionProvider for Region {
333 fn start(&self) -> Loc {
334 self.start.clone()
335 }
336
337 fn end(&self) -> Loc {
338 self.end.clone()
339 }
340}