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