1use std::cmp;
2use std::fmt;
3use std::cell::RefCell;
4use std::str::Chars;
5use std::iter::Peekable;
6
7#[derive(Debug, Clone)]
8pub struct Cursor<'a> {
9 base: u32,
10 token: RefCell<String>,
11 chars: Peekable<Chars<'a>>,
12}
13
14impl<'a> Iterator for Cursor<'a> {
15 type Item = char;
16 #[inline(always)]
17 fn next(&mut self) -> Option<Self::Item> {
18 let ret = self.chars.next();
19 if let Some(ch) = ret {
20 self.token.borrow_mut().push(ch);
21 }
22 ret
23 }
24}
25
26impl<'a> Cursor<'a> {
27 pub fn new_file(name: &str, src: &'a str) -> Cursor<'a> {
28 SOURCE_MAP.with(|cm| {
29 let mut cm = cm.borrow_mut();
30 let span = cm.add_file(name, src);
31 let base = span.lo;
32 Cursor {
33 base,
34 token: RefCell::new(String::new()),
35 chars: src.chars().peekable(),
36 }
37 })
38 }
39
40 #[inline(always)]
41 pub fn peek(&mut self) -> Option<&char> {
42 self.chars.peek()
43 }
44
45 #[inline(always)]
46 pub fn leap(&mut self) {
47 self.chars.next();
48 }
49
50 #[inline(always)]
51 pub fn leap_until(&mut self, func: impl Fn(char) -> bool) {
52 while let Some(&ch) = self.chars.peek() {
53 if func(ch) {
54 break;
55 } else {
56 self.leap();
57 }
58 }
59 }
60
61 #[inline(always)]
62 pub fn get_token(&mut self) -> (String, Span) {
63 let lo = self.base as u32;
64 self.base += self.token.borrow().len() as u32;
65 let hi = self.base as u32;
66 (self.token.replace(String::new()), Span{ lo, hi } )
67 }
68}
69
70
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub struct LineColumn {
73 pub line: usize,
74 pub column: usize,
75}
76
77struct FileInfo {
78 name: String,
79 span: Span,
80 lines: Vec<usize>,
81}
82
83impl FileInfo {
84 fn offset_line_column(&self, offset: usize) -> LineColumn {
85 assert!(self.span_within(Span {
86 lo: offset as u32,
87 hi: offset as u32
88 }));
89 let offset = offset - self.span.lo as usize;
90 match self.lines.binary_search(&offset) {
91 Ok(found) => LineColumn {
92 line: found + 1,
93 column: 0,
94 },
95 Err(idx) => LineColumn {
96 line: idx,
97 column: offset - self.lines[idx - 1],
98 },
99 }
100 }
101
102 fn span_within(&self, span: Span) -> bool {
103 span.lo >= self.span.lo && span.hi <= self.span.hi
104 }
105}
106
107fn lines_offsets(s: &str) -> (usize, Vec<usize>) {
110 let mut lines = vec![0];
111 let mut total = 0;
112
113 for ch in s.chars() {
114 total += 1;
115 if ch == '\n' {
116 lines.push(total);
117 }
118 }
119 (total, lines)
120}
121
122
123struct SourceMap {
124 files: Vec<FileInfo>,
125}
126
127impl SourceMap {
128 fn next_start_pos(&self) -> u32 {
129 self.files.last().unwrap().span.hi + 1
134 }
135
136 fn add_file(&mut self, name: &str, src: &str) -> Span {
137 let (len, lines) = lines_offsets(src);
138 let lo = self.next_start_pos();
139 let span = Span {
140 lo,
141 hi: lo + (len as u32),
142 };
143
144 self.files.push(FileInfo {
145 name: name.to_owned(),
146 span,
147 lines,
148 });
149
150 span
151 }
152
153 fn fileinfo(&self, span: Span) -> &FileInfo {
154 for file in &self.files {
155 if file.span_within(span) {
156 return file;
157 }
158 }
159 panic!("Invalid span with no related FileInfo!");
160 }
161}
162
163thread_local! {
164 static SOURCE_MAP: RefCell<SourceMap> = RefCell::new(SourceMap {
165 files: vec![FileInfo {
168 name: "<unspecified>".to_owned(),
169 span: Span { lo: 0, hi: 0 },
170 lines: vec![0],
171 }],
172 });
173}
174
175#[derive(Clone, Copy, PartialEq, Eq)]
176pub struct Span {
177 pub(crate) lo: u32,
178 pub(crate) hi: u32,
179}
180
181impl Span {
182
183 pub fn source_name(&self) -> String {
184 SOURCE_MAP.with(|cm| {
185 let cm = cm.borrow();
186 let fi = cm.fileinfo(*self);
187 fi.name.clone()
188 })
189 }
190
191 pub fn start(&self) -> LineColumn {
192 SOURCE_MAP.with(|cm| {
193 let cm = cm.borrow();
194 let fi = cm.fileinfo(*self);
195 fi.offset_line_column(self.lo as usize)
196 })
197 }
198
199 pub fn end(&self) -> LineColumn {
200 SOURCE_MAP.with(|cm| {
201 let cm = cm.borrow();
202 let fi = cm.fileinfo(*self);
203 fi.offset_line_column(self.hi as usize)
204 })
205 }
206
207 pub fn join(&self, other: Span) -> Option<Span> {
208 SOURCE_MAP.with(|cm| {
209 let cm = cm.borrow();
210 if !cm.fileinfo(*self).span_within(other) {
212 return None;
213 }
214 Some(Span {
215 lo: cmp::min(self.lo, other.lo),
216 hi: cmp::max(self.hi, other.hi),
217 })
218 })
219 }
220
221 pub fn first_byte(self) -> Self {
222 Span {
223 lo: self.lo,
224 hi: cmp::min(self.lo.saturating_add(1), self.hi),
225 }
226 }
227
228 pub fn last_byte(self) -> Self {
229 Span {
230 lo: cmp::max(self.hi.saturating_sub(1), self.lo),
231 hi: self.hi,
232 }
233 }
234}
235
236impl fmt::Debug for Span {
237 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
238 return write!(f, "bytes({}..{})", self.lo, self.hi);
239 }
240}
241