Skip to main content

macroonz_compiler/token/capture/cursor/
read.rs

1//! Generic mechanical reads over an already normalized captured-token sequence.
2//!
3//! This file knows token structure and nothing about the declaration written with it.
4//! A caller supplies exact words, chooses which operations compose one clause, and maps a typed mechanical refusal into its own diagnostic policy.
5
6use super::{
7    CaptureCursor, CaptureExpectation, CaptureReadIssue, CaptureReadRefusal, CapturedDelimiter,
8    CapturedInput, CapturedSpacing, CapturedTokenTree,
9};
10use crate::bounded::Bounded;
11
12impl CapturedInput {
13    /// Open a mechanical read cursor over the top-level captured sequence.
14    #[must_use]
15    pub fn cursor(&self) -> CaptureCursor<'_> {
16        CaptureCursor::over(self.trees())
17    }
18}
19
20impl<'tokens> super::CapturedFragment<'tokens> {
21    /// Open the generic mechanical cursor over this exact fragment.
22    #[must_use]
23    pub fn cursor(self) -> CaptureCursor<'tokens> {
24        let mut cursor = CaptureCursor::over(self.tokens);
25        cursor.end = self.end;
26        cursor
27    }
28}
29
30impl<'tokens> CaptureCursor<'tokens> {
31    /// Open the top-level cursor while keeping raw slice construction inside the capture owner.
32    pub(super) const fn over(tokens: &'tokens [CapturedTokenTree]) -> Self {
33        Self {
34            tokens,
35            next: 0,
36            end: None,
37        }
38    }
39
40    /// Whether this sequence has no token left to read.
41    #[must_use]
42    pub fn is_finished(&self) -> bool {
43        self.next == self.tokens.len()
44    }
45
46    /// Read the next ordinary word without advancing this cursor.
47    #[must_use]
48    pub(crate) fn next_word(&self) -> Option<&'tokens str> {
49        self.tokens.get(self.next).and_then(CapturedTokenTree::word)
50    }
51
52    /// Read the next captured token without advancing this cursor.
53    #[must_use]
54    pub(crate) fn next_token(&self) -> Option<&'tokens CapturedTokenTree> {
55        self.tokens.get(self.next)
56    }
57
58    /// Read one caller-defined shape and retain the exact captured run it consumed.
59    ///
60    /// The callback may consume no token where an empty exact Rust seat is lawful.
61    /// The returned fragment still belongs to this cursor's original captured sequence and retains its enclosing group boundary.
62    ///
63    /// # Errors
64    ///
65    /// Returns the callback's exact typed mechanical refusal without advancing past that refusal.
66    pub fn fragment<T>(
67        &mut self,
68        read: impl FnOnce(&mut Self) -> Result<T, CaptureReadRefusal>,
69    ) -> Result<(super::CapturedFragment<'tokens>, T), CaptureReadRefusal> {
70        let start = self.next;
71        let value = read(self)?;
72        let tokens = self
73            .tokens
74            .get(start..self.next)
75            .ok_or(CaptureReadRefusal {
76                issue: CaptureReadIssue::CursorRangeContradiction,
77                at: self.current_span(),
78            })?;
79        Ok((super::CapturedFragment::over(tokens, self.end), value))
80    }
81
82    /// Read any one captured token.
83    ///
84    /// # Errors
85    ///
86    /// Returns a typed missing-token refusal at the current sequence boundary.
87    pub fn token(&mut self) -> Result<&'tokens CapturedTokenTree, CaptureReadRefusal> {
88        self.take(|| CaptureExpectation::Token, |_| true)
89    }
90
91    /// Read one exact ordinary word.
92    ///
93    /// A raw identifier does not satisfy this operation because rawness is declaration material.
94    /// Use [`CaptureCursor::identifier`] where either identifier form is lawful.
95    ///
96    /// # Errors
97    ///
98    /// Returns a typed missing or unexpected-token refusal at the exact available span.
99    pub fn word(
100        &mut self,
101        expected: &str,
102    ) -> Result<&'tokens CapturedTokenTree, CaptureReadRefusal> {
103        self.take(
104            || CaptureExpectation::Word(expected.to_owned()),
105            |token| token.word() == Some(expected),
106        )
107    }
108
109    /// Read one ordinary or raw identifier token.
110    ///
111    /// The returned token retains which form it carried through [`CapturedTokenTree::word`] and [`CapturedTokenTree::raw_identifier`].
112    ///
113    /// # Errors
114    ///
115    /// Returns a typed missing or unexpected-token refusal at the exact available span.
116    pub fn identifier(
117        &mut self,
118    ) -> Result<(&'tokens CapturedTokenTree, &'tokens str), CaptureReadRefusal> {
119        let token = self.take(
120            || CaptureExpectation::Identifier,
121            |token| token.word().is_some() || token.raw_identifier().is_some(),
122        )?;
123        let ((Some(spelling), None) | (None, Some(spelling))) =
124            (token.word(), token.raw_identifier())
125        else {
126            return Err(CaptureReadRefusal {
127                issue: CaptureReadIssue::Unexpected(CaptureExpectation::Identifier),
128                at: Some(token.span()),
129            });
130        };
131        Ok((token, spelling))
132    }
133
134    /// Read one numeric literal spelling.
135    ///
136    /// # Errors
137    ///
138    /// Returns a typed missing or unexpected-token refusal at the exact available span.
139    pub fn number(
140        &mut self,
141    ) -> Result<(&'tokens CapturedTokenTree, &'tokens str), CaptureReadRefusal> {
142        let token = self.take(
143            || CaptureExpectation::Number,
144            |token| token.number().is_some(),
145        )?;
146        match token.number() {
147            Some(spelling) => Ok((token, spelling)),
148            None => Err(CaptureReadRefusal {
149                issue: CaptureReadIssue::Unexpected(CaptureExpectation::Number),
150                at: Some(token.span()),
151            }),
152        }
153    }
154
155    /// Read one punctuation seat with both character and adjacency stated.
156    ///
157    /// # Errors
158    ///
159    /// Returns a typed missing or unexpected-token refusal at the exact available span.
160    pub fn punctuation(
161        &mut self,
162        mark: char,
163        spacing: CapturedSpacing,
164    ) -> Result<&'tokens CapturedTokenTree, CaptureReadRefusal> {
165        self.take(
166            || CaptureExpectation::Punctuation { mark, spacing },
167            |token| match spacing {
168                CapturedSpacing::Alone => {
169                    token.punct() == Some(mark) && token.joint_punct().is_none()
170                }
171                CapturedSpacing::Joint => token.joint_punct() == Some(mark),
172            },
173        )
174    }
175
176    /// Read the two punctuation seats of `->`.
177    ///
178    /// # Errors
179    ///
180    /// Returns the exact first seat that is missing or disagrees.
181    pub fn thin_arrow(&mut self) -> Result<[&'tokens CapturedTokenTree; 2], CaptureReadRefusal> {
182        let dash = self.punctuation('-', CapturedSpacing::Joint)?;
183        let arrow = self.punctuation('>', CapturedSpacing::Alone)?;
184        Ok([dash, arrow])
185    }
186
187    /// Read the two punctuation seats of `=>`.
188    ///
189    /// # Errors
190    ///
191    /// Returns the exact first seat that is missing or disagrees.
192    pub fn fat_arrow(&mut self) -> Result<[&'tokens CapturedTokenTree; 2], CaptureReadRefusal> {
193        let equals = self.punctuation('=', CapturedSpacing::Joint)?;
194        let arrow = self.punctuation('>', CapturedSpacing::Alone)?;
195        Ok([equals, arrow])
196    }
197
198    /// Read one group and open a cursor over its captured members.
199    ///
200    /// A missing token inside the returned cursor points at this group token, which is the exact source boundary the capture retains for an empty or exhausted group.
201    ///
202    /// # Errors
203    ///
204    /// Returns a typed missing or unexpected-token refusal at the exact available span.
205    pub fn group(&mut self, delimiter: CapturedDelimiter) -> Result<Self, CaptureReadRefusal> {
206        let token = self.take(
207            || CaptureExpectation::Group(delimiter),
208            |token| token.group().is_some_and(|(found, _)| found == delimiter),
209        )?;
210        match token.group() {
211            Some((_, members)) => Ok(Self {
212                tokens: members,
213                next: 0,
214                end: Some(token.span()),
215            }),
216            None => Err(CaptureReadRefusal {
217                issue: CaptureReadIssue::Unexpected(CaptureExpectation::Group(delimiter)),
218                at: Some(token.span()),
219            }),
220        }
221    }
222
223    /// Read a sequence whose every member is followed by one standalone separator.
224    ///
225    /// Empty standing is deliberately left to the caller: this operation returns an empty [`Bounded`] where the sequence is empty.
226    /// The member reader owns one member's shape and meaning, while this operation owns cursor progress, the separator seat, and the declared member magnitude.
227    ///
228    /// # Errors
229    ///
230    /// Returns the member reader's refusal, an exact separator refusal, a nonadvancing-reader refusal, or a refusal at the first member beyond `LIMIT`.
231    pub fn trailing_separated<T, const LIMIT: usize>(
232        mut self,
233        separator: char,
234        mut read: impl FnMut(&mut Self) -> Result<T, CaptureReadRefusal>,
235    ) -> Result<Bounded<T, LIMIT>, CaptureReadRefusal> {
236        let mut members = Bounded::empty();
237        while !self.is_finished() {
238            let member_at = self.current_span();
239            if members.len() >= LIMIT {
240                return Err(CaptureReadRefusal {
241                    issue: CaptureReadIssue::SequenceUnbounded { limit: LIMIT },
242                    at: member_at,
243                });
244            }
245            let before = self.next;
246            let member = read(&mut self)?;
247            if self.next == before {
248                return Err(CaptureReadRefusal {
249                    issue: CaptureReadIssue::SequenceMemberDidNotAdvance,
250                    at: member_at,
251                });
252            }
253            self.punctuation(separator, CapturedSpacing::Alone)?;
254            members.try_push(member).map_err(|_| CaptureReadRefusal {
255                issue: CaptureReadIssue::SequenceUnbounded { limit: LIMIT },
256                at: member_at,
257            })?;
258        }
259        Ok(members)
260    }
261
262    /// Finish this sequence after every token has been consumed.
263    ///
264    /// # Errors
265    ///
266    /// Returns [`CaptureReadIssue::InputRemaining`] at the first unconsumed token.
267    pub fn finish(self) -> Result<(), CaptureReadRefusal> {
268        match self.tokens.get(self.next) {
269            Some(token) => Err(CaptureReadRefusal {
270                issue: CaptureReadIssue::InputRemaining,
271                at: Some(token.span()),
272            }),
273            None => Ok(()),
274        }
275    }
276
277    /// Read one token satisfying a mechanical expectation without advancing on disagreement.
278    fn take(
279        &mut self,
280        expected: impl FnOnce() -> CaptureExpectation,
281        accepts: impl FnOnce(&CapturedTokenTree) -> bool,
282    ) -> Result<&'tokens CapturedTokenTree, CaptureReadRefusal> {
283        let Some(token) = self.tokens.get(self.next) else {
284            return Err(CaptureReadRefusal {
285                issue: CaptureReadIssue::Missing(expected()),
286                at: self.end,
287            });
288        };
289        if !accepts(token) {
290            return Err(CaptureReadRefusal {
291                issue: CaptureReadIssue::Unexpected(expected()),
292                at: Some(token.span()),
293            });
294        }
295        self.next = self.next.saturating_add(1);
296        Ok(token)
297    }
298
299    /// The current token's span, or the enclosing group span at its end.
300    fn current_span(&self) -> Option<super::SpanHandle> {
301        self.tokens
302            .get(self.next)
303            .map(CapturedTokenTree::span)
304            .or(self.end)
305    }
306}
307
308impl CaptureReadRefusal {
309    /// Bind one compiler-owned higher grammar operation to an already established mechanical issue and span.
310    pub(crate) const fn projected(issue: CaptureReadIssue, at: Option<super::SpanHandle>) -> Self {
311        Self { issue, at }
312    }
313
314    /// The mechanical disagreement this read established.
315    pub const fn issue(&self) -> &CaptureReadIssue {
316        &self.issue
317    }
318
319    /// The exact producer span available at the refusal site.
320    ///
321    /// Root end of input has no token and therefore no span.
322    #[must_use]
323    pub const fn token(&self) -> Option<super::SpanHandle> {
324        self.at
325    }
326
327    /// Consume this refusal into its mechanical issue and exact available span.
328    pub(crate) fn into_parts(self) -> (CaptureReadIssue, Option<super::SpanHandle>) {
329        (self.issue, self.at)
330    }
331}