dpscript/util/
cursor.rs

1use super::{bits::HasBits, HasSpan};
2use crate::{Result, TokenizerError, UnnamedTokenizerError};
3use miette::{NamedSource, SourceOffset, SourceSpan};
4
5#[derive(Debug, Clone)]
6pub struct Cursor<T: HasBits + Clone, M = ()> {
7    src: T,
8    inner: Vec<T::Bit>,
9    pos: usize,
10    meta: M,
11}
12
13impl<T: HasBits + Clone, M: Default> Cursor<T, M> {
14    pub fn new(data: T) -> Self {
15        Self {
16            src: data.clone(),
17            inner: data.get_bits(),
18            pos: 0,
19            meta: M::default(),
20        }
21    }
22}
23
24impl<T: HasBits + Clone> Cursor<T, NamedSource<String>> {
25    pub fn new_from_src(file: impl AsRef<str>, code: impl AsRef<str>, data: T) -> Self {
26        Self {
27            src: data.clone(),
28            inner: data.get_bits(),
29            pos: 0,
30            meta: NamedSource::new(file, code.as_ref().into()),
31        }
32    }
33}
34
35impl<T: HasBits + Clone> Cursor<T, String> {
36    pub fn new_from_src(code: impl AsRef<str>, data: T) -> Self {
37        Self {
38            src: data.clone(),
39            inner: data.get_bits(),
40            pos: 0,
41            meta: code.as_ref().into(),
42        }
43    }
44}
45
46impl<T: HasBits + Clone, M> Cursor<T, M> {
47    pub fn is_empty(&self) -> bool {
48        self.inner.len() <= self.pos
49    }
50
51    pub fn has_next(&self) -> bool {
52        !self.is_empty()
53    }
54
55    pub fn next(&mut self) -> Option<T::Bit> {
56        self.pos += 1;
57        self.inner.get(self.pos - 1).cloned()
58    }
59
60    pub fn peek(&self) -> Option<T::Bit> {
61        self.inner.get(self.pos).cloned()
62    }
63
64    pub fn peek_ahead(&self, num: usize) -> Option<T::Bit> {
65        self.inner.get(self.pos + num).cloned()
66    }
67
68    pub fn skip(&mut self, num: usize) {
69        self.pos += num;
70    }
71
72    pub fn pos(&self) -> usize {
73        self.pos
74    }
75}
76
77impl<T: HasBits + Clone + FromIterator<T::Bit>, M> Cursor<T, M> {
78    pub fn peek_many(&self, start: usize, num: usize) -> Option<T> {
79        let mut parts = Vec::new();
80
81        for i in 0..num {
82            if let Some(bit) = self.peek_ahead(start + i) {
83                parts.push(bit);
84            } else {
85                return None;
86            }
87        }
88
89        Some(parts.iter().cloned().collect())
90    }
91}
92
93impl<T: HasBits + Clone> Cursor<T, String> {
94    pub fn source(&self) -> String {
95        self.meta.clone()
96    }
97}
98
99impl<B: Clone + HasSpan, T: HasBits<Bit = B> + Clone> Cursor<T, String> {
100    pub fn next_or_die(&mut self) -> Result<T::Bit> {
101        self.pos += 1;
102
103        match self.inner.get(self.pos - 1).cloned() {
104            Some(v) => Ok(v),
105            None => Err(UnnamedTokenizerError {
106                src: self.source(),
107                at: self.inner.get(self.pos - 2).clone().unwrap().get_span(),
108                err: "Unexpected end of file!".into(),
109            }
110            .into()),
111        }
112    }
113}
114
115impl<T: HasBits + Clone> Cursor<T, NamedSource<String>> {
116    pub fn source(&self) -> NamedSource<String> {
117        self.meta.clone()
118    }
119
120    pub fn next_or_die(&mut self, span: SourceSpan) -> Result<T::Bit> {
121        self.pos += 1;
122
123        match self.inner.get(self.pos - 1).cloned() {
124            Some(v) => Ok(v),
125            None => Err(TokenizerError {
126                src: self.source(),
127                at: span,
128                err: "Unexpected end of file!".into(),
129            }
130            .into()),
131        }
132    }
133}
134
135impl Cursor<String, String> {
136    pub fn new_from_code(data: impl AsRef<str>) -> Self {
137        let s = data.as_ref().to_string();
138
139        Self {
140            src: s.clone(),
141            inner: s.chars().collect(),
142            pos: 0,
143            meta: s,
144        }
145    }
146
147    fn find_line(&self) -> usize {
148        let mut lines = 0;
149
150        for item in &self.inner[0..self.pos] {
151            if *item == '\n' {
152                lines += 1;
153            }
154        }
155
156        lines
157    }
158
159    fn find_char(&self) -> usize {
160        let line = self.find_line();
161        let mut lines = 0;
162        let mut chars = 0;
163
164        for item in &self.inner[0..self.pos] {
165            if *item == '\n' {
166                lines += 1;
167            } else {
168                if line == lines {
169                    chars += 1;
170                }
171            }
172        }
173
174        chars
175    }
176
177    pub fn span(&self, length: usize) -> SourceSpan {
178        SourceSpan::new(
179            SourceOffset::from_location(&self.src, self.find_line() + 1, self.find_char()),
180            length,
181        )
182    }
183}
184
185impl Cursor<String, NamedSource<String>> {
186    pub fn new_from_code(file: impl AsRef<str>, data: impl AsRef<str>) -> Self {
187        let s = data.as_ref().to_string();
188
189        Self {
190            src: s.clone(),
191            inner: s.chars().collect(),
192            pos: 0,
193            meta: NamedSource::new(file, s),
194        }
195    }
196
197    fn find_line(&self) -> usize {
198        let mut lines = 0;
199
200        for item in &self.inner[0..self.pos] {
201            if *item == '\n' {
202                lines += 1;
203            }
204        }
205
206        lines
207    }
208
209    fn find_char(&self) -> usize {
210        let line = self.find_line();
211        let mut lines = 0;
212        let mut chars = 0;
213
214        for item in &self.inner[0..self.pos] {
215            if *item == '\n' {
216                lines += 1;
217            } else {
218                if line == lines {
219                    chars += 1;
220                }
221            }
222        }
223
224        chars
225    }
226
227    pub fn span(&self, length: usize) -> SourceSpan {
228        SourceSpan::new(
229            SourceOffset::from_location(&self.src, self.find_line() + 1, self.find_char()),
230            length,
231        )
232    }
233}