moxy_token/source/
file.rs1use std::cell::RefCell;
2use std::collections::BTreeMap;
3use std::path::PathBuf;
4
5use super::Location;
6use crate::span::fallback::Span;
7
8#[derive(Debug)]
11pub struct Source {
12 path: Option<PathBuf>,
15
16 text: String,
18
19 span: Span,
21
22 lines: Vec<usize>,
24
25 char_to_byte: RefCell<BTreeMap<usize, usize>>,
27}
28
29impl Source {
30 pub(crate) fn new(start: usize, src: impl Into<String>) -> Self {
31 let text = src.into();
32 let mut lines = vec![0];
33 let mut total = 0usize;
34
35 for ch in text.chars() {
36 total += 1;
37
38 if ch == '\n' {
39 lines.push(total);
40 }
41 }
42
43 Self {
44 path: None,
45 text,
46 span: Span::new(start as u32, (start + total) as u32),
47 lines,
48 char_to_byte: RefCell::new(BTreeMap::new()),
49 }
50 }
51
52 pub fn from_file(path: impl Into<PathBuf>) -> std::io::Result<Self> {
53 let path = path.into();
54 let src = std::fs::read_to_string(&path)?;
55 let mut src = Self::new(0, src);
56
57 src.path = Some(path);
58 Ok(src)
59 }
60
61 pub fn path(&self) -> Option<&PathBuf> {
62 self.path.as_ref()
63 }
64
65 pub fn text(&self) -> &str {
66 &self.text
67 }
68
69 pub fn span(&self) -> Span {
70 self.span
71 }
72
73 pub fn range(&self, span: Span) -> std::ops::Range<usize> {
75 let r = span.byte_range();
76 self.byte(r.start)..self.byte(r.end)
77 }
78
79 pub fn slice(&self, span: Span) -> String {
81 self.text[self.range(span)].to_owned()
82 }
83
84 pub fn location(&self, i: usize) -> Location {
86 let index = i - self.span.byte_range().start;
87
88 match self.lines.binary_search(&index) {
89 Err(next) => Location::new(index, next - 1, index - self.lines[next - 1]),
90 Ok(line) => Location::new(index, line, 0),
91 }
92 }
93
94 pub fn byte(&self, i: usize) -> usize {
96 let index = i - self.span.byte_range().start;
97 let mut cache = self.char_to_byte.borrow_mut();
98
99 if let Some(byte_index) = cache.get(&index) {
100 return *byte_index;
101 }
102
103 let (ci, bi) = cache
104 .range(..=index)
105 .next_back()
106 .map(|(&char_index, &byte_index)| (char_index, byte_index))
107 .unwrap_or((0, 0));
108
109 let mut char_index = ci;
110 let mut byte_index = bi;
111
112 for ch in self.text[bi..].chars() {
113 if char_index == index {
114 cache.insert(index, byte_index);
115 return byte_index;
116 }
117
118 char_index += 1;
119 byte_index += ch.len_utf8();
120 }
121
122 cache.insert(index, byte_index);
123 byte_index
124 }
125}
126
127impl Default for Source {
128 fn default() -> Self {
129 Self {
130 path: None,
131 text: String::default(),
132 span: Span::default(),
133 lines: vec![0],
134 char_to_byte: RefCell::new(BTreeMap::default()),
135 }
136 }
137}